php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69199 require_once doesn't require ./ files
Submitted: 2015-03-07 15:29 UTC Modified: 2015-06-05 14:01 UTC
From: pegasus at vaultwiki dot org Assigned:
Status: Re-Opened Package: opcache
PHP Version: master-Git-2015-03-07 (Git) OS: Centos 6 64-bit
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2015-03-07 15:29 UTC] pegasus at vaultwiki dot org
Description:
------------
After trying to run vBulletin under Master, I've found that I get Fatal Error: undefined function for a lot of functions which are normally included under PHP 5.6.

After investigating more closely, the affected functions are all included using the format:

require_once('./includes/filename.php');

If I dump the results of get_included_files(), I find that this file never gets included.

However, if I modify the line like so:

require_once('includes/filename.php');

Then all the functions are included as expected. It seems to me that require_once just skips anything starting with ./
If my include paths were configured incorrectly, I would expect a Fatal Error due to file-not-being-found.

Expected result:
----------------
Functions in ./ includes should be included.

Actual result:
--------------
Functions in ./ includes are not included.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-03-07 18:08 UTC] demon@php.net
-Status: Open +Status: Feedback
 [2015-03-07 18:08 UTC] demon@php.net
It seems that include_path causes the issue. When you are running the script, "./xxxx.php" will be searched in the current directory and include_path.

e.g. The scriptname that you are going to run is /a/b/c.php. 
1. cd /a/b
2. php c.php

Could you please check if the issue is fixed?
 [2015-03-07 18:37 UTC] pegasus at vaultwiki dot org
Which commit should I be trying for the fix? I'm not seeing any related changes in the shortlog.

Even if include_path causes the issue, I would expect that a Fatal Error is thrown when it can't find the required file in either the include_path or the current directory. The behavior I experienced was that it just ignored the file entirely and proceeded with execution until something in the unloaded file was used.
 [2015-03-08 02:55 UTC] demon@php.net
Could you please check your report_erroring level? Maybe the level is too High. In my computer I get two Warning:

Warning: include_once(./2.php): failed to open stream: No such file or directory in /tmp/1.php on line 3

Warning: include_once(): Failed opening './2.php' for inclusion (include_path='.:') in /tmp/1.php on line 3
array(1) {
  [0]=>
  string(10) "/tmp/1.php"
}
 [2015-03-08 05:01 UTC] rasmus@php.net
I am not able to reproduce this. Can you provide a standalone example?
Looking at both the code and an strace, nothing has changed here. An explicit require of ./includes/filename.php will only attempt to load that file from the directory the parent script is in.
 [2015-03-09 17:04 UTC] pegasus at vaultwiki dot org
Strange. Re-did make install, restarted php-fpm, and this issue has magically disappeared.
 [2015-03-21 13:28 UTC] pegasus at vaultwiki dot org
This issue was fixed by re-installing the affected PHP installation.
 [2015-03-21 13:43 UTC] pegasus at vaultwiki dot org
My mistake, this still occurs in the installation after running the script a few times. It seems this has something to do with the Zend Opcache. When it is disabled, I can no longer reproduce the issue.
 [2015-03-22 04:22 UTC] php-bugs at lists dot php dot net
No feedback was provided. The bug is being suspended because
we assume that you are no longer experiencing the problem.
If this is not the case and you are able to provide the
information that was requested earlier, please do so and
change the status of the bug back to "Re-Opened". Thank you.
 [2015-03-22 17:15 UTC] pegasus at vaultwiki dot org
Here is an example script that shows one way:
### a.php ###

function test_function()
{
}

### b.php ###

class Test_Class
{
public static function runTest() {}
}

### main.php ###

require_once('./a.php'); // first ./ require seems to always be included
require_once('./b.php'); // eventually might not be included

test_function();
Test_Class::runTest();

echo 'complete';

####

EXPECT: 'complete'
ACTUAL: mostly 'complete'. Eventually, maybe Fatal error: class 'Test_Class' not found.

Some more information:

I cannot provide a test script that always reproduces the issue because the behavior is inconsistent. The issue seems related to the Zend Opcache, since I no longer experienced the issue when opcache was disabled for about 24 hours. However, perhaps I just didn't test vigorously enough.

*Sometimes* ./ requires will simply be overlooked, as if the require line is not present in the script at all. Restarting php-fpm fixes the issue for a while.

I have had this occur in different PHP scripts for different requires and the result is the same. Once it happens in one script, it will continue happening in the same script until php-fpm is restarted. The curious part, if this was related to Zend Opcache, is that even if the script is modified e.g. to check get_included_files(), the modified version of the script will still exhibit the same behavior (I'm not familiar with how Zend Opcache works -- perhaps it updates using deltas?)

I have not yet tried with opcache.use_cwd=0
Perhaps it is related to this config variable being turned on.

I have done some searching on this behavior, and I came across this: http://stackoverflow.com/questions/8490667/php-class-not-found-but-its-included
Although the difference is that I am using ./, the end result is similar (PHP class not found even though it's included).

Perhaps PHP is internally converting ./ to http://localhost/ on some occasions, although I do not know why it would. But this would give the result that I am experiencing. Just some ideas.
 [2015-03-22 22:13 UTC] requinix@php.net
-Status: No Feedback +Status: Re-Opened
 [2015-03-23 18:17 UTC] rasmus@php.net
Is there something unique about your setup? This code runs in production on tons of sites taking billions of requests, so I doubt there is a general issue here. Are you using any chroots? Hard-links? nfs?
 [2015-03-23 19:04 UTC] pegasus at vaultwiki dot org
The only thing that stands out to me is that ./b.php has more frequently referred to a file like ./path/through/symlink/that/points/to/another/symlink/b.php

In my setup,
symlink-A links to symlink-B.
symlink-B links to realdir-C.
realdir-C contains b.php.
Hence, we have symlink-A/b.php
My main script uses require(./symlink-A/b.php');

Although I have had the issue with files in non-symlinked directories in the past. I once thought it was related to a request occurring simultaneously as the file was locked for writing (Mercurial push), but I have gone to bed with it working fine and woken up to a broken site due to this issue, and I have had it happen when the only thing that changed was PHP being rebuilt (when I opened the report that was the case). Sometimes it simply takes hours for the issue to occur.

That said, I have not noticed it happen with any non-symlinked require since Febbruary.
 [2015-03-24 14:27 UTC] pegasus at vaultwiki dot org
This might be a red herring, but while looking in the error logs, I've noticed this for the past few days:

1. An error of any level (e.g. E_WARNING) is raised in a script or one of its includes.
2. On the next request to the same script, the chance of Fatal error: class [already_included] not found is increased via the same include.

I should also note, that in some cases the [class_not_found] is not being found in a context where the class had to have been loaded and partly executed in order to reach that point:

[22-Mar-2015 21:36:21 America/Chicago] PHP Warning:  Invalid argument supplied for foreach() in /[path]/upload/vault/core/controller/ui/cart/vw.php on line 471
[22-Mar-2015 21:37:08 America/Chicago] PHP Fatal error:  Class 'vw_Hard_Core' not found in /[path]/upload/vault/core/controller/ui/cart/vw.php on line 12

Note above a warning occurs in controller/ui/cart/vw.php
Seemingly on the next request, controller/ui/cart/vw.php can't find 'vw_Hard_Core'. However, this defies logic: controller/ui/cart is actually instantiated by the vw_Hard_Core class earlier in the same script, so in this case it has been loaded and is known to be working as expected -- yet it is not available in the scope of an include that recently encountered an error.
 [2015-03-26 13:07 UTC] pegasus at vaultwiki dot org
Following the logic of my previous post, I am now able to consistently reproduce this bug in a very simple script with all files in the same local directory (no sub-directories, no symlinked directories, and none of the parent directories are symlinked anywhere as far as I know):

#### 1.php ####

require_once('./2.php');

function test1()
{
	echo 'never used';
}

#### 2.php ####

function test2()
{
	echo 'SUCCESS';
}

#### main.php ####

require_once('./1.php');
number_format('string');

test2();
exit;

#### main.php attempt #1 EXPECT & ACTUAL ####

Warning: number_format() expects parameter 1 to be float, string given in main.php on line X

SUCCESS

#### 

After receiving the correct actual proceed to place an "exit;" after the line that throws an error. Run main.php again.

#### main.php attempt #2 EXPECT & ACTUAL ####

Warning: number_format() expects parameter 1 to be float, string given in main.php on line X

####

After receiving this correct actual proceed to remove the previously added "exit;" and run main.php again. EXPECT: same result as pass #1

#### main.php attempt #3 ACTUAL ####

Warning: number_format() expects parameter 1 to be float, string given in main.php on line X

Fatal error: Call to undefined function test2() in main.php on line Y

###

Hope this helps. Basically in practice I am thinking this means that any time our real code goes through a custom error or exception handler, future calls to the second ./ include from that process will be ignored. This gives me some ideas where to start looking to workaround this issue while you are figuring out the root cause.
 [2015-03-26 13:14 UTC] pegasus at vaultwiki dot org
As a follow-up, I have discovered that once this error occurs, the PHP output is used for every PHP script in the future.

e.g. we made the error occur in main.php
But if we have another script main2.php that has completely different code and output, the previous incorrect output of main.php is used. This explains why all the sites on my server go down together when this error occurs on just one of them (even sites using different FPM pools).
 [2015-03-27 07:05 UTC] rasmus@php.net
Still unable to reproduce. I followed your steps exactly. My pass #1 and pass #3 outputs are identical. Current PHP 7 build running php-fpm under nginx on Debian8.
 [2015-03-27 15:48 UTC] pegasus at vaultwiki dot org
Confirmed on the latest build that this issue is only reproducible with the following ini:

opcache.use_cwd=1

The issue does not occur with:

opcache.use_cwd=0
 [2015-03-27 16:06 UTC] pegasus at vaultwiki dot org
I also cannot reproduce this when the following ini is present:

opcache.revalidate_path=1
 [2015-06-05 14:01 UTC] cmb@php.net
-Package: Scripting Engine problem +Package: opcache
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Wed Nov 25 20:01:23 2020 UTC