A friendly, object-oriented library for parsing and filtering CSV files with PHP.
This (above) is an example of how to use Coseva in the simplest form. However, it is much more capable than this. :)
Coseva (pronounced co•see•vah) is an abstraction library for making .csv
files easier to work with. But what makes it special is it allows you to have a clean separation of concerns in your data-filtering logic. This is one of the main points of Coseva, to give the developer the ability to keep unrelated logic separate from each other. In many cases, all data-filtering logic is contained in one big loop. This "spaghetti code" leads to a codebase that is increasingly difficult to maintain and update, and near impossible to read months later when you come back to it. But by breaking down your logic into smaller distinct chunks (Coseva calls these chunks "filters"), you can avoid many of the headaches that come with the everything-in-one-giant-loop approach.
The recommended way to install Coseva is through composer. Just create a composer.json file and run the composer install
command to install it:
{
"minimum-stability": "dev",
"require": {
"johnnyfreeman/coseva": "*"
}
}
Or, you can download the coseva.zip file and extract it.
There are a few things you should know before diving in.
The first thing, is that Coseva doesn't do anything other than parse a csv, and give you the results; no querying a database, no jumping on one foot, etc. That's left up to you.
The second thing is the order in which filters are run. The parser loops through the csv file, line by line, from top to bottom, and at each line it runs all filters in the same order they were registered in.
So if we were to register two filters like this:
<?php
// delete 1st column
$csv->filter(function($columns) {
unset($columns[0]);
return $columns;
});
// Capitalize first letter of 1st column (which used to be the 2nd column)
$csv->filter(0, function($column) {
return ucfirst($column);
});
First, the first column will be deleted from the array, then during execution of the second filter a "PHP Notice: Undefined offset" will be raised because the column no longer exists.
<?php
$csv = new CSV('path/to/file.csv');
// parse first column as date
$csv->filter(0, function($column1) {
return (new DateTime($column1))->format('Y-m-d H:i:s');
});
// split column five at every colon and serialize
$csv->filter(4, function($column5) {
return serialize(explode(':', $column5));
});
$csv->parse();
To instantiate the CSV
object, just pass the path to the .csv file to the CSV
constructor.
name | type | description |
---|---|---|
$filename | String | The file to read. |
$open_mode | String | The mode in which to open the file. See fopen() for a list of allowed modes. |
$use_include_path | Boolean | Whether to search in the include_path for filename. |
Returns CSV instance.
<?php
$csv = new CSV('path/to/file.csv');
This method allows you to register any number of filters on your CSV content. But there are two ways you can utilize this method.
The first method, you'll pass a column number and a callable, like so:
// convert data in column 2 to a `number` if it is numeric
$csv->filter(1, function($value) {
return is_numeric($value) ? (float) $value : $value;
});
// trim the whitespace around column 1
$csv->filter(0, 'trim');
// Format a numeric column to always display 2 decimals.
$csv->filter(1, 'number_format', 2);
The second method, you'll pass only a callback, like so:
// overwrite column three based on values from columns 1 and 2
$csv->filter(function($columns) {
if ($columns[0] == 'this' && $columns[1] == 'that') {
$columns[2] = 'something';
}
return $columns;
});
// reverse the order of all columns
$csv->filter('array_reverse');
name | type | description |
---|---|---|
$column | Integer | Optional: Zero-based column number. If this parameter is present the $callable will recieve the contents of the current column (as a string), and will receive the entire (array based) row otherwise. |
$callable | Callable | Callable receives either the current row (as an array) or the current column (as a string) as the first parameter. The callable must return the new filtered row or column. Note: You can also use any native PHP functions that permit one parameter and return the new value, like trim, htmlspecialchars, urlencode, etc. |
Returns the CSV
instance to allow method chaining.
<?php
// split column four at every colon and serialize
$csv->filter(3, function($column4) {
return serialize(explode(':', $column4));
});
// remove the first column from the results
$csv->filter(function($columns) {
unset($columns[0]);
return $columns;
});
This method will convert the csv to an array and run all registered filters against it.
name | type | description |
---|---|---|
$offset | Integer | Determines which row the parser will start on. Zero-based index. |
Returns the CSV
instance to allow method chaining.
<?php
// parse csv while executing any filters that may have been registered.
$csv->parse();
Use this to get the entire CSV in JSON format.
Returns a JSON String
.
<?php
// to JSON
echo $csv->toJSON();
This is a great way to display the filtered contents of the csv to you during the development process (for debugging purposes).
Returns an HTML String
.
<?php
// let's take a look
echo $csv->toTable();
We also allow for instances, preserving memory and maintaining reachability across scopes.
name | type | description |
---|---|---|
$filename | String | A readable filename to reference the instances by. The filename will be resolved to the absolute path, following symlinks, to improve chances of finding a matching instance. |
$open_mode | String | The mode in which to open the file. See fopen() for a list of allowed modes. |
$use_include_path | Boolean | Whether to search in the include_path for filename. |
<?php
use \Coseva\CSV;
// Create an instance of CSV.
$csv = CSV::getInstance('comma-separated-nonsense.csv');
// Fetch another one.
$dupe = CSV::getInstance('comma-separated-nonsense.csv');
// Parse the CSV.
$csv->parse();
// And display that parsed CSV as JSON.
echo $dupe->toJSON();
For convenience's sake, we created a packager so you can combine your script and CSV in one compressed and executable bundle. We make use of the Phar technology that has been available since 5.3.0.
The benefits are simple:
- Only one file to keep around
- Your data is compressed
- You keep all your scripting functionality alongside your CSV file
- No need for Coseva as an external library
- PHP 5.2 and above are able to execute it
NAME
packager - Combine Coseva, your script and CSV
SYNOPSIS
packager input.csv script.php [output.phar [alias.phar]]
DESCRIPTION
input.csv
The input file which holds the CSV data.
script.php
The PHP script which uses Coseva to do filtering and parsing.
It can make use of the automatically defined constant SOURCE_FILE
to succesfully target the CSV file for file operations.
Also, including / requiring Coseva is no longer needed, as the
package will do so when bootstrapping.
output.phar [optional]
The output location of the package.
alias.phar [optional]
The internal alias of the package. Really useful when wanting to
target the package from within your script.
E.g. "phar://alias.phar/input.csv"
packager should be able to run as it is. On some machines, certain flags may have to be set. No worries, though. Packager will automatically detect which of these flags need to be set and where you can find them.
On most machines, setting phar.readonly = Off
will suffice. For machines running suhosin, there is an instruction for
adding "phar" to the executor whitelist of suhosin when needed.