Last Updated: February 25, 2016
·
549
· cspray

Casting SplFile* objects as strings

There's a tl;dr at the bottom if you can't be bothered to read

The SplFile* implementations provide an object oriented API to work with the filesystem in PHP. At times these can be very useful constructs for easily working with and manipulating files.

However, the behavior when casting these objects to a string can only best be described as "undefined". First, let's take a look at the PHP docs for SplFileInfo::__toString & SplFileObject::__toString. So, SplFileInfo is supposed to return the path of the file, completely reasonable behavior and a pretty awesome use case. SplFileObject, on the other hand, is aliased to…current()? In case you aren't familiar with iterators this is a method called to retrieve the current element the iterator is pointing to. In the case of a SplFileObject this is the current line of the file being pointed at.

So, let's take a look at first casting both of these types of objects into strings without worrying about iteration. We want to take a look at a lot of language versions and the best place to do that is 3v4l.org. Here's the link to the simple string casting example: http://3v4l.org/rGkpb

As we can tell from the example SplFileInfo does exactly what it is supposed to do in all versions, print out the path to the file, since it was introduced in 5.1.0. However, take a look at the output of SplFileObject. For those of you who haven't clicked on the link here's the output of the casting:

For versions 5.1.2 - 5.3.21, 5.4.0 - 5.4.6:

SplFileObject: /in/rGkpb
SplFileInfo: /in/rGkpb

For versions 5.3.22 - 5.3.24, 5.4.7 - 5.5.0beta2:

SplFileObject: <?php

SplFileInfo: /in/rGkpb

In this example the versions 5.3.22-5.3.24 and 5.4.7-5.5.0beta2 are the only versions to implement the object according to docs. The other versions are outputting the implementation of SplFileInfo. Weird to say the least.

However, since we're dealing with the current() method as the result of casting SplFileObject to a string we also must understand how this behavior works with iterating. So, let's take a look at that script: http://3v4l.org/a7YkN

I won't paste any of the output here as it is a tad bit longer but this is even stranger behavior. There are four different types of output depending on versions.

  • 5.1.0 = gibberish that changes every time
  • 5.1.1 = also gibberish that changes every time but different gibberish than 5.1.0
  • 5.1.2 - 5.3.21, 5.4.0 - 5.4.6 = the current file path
  • 5.3.22 - 5.3.24, 5.4.7 - 5.5.0beta2 = expected output, current line of file

We can tell that all the implementations listed, except the last set, do not properly implement the __toString method according to docs by this example: http://3v4l.org/pmTmR

Clearly casting the SplFileObject to a string should be highly discouraged. The behavior is simply too varied, even in same minor language versions. In short, do not cast the SplFileObject to a string!

What about the SplFileInfo object? Its behavior is pretty defined and could be used right? Well, no. You see the SplFileObject, and all its wackiness, can be passed as if it were an SplFileInfo object; because it is an SpleFileInfo object. So, to use the SplFileInfo::__toString method properly you have to add a bunch of boilerplate to check that you're actually using SplFileInfo and not SplFileObject. Not worth it. Use the SplFileInfo::getPathName method instead.

tl;dr

Do NOT cast the SplFile* objects to a string. The behavior is undefined at best and you will get bit.