Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclusive locks are not supported for this stream php://stdout #13777

Open
staabm opened this issue Mar 21, 2024 · 7 comments
Open

Exclusive locks are not supported for this stream php://stdout #13777

staabm opened this issue Mar 21, 2024 · 7 comments

Comments

@staabm
Copy link
Contributor

staabm commented Mar 21, 2024

Description

I wonder why the file_put_contents emitts a Exclusive locks are not supported for this stream error and putting the very same stream into stream_supports_lock does not return false, but also errors.

The following code:

<?php

var_dump(stream_supports_lock("php://stdout"));

file_put_contents(
    "php://stdout",
    "x",
    FILE_APPEND | LOCK_EX,
);

Resulted in this output:

$ php test.php 
PHP Fatal error:  Uncaught TypeError: stream_supports_lock(): Argument #1 ($stream) must be of type resource, string given in C:\dvl\Workspace\phpunit\test.php:3
Stack trace:
#0 C:\dvl\Workspace\phpunit\test.php(3): stream_supports_lock()
#1 {main}
  thrown in C:\dvl\Workspace\phpunit\test.php on line 3

Fatal error: Uncaught TypeError: stream_supports_lock(): Argument #1 ($stream) must be of type resource, string given in C:\dvl\Workspace\phpunit\test.php:3
Stack trace:
#0 C:\dvl\Workspace\phpunit\test.php(3): stream_supports_lock()
#1 {main}
  thrown in C:\dvl\Workspace\phpunit\test.php on line 3

But I expected this output instead:

$ php test.php 
bool(false)
PHP Warning:  file_put_contents(): Exclusive locks are not supported for this stream in C:\dvl\Workspace\phpunit\test.php on line 5

Warning: file_put_contents(): Exclusive locks are not supported for this stream in C:\dvl\Workspace\phpunit\test.php on line 5

might be a windows only error, because 3v4l.org does not report the same error
https://3v4l.org/98p6T

PHP Version

$ php -v
PHP 8.2.12 (cli) (built: Oct 24 2023 21:15:35) (NTS Visual C++ 2019 x64)
Copyright (c) The PHP Group
Zend Engine v4.2.12, Copyright (c) Zend Technologies

Operating System

windows11

@staabm
Copy link
Contributor Author

staabm commented Mar 21, 2024

turned differently: how to check a stream I can put into file_put_contents whether it supports LOCK_EX?

I tried stream_supports_lock because I found the error message only in

php-src/ext/standard/file.c

Lines 498 to 502 in 79e4ca1

if ((flags & LOCK_EX) && (!php_stream_supports_lock(stream) || php_stream_lock(stream, LOCK_EX))) {
php_stream_close(stream);
php_error_docref(NULL, E_WARNING, "Exclusive locks are not supported for this stream");
RETURN_FALSE;
}

@nielsdos
Copy link
Member

I can answer the first question: stream_supports_lock takes a resource, not a uri/path: https://www.php.net/manual/en/function.stream-supports-lock.php

So var_dump(stream_supports_lock("php://stdout")); cannot work.
This is the proper usage: https://3v4l.org/n3jvr

@DeveloperRob
Copy link

On Windows then stream_supports_lock returns true, however no locks appear to actually be supported:

var_dump(stream_supports_lock(STDOUT)); // bool(true)
var_dump(flock(STDOUT, LOCK_EX)); // bool(false)
var_dump(flock(STDOUT, LOCK_SH)); // bool(false)
var_dump(file_put_contents("php://stdout", "x", FILE_APPEND | LOCK_EX,)); // bool(false) / Warning: file_put_contents(): Exclusive locks are not supported for this stream in php shell code on line 1

@nielsdos
Copy link
Member

nielsdos commented Apr 6, 2024

The discrepancy between stream_supports_lock and flock happens because of this code:

if ((uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) {
return 0;
}

i.e. this just always says that locking is supported for file descriptors.
I don't immediately see a portable way to check if a file descriptor is lockable (without actually trying to lock).

@cmb69
Copy link
Member

cmb69 commented Jul 15, 2024

On Windows then stream_supports_lock returns true, however no locks appear to actually be supported:

I don't think that it's possible to lock STDOUT on Windows (or any other console handle for that matter).

I don't immediately see a portable way to check if a file descriptor is lockable (without actually trying to lock).

For Windows, the following might do:

 main/streams/plain_wrapper.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c
index fb2addb9e6..080f88a509 100644
--- a/main/streams/plain_wrapper.c
+++ b/main/streams/plain_wrapper.c
@@ -731,6 +731,13 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void
 			}
 
 			if ((uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) {
+#ifdef PHP_WIN32
+				if (GetFileType((HANDLE) _get_osfhandle(fd)) == FILE_TYPE_CHAR) {
+					return -1;
+				} else {
+					return 0;
+				}
+#endif
 				return 0;
 			}

That would cater to console handles and other character devices (e.g. LPT), but I assume that none of them is lockable (and certainly not with our current flock() implementation which uses LockFileEx()).

I don't quite understand "a portable way", though – isn't this a Windows only issue (at least it's tagged as such).

@nielsdos
Copy link
Member

I don't quite understand "a portable way", though – isn't this a Windows only issue (at least it's tagged as such).

As far as we know yes. What I mean is that we don't check whether something is lockable on other platforms too. Although the behaviour is observed on Windows, there can be other platforms that have similar issues. I looked for a portable (e.g. posix or standard C) solution but couldn't find one.

@cmb69
Copy link
Member

cmb69 commented Jul 15, 2024

What I mean is that we don't check whether something is lockable on other platforms too.

Ah, right. That might be meaningless anyway, because even if general "lockability" would be supported, you can never know if the file can actually be locked with a certain operation (LOCK_EX/LOCK_SH), so in practice you probably just try (possibly non blocking). PHP's stream_supports_lock() might not have been the most well-thought out invention; at least it might have been a good idea to add a third return option (something like "don't know").

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants