Object Oriented File Access in PHP

Object-oriented file access (OOFA) is a programming paradigm that uses objects to represent files and file operations. This approach provides a more modular and reusable way to work with files, as it allows you to encapsulate file-related functionality within objects. OOFA can also make it easier to manage complex file systems and to develop applications that need to interact with multiple types of files.

What if you really want to test your file processing classes without having real files? Enter SplFileObject and its parent SplFileInfo.

SplFileObject and SplFileInfo provide an object-oriented interface to the file system, providing a wrapper for many of the  low-level file system calls. The wrapper can be overwritten and replaced with a test double in your unit tests.

Getting file info

Let’s see how testing becomes easier. Given we have this example function that accesses the file system:

function checkFileAge( string $name ) {
    if( time() - filemtime( $name ) > 3600 ) {
       throw new FilecheckException( 'File is too old!' );
    }
}

For testing this function, you’d have to create a two files with different time stamps. If you’re using SplFileInfo instead, you’ll be able to pass a test double:

function checkFileAge( SplFileInfo $file ) {
    if( time() - $file->getMTime() > 3600 ) {
       throw new FilecheckException( 'File is too old!' );
    }
}

The test could look like this:

function testWhenFileIsTwoDaysOldExceptionIsThrown() {
    $file = $this->getMockBuilder()
        ->disableOriginalConstructor()
        ->getMock();
    $file->method( 'getMTime' )->willReturn( time() - 7200 );
    $this->expectException( FilecheckException::class );
    checkFileAge( $file );
}

Reading files

Almost all the low-level file system calls for getting content out of files can be accessed with an object-oriented API:

$file = new SplFileObject( 'file.txt' );
$char = $file->fgetc();
$file->fseek(0);
$line = $file->fgets();

SplFileObject also implements IteratorInterface, so you can read a file line by line. So instead of

function lameEncrypt( string $name ) {
    foreach( file( $name ) as $line ) {
        echo str_rot13( $line );
    }
}

you do

function lameEncrypt( SplFileObject $file ) {
    foreach( $file as $line ) {
        echo str_rot13( $line );
    }
}

Now you can pass in any object that implements Traversable in your unit tests without the need for real files.

Iterating over CSV data

You can even iterate over CSV data instead of using fgetcsv:

$file = new SplFileObject( 'file.txt' );
$file->setCsvControl( ';', '"' );
$file->setFlags( \SplFileObject::READ_CSV | \SplFileObject::READ_AHEAD |
    \SplFileObject::SKIP_EMPTY | \SplFileObject::DROP_NEW_LINE );
foreach( $file as $row ) {
    echo $row[0] . ' --> ' . $row[3] . "\n";
 }

Using an iterator has the additional benefit of being able to manipulate your data further by wrapping the iterator in other iterator classes. Imagine combining several CSV files with AppendIterator, using only valid rows with a FilterIterator and limiting the amount of rows with a LimitIterator!


Using SplFileObject and SplFileInfo makes your code more testable and adds all the possibilities of iterators, all without adding any new libraries.

Thanks for reading !!!

#php #programming

Object Oriented File Access in PHP
10.55 GEEK