php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #66773 Autoload with Opcache allows importing conflicting class name to namespace
Submitted: 2014-02-25 21:29 UTC Modified: 2016-01-13 10:10 UTC
Votes:112
Avg. Score:4.4 ± 0.9
Reproduced:106 of 107 (99.1%)
Same Version:44 (41.5%)
Same OS:56 (52.8%)
From: carl dot vuorinen at w3 dot fi Assigned: dmitry
Status: Closed Package: opcache
PHP Version: 5.5.9 OS: Linux (Ubuntu)
Private report: No CVE-ID:
 [2014-02-25 21:29 UTC] carl dot vuorinen at w3 dot fi
Description:
------------
Autoloading with Opcache enabled allows importing a class from another namespace with a name that is already in use in the current namespace and the code to execute successfully. Without Opcache the same code gives Fatal error.

Test script:
---------------
Here is a small sample script with a few files that will be autoloaded by the execute.php script. Some test results can be found in the comments. 

https://gist.github.com/cvuorinen/e7a19101d40c9fa0a959

Expected result:
----------------
Fatal error: Cannot use Other\Bar as Bar because the name is already in use in /tmp/path/some_foo.php on line 5

Actual result:
--------------
self: Some\Bar
parent: Other\Bar

Patches

bug66773.diff (last revision 2016-01-13 10:06 UTC) by dmitry at zend dot com)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-03-11 12:25 UTC] carl dot vuorinen at w3 dot fi
Here is a more detailed output with both OPcache enabled and OPcache disabled.
Same behaviour also reproduced in few other environments.

$ php -v      
PHP 5.5.9-1+sury.org~saucy+1 (cli) (built: Feb 13 2014 15:58:58) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies
    with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans

$ php -i | grep opcache.enable_cli
opcache.enable_cli => On => On

$ php execute.php                 
self: Some\Bar
parent: Other\Bar

$ sudo nano /etc/php5/cli/php.ini 

$ php -i | grep opcache.enable_cli
opcache.enable_cli => Off => Off

$ php execute.php                 

Fatal error: Cannot use Other\Bar as Bar because the name is already in use 
in /tmp/path/some_foo.php on line 5

Call Stack:
    0.0005     234520   1. {main}() /tmp/path/execute.php:0
    0.0036     241464   2. spl_autoload_call() /tmp/path/execute.php:13
    0.0036     241496   3. {closure:/tmp/path/execute.php:6-8}() /tmp/path/execute.php:0
    0.0036     241624   4. spl_autoload() /tmp/path/execute.php:7
 [2014-05-27 01:11 UTC] adrien dot crivelli at gmail dot com
Same issue here. The test script could be reproduced:

$ lsb_release -a
LSB Version:	core-2.0-amd64:core-2.0-noarch:core-3.0-amd64:core-3.0-noarch:core-3.1-amd64:core-3.1-noarch:core-3.2-amd64:core-3.2-noarch:core-4.0-amd64:core-4.0-noarch
Distributor ID:	Ubuntu
Description:	Ubuntu 14.04 LTS
Release:	14.04
Codename:	trusty

$ php -v
PHP 5.5.9-1ubuntu4 (cli) (built: Apr  9 2014 17:11:57) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
    with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies
    with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans

$ php -i | grep opcache.enable_cli
opcache.enable_cli => On => On
$ php execute.php 
self: Some\Bar
parent: Other\Bar

$ php -i | grep opcache.enable_cli
opcache.enable_cli => Off => Off
$ php execute.php 
PHP Fatal error:  Cannot use Other\Bar as Bar because the name is already in use in /tmp/a/some_foo.php on line 5
PHP Stack trace:
PHP   1. {main}() /tmp/a/execute.php:0
PHP   2. spl_autoload_call() /tmp/a/execute.php:13
PHP   3. {closure:/tmp/a/execute.php:6-8}() /tmp/a/execute.php:0
PHP   4. spl_autoload() /tmp/a/execute.php:7

Fatal error: Cannot use Other\Bar as Bar because the name is already in use in /tmp/a/some_foo.php on line 5

Call Stack:
    0.0002     235776   1. {main}() /tmp/a/execute.php:0
    0.0005     242720   2. spl_autoload_call() /tmp/a/execute.php:13
    0.0005     242752   3. {closure:/tmp/a/execute.php:6-8}() /tmp/a/execute.php:0
    0.0005     242880   4. spl_autoload() /tmp/a/execute.php:7
 [2014-06-23 12:30 UTC] jpauli@php.net
Reproduced.
Seems like a bug.

@dmitry: any idea ? I found that the compiler class_table is reseted between each compilation by opcache (https://github.com/zendtech/ZendOptimizerPlus/blob/master/ZendAccelerator.c#L1411), thus the class can't be found and detected as already used.
 [2014-06-23 16:17 UTC] devosc at gmail dot com
I prefer the new behaviour and would like it to be kept/maintained.
 [2014-06-23 20:56 UTC] dmitry@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: dmitry
 [2014-10-22 09:41 UTC] jpauli@php.net
I got a patch at https://github.com/jpauli/ZendOptimizerPlus/compare/66773

I compiles and works just fine for 5.5, shouldn't diff for other versions.
Dmitry, this needs review
 [2014-10-23 09:17 UTC] dmitry@php.net
Unfortunately, the patch is wrong.

Opcache compiles each script separately by design.
Otherwise, it'll may make dependencies on other scripts.
And later to unpredictable behavior, if some of those scripts are changed and recompiled but depended is not.

The patch breaks this rule.

Actually, the behavior with opcache seems better :)
May be it makes sense switching to it in PHP7.
 [2014-10-23 10:07 UTC] nikic@php.net
I agree with Dmitry, it would be best to allow shadowing class definitions with imports in PHP 7. The current behavior is pretty weird, especially as it depends on the order in which classes are loaded.
 [2014-10-23 11:39 UTC] dmitry@php.net
Nikta, it would be great if you can take care about this.
I completely forgot namespaces implementation details.
I'm even not sure, if it can be done without hacks.
 [2014-10-23 15:40 UTC] nikic@php.net
I think all we need to do is drop the if (+ 10 lines) at http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_compile.c#4723, which checks whether a class with the imported name already exists.
 [2014-11-07 17:15 UTC] john at zerocrates dot org
Isn't the "expected" error behavior here directly in conflict with the documentation?

http://php.net/manual/en/language.namespaces.faq.php#language.namespaces.faq.conflict

That documentation section gives the following example:

file1.php
---------
<?php
namespace my\stuff;
class MyClass {}
?>

another.php
-----------
<?php
namespace another;
class thing {}
?>

file2.php
---------
<?php
namespace my\stuff;
include 'file1.php';
include 'another.php';

use another\thing as MyClass;
$a = new MyClass; // instantiates class "thing" from namespace another
?>

So, in file2.php, once the includes are done, the class name "my\stuff\MyClass" is already "in use." But, the documentation says that the "conflicting" use statement "use another\thing as MyClass" should be fine, so long as the other MyClass is declared in another file (which it is). On this subject the documentation says:

"There is no name conflict, even though the class MyClass exists within the my\stuff namespace, because the MyClass definition is in a separate file."

Other than the use of autoloading, I can't see any distinction between that example and the test script here, and it doesn't seem like the autoloading should make any difference.
 [2015-06-02 13:53 UTC] Erutan409 at Hotmail dot com
Has this been resolved, yet and/or is this still considered a bug?
 [2015-07-06 04:22 UTC] kernins at gmail dot com
>Isn't the "expected" error behavior here directly in conflict with the documentation?

Absolutely! The actual bug here is the "expected" behaviour itself. The code in some_foo.php from test example is perfectly valid as imports are _per_file_, not per process or thread. Imports have precedence and allows to "shadow" classes from current NS, and they shouldn't care what is already loaded or imported, except from current file. Otherwise, with "expected" behaviour, they not only would be alogical and almost useless, but also a hell to maintain in a big projects.


I have a project, that worked perfectly on some older 5.4.x and all previous versions I've used since NS was introduced in 5.3. But after update to 5.5.9 I see exactly the same fatal errors here and there on previously flawlessly working code when runing in CLI. Opcache is and definitely was turned off for CLI SAPI. With FPM, wich has opcache enabled, it worked fine.

It seems, that bug was also backported to 5.4 branch - 5.4.34 I have on hand is also affected, but I'm absolutely confident some previous 5.4.x was not.

Please, fix this nonsense asap! This bug is pretty annoying
 [2015-12-14 10:55 UTC] pawel dot zegardlo at bm dot pl
@dmitry - what is the state of this bug? it has been fixed in php 7 or another version? which behaviour is now valid?
 [2015-12-27 13:19 UTC] incubeftw at gmail dot com
Bug still persists in PHP 7.0, 7.0.1, 7.1-dev
 [2016-01-13 10:10 UTC] dmitry@php.net
The patch (for PHP-7.0), attached to the bug report, removes error without opcache as well.
Please review (I'm not sure about all possible consequences).
 [2016-03-24 12:25 UTC] klein dot shaked+php at gmail dot com
Hey, 

What is the status of this issue? This is still happening using the gist above and also happened on one of our php7 hosts on production. 
 
$ php -v
PHP 7.0.4-6+deb.sury.org~trusty+5 (cli) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies

uname -a "Linux dev 3.13.0-79-generic #123-Ubuntu SMP Fri Feb 19 14:27:58 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux"

I have checked PHP's source code but haven't seen the patch there. 

Any idea how this can be resolved? 

Thanks
 [2016-03-31 10:12 UTC] klein dot shaked+php at gmail dot com
Hey, 

I have tried the attached patch: 

$ /usr/bin/php-backup -v
PHP 7.0.4-6+deb.sury.org~trusty+5 (cli) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies
$ /usr/bin/php-backup execute.php
PHP Fatal error:  Cannot use Other\Bar as Bar because the name is already in use in /php7test/some_foo.php on line 5

Fatal error: Cannot use Other\Bar as Bar because the name is already in use in /php7test/some_foo.php on line 5
$ php -v
PHP 7.1.0-dev (cli) (built: Mar 31 2016 09:45:43) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.1.0-dev, Copyright (c) 1998-2016 Zend Technologies
$ php execute.php
self: Some\Bar
parent: Other\Bar
$

It seems like it works with the gist example. 

Having said that, it's hard to say if all cases are covered, plus I did have segfault while compiling (and I have filed a bug) which I am not sure if it was related or not.
 [2016-10-08 15:03 UTC] nikic@php.net
Automatic comment on behalf of nikic
Revision: http://git.php.net/?p=php-src.git;a=commit;h=2a75f5026a47099f585e29c5a9d8a2989dab42af
Log: Fix bug #66773, #66862
 [2016-10-08 15:03 UTC] nikic@php.net
-Status: Assigned +Status: Closed
 [2016-10-17 10:07 UTC] bwoebi@php.net
Automatic comment on behalf of nikic
Revision: http://git.php.net/?p=php-src.git;a=commit;h=2a75f5026a47099f585e29c5a9d8a2989dab42af
Log: Fix bug #66773, #66862
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Feb 21 18:01:40 2017 UTC