PHP 5.3 bug or changed feature??

php.internals

Frank M. Kromann

18 years ago
Hello Everyon, Casting a SimpleXML object to an array gives different results in PHP 5.2.5 and PHP 5.3-dev. Source: $xml = simplexml_load_file("sample.xml"); foreach($xml->column as $column) { var_dump($column); var_dump((array)$column); } sample.xml ?xml version="1.0"?> <cpdata> <column name="ENTERTAINMENT"> <module>cv</module> <module>entsimp</module> </column> <column name="SEAT CONTROL"> <module>pp</module> <module>sc</module> </column> </cpdata> PHP 5.2 output: object(SimpleXMLElement)#4 (2) { ["@attributes"]=> array(1) { ["name"]=> string(13) "ENTERTAINMENT" } ["module"]=> array(2) { [0]=> string(2) "cv" [1]=> string(7) "entsimp" } } array(2) { ["@attributes"]=> array(1) { ["name"]=> string(13) "ENTERTAINMENT" } ["module"]=> array(2) { [0]=> string(2) "cv" [1]=> string(7) "entsimp" } } object(SimpleXMLElement)#5 (2) { ["@attributes"]=> array(1) { ["name"]=> string(12) "SEAT CONTROL" } ["module"]=> array(2) { [0]=> string(2) "pp" [1]=> string(2) "sc" } } array(2) { ["@attributes"]=> array(1) { ["name"]=> string(12) "SEAT CONTROL" } ["module"]=> array(2) { [0]=> string(2) "pp" [1]=> string(2) "sc" } } PHP 5.3 output: object(SimpleXMLElement)#4 (2) { ["@attributes"]=> array(1) { ["name"]=> string(13) "ENTERTAINMENT" } ["module"]=> array(2) { [0]=> string(2) "cv" [1]=> string(7) "entsimp" } } array(1) { ["module"]=> array(2) { [0]=> string(2) "cv" [1]=> string(7) "entsimp" } } object(SimpleXMLElement)#5 (2) { ["@attributes"]=> array(1) { ["name"]=> string(12) "SEAT CONTROL" } ["module"]=> array(2) { [0]=> string(2) "pp" [1]=> string(2) "sc" } } array(1) { ["module"]=> array(2) { [0]=> string(2) "pp" [1]=> string(2) "sc" } } Not that the attributes are gone when SimpleXML objects are converted in PHP 5.3. Is this a bug or a feature change? - Frank

Marco Kaiser

18 years ago
Hi Frank, please open a bugreport about this issue. This would start the internal process of verifying this. -- Marco On Dec 7, 2007 11:09 PM, Frank M. Kromann <frank@kromann.info> wrote:
> Hello Everyon, > > Casting a SimpleXML object to an array gives different results in PHP > 5.2.5 and PHP 5.3-dev. > > Source: > > $xml = simplexml_load_file("sample.xml"); > > foreach($xml->column as $column) { > var_dump($column); > var_dump((array)$column); > } > > sample.xml > > ?xml version="1.0"?> > <cpdata> > <column name="ENTERTAINMENT"> > <module>cv</module> > <module>entsimp</module> > </column> > <column name="SEAT CONTROL"> > <module>pp</module> > <module>sc</module> > </column> > </cpdata> > > PHP 5.2 output: > object(SimpleXMLElement)#4 (2) { > ["@attributes"]=> > array(1) { > ["name"]=> > string(13) "ENTERTAINMENT" > } > ["module"]=> > array(2) { > [0]=> > string(2) "cv" > [1]=> > string(7) "entsimp" > } > } > array(2) { > ["@attributes"]=> > array(1) { > ["name"]=> > string(13) "ENTERTAINMENT" > } > ["module"]=> > array(2) { > [0]=> > string(2) "cv" > [1]=> > string(7) "entsimp" > } > } > object(SimpleXMLElement)#5 (2) { > ["@attributes"]=> > array(1) { > ["name"]=> > string(12) "SEAT CONTROL" > } > ["module"]=> > array(2) { > [0]=> > string(2) "pp" > [1]=> > string(2) "sc" > } > } > array(2) { > ["@attributes"]=> > array(1) { > ["name"]=> > string(12) "SEAT CONTROL" > } > ["module"]=> > array(2) { > [0]=> > string(2) "pp" > [1]=> > string(2) "sc" > } > } > > PHP 5.3 output: > > object(SimpleXMLElement)#4 (2) { > ["@attributes"]=> > array(1) { > ["name"]=> > string(13) "ENTERTAINMENT" > } > ["module"]=> > array(2) { > [0]=> > string(2) "cv" > [1]=> > string(7) "entsimp" > } > } > array(1) { > ["module"]=> > array(2) { > [0]=> > string(2) "cv" > [1]=> > string(7) "entsimp" > } > } > object(SimpleXMLElement)#5 (2) { > ["@attributes"]=> > array(1) { > ["name"]=> > string(12) "SEAT CONTROL" > } > ["module"]=> > array(2) { > [0]=> > string(2) "pp" > [1]=> > string(2) "sc" > } > } > array(1) { > ["module"]=> > array(2) { > [0]=> > string(2) "pp" > [1]=> > string(2) "sc" > } > } > > Not that the attributes are gone when SimpleXML objects are converted in > PHP 5.3. Is this a bug or a feature change? > > - Frank > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- Marco Kaiser

Rob Richards

18 years ago
Hi Frank, Frank M. Kromann wrote:
> Hello Everyon, > > Casting a SimpleXML object to an array gives different results in PHP > 5.2.5 and PHP 5.3-dev. >
This is due to the implementation of the get_debug_info handler merged from HEAD. The same result happens when calling get_object_vars on a SimpleXMLElement object. Marcus, was it intended to only include @attributes with print_r/var_dump? Rob

Marco Kaiser

18 years ago
This happens with many more functions. So i can verify this bug. php -r "echo md5(serialize(simplexml_load_file('example.xml'))) . PHP_EOL . phpversion() . PHP_EOL;" Output PHP 5.2.5: a6d7776fcb0e9c085b0d5972df792dac 5.2.5 Output PHP 5.3.0-dev (latest snap) 3e442cb7c8507c8941011735bb46e6de 5.3.0-dev -- Marco On Dec 9, 2007 1:22 PM, Rob Richards <rrichards@ctindustries.net> wrote:
> Hi Frank, > > Frank M. Kromann wrote: > > Hello Everyon, > > > > Casting a SimpleXML object to an array gives different results in PHP > > 5.2.5 and PHP 5.3-dev. > > > This is due to the implementation of the get_debug_info handler merged > from HEAD. > The same result happens when calling get_object_vars on a > SimpleXMLElement object. > > Marcus, was it intended to only include @attributes with print_r/var_dump? > > Rob > > Source: > > > > $xml = simplexml_load_file("sample.xml"); > > > > foreach($xml->column as $column) { > > var_dump($column); > > var_dump((array)$column); > > } > > > > sample.xml > > > > ?xml version="1.0"?> > > <cpdata> > > <column name="ENTERTAINMENT"> > > <module>cv</module> > > <module>entsimp</module> > > </column> > > <column name="SEAT CONTROL"> > > <module>pp</module> > > <module>sc</module> > > </column> > > </cpdata> > > > > PHP 5.2 output: > > object(SimpleXMLElement)#4 (2) { > > ["@attributes"]=> > > array(1) { > > ["name"]=> > > string(13) "ENTERTAINMENT" > > } > > ["module"]=> > > array(2) { > > [0]=> > > string(2) "cv" > > [1]=> > > string(7) "entsimp" > > } > > } > > array(2) { > > ["@attributes"]=> > > array(1) { > > ["name"]=> > > string(13) "ENTERTAINMENT" > > } > > ["module"]=> > > array(2) { > > [0]=> > > string(2) "cv" > > [1]=> > > string(7) "entsimp" > > } > > } > > object(SimpleXMLElement)#5 (2) { > > ["@attributes"]=> > > array(1) { > > ["name"]=> > > string(12) "SEAT CONTROL" > > } > > ["module"]=> > > array(2) { > > [0]=> > > string(2) "pp" > > [1]=> > > string(2) "sc" > > } > > } > > array(2) { > > ["@attributes"]=> > > array(1) { > > ["name"]=> > > string(12) "SEAT CONTROL" > > } > > ["module"]=> > > array(2) { > > [0]=> > > string(2) "pp" > > [1]=> > > string(2) "sc" > > } > > } > > > > PHP 5.3 output: > > > > object(SimpleXMLElement)#4 (2) { > > ["@attributes"]=> > > array(1) { > > ["name"]=> > > string(13) "ENTERTAINMENT" > > } > > ["module"]=> > > array(2) { > > [0]=> > > string(2) "cv" > > [1]=> > > string(7) "entsimp" > > } > > } > > array(1) { > > ["module"]=> > > array(2) { > > [0]=> > > string(2) "cv" > > [1]=> > > string(7) "entsimp" > > } > > } > > object(SimpleXMLElement)#5 (2) { > > ["@attributes"]=> > > array(1) { > > ["name"]=> > > string(12) "SEAT CONTROL" > > } > > ["module"]=> > > array(2) { > > [0]=> > > string(2) "pp" > > [1]=> > > string(2) "sc" > > } > > } > > array(1) { > > ["module"]=> > > array(2) { > > [0]=> > > string(2) "pp" > > [1]=> > > string(2) "sc" > > } > > } > > > > Not that the attributes are gone when SimpleXML objects are converted in > > PHP 5.3. Is this a bug or a feature change? > > > > - Frank > > > > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- Marco Kaiser

Marcus Börger

18 years ago
Hello Rob, this is intended. The 5.2 solution is just a hack and returns a wrong result in the first place. The hack was however introduced to make development easier. Now with 5.3 we were able to change the API and coudl apply the solution that was first developed in HEAD. marcus Sunday, December 9, 2007, 1:22:09 PM, you wrote:
> Hi Frank,
> Frank M. Kromann wrote: >> Hello Everyon, >> >> Casting a SimpleXML object to an array gives different results in PHP >> 5.2.5 and PHP 5.3-dev. >> > This is due to the implementation of the get_debug_info handler merged > from HEAD. > The same result happens when calling get_object_vars on a > SimpleXMLElement object.
> Marcus, was it intended to only include @attributes with print_r/var_dump?
> Rob >> Source: >> >> $xml = simplexml_load_file("sample.xml"); >> >> foreach($xml->column as $column) { >> var_dump($column); >> var_dump((array)$column); >> } >> >> sample.xml >> >> ?xml version="1.0"?> >> <cpdata> >> <column name="ENTERTAINMENT"> >> <module>cv</module> >> <module>entsimp</module> >> </column> >> <column name="SEAT CONTROL"> >> <module>pp</module> >> <module>sc</module> >> </column> >> </cpdata> >> >> PHP 5.2 output: >> object(SimpleXMLElement)#4 (2) { >> ["@attributes"]=> >> array(1) { >> ["name"]=> >> string(13) "ENTERTAINMENT" >> } >> ["module"]=> >> array(2) { >> [0]=> >> string(2) "cv" >> [1]=> >> string(7) "entsimp" >> } >> } >> array(2) { >> ["@attributes"]=> >> array(1) { >> ["name"]=> >> string(13) "ENTERTAINMENT" >> } >> ["module"]=> >> array(2) { >> [0]=> >> string(2) "cv" >> [1]=> >> string(7) "entsimp" >> } >> } >> object(SimpleXMLElement)#5 (2) { >> ["@attributes"]=> >> array(1) { >> ["name"]=> >> string(12) "SEAT CONTROL" >> } >> ["module"]=> >> array(2) { >> [0]=> >> string(2) "pp" >> [1]=> >> string(2) "sc" >> } >> } >> array(2) { >> ["@attributes"]=> >> array(1) { >> ["name"]=> >> string(12) "SEAT CONTROL" >> } >> ["module"]=> >> array(2) { >> [0]=> >> string(2) "pp" >> [1]=> >> string(2) "sc" >> } >> } >> >> PHP 5.3 output: >> >> object(SimpleXMLElement)#4 (2) { >> ["@attributes"]=> >> array(1) { >> ["name"]=> >> string(13) "ENTERTAINMENT" >> } >> ["module"]=> >> array(2) { >> [0]=> >> string(2) "cv" >> [1]=> >> string(7) "entsimp" >> } >> } >> array(1) { >> ["module"]=> >> array(2) { >> [0]=> >> string(2) "cv" >> [1]=> >> string(7) "entsimp" >> } >> } >> object(SimpleXMLElement)#5 (2) { >> ["@attributes"]=> >> array(1) { >> ["name"]=> >> string(12) "SEAT CONTROL" >> } >> ["module"]=> >> array(2) { >> [0]=> >> string(2) "pp" >> [1]=> >> string(2) "sc" >> } >> } >> array(1) { >> ["module"]=> >> array(2) { >> [0]=> >> string(2) "pp" >> [1]=> >> string(2) "sc" >> } >> } >> >> Not that the attributes are gone when SimpleXML objects are converted in >> PHP 5.3. Is this a bug or a feature change? >> >> - Frank >> >>
Best regards, Marcus

Sam Barrow

18 years ago
Ok, it's supposed to be this way right? If i define a custom class in the global namespace called "myClass" and I'm in another namespace, I can only access it using ::myClass, not just myClass (without the colons)? Seems to me that it should check the local namespace and then the global, but it only checks the globl if i specifically tell it to with the preceding "::".

Matthias Pigulla

18 years ago
> -----Ursprüngliche Nachricht----- > Von: Sam Barrow [mailto:sam@sambarrow.com] > Gesendet: Montag, 10. Dezember 2007 22:48 > An: internals@lists.php.net > Betreff: [PHP-DEV] Namespace resolution > > Ok, it's supposed to be this way right? If i define a custom class in > the global namespace called "myClass" and I'm in another namespace, I > can only access it using ::myClass, not just myClass (without the > colons)? Seems to me that it should check the local namespace and then > the global, but it only checks the globl if i specifically tell it to > with the preceding "::".
From http://www.php.net/manual/en/language.namespaces.rules.php:
-- Inside namespace (say A::B), calls to unqualified class names are resolved at run-time. Here is how a call to new C() is resolved: 1. It looks for a class from the current namespace : A::B::C(). 2. It tries to find and call the internal class C(). 3. It attemts to autoload A::B::C(). C(). To reference a user defined class in the global namespace, new ::C() has to be used. -- Which is exactly what you observed. -mp.

David Zülke

18 years ago
The problem I see with that is that if I have an application that uses a 3rd-party library which does not use namespaces, I need to use ::LibClass everywhere. Until they switch to namespaces - then I need to touch hundreds and thousands lines of code. If LibClass were looked up in the global namespace, too, I could simply add the appropriate import statements at the top of each file. Anyways, the process you described seems flawed in another fashion: It looks up a class in the namespace first, then an internal php class, then it tries an autoload. That does not make sense. Lets say I run some code as you described, and there is a class C(), but it's not been autoloaded yet. The internal class C is used. Then, later, the autoloading of A::B::C() is somehow forced. Now, if the same code runs again, a different class is used. That sounds like nonsense. David Am 11.12.2007 um 08:31 schrieb Matthias Pigulla:

Lokrain

18 years ago
Calm down David :) He is referring only 3 cases. Please check the link he is providing too :)

Matthias Pigulla

18 years ago
> Von: David Zülke [mailto:dz@bitxtender.com]
> The problem I see with that is that if I have an application that uses > a 3rd-party library which does not use namespaces, I need to > use ::LibClass everywhere. Until they switch to namespaces - then I > need to touch hundreds and thousands lines of code. If LibClass were > looked up in the global namespace, too, I could simply add the > appropriate import statements at the top of each file.
You got a point.
> Anyways, the process you described seems flawed in another fashion: It > looks up a class in the namespace first, then an internal php class, > then it tries an autoload. That does not make sense. Lets say I run > some code as you described, and there is a class C(), but it's not > been autoloaded yet. The internal class C is used. Then, later, the > autoloading of A::B::C() is somehow forced. Now, if the same code runs > again, a different class is used. That sounds like nonsense.
Exactly what Chuck and Greg tried to point out, providing examples for what you describe. http://www.mail-archive.com/internals@lists.php.net/msg31217.html In my opinion one of the biggest weak points. In order to make things work consistently and reliable (behaviour does not change because of the order in which files are included or because new core classes are added), you need to 1) make sure all the stuff you use is already known (that is, refrain from using __autoload and revert to require() at the top of your files) or 2) explicitly use/import all your classes, even the ones from your namespace inside your namespace. That is because __autoload comes after internal classes (which comes after already known classes from the current NS) for unqualified names. Approach 1) makes the names "already known", 2) turns the unqualified names into qualified ones by means of aliasing. :( -mp.

Stanislav Malyshev

18 years ago
> The problem I see with that is that if I have an application that uses a > 3rd-party library which does not use namespaces, I need to use > ::LibClass everywhere. Until they switch to namespaces - then I need to > touch hundreds and thousands lines of code. If LibClass were looked up > in the global namespace, too, I could simply add the appropriate import > statements at the top of each file.
But also it means that namespaced library would be influenced by any use code around, which was exactly the thing we tried to avoid.
> Anyways, the process you described seems flawed in another fashion: It > looks up a class in the namespace first, then an internal php class, > then it tries an autoload. That does not make sense. Lets say I run some
Actually, it does :)
> code as you described, and there is a class C(), but it's not been > autoloaded yet. The internal class C is used. Then, later, the > autoloading of A::B::C() is somehow forced. Now, if the same code runs > again, a different class is used. That sounds like nonsense.
Not, it's not nonsense - try to find other way that allows overriding internal class names and doesn't force exhaustive autoload search on each access to internal class - and you'll understand why it makes sense.
-- Stanislav Malyshev, Zend Software Architect stas@zend.com http://www.zend.com/ (408)253-8829 MSN: stas@zend.com

David Zülke

18 years ago
Am 11.12.2007 um 19:00 schrieb Stanislav Malyshev:
>> The problem I see with that is that if I have an application that >> uses a 3rd-party library which does not use namespaces, I need to >> use ::LibClass everywhere. Until they switch to namespaces - then I >> need to touch hundreds and thousands lines of code. If LibClass >> were looked up in the global namespace, too, I could simply add the >> appropriate import statements at the top of each file. > > But also it means that namespaced library would be influenced by any > use code around, which was exactly the thing we tried to avoid. > >> Anyways, the process you described seems flawed in another fashion: >> It looks up a class in the namespace first, then an internal php >> class, then it tries an autoload. That does not make sense. Lets >> say I run some > > Actually, it does :) > >> code as you described, and there is a class C(), but it's not been >> autoloaded yet. The internal class C is used. Then, later, the >> autoloading of A::B::C() is somehow forced. Now, if the same code >> runs again, a different class is used. That sounds like nonsense. > > Not, it's not nonsense - try to find other way that allows > overriding internal class names and doesn't force exhaustive > autoload search on each access to internal class - and you'll > understand why it makes sense.
Ah. Yes, that makes perfect sense to me. The logical solution must be then, however, not to implement namespaces at all, or requiring code that is either namespaced or has imported a namespace to do sth like "use __php__;", as others suggested last week. I understand the reason for the behavior from an implementational standpoint, but I think it really is obvious that such a namespace solution would be completely useless, as it would not be possible under any circumstances to provide code that works in a reliable fashion. Or, as an alternative, introduce an optional third argument to spl_autoload_register where people can give a namespace name for which the autoloading should be performed. That would cut down overhead since just the autoload functions of currently imported namespaces would have to be called. I don't really think, though, that doing something like use __php__; is a tall order, is it? It would only be necessary for those that consume namespaces in their code via "use" statements, hence no BC break. - David

Stanislav Malyshev

18 years ago
> Ah. Yes, that makes perfect sense to me. The logical solution must be > then, however, not to implement namespaces at all, or requiring code
It's not a solution, it's refusing to solve a problem.
> for the behavior from an implementational standpoint, but I think it > really is obvious that such a namespace solution would be completely > useless, as it would not be possible under any circumstances to provide
If it's "obvious" for you that namespaces are useless, probably you need to give your obviousness-meter a check-up. They were found useful by many people, even if there are things to work out here and there - which happens with each new concept not tested in the field.
> Or, as an alternative, introduce an optional third argument to > spl_autoload_register where people can give a namespace name for which > the autoloading should be performed. That would cut down overhead since
There's no such thing as "namespace name for which autoloding is performed". Autoloader receives a full class name, that's it. How this name was created is irrelevant.
> just the autoload functions of currently imported namespaces would have > to be called.
I don't understand what do you mean here, but it doesn't matter, since autoload call on each class access is very bad regardless of how it's implemented internally.
> I don't really think, though, that doing something like use __php__; is > a tall order, is it? It would only be necessary for those that consume
"use whatever;" is a no-op. There's even warning about it in the engine.
-- Stanislav Malyshev, Zend Software Architect stas@zend.com http://www.zend.com/ (408)253-8829 MSN: stas@zend.com

David Zülke

18 years ago
Am 11.12.2007 um 21:22 schrieb Stanislav Malyshev:
>> Ah. Yes, that makes perfect sense to me. The logical solution must >> be then, however, not to implement namespaces at all, or requiring >> code > > It's not a solution, it's refusing to solve a problem.
Well, if your answer is that it makes sense this way and that it's not gonna be changed because it cannot be implemented without significant overhead, then I need to say that it would be better to have no namespaces rather than significantly flawed namespaces.
>> for the behavior from an implementational standpoint, but I think >> it really is obvious that such a namespace solution would be >> completely useless, as it would not be possible under any >> circumstances to provide > > If it's "obvious" for you that namespaces are useless, probably you > need to give your obviousness-meter a check-up. They were found > useful by many people, even if there are things to work out here and > there - which happens with each new concept not tested in the field.
They are not useless. They would just be useless
>> Or, as an alternative, introduce an optional third argument to >> spl_autoload_register where people can give a namespace name for >> which the autoloading should be performed. That would cut down >> overhead since > > There's no such thing as "namespace name for which autoloding is > performed". Autoloader receives a full class name, that's it. How > this name was created is irrelevant. > >> just the autoload functions of currently imported namespaces would >> have to be called. > > I don't understand what do you mean here, but it doesn't matter, > since autoload call on each class access is very bad regardless of > how it's implemented internally.
Yes, because you didn't bother to even read what I wrote. I'll explain it with an example for you, maybe that helps: Let's assume we have ten autoloaders. We are, by import, in namespace Name::Space. Someone does new DateTime(); You would now have to go through all ten autoloaders before you can decide that no userspace class DateTime exists in any namespace, and thus the PHP internal class DateTime may be used. However, if it were possible to do this: spl_autoload_register(array('Name::Space::Ship', 'autoload'), 'Name::Space'); Then you could would not have to call ten autoloaders, but just those registered for the currently imported namespaces. As we've only imported "Name::Space", this is just the one I showed above. Class not found? Okay, use PHP's DateTime. Overhead gone.
>> I don't really think, though, that doing something like use >> __php__; is a tall order, is it? It would only be necessary for >> those that consume > > "use whatever;" is a no-op. There's even warning about it in the > engine.
Stas, really, stop retreating to technical details and making me look like a fool when you're tired of coming up with arguments. You very well know I meant "import". Reply to the suggestion in a respectful manner, or give it a miss. David