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

windows: fgets() does not work with shebang #16147

Closed
staabm opened this issue Oct 1, 2024 · 4 comments
Closed

windows: fgets() does not work with shebang #16147

staabm opened this issue Oct 1, 2024 · 4 comments

Comments

@staabm
Copy link
Contributor

staabm commented Oct 1, 2024

Description

the example in question is reproducible with phpunit on windows, but I was not able to reduce it further to the actual root cause.
its super weird.

The following code:

<?php

$input = 'C:\dvl\Workspace\PHP-Parallel-Lint\tests/fixtures/fixture-07/example.php';

foreach (explode(PHP_EOL, $input) as $file) {
    $skip = false;
    $f = @fopen($file, 'r');
    if ($f) {
        $firstLine = fgets($f);

        var_dump($firstLine);
        var_dump($firstLine[0]);
        var_dump($firstLine[1]);
        var_dump(strpos($firstLine, '#!'));
    }

    echo $file . ';' . ($skip ? '1' : '0') . PHP_EOL;
}

with example.php:

#!/usr/bin/php
<?php // lint < 5.3

$myString = 'This is always skipped';
echo $myString;

Resulted in this output:

string(15) "#!/usr/bin/php
"
string(1) "#"
string(1) "!"
bool(false)

But I expected this output instead:

string(15) "#!/usr/bin/php
"
string(1) "#"
string(1) "!"
bool(0)

No problems with the same code on Ubuntu.

The above example is not reproducible as is when running in isolation.
its reproducible though when running as part of the test-suite of PHP-Parallel-Lint (see steps below).

I think its somehow related to that the code is invoked via
$this->process = proc_open($cmdLine, $descriptors, $pipes, null, null, array('bypass_shell' => true));

Steps to reproduce the problem on a Windows machine

git clone https://github.com/php-parallel-lint/PHP-Parallel-Lint
composer install
./vendor/bin/phpunit --no-coverage tests/Unit/ParallelLintLintTest.php --filter=testSkipShebang

-> results in a PHPUnit test-failure but should succeed

this test executes skip-linting.php which fails because of the strange logic happening I described earlier.

PHP Version

PHP 8.2.12 and others

Operating System

Windows

@cmb69
Copy link
Member

cmb69 commented Oct 1, 2024

Well, see what escapeshellarg() does to the line on Windows

if (strpos($firstLine, '# ') === 0) {

According to the fine manual, this is expected:

On Windows, escapeshellarg() instead replaces percent signs, exclamation marks (delayed variable substitution) and double quotes with spaces and adds double quotes around the string. Furthermore, each streak of consecutive backslashes () is escaped by one additional backslash.

If skip-linting.php would use double-quotes instead of single-quotes, likely all tests would fail.

Quick "fix" for PHP-Parallel-Lint which makes the test pass on Windows:

 src/Process/Process.php | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/Process/Process.php b/src/Process/Process.php
index 4e72e58..c18dc6e 100644
--- a/src/Process/Process.php
+++ b/src/Process/Process.php
@@ -45,7 +45,11 @@ class Process
             self::STDERR => array('pipe', self::WRITE),
         );
 
-        $cmdLine = $executable . ' ' . implode(' ', array_map('escapeshellarg', $arguments));
+        $args = "";
+        foreach ($arguments as $arg) {
+            $args .= ' "' . $arg . '"';
+        }
+        $cmdLine = $executable . ' ' . $args;
         $this->process = proc_open($cmdLine, $descriptors, $pipes, null, null, array('bypass_shell' => true));
 
         if ($this->process === false || $this->process === null) {

So this doesn't look like a bug in php-src ("fixing" escapeshellarg() is not really possible).

@cmb69 cmb69 closed this as not planned Won't fix, can't repro, duplicate, stale Oct 1, 2024
@staabm
Copy link
Contributor Author

staabm commented Oct 1, 2024

thank you for investigating this.

I still wonder why it has such a strange output on windows

        var_dump($firstLine);
        var_dump($firstLine[0]);
        var_dump($firstLine[1]);
        var_dump(strpos($firstLine, '#!'));
string(15) "#!/usr/bin/php
"
string(1) "#"
string(1) "!"
bool(false)

does it mean the "#" in the string is not the same character as the literal "#" in the source-code?


update:

I just realized its not the "#" but the "!":

        var_dump($firstLine[0] == '#'); // true
        var_dump($firstLine[1] == '!'); // false
        var_dump(strpos($firstLine, '#!')); // false

@staabm
Copy link
Contributor Author

staabm commented Oct 1, 2024

Quick "fix" for PHP-Parallel-Lint which makes the test pass on Windows:

I found a easy fix, thanks again.

@cmb69
Copy link
Member

cmb69 commented Oct 1, 2024

Ah, I wanted to suggest to use a file, but that might have been a bit slow; but since the file is already there, that looks like the best solution.

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

2 participants