Skip to content

Instantly share code, notes, and snippets.

@amirkdv
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amirkdv/9764785 to your computer and use it in GitHub Desktop.
Save amirkdv/9764785 to your computer and use it in GitHub Desktop.
simple CSV r/w utility
<?php
// First pass at a small CSV r/w utility. Before hacking at this, consider https://packagist.org/search/?q=csv
/**
* extracts the list of fields of a given associative array,
* @param array[][] $data
* @return array[]
* @throws Exception
* if provided associative array is inconsistent
*/
function fields($data){
// extract field names from first item
if(empty($data)){
return array();
}
$sample=$data[0];
$fields=array();
foreach($sample as $key=>$value){
$fields[] = $key;
}
// check consistency
foreach($data as $datum){
// ensure datum does not have extra fields
foreach($datum as $key=>$value){
if (!in_array($key,$fields)){
$message = "the following item has an extra field '$key':\n" . print_r($datum,1);
throw new Exception($message);
}
}
// ensure datum is not missing fields
// array_key_exists returns TRUE if value is NULL, isset does not
foreach($fields as $field){
if (!array_key_exists($field, $datum)){
$message = "the following item does not have a field '$field':\n" . print_r($datum,1);
throw new Exception($message);
}
}
}
return $fields;
}
/**
* assembles CSV equivalent for given associative array
* @param array[][] $data
* @return string
* @throws Exception
* if provided associative array is inconsistent
*/
function write_csv($data){
$fields = fields($data);
// write data to string stream
$handle = fopen('data://text/plain;base64,','w');
fputcsv($handle,$fields,',', '"');
foreach($data as $datum){
fputcsv($handle, $datum, ',', '"');
}
// dump and close string stream
$output = stream_get_contents($handle,-1, 0);
fclose($handle);
return $output;
}
/**
* reads multiple lines of CSV data into associative arrays
* does not respect header/non-header
*
* @param string $string
* @return array[]|array[][]
*/
function read_csv_rows($string){
$data = array();
$handle = fopen('data://text/plain;base64,','w');
fwrite($handle, $string);
fseek($handle, 0);
while ( $item = fgetcsv($handle, 0, ',', '"', '\\') ){
$data[] = $item;
}
fclose($handle);
return $data;
}
/**
* reads CSV string including a header into an associative array
* @param string $string
* @return array[][]
* @throws Exception
* if provided CSV string is inconsistent
*/
function read_csv($string){
list($header,$data) = explode("\n", $string, 2);
// extract field names from header
$header_export = read_csv_rows($header);
$fields = $header_export[0];
// extract data from the rest
$data_export = read_csv_rows($data);
// assign labels to retrieved data:
$data = array();
foreach($data_export as $item){
$datum = array();
foreach($item as $key => $value){
if (!isset($fields[$key])){
$message = "The following item has extra fields:\n" . print_r($item,1);
throw new Exception($message);
}
$datum[$fields[$key]]= $value;
}
$data[] = $datum;
}
// check whether assembled data is consitent
fields($data);
return $data;
}
/**
* dummy testing utility to be killed in favor of PHPUnit ASAP.
*/
function test_case($json_data){
try {
$data = json_decode($json_data,true);
$csv = write_csv($data);
print $csv;
$ext = read_csv($csv);
if (print_r($data,1) === print_r($ext,1)){
print "success\n";
} else {
print "failure, mismatch\n";
}
} catch (Exception $e){
print "failure, caught Exception:\n";
print $e->getMessage();
}
}
$test= 'good.json';
print "running test $test, should succeed:\n";
test_case(file_get_contents($test));
print "---------\n";
$test= 'missing_field.json';
print "running test $test, should fail; missing field comment:\n";
test_case(file_get_contents($test));
print "---------\n";
$test= 'extra_field.json';
print "running test $test, should fail; extra field age:\n";
test_case(file_get_contents($test));
print "---------\n";
[
{
"id": 12,
"name": "Mary-Anne",
"comment": "Multiline\nis\nOK"
},
{
"id": "13",
"name": "John O'connor",
"comment": "Double Quotes like '\"' OK",
"age" : 42
},
{
"id": 14,
"name": "",
"comment": null
}
]
[
{
"id": 12,
"name": "Mary-Anne",
"comment": "Multiline\nis\nOK"
},
{
"id": "13",
"name": "John O'connor",
"comment": "Double Quotes like '\"' OK"
},
{
"id": 14,
"name": "",
"comment": null
}
]
[
{
"id": 12,
"name": "Mary-Anne",
"comment": "Multiline\nis\nOK"
},
{
"id": "13",
"name": "John O'connor",
"comment": "Double Quotes like '\"' OK"
},
{
"id": 14,
"name": ""
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment