php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78124 Subclass of an included class not available
Submitted: 2019-06-07 19:02 UTC Modified: 2021-03-15 15:45 UTC
Votes:1
Avg. Score:4.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: aschmidt at anamera dot net Assigned: cmb (profile)
Status: Wont fix Package: Scripting Engine problem
PHP Version: 7.2.19 OS: Win x64
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2019-06-07 19:02 UTC] aschmidt at anamera dot net
Description:
------------
If a class extends a parent class from an include/require, then the subclass cannot be referenced until AFTER the class definition.

However, if a class extends a parent class from the SAME script, then the subclass CAN be referenced anywhere in the code.

I can't tell at what point this broke, but this appears to have worked in earlier 7.2 releases.

Test script:
---------------
<?php
declare(strict_types=1);

require( 'bug2.php' );

// Error: mySubClass2 does NOT exist!
var_dump( class_exists( 'myClass1', FALSE ), class_exists( 'mySubClass1', FALSE ), class_exists( 'myParentClass', FALSE ), class_exists( 'mySubClass2', FALSE ) );

class myClass1 {
    const myConst 			= 'C1';
}

class mySubClass1 extends myClass1 {
    const myConst 			= 'S1';
}

class mySubClass2 extends myParentClass {
    const myConst 			= 'S2';
}

// Correct: Now mySubClass2 DOES exist!
var_dump( class_exists( 'myClass1', FALSE ), class_exists( 'mySubClass1', FALSE ), class_exists( 'myParentClass', FALSE ), class_exists( 'mySubClass2', FALSE ) );


Content of file "bug2.php":
<?php
declare(strict_types=1);

class myParentClass {
    const myConst 			= 'Parent';
}






Expected result:
----------------
subclass_bug.php:7:boolean true
subclass_bug.php:7:boolean true
subclass_bug.php:7:boolean true
subclass_bug.php:7:boolean true


Actual result:
--------------
subclass_bug.php:7:boolean true
subclass_bug.php:7:boolean true
subclass_bug.php:7:boolean true
subclass_bug.php:7:boolean false

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-06-07 19:27 UTC] nikic@php.net
-Status: Open +Status: Feedback
 [2019-06-07 19:27 UTC] nikic@php.net
You likely have opcache enabled on one version and disabled on the other. Is that the case?
 [2019-06-07 19:51 UTC] aschmidt at anamera dot net
-Status: Feedback +Status: Open
 [2019-06-07 19:51 UTC] aschmidt at anamera dot net
I had ASSUMED that to be the case. 
But then I reproduced the problem even after I updated the PHP.INI to comment out all caching includes, and restarting the web server:

;zend_extension=php_opcache.dll

[XDebug]
; Must be loaded after OpCache !
;zend_extension = "C:\Program Files\PHP\v7.2\ext\php_xdebug-2.7.2-7.2-vc15-nts-x86_64.dll"
xdebug.coverage_enable=0

[PHP_WINCACHE]
;extension=php_wincache.dll
 [2019-06-08 09:22 UTC] sjon@php.net
this behavior seems pretty consistent: https://3v4l.org/GnDr2
 [2019-06-09 12:54 UTC] aschmidt at anamera dot net
Thanks for that test. So in my attempt to strip down the code of my (possibly SECONDARY problem) to the bare essentials, I unintentionally may have stumbled on the the more generic, primary problem?

Now that it's reproduced in all PHP releases, is this accepted as an unintended behavior/bug?

PS: My ORIGINAL problem occurs in a complex application (a Plug-In file in a WordPress environment), where the "test script" code itself is part of a deep multi-level code and include sequence in WordPress.

In my "real" problem, the code that is unable to reference the subclass (demonstrated by the "class_exists(...);") is actually inside a function that gets called much later by WordPress in its execution order. Lucky for me in that combination, the PHP parser will have already parsed the entire script (including subclass that extends an included class) before the function is ever called, effectively MASKING the above problem.

But, my production code has a "return;" statement BEFORE the subclass definition. I have the suspicion that given the primary problem you reproduced, there is then a secondary problem when OPcache is running: With OPcache running, the entire script appears not be parsed unless I remove the "return;". That then UNmasks the primary problem of extending parent classes that reside in includes.
 [2021-03-15 15:45 UTC] cmb@php.net
-Status: Open +Status: Wont fix -Assigned To: +Assigned To: cmb
 [2021-03-15 15:45 UTC] cmb@php.net
The VLD output[1] hints at what's happening.  The point is that
PHP first compiles a file, and runs it afterwards.  When it
compiles the main script, myClass1 and mySubClass1 are fully
defined, and so can be linked.  However, mySubClass2 cannot be
linked, because myParentClass is not yet known.  Then the main
script is run, starting with including the file where
myParentClass is defined; then the script proceeds to the first
var_dump() where mySubClass2 is still not defined, because it is
only linked afterwards (see the ADD_INTERFACE opcode). For the
second var_dump(), all classes are defined.

Well, bug or not a bug?  Let's say wont-fix.  That is just how PHP
works.

The best *general* advice to never face these kinds of issues is
to never mix declarations and execution logic[2].  And then rely
on autoloading, to avoid loading classes you don't need.

> With OPcache running, the entire script appears not be parsed
> unless I remove the "return;".

I presume you're hitting OPcache's optimizer's dead code elimination
here.  The code after the `return` is never supposed to be executed,
so isn't even compiled (practically).  I think you can deactivate
DCE with

    opcache.optimization_level=0x7FFE9FFF

[1] <https://3v4l.org/GnDr2/vld#output>
[2] <https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md#23-side-effects>
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 22:01:26 2024 UTC