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.
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 );
}
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.
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