Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.php > #16877 > unrolled thread
| Started by | Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> |
|---|---|
| First post | 2016-07-24 11:23 +0200 |
| Last post | 2016-07-27 20:51 +0200 |
| Articles | 9 — 3 participants |
Back to article view | Back to comp.lang.php
Order of class definitions within the same file Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> - 2016-07-24 11:23 +0200
Re: Order of class definitions within the same file "Christoph M. Becker" <cmbecker69@arcor.de> - 2016-07-27 00:45 +0200
Re: Order of class definitions within the same file Jerry Stuckle <jstucklex@attglobal.net> - 2016-07-26 23:15 -0400
Re: Order of class definitions within the same file Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> - 2016-07-27 16:57 +0200
Re: Order of class definitions within the same file Jerry Stuckle <jstucklex@attglobal.net> - 2016-07-27 12:00 -0400
Re: Order of class definitions within the same file Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> - 2016-07-27 20:35 +0200
Re: Order of class definitions within the same file "Christoph M. Becker" <cmbecker69@arcor.de> - 2016-08-29 12:07 +0200
Re: Order of class definitions within the same file "Christoph M. Becker" <cmbecker69@arcor.de> - 2016-07-27 18:06 +0200
Re: Order of class definitions within the same file Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> - 2016-07-27 20:51 +0200
| From | Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> |
|---|---|
| Date | 2016-07-24 11:23 +0200 |
| Subject | Order of class definitions within the same file |
| Message-ID | <nn21eo$dmq$1@news.albasani.net> |
Consider the following code:
class C extends B {}
class B extends A {}
class A {}
This will cause an error:
Fatal error: Class 'B' not found in [...] on line 1
However, when switching lines 2 and 3, the error goes away:
class C extends B {}
class A {}
class B extends A {}
Note that the error had occurred at line 1 where class C was defined as
an extension of class B, and line 1 was not changed here! I tested this
with PHP 5.4, 5.6 and 7.0. In fact, *any* order of the three class
definitions will work *except* C-B-A. The PHP manual says
(<http://php.net/extends>):
| Note: Classes must be defined before they are used! If you want the
| class Named_Cart to extend the class Cart, you will have to define
| the class Cart first. If you want to create another class called
| Yellow_named_cart based on the class Named_Cart you have to define
| Named_Cart first. To make it short: the order in which the classes
| are defined is important.
Which contradicts the result of the experiment above. I would have
expected either A-B-C to be the *only* order that works, or *any* order
to work (including, of course C-B-A). So what's going on here? Google
was not too helpful -- I only found
<http://stackoverflow.com/q/27627045>, which does not really give a
satisfactory answer.
Greetings,
Thomas
--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)
[toc] | [next] | [standalone]
| From | "Christoph M. Becker" <cmbecker69@arcor.de> |
|---|---|
| Date | 2016-07-27 00:45 +0200 |
| Message-ID | <nn8p5j$m6l$1@solani.org> |
| In reply to | #16877 |
On 24.07.2016 at 11:23, Thomas Mlynarczyk:
> Consider the following code:
>
> class C extends B {}
> class B extends A {}
> class A {}
>
> This will cause an error:
>
> Fatal error: Class 'B' not found in [...] on line 1
>
> However, when switching lines 2 and 3, the error goes away:
>
> [snip]
>
> […] I would have
> expected either A-B-C to be the *only* order that works, or *any* order
> to work (including, of course C-B-A). So what's going on here? […]
Interesting question! A debug session might be helpful to *surmise*
what's going on. At least in PHP 5.6 the compilation of a file ends
with a call to zend_do_end_compilation(), so set a breakpoint there.
Then view CG(class_table) (which is the class table of the compiler).
The original code shows the following relevant keys:
"\0c/vagrant/thomas.php0x7ffff7ff2019"
"\0b/vagrant/thomas.php0x7ffff7ff202e"
"a\0"
Apparently, class C and B are marked as being somehow incomplete. Note
that the order of the entries correlates to the definition of the
classes. That makes sense, as the (1-pass) compiler can't know the
details of the superclasses, which are defined only later.
Compare that to the relevant keys after having switched line 2 and 3:
"\0c/vagrant/thomas.php0x7ffff7ff2019"
"a\0"
"b\0"
Appears to be consistent.
Later on the executor tries to look up class B (`extends B`) what will
succeed in the second case, but fails in the first one, and so the error
is thrown.
It seems to me that the PHP manual (<http://php.net/extends>) is not
completely correct wrt. the implementation, but the fact that the second
script is accepted might be regarded as implementation detail (an
implementation which is currently slightly less restricting).
For me, this answer seems rather unsatisfactory, but I don't know
better, and there's not much documentation about the Zend engine, and
what is there seems to be pretty much scattered, unfortunately.
--
Christoph M. Becker
[toc] | [prev] | [next] | [standalone]
| From | Jerry Stuckle <jstucklex@attglobal.net> |
|---|---|
| Date | 2016-07-26 23:15 -0400 |
| Message-ID | <nn98vp$65i$1@jstuckle.eternal-september.org> |
| In reply to | #16879 |
On 7/26/2016 6:45 PM, Christoph M. Becker wrote:
> On 24.07.2016 at 11:23, Thomas Mlynarczyk:
>
>> Consider the following code:
>>
>> class C extends B {}
>> class B extends A {}
>> class A {}
>>
>> This will cause an error:
>>
>> Fatal error: Class 'B' not found in [...] on line 1
>>
>> However, when switching lines 2 and 3, the error goes away:
>>
>> [snip]
>>
>> […] I would have
>> expected either A-B-C to be the *only* order that works, or *any* order
>> to work (including, of course C-B-A). So what's going on here? […]
>
> Interesting question! A debug session might be helpful to *surmise*
> what's going on. At least in PHP 5.6 the compilation of a file ends
> with a call to zend_do_end_compilation(), so set a breakpoint there.
> Then view CG(class_table) (which is the class table of the compiler).
> The original code shows the following relevant keys:
>
> "\0c/vagrant/thomas.php0x7ffff7ff2019"
> "\0b/vagrant/thomas.php0x7ffff7ff202e"
> "a\0"
>
> Apparently, class C and B are marked as being somehow incomplete. Note
> that the order of the entries correlates to the definition of the
> classes. That makes sense, as the (1-pass) compiler can't know the
> details of the superclasses, which are defined only later.
>
> Compare that to the relevant keys after having switched line 2 and 3:
>
> "\0c/vagrant/thomas.php0x7ffff7ff2019"
> "a\0"
> "b\0"
>
> Appears to be consistent.
>
> Later on the executor tries to look up class B (`extends B`) what will
> succeed in the second case, but fails in the first one, and so the error
> is thrown.
>
> It seems to me that the PHP manual (<http://php.net/extends>) is not
> completely correct wrt. the implementation, but the fact that the second
> script is accepted might be regarded as implementation detail (an
> implementation which is currently slightly less restricting).
>
> For me, this answer seems rather unsatisfactory, but I don't know
> better, and there's not much documentation about the Zend engine, and
> what is there seems to be pretty much scattered, unfortunately.
>
Christopher, I'm not so sure the documentation (what little there is) is
incorrect in the way it is intended to work. Rather, I suspect it's not
working as intended.
I'm with you - I would expect the only sequence to work would be A-B-C
and anything else get compile error. Perhaps its an unanticipated side
effect from another change. But until the doc changes to match it, I
wouldn't depend on it. It might easily go away in the next version.
--
==================
Remove the "x" from my email address
Jerry Stuckle
jstucklex@attglobal.net
==================
[toc] | [prev] | [next] | [standalone]
| From | Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> |
|---|---|
| Date | 2016-07-27 16:57 +0200 |
| Message-ID | <nnai3s$l47$1@news.albasani.net> |
| In reply to | #16879 |
[Sorry, I had sent this to the poster instead of the group by mistake -- Thunderbird's Reply button vs. Followup... Grrr...] On 27/07/16 00:45, Christoph M. Becker wrote: > Then view CG(class_table) (which is the class table of the compiler). > The original code shows the following relevant keys: > > "\0c/vagrant/thomas.php0x7ffff7ff2019" > "\0b/vagrant/thomas.php0x7ffff7ff202e" > "a\0" > > Apparently, class C and B are marked as being somehow incomplete. Note > that the order of the entries correlates to the definition of the > classes. That makes sense, as the (1-pass) compiler can't know the > details of the superclasses, which are defined only later. > > Compare that to the relevant keys after having switched line 2 and 3: > > "\0c/vagrant/thomas.php0x7ffff7ff2019" > "a\0" > "b\0" > > Appears to be consistent. > > Later on the executor tries to look up class B (`extends B`) what will > succeed in the second case, but fails in the first one, and so the error > is thrown. I still don't quite understand it. In the second example (C-A-B), the executer notices that C is marked as incomplete, because of the parent B. So it looks for B and finds it. In the first case (C-B-A), it's the same, but B is marked as incomplete as well -- so why does the executor give up instead of simply doing another "level" of look-up? This must have been a deliberate decision made by the programmers. > It seems to me that the PHP manual (<http://php.net/extends>) is not > completely correct wrt. the implementation, but the fact that the second > script is accepted might be regarded as implementation detail (an > implementation which is currently slightly less restricting). Indeed, as Jerry remarked, according to the manual, A-B-C is the only valid order and whether any other order works or not should be regarded as undocumented behaviour and hence not be relied upon. But it would be nice if any order worked. Good programming practise requires you to put each class definition in a separate file. For distributing code, all files are sometimes combined into one. This is easily achieved by concatenating every file in the source directory. However, the order in which the files are concatenated, cannot be controlled as easily, so one might end up with any order of the three classes A, B and C. And that's where it would come in really handy if any order worked. I know: PHAR exists, but still... Anyway, thank you for your analysis. Greetings, Thomas -- Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison! (Coluche)
[toc] | [prev] | [next] | [standalone]
| From | Jerry Stuckle <jstucklex@attglobal.net> |
|---|---|
| Date | 2016-07-27 12:00 -0400 |
| Message-ID | <nnalpt$i0p$1@jstuckle.eternal-september.org> |
| In reply to | #16881 |
On 7/27/2016 10:57 AM, Thomas Mlynarczyk wrote: > [Sorry, I had sent this to the poster instead of the group by mistake -- > Thunderbird's Reply button vs. Followup... Grrr...] > > On 27/07/16 00:45, Christoph M. Becker wrote: > >> Then view CG(class_table) (which is the class table of the compiler). >> The original code shows the following relevant keys: >> >> "\0c/vagrant/thomas.php0x7ffff7ff2019" >> "\0b/vagrant/thomas.php0x7ffff7ff202e" >> "a\0" >> >> Apparently, class C and B are marked as being somehow incomplete. Note >> that the order of the entries correlates to the definition of the >> classes. That makes sense, as the (1-pass) compiler can't know the >> details of the superclasses, which are defined only later. >> >> Compare that to the relevant keys after having switched line 2 and 3: >> >> "\0c/vagrant/thomas.php0x7ffff7ff2019" >> "a\0" >> "b\0" >> >> Appears to be consistent. >> >> Later on the executor tries to look up class B (`extends B`) what will >> succeed in the second case, but fails in the first one, and so the error >> is thrown. > > I still don't quite understand it. In the second example (C-A-B), the > executer notices that C is marked as incomplete, because of the parent > B. So it looks for B and finds it. In the first case (C-B-A), it's the > same, but B is marked as incomplete as well -- so why does the executor > give up instead of simply doing another "level" of look-up? This must > have been a deliberate decision made by the programmers. > >> It seems to me that the PHP manual (<http://php.net/extends>) is not >> completely correct wrt. the implementation, but the fact that the second >> script is accepted might be regarded as implementation detail (an >> implementation which is currently slightly less restricting). > > Indeed, as Jerry remarked, according to the manual, A-B-C is the only > valid order and whether any other order works or not should be regarded > as undocumented behaviour and hence not be relied upon. > > But it would be nice if any order worked. Good programming practise > requires you to put each class definition in a separate file. For > distributing code, all files are sometimes combined into one. This is > easily achieved by concatenating every file in the source directory. > However, the order in which the files are concatenated, cannot be > controlled as easily, so one might end up with any order of the three > classes A, B and C. And that's where it would come in really handy if > any order worked. I know: PHAR exists, but still... > > Anyway, thank you for your analysis. > > Greetings, > Thomas > > > The problem with any order working is requiring additional passes to parse the code. For instance, if it were C-B-A, the parser would have to go through three times. The first would mark C and B as incomplete, and A as complete. Then it would have to parse C and B again; this time it would mark C as incomplete and B as complete. Finally, a third pass would mark C as complete. Of course, this means parsing (and everything else) takes longer. Maybe they actually use a two pass parser - which would account for the behavior. I haven't looked at the source code in depth, so I don't know. -- ================== Remove the "x" from my email address Jerry Stuckle jstucklex@attglobal.net ==================
[toc] | [prev] | [next] | [standalone]
| From | Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> |
|---|---|
| Date | 2016-07-27 20:35 +0200 |
| Message-ID | <nnauue$f2k$1@news.albasani.net> |
| In reply to | #16882 |
On 27/07/16 18:00, Jerry Stuckle wrote: > The problem with any order working is requiring additional passes to > parse the code. For instance, if it were C-B-A, the parser would have > to go through three times. The first would mark C and B as incomplete, > and A as complete. Then it would have to parse C and B again; this time > it would mark C as incomplete and B as complete. Finally, a third pass > would mark C as complete. Of course, this means parsing (and everything > else) takes longer. I think I get it now. There *are* actually two "passes": the first one when the script is compiled and the second one when the script is executed. And the latter expects all class definitions to be either complete or available via autoloading but it will not bother with multiple look-ups. Greetings, Thomas -- Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison! (Coluche)
[toc] | [prev] | [next] | [standalone]
| From | "Christoph M. Becker" <cmbecker69@arcor.de> |
|---|---|
| Date | 2016-08-29 12:07 +0200 |
| Message-ID | <nq11hc$910$1@solani.org> |
| In reply to | #16884 |
On 27.07.2016 at 20:35, Thomas Mlynarczyk wrote: > On 27/07/16 18:00, Jerry Stuckle wrote: > >> The problem with any order working is requiring additional passes to >> parse the code. For instance, if it were C-B-A, the parser would have >> to go through three times. The first would mark C and B as incomplete, >> and A as complete. Then it would have to parse C and B again; this time >> it would mark C as incomplete and B as complete. Finally, a third pass >> would mark C as complete. Of course, this means parsing (and everything >> else) takes longer. > > I think I get it now. There *are* actually two "passes": the first one > when the script is compiled and the second one when the script is > executed. And the latter expects all class definitions to be either > complete or available via autoloading but it will not bother with > multiple look-ups. Indeed. I just found a more detailed explanantion by one of the PHP developers: <http://jpauli.github.io/2015/03/24/zoom-on-php-objects.html#class-binding>. (At the end of the section there's even the example that started this thread.) -- Christoph M. Becker
[toc] | [prev] | [next] | [standalone]
| From | "Christoph M. Becker" <cmbecker69@arcor.de> |
|---|---|
| Date | 2016-07-27 18:06 +0200 |
| Message-ID | <nnam6a$u5b$1@solani.org> |
| In reply to | #16881 |
On 27.07.2016 at 16:57, Thomas Mlynarczyk wrote: > I still don't quite understand it. In the second example (C-A-B), the > executer notices that C is marked as incomplete, because of the parent > B. So it looks for B and finds it. In the first case (C-B-A), it's the > same, but B is marked as incomplete as well -- so why does the executor > give up instead of simply doing another "level" of look-up? This must > have been a deliberate decision made by the programmers. There are cases which can't be resolved, at least mutual and circular inheritance (A extends B, B extends A). Not doing further lookup might just be a simplification to avoid tracking such cycles. Anyhow, I found that your second example already works as of PHP 4.3 at least, see <https://3v4l.org/2DJJh>. However, the respective manual section has only been added in 2010[1], to resolve <https://bugs.php.net/bug.php?id=51381>, which also notes that there are exceptions from the rule "declare parent before child", which have not been addressed. Interestingly, there is another bug report with regard to this very issue: <https://bugs.php.net/bug.php?id=70800>. I suppose we'd need to ask Zend engine developers about the details. internals@lists.php.net might be a good place to reach these. > Indeed, as Jerry remarked, according to the manual, A-B-C is the only > valid order and whether any other order works or not should be regarded > as undocumented behaviour and hence not be relied upon. ACK. > […] Good programming practise > requires you to put each class definition in a separate file. For > distributing code, all files are sometimes combined into one. […] Well, nowadays we may rely on OPcache (or another bytecode cache) to be available, which solves the issue. But I agree that combining in a single file might still make sense. [1] <http://svn.php.net/viewvc/?view=revision&revision=304561> -- Christoph M. Becker
[toc] | [prev] | [next] | [standalone]
| From | Thomas Mlynarczyk <thomas@mlynarczyk-webdesign.de> |
|---|---|
| Date | 2016-07-27 20:51 +0200 |
| Message-ID | <nnavqv$gpj$1@news.albasani.net> |
| In reply to | #16883 |
On 27/07/16 18:06, Christoph M. Becker wrote:
> Anyhow, I found that your second example already works as of PHP 4.3 at
> least, see <https://3v4l.org/2DJJh>. However, the respective manual
> section has only been added in 2010[1], to resolve
> <https://bugs.php.net/bug.php?id=51381>, which also notes that there are
> exceptions from the rule "declare parent before child", which have not
> been addressed. Interestingly, there is another bug report with regard
> to this very issue: <https://bugs.php.net/bug.php?id=70800>.
Interesting. I had not found these bug reports via Google.
> Well, nowadays we may rely on OPcache (or another bytecode cache) to be
> available, which solves the issue. But I agree that combining in a
> single file might still make sense.
Yes, with opcode caches and/or PHAR archives any overhead from including
multiple files is probably reduced to a negligible minimum. Still,
whenever combining several files into a single one is done, a way to
ensure the correct order would be to sort the file names by length --
the idea behind this being that derived classes should have longer names
than their parent class, like
class Thing {}
class FooThing extends Thing {}
class GreenFooThing extends FooThing {}
which would be reflected in the respective file names. Of course, this
requires discipline when naming classes and still feels like a hack.
Greetings,
Thomas
--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.php
csiph-web