About a year ago, a couple friends and I were working on a PHP-based web application for one of our classes. The web application needed to take in input from another website (the school’s calendar system), which provided data in CSV format. As a result, I became very familiar with PHP’s (lack of) CSV parsing functionality and there’s one tip in particular that I think is worth sharing.

PHP has two main functions for parsing CSV data: fgetcsv and str_getcsv. fgetcsv complements other functions like fopen, fsockopen, etc: it’s used to read data from files, similar to fread. In contrast, str_getcsv works on strings: there’s no file I/O necessary. This is much more appealing than having to perform low-level reads/writes to fetch data (especially when your data is coming from another website). Unfortunately, it only exists in PHP >= 5.3.0.

So, for our project, we took in data from a website (using an HTTP client class provided by the PHP framework we were using) and stored it in a string. But since we didn’t have PHP 5.3, we needed to turn the data into a file if we wanted to read from it. Luckily, a comment on PHP.net alerted us to a convenient way to “work around the issue”:

// Assume this variable contains CSV data from some external source, stored as a string
$my_csv_text = get_csv_from_internet();

// Open a memory "file" for read/write...
$f = fopen('php://temp', 'r+');
fwrite($f, $my_csv_text);

while (($data = fgetcsv($f)) !== FALSE)
    // do stuff with the data!

What the code does is it takes advantage of a PHP 5.1 feature: the ability to write to a temporary location (which, according to the PHP docs, is stored in memory unless the data is above a certain size, at which point it’s written to disk instead). That allows for the data to be read without any messy cleanup. The only disadvantage is slightly higher memory usage, since the entire CSV file is stored twice (once as a string in PHP, once after being written to php://temp). In addition, anyone using an earlier version than 5.1 or who wants to avoid the memory usage can just write to a file instead: the code should work exactly the same.