Well; after several wasted hours looking into what I believed to be a bug within the PHP core, I have figured it out. I want to post this in the hope that you, the reader, do not have to go through the stress I went through in attempting to resolve this issue (and almost restorting to a standards-defiant workaround).

I speak of creating a stream wrapper (I’m creating the datapack wrapper for the MyCustomBB project). When you call ftell() on the handle to the stream, the PHP core calls the stream_tell() function within the class defined for that wrapper (in this case, datapack::stream_tell). However, PHP only seems to call this function after you use fseek() on your stream. Until then, it caches the file pointer position itself. Okay, seems reasonable. However, unless your stream_seek() function returns a value of TRUE (or any non-false value), PHP ignores the fact that the function was called, and continues to cache it itself.

Okay, so what’s the problem? If you check the PHP documentation for the fseek() function, you will see the following quote for the return value:

Upon success, returns 0; otherwise, returns -1. Note that seeking past EOF is not considered an error.

Okay; to be complaint with this standard, my stream_seek() function returned a value of zero upon success, and -1 upon failure. Rather, it simply returned the value that was returned by fseek() (return fseek( $this->handle, $seek_pos);). However, 0 = FALSE. Therefore, PHP ignored it and would not call stream_tell() until I had stream_seek() return a value of TRUE upon success:

return ( fseek( $this->handle, $seek_pos ) == -1 ) ? FALSE : TRUE;

Then, it worked. You can imagine my irritation. However, in the PHP documentation, which I did overlook, it states that stream_seek() should indeed return TRUE on success. My bad, but very stupid of the PHP developers to do.

And just to think; I almost had to return the position via fseek() and use fseek( $handle, 0, SEEK_CUR ) in place of ftell().