Class TM::MyCSV by Thiemo Mättig
Version 2009-09-02

A text file based database complement.

This class handles standard CSV or TXT text files as they where database tables. It supports most benefits of both SQL tables and PHP arrays. It doesn't need a real database management system nor does it require any knowlege of the SQL language. It hides all filesystem functions so you don't have to deal with file pointers, field delimiters, escape sequences and so on. Because it uses the widespreaded standard CSV file format you are able to create, read and update the tables using any spreadsheet software (e.g. Excel). It supports user defined table sort similar to ORDER BY, auto incremented ID numbers, limitation and joins similar to LIMIT and LEFT OUTER JOIN, it's binary safe (uses work arounds for all known fgetcsv() related bugs) and lots more.

File format restrictions by design ("it's not a bug, it's a feature"): See MyCSV(), dump(), limit() or join() for some examples.

Don't hesitate to report bugs or feature requests.

Variable Summary
data -- Two dimensional associative array containing all the table row data.
delimiter -- Field delimiter.
fields -- Array containing all the table field names.

Constructor Summary
MyCSV -- Reads a CSV file and returns it as a MyCSV object.

Method Summary
add_field -- Adds a new field (column) to the table.
count -- Gets the number of rows in the table.
data -- Gets a table row including their ID number.
data_seek -- Moves the internal row pointer to the specified row number.
delete -- Deletes a table row specified by the id.
drop_field -- Deletes a field/column from the table.
drop_table -- Clears the table.
dump -- Dumps the table to screen.
each -- Gets the current data row and increases the internal pointer.
end -- Sets the internal pointer to the last data row.
exists -- Checks if the CSV file for this table already exists.
export -- Returns a complete CSV dump of the table.
fetch_assoc -- Gets the current data row and increase the internal pointer.
first -- Gets the first ID number from the table.
id_exists -- Checks if the data row specified by the ID exists.
ids -- Gets an array containing all the IDs of the table.
insert -- Inserts a new table row using the next free auto incremented ID number.
insert_id -- Gets the ID generated from the previous insert() call.
is_writeable -- Checks if the CSV file for this table is writeable.
join -- Performs a left outer join with another table.
krsort -- Sorts the table rows by ID in reverse order.
ksort -- Sorts the table rows by ID.
last -- Gets the last ID number used in the table.
limit -- Limits the number of rows to be fetched.
max -- Gets the biggest ID number used in the table.
min -- Gets the smallest ID number used in the table.
next -- Gets the next ID number.
num_rows -- Gets the number of rows in the table.
prev -- Gets the previous ID number.
rand -- Picks one or more random ID numbers out of the table.
reset -- Sets the internal pointer to the first data row.
row_exists -- Looks if a data row is already in the table.
seek -- Sets the internal pointer to the data row specified by an ID or offset.
sort -- Orders the table rows by one or more columns.
tablename -- Gets the table name without the default .csv file extension.
update -- Updates a table row with some new field/value pairs.
write -- Rewrites the CSV table file or creates a new one.

Variable Detail

data

Two dimensional associative array containing all the table row data.

array $data

See also data(), each().

delimiter

Field delimiter.

string $delimiter

The field delimiter for separating values in the CSV file. Default is "," (default CSV style). If not, the class tries to use ";" (European/German CSV style), "\t" (tabulator separated values), "\0", "|", "&" (URI encoded/parameter style), ":" (Unix /etc/passwd style) and " " (log file style). Normaly you don't have to touch this variable. Simply choose your delimiter when creating your initial CSV file.

fields

Array containing all the table field names.

array $fields

First have to be "id".

See also add_field(), insert().

Constructor Detail

MyCSV

Reads a CSV file and returns it as a MyCSV object.

MyCSV MyCSV ( [ string tablename [, int length]])

Reads a table into a new MyCSV object. The file name may be entered with or without the .csv file extension. If the file does not exist it will be created when calling write(). Set length to the maximum number of bytes per row you expect (as you did in fgetcsv()). Default is 10000 bytes per line. Setting this to 1000 may speed up the method if you'r sure there is no longer line.

For example, create a file called table.csv with the following content and call the script below.

id,value
3,Example
4,Another value
7,Blue
<?php
require_once("MyCSV.class.php");
$table = new MyCSV("table");
while ($row = $table->each()) {
    echo $row['id'] . " is " . $row['value'] . "<br>";
}
?>

Method Detail

add_field

Adds a new field (column) to the table.

bool add_field ( string field [, string afterField])

Returns false on failure, e.g. if the field already exists.

See also insert(), drop_field().

count

Gets the number of rows in the table.

int count ( void)

This is an alias for num_rows().

data

Gets a table row including their ID number.

array data ( mixed id)

Returns false if the row does not exist.

data_seek

Moves the internal row pointer to the specified row number.

bool data_seek ( int row_number)

This is an alias for seek(row_number, SEEK_SET).

delete

Deletes a table row specified by the id.

void delete ( [ mixed id])

Deletes a table row specified by the id. Deletes all rows if no id is given.

drop_field

Deletes a field/column from the table.

bool drop_field ( string field)

Returns false on failure, e.g. if field does not exists. Rewinds the internal array pointer to the first element on success.

drop_table

Clears the table.

void drop_table ( void)

Remove all columns and all fields too.

dump

Dumps the table to screen.

void dump ( void)

Example:

<?php
require_once("MyCSV.class.php");
$table = new MyCSV("people");
$table->insert(array('name' => "Adam", 'age'  => 23));
$table->insert(array('name' => "Bill", 'age'  => 19));
echo "<pre>";
$table->dump();
?>

See also export().

each

Gets the current data row and increases the internal pointer.

array each ( void)

See MyCSV() for an example.

end

Sets the internal pointer to the last data row.

array end ( void)

Returns the last data row.

See also reset(), last().

exists

Checks if the CSV file for this table already exists.

bool exists ( void)

export

Returns a complete CSV dump of the table.

string export ( void)

See also write(), dump().

fetch_assoc

Gets the current data row and increase the internal pointer.

array fetch_assoc ( void)

This is an alias for each().

first

Gets the first ID number from the table.

int first ( void)

This depends on how's the table sorted and isn't identical to min() in all cases.

See also last(), prev(), reset().

id_exists

Checks if the data row specified by the ID exists.

bool id_exists ( mixed id)

See also row_exists().

ids

Gets an array containing all the IDs of the table.

array ids ( void)

See also min(), max(), first(), last(), prev(), next(), rand().

insert

Inserts a new table row using the next free auto incremented ID number.

void insert ( array data)

insert_id

Gets the ID generated from the previous insert() call.

int insert_id ( void)

is_writeable

Checks if the CSV file for this table is writeable.

bool is_writeable ( void)

join

Performs a left outer join with another table.

void join ( array rightTable, string foreignKey)

The tables are merged using a foreign key of the left table and the primary key of the right table. This adds temporary columns to the left table (temporary means, they aren't stored using write()). A slightly complex example:

echo "<pre>";
$rightTable = new MyCSV();
$rightTable->insert(array('id' => 7, 'color' => "red"));
$rightTable->insert(array('id' => 8, 'color' => "yellow"));
$rightTable->dump();
echo "\n";
$leftTable = new MyCSV();
$leftTable->insert(array('thing' => "Table", 'color_id' => 7));
$leftTable->insert(array('thing' => "Chair", 'color_id' => 8));
$leftTable->insert(array('thing' => "Lamp", 'color_id' => 7));
$leftTable->dump();
echo "\n";
$leftTable->join($rightTable, "color_id");
while ($row = $leftTable->each()) {
    echo $row['thing'] . " is " . $row['color'] . "\n";
}

krsort

Sorts the table rows by ID in reverse order.

void krsort ( [ int sort_flags])

This is identical to sort("id DESC") but a bit faster.

ksort

Sorts the table rows by ID.

void ksort ( [ int sort_flags])

This is identical to sort("id") but a bit faster.

last

Gets the last ID number used in the table.

int last ( void)

This depends on how's the table sorted and isn't identical to max() in all cases.

See also first(), next(), end().

limit

Limits the number of rows to be fetched.

bool limit ( [ int rows [, mixed id [, int whence]]])

Use limit(2) to fetch the first two rows only when calling each() (or fetch_assoc()). Use limit(2, $id) to fetch the next two rows, where $id is calculated using first() for the first page and using next($id, 2), next($id, 4) and so on for all other pages. Example:

$table = new MyCSV("table");
for ($i = 10; $i < 21; $i++) {
  $table->insert(array('text' => "Text $i"));
}
// Order the table first because limit() depends on this.
$table->sort("text DESC");
// Limit to 5 rows starting from a specific id.
$rows = 5;
$id = isset($_REQUEST['id']) ? $_REQUEST['id'] : $table->first();
$table->limit($rows, $id);
while ($row = $table->each()) {
  echo "ID $row[id]: $row[text]<br>";
}
// Calculate and display the link targets for paging.
$first = $table->first();
$prev  = $table->prev($id, $rows);
$next  = $table->next($id, $rows);
$last  = $table->prev($table->last(), ($table->count() - 1) % $rows);
if (strcmp($first, $id)) echo "<a href=\"$PHP_SELF?id=$first\">First</a> ";
if ($prev)               echo "<a href=\"$PHP_SELF?id=$prev\">Prev</a> ";
if ($next)               echo "<a href=\"$PHP_SELF?id=$next\">Next</a> ";
if (strcmp($last, $id))  echo "<a href=\"$PHP_SELF?id=$last\">Last</a>";
Call limit() (or limit(0) or something like that) to reset the limitation.

Warning! The limitation has no effect on delete(), update() and so on! All following method calls like sort() or join() that sets or resets the internal pointer will change the starting ID (but not the number of rows) set by limit().

See also seek().

max

Gets the biggest ID number used in the table.

int max ( void)

This is often the same as insert_id() which returns the last inserted ID. But unlike that, max() doesn't depend on a previous call of insert().

min

Gets the smallest ID number used in the table.

int min ( void)

Typically, this is 1.

next

Gets the next ID number.

int next ( mixed id [, int offset])

Use offset to get another ID near to the row specified by id. Default is 1 (one forward). Returns false if there is no row at this position.

See also prev(), last().

num_rows

Gets the number of rows in the table.

int num_rows ( void)

See also count().

prev

Gets the previous ID number.

int prev ( mixed id [, int offset])

Use offset to get another ID near to the row specified by id. Default is 1 (one backward). Returns false if there is no row at this position.

See also next(), first().

rand

Picks one or more random ID numbers out of the table.

int rand ( [ int num_req])

See also ids().

reset

Sets the internal pointer to the first data row.

array reset ( void)

Returns the first data row.

See also end(), each(), first().

row_exists

Looks if a data row is already in the table.

bool row_exists ( array search)

See also id_exists().

seek

Sets the internal pointer to the data row specified by an ID or offset.

bool seek ( [ mixed id [, int whence]])

If whence is left out, seek jumps to a specific ID (default).

whence may be SEEK_SET to set an absolute position counted from the start of the table, SEEK_CUR for a relative position or SEEK_END for an absolute position counted from the end of the table. The behaviour of these options is identical to fseek(). Keep in mind that id represents an offset instead of a row ID in these cases. Example:

$table = new MyCSV("table");
$table->insert(array('id' => 3)); // 1st row
$table->insert(array('id' => 7)); // 2nd row
$table->seek(1, SEEK_SET); // Jump to 2nd row
$row = $table->fetch_assoc();
echo $row['id']; // Output: 7
$table->seek(7); // Jump to 2nd row

See also limit().

sort

Orders the table rows by one or more columns.

void sort ( mixed sort_flags)

Sorting order flags:

Sorting type flags: Special condition flag: SORT_NULL - Move empty elements to the end.

No two sorting flags of the same type can be specified after each field. Some examples:
setlocale(LC_ALL, "de_DE@euro", "de_DE", "deu_deu");
$table->sort("a, b DESC");
$table->sort("a b DESC"); // Same as above
$table->sort("a", "b", SORT_DESC); // Same as above
$table->sort("a SORT_LOCALE_STRING SORT_NULL b SORT_NULL");
$table->sort("a SORT_NAT, b SORT_NAT, c");

tablename

Gets the table name without the default .csv file extension.

string tablename ( void)

The path returned can be used in MyCSV() without any change. Directories are not removed from the string, if present.

update

Updates a table row with some new field/value pairs.

bool update ( array data [, mixed id])

Examples:

$table->update(array(...), 3);
$table->update(array('id' => 3, ...));
$table->update(array('id' => 7, ...), 3); // Moves ID 3 to ID 7

write

Rewrites the CSV table file or creates a new one.

bool write ( [ string tablename [, string delimiter]])

write() closes the file when done.

The files created are binary-safe and compatible with any external spread sheet software (e.g. Excel) with a few exceptions:

That's what I call "smart backslashes". You don't need to know about this if you'r not using external software to modify your CSV files. Due to the replacements described above, the class is able to process any binary data. MyCSV() knows about these rules and undo the replacements immediatelly.

Binary safety tested with the following PHP versions: 4.3.1, 4.3.3, 4.3.5, 4.3.9, 4.3.10, 4.4.0, 5.0.4.

Documentation generated by TM::PHPDoc