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:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (100.0%)
From: peehaa@php.net Assigned:
Status: Open Package: phpdbg
PHP Version: 7.2.9 OS: Windows
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: peehaa@php.net
New email:
PHP Version: OS:

 

 [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

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.
 
PHP Copyright © 2001-2018 The PHP Group
All rights reserved.
Last updated: Thu Dec 13 08:01:25 2018 UTC