php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76801 require()ing a file blocks writing or deleting the file
Submitted: 2018-08-27 14:47 UTC Modified: 2018-08-30 15:00 UTC
Votes:15
Avg. Score:4.8 ± 0.5
Reproduced:14 of 14 (100.0%)
Same Version:3 (21.4%)
Same OS:1 (7.1%)
From: peehaa@php.net Assigned:
Status: Closed Package: phpdbg
PHP Version: 7.2.9 OS: Windows
Private report: No CVE-ID: None
 [2018-08-27 14:47 UTC] peehaa@php.net
Description:
------------
On Windows when requiring a file it seems phpdbg blocks writing to that same file. Same goes trying to unlink() the file

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

file_put_contents(__DIR__ . '/configuration.php', 'test');

require __DIR__ . '/configuration.php';

file_put_contents(__DIR__ . '/configuration.php', 'other data');

// neither file_put_contents nor unlink work
//unlink(__DIR__ . '/configuration.php');


Expected result:
----------------
> phpdbg -qrr test.php

test

Actual result:
--------------
> php test.php (with file_put_contents())

test

> php test.php (with unlink())

test

> phpdbg -qrr test.php (with file_put_contents())

test
[PHP Warning:  file_put_contents(\path\to/configuration.php): failed to open stream: Permission denied in \path\to\test.php on line 7]

> phpdbg -qrr test.php (with unlink())

[PHP Warning:  unlink(\path\to/configuration.php): Resource temporarily unavailable in \path\to\test.php on line 9]



Patches

release-file-handle (last revision 2018-08-28 12:55 UTC by cmb@php.net)

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-08-28 11:12 UTC] sjon at hortensius dot net
FYI: This is a well known issue with windows in general (not PHP specific) and is  explained here: https://en.wikipedia.org/wiki/File_locking#In_Microsoft_Windows

> Windows inherits the semantics of share-access controls from the MS-DOS system, where sharing was introduced in MS–DOS 3.3. Thus, an application must explicitly allow sharing; otherwise an application has exclusive read, write, and delete access to the file (other types of access, such as those to retrieve the attributes of a file are allowed.)
 [2018-08-28 12:55 UTC] cmb@php.net
The following patch has been added/updated:

Patch Name: release-file-handle
Revision:   1535460904
URL:        https://bugs.php.net/patch-display.php?bug=76801&patch=release-file-handle&revision=1535460904
 [2018-08-28 12:55 UTC] cmb@php.net
> This is a well known issue with windows in general […]

Yes.  However, the CLI SAPI closes the file handle after
inclusion, but the phpdbg SAPI does not, which may not be
intended.  A comment on phpdbg_compile_file()[1] states that the
file handler[sic] is supposed to be freed by original
compile_file() or the caller. The caller is
phpdbg_init_compile_file()[2] in this case which replaces
zend_compile_file[3], so likely phpdbg_init_compile_file() should
release the file handle.  The attached patch “release-file-handle”
would do this.

[1] <https://github.com/php/php-src/blob/php-7.3.0beta2/sapi/phpdbg/phpdbg_list.c#L234>
[2] <https://github.com/php/php-src/blob/php-7.3.0beta2/sapi/phpdbg/phpdbg_list.c#L296>
[3] <https://github.com/php/php-src/blob/php-7.3.0beta2/sapi/phpdbg/phpdbg_list.c#L389>
 [2018-08-29 11:59 UTC] bwoebi@php.net
No, the caller is the caller calling zend_compile_file().

Compare with the Zend compile_file() https://github.com/php/php-src/blob/php-7.3.0beta2/Zend/zend_language_scanner.l#L621 implementation, which doesn't destroy the handle either

Effectively in https://github.com/php/php-src/blob/php-7.3.0beta2/sapi/phpdbg/phpdbg_prompt.c#L627 phpdbg is just mirroring the behavior of compile_filename() https://github.com/php/php-src/blob/php-7.3.0beta2/Zend/zend_language_scanner.l#L672.

However, the change in https://github.com/php/php-src/blob/php-7.3.0beta2/sapi/phpdbg/phpdbg_list.c#L291 might be fine, where we create our own file handle thus we must close that properly as well.

So change that patch to just call zend_destroy_file_handle() as well inside phpdbg_compile_file() (but not the init).
 [2018-08-29 14:41 UTC] cmb@php.net
> So change that patch to just call zend_destroy_file_handle() as
> well inside phpdbg_compile_file() (but not the init).

That wouldn't fix the issue, though.
 [2018-08-30 06:03 UTC] bwoebi@php.net
Then I'm seriously confused, as phpdbg_init_compile_file() does exactly the same thing as compile_file() does - what am I missing?
 [2018-08-30 15:00 UTC] cmb@php.net
It seems that under the CLI SAPI for the included file
compile_filename()[1] is called, but not under the phpdbg SAPI.
compile_filename() calls zend_destroy_file_hande()[2] after having
called zend_compile_file(). I have checked this on Windows only,
but I guess the calls are the same on Linux.

[1] <https://github.com/php/php-src/blob/php-7.3.0beta2/Zend/zend_language_scanner.l#L643>
[2] <https://github.com/php/php-src/blob/php-7.3.0beta2/Zend/zend_language_scanner.l#L672>
 [2018-09-17 00:25 UTC] andrew at nicols dot co dot uk
I've just tried your patch cmb and it is indeed freeing up files now, but I'm also seeing files loaded twice in a require_once with this patch.

I haven't been able to replicate it outside of phpunit yet with a simple testcase. This wasn't happening before I applied the patch.
 [2018-09-17 04:05 UTC] andrew at nicols dot co dot uk
The attached patch fails with the following test:

a.php:
```
<?php

require('b.php');
require_once('b.php');
```

b.php:
```
<?php
echo __FILE__ . " was included\n";
```
 [2018-09-17 04:11 UTC] andrew at nicols dot co dot uk
Sorry, to confirm the previous comment relates to the patch suggested by cmb@php.net

Calling a.php via php directly:
2054 nicols@boysenberry:~/git/tmp> php a.php
/Users/nicols/git/tmp/b.php was included

Calling it via phpdbg -qqr:
2055 nicols@boysenberry:~/git/tmp> phpdbg -qqr a.php
/Users/nicols/git/tmp/b.php was included
/Users/nicols/git/tmp/b.php was included
[Script ended normally]
prompt>

Essentially it appears that when called via PHP CLI, a file included via require or include is not subsequently loaded if called via require_once or include_once.
However if it is called via phpdbg, it is included again.
 [2019-03-19 01:36 UTC] andrew at nicols dot co dot uk
Sadly I'm still seeing this issue on MacOS, and Linux, and it still renders phpdbg entirely useless for code coverage.

I've just tried applying this patch to the latest 7.2 release and it seems to be working as it should. The multiple includes are no longer an issue, but I am hitting segfaults on master so I'm unable to test things further.

cmb@php.net, are you able to progress this by any chance?
 [2019-03-20 00:45 UTC] andrew at nicols dot co dot uk
I've been debugging with this further and rediscovered what I was seeing  with this patch

Using phpdbg, files which were included using `include` or `require` are not added to the list of included files, and are then included again when `include_once` is called.

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

require('example.php');
print_r(get_included_files());

Expected result:
---------------
Array
(
    [0] => /private/tmp/argh/test2.php
    [1] => /private/tmp/argh/example.php
)

Actual result with PHP:
---------------
2015 nicols@boysenberry:/tmp/argh> php test2.php
Array
(
    [0] => /private/tmp/argh/test2.php
    [1] => /private/tmp/argh/example.php
)


Actual result with PHPDBG before this patch:
---------------
2011 nicols@boysenberry:/tmp/argh> phpdbg -rr test2.php
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
Array
(
    [0] => /private/tmp/argh/example.php
)

Actual result with PHPDBG after this patch:
---------------
2016 nicols@boysenberry:/tmp/argh> phpdbg -rr test2.php
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
Array
(
)


Overall effect of the proposed change:
---------------
File handles are now closed correctly
Files which have been included with `include` or `require` are not added to the list of included files, and are re-included the first time that `include_once` or `require_once` is called.
 [2019-03-20 00:48 UTC] andrew at nicols dot co dot uk
Also, it's worth pointing out that the behaviour was also broken before this patch (The script being run is not in the list of includedfiles like it is in PHP). Either way `Zend/tests/014.phpt` will fail both before and after this patch when called with phpdbg.
 [2019-03-20 07:14 UTC] andrew at nicols dot co dot uk
Pull request created on https://github.com/php/php-src/pull/3968
 [2019-03-20 08:03 UTC] cmb@php.net
The following pull request has been associated:

Patch Name: Fix bug #76801: phpdbg too many open files error
On GitHub:  https://github.com/php/php-src/pull/3965
Patch:      https://github.com/php/php-src/pull/3965.patch
 [2019-03-20 08:04 UTC] cmb@php.net
The following pull request has been associated:

Patch Name: Close file handles and add files to included files after opening
On GitHub:  https://github.com/php/php-src/pull/3968
Patch:      https://github.com/php/php-src/pull/3968.patch
 [2019-03-23 08:50 UTC] krakjoe@php.net
Automatic comment on behalf of alekitto@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=b8b880932e95ff1e575a04a6988bc0cf00b89842
Log: fix bug #76801: phpdbg too many open files error
 [2019-03-23 08:50 UTC] krakjoe@php.net
-Status: Open +Status: Closed
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Thu May 23 09:01:32 2019 UTC