News Archive
PhpRiot Newsletter
Your Email Address:

More information

Watch-out PHP 5.3.7+ is about.. and the is_a() / __autoload() mess.

Note: This article was originally published at Planet PHP on 2 September 2011.
Planet PHP
Article originally from rooJSolutions blog
Well, for the first time in a very long while I had to post to the PHP core developers list last week,AunfortunatelyAthe result of which was not particulary usefull.
The key issue was that 5.3.7 accidentally broke is_a() for a reasonably large number of users.AUnfortunatelyAthe fixup release 5.3.8 did not address this 'mistake', and after a rather fruitless exchange I gave up trying to persuade the group (most people on mailing list), that reverting the change was rather critical (at least pierre supported reverting it in the 5.3.* series).
Anyway, what's this all about, basically if you upgrade to any of these versions and
a) use __autoload()Aorb) any of your code calls is_a() on a string,A
you will Avery likely get strange failures..

The change in detail.

in all versions of PHP since 4.2 the is_a signature looked like this
bool is_a (object $object, string $class_name)
As a knock on effect from fixing a bug with is_subclass_of, somebody thought it was a good idea to make the two functions signature consistant, so in 5.3.7+ the signature is now
bool is_a (mixed $object_or_string, string $class_name)
And to make matters worse, that change to the first 'object_or_string', will also call the autoloader if the class is not found.

How is_a() has been used in the past.

On the face of this, it would not seem like a significant change, however, you have to understand the history of is_a(), and why it was introduced. In the early days of PEAR (before PHP 4.2) there was a method called PEAR::isError($mixed), which contained quite a few tests to check if the $mixed was an object, and was an instance of 'PEAR_Error'. A while after PHP 4.2 was released, this was changed to use this new wonderfull feature, and basically became return is_a($mixed, 'PEAR_Error').
Since PEAR existed before exceptions (and is still a reasonable pattern to handle errors), It became quite common practice to have returns from methods which looked like this.
@return {String|PEAR_Error} $mixed Areturn some data..
So the callee would check the return using PEAR::isError(), or quite often just is_a($ret,'PEAR_Error'), if you knew that the PEAR class might not have been loaded.A
So now comes the change and let's see what happens.

The __autoload() issue.

Personally I never use __autoload, it's the new magic_quotes for me, making code unpredicatable and difficult to follow (read the post about require_once is part of your documentation). But anyway, each to their own, and for the PEAR packages I support I will usually commit any reasonable change that helps out people who are using autoload.
So there are users out there using autoload with my PEAR packages, as I quickly found last week. Quite a few of these packages use the is_a() pattern, and the users who had implemented __autoload() had very smartly decided that calling autoload with a name of a class that could not or did not exist was a serious error condition, and they either died, or threw exceptions.
Unfortunatly, since is_a() was sending all of the string data it got straight into __autoload(), this happened rather a lot. Leading to a run around hunt for all calls to is_a(), and code changes being put it to ensure that it never puts a string in the first argument.

The is_a(string) issue

While I'm not likely to see the autoload issue on my code, I'm not sure I really appreciate having to fix it so quickly without a timetable to change it. The other change that may cause random, undetectable bugs is the accepting a string.
imagine this bit of code
function nextTokString() {A
A A if (!is_string($this-tok[$this-pos])) {
return PEAR::raiseError('....')
A A }
A A return $this-tok[$this-pos++];
... some code..$tok =$this-nextTokString()
if (is_a($tok,'PEAR_Error')) {
A A return $tok;
}... do stuff with string.
Now what happens if the token is 'PEAR_Error', is_a() will now return true. The big issue with t

Truncated by Planet PHP, read more at the original (another 1644 bytes)