Bug #71341 [NEW]: Engine can forget about classes in edge-cases with streams and opcache enabled

From: Date: Mon, 11 Jan 2016 18:59:55 +0000
Subject: Bug #71341 [NEW]: Engine can forget about classes in edge-cases with streams and opcache enabled
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-198580@lists.php.net to get a copy of this message
From:             lisachenko dot it at gmail dot com
Operating system: MacOS
PHP version:      7.0.2
Package:          opcache
Bug Type:         Bug
Bug description:Engine can forget about classes in edge-cases with streams and opcache enabled

Description:
------------
I'm playing with hidden PHP features and noticed one bug, when PHP
forgets about all loaded classes (get_declared_classes() will return
empty array). This case is reproduced only with enabled opcache module
and some dark magic:

1. Register new stream filter class implementation (it performs an
AST-analysis and transformation for my case)
2. Unregister the standard stream wrapper handler for the 'file'
protocol via stream_wrapper_unregister('file') and then register a
custom one handler (see
https://github.com/goaop/ast-manipulator/blob/master/src/Hook/StreamWrapperHook.php).
stream_open() method implementation temporary restores original handler
and opens a file with enabled stream filter via
'php://filter/read=filter.id' mechanism. After that it enables custom
hook again.
3. Now include a class via "include" keyword to trigger stream wrapper
and stream filter processing.

If opcache is enabled then PHP tries to load DemoClass and then goes
crazy starting to report about missing classes that was previously
loaded and crashing with fatal errors that class is not loaded yet, even
built-in, such as \Exception, \stdClass, etc.

So, this related to opcache + "file" stream wrapper + stream filter +
include keyword handling. For opcache it will be better not to cache
such dirty cases or take an $openedPath from
StreamWrapper::stream_open() argument for checking the hash key:

public function stream_open($path, $mode, $options, &$openedPath)

If this openedPath argument is filled and is not empty, then opcache can
take the compiled version of opcodes from that key instead of original
one $path.

Test script:
---------------
<?php

// composer require goaop/ast-manipulator:dev-master to clone project
// the put this content into the root and run in web

include __DIR__ . '/vendor/autoload.php';

\Go\AstManipulator\Engine::init();
\Go\AstManipulator\Hook\StreamWrapperHook::activateHook();

$class = new \Go\AstManipulator\DemoClass(); <== weird stuff here
$class->foo();

Expected result:
----------------
Hello, world!

Actual result:
--------------
Warning: stream_wrapper_register(): class
'Go\AstManipulator\Hook\StreamWrapperHook' is undefined in
../ast-manipulator/src/Hook/StreamWrapperHook.php on line 39

Warning: stream_wrapper_register(): class
'Go\AstManipulator\Hook\StreamWrapperHook' is undefined in
../ast-manipulator/src/Hook/StreamWrapperHook.php on line 39

Fatal error: Call to a member function parse() on a non-object in
../ast-manipulator/src/Engine.php on line 97

-- 
Edit bug report at https://bugs.php.net/bug.php?id=71341&edit=1
-- 
Try a snapshot (PHP 5.4):   https://bugs.php.net/fix.php?id=71341&r=trysnapshot54
Try a snapshot (PHP 5.5):   https://bugs.php.net/fix.php?id=71341&r=trysnapshot55
Try a snapshot (trunk):     https://bugs.php.net/fix.php?id=71341&r=trysnapshottrunk
Fixed in SVN:               https://bugs.php.net/fix.php?id=71341&r=fixed
Fixed in release:           https://bugs.php.net/fix.php?id=71341&r=alreadyfixed
Need backtrace:             https://bugs.php.net/fix.php?id=71341&r=needtrace
Need Reproduce Script:      https://bugs.php.net/fix.php?id=71341&r=needscript
Try newer version:          https://bugs.php.net/fix.php?id=71341&r=oldversion
Not developer issue:        https://bugs.php.net/fix.php?id=71341&r=support
Expected behavior:          https://bugs.php.net/fix.php?id=71341&r=notwrong
Not enough info:            https://bugs.php.net/fix.php?id=71341&r=notenoughinfo
Submitted twice:            https://bugs.php.net/fix.php?id=71341&r=submittedtwice
register_globals:           https://bugs.php.net/fix.php?id=71341&r=globals
PHP 4 support discontinued: https://bugs.php.net/fix.php?id=71341&r=php4
Daylight Savings:           https://bugs.php.net/fix.php?id=71341&r=dst
IIS Stability:              https://bugs.php.net/fix.php?id=71341&r=isapi
Install GNU Sed:            https://bugs.php.net/fix.php?id=71341&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=71341&r=float
No Zend Extensions:         https://bugs.php.net/fix.php?id=71341&r=nozend
MySQL Configuration Error:  https://bugs.php.net/fix.php?id=71341&r=mysqlcfg



Thread (4 messages)

« previous php.bugs (#198580) next »