Repository: php/pie (php)

Issue:
broken setup of any extensions causes wrong validation of php binary
If have a wrong reference to a php extension the warning cause pie to discard the php binary as invalid. 

For example:
```
PHP Warning:  PHP Startup: Unable to load dynamic library 'redis' (tried: /opt/homebrew/lib/php/pecl/20240924/redis (dlopen(/opt/homebrew/lib/php/pecl/20240924/redis, 0x0009): [...] in Unknown on line 0
```

```
In InvalidPhpBinaryPath.php line 31:

  The php binary at "/opt/homebrew/Cellar/php/8.4.8/bin/php" does not appear to be a PHP binary
```

Pull request:
273: Fix PHP warnings not being cleaned from output
Fixes #273 

Patch:
diff --git a/src/Platform/TargetPhp/PhpBinaryPath.php b/src/Platform/TargetPhp/PhpBinaryPath.php
index cb725f9..8163b25 100644
--- a/src/Platform/TargetPhp/PhpBinaryPath.php
+++ b/src/Platform/TargetPhp/PhpBinaryPath.php
@@ -17,6 +17,7 @@ use Symfony\Component\Process\PhpExecutableFinder;
 use Webmozart\Assert\Assert;
 
 use function array_combine;
+use function array_filter;
 use function array_key_exists;
 use function array_keys;
 use function array_map;
@@ -349,16 +350,12 @@ PHP,
 
     private static function cleanWarningAndDeprecationsFromOutput(string $testOutput): string
     {
-        $testOutput = explode("\n", $testOutput);
-
-        foreach ($testOutput as $key => $line) {
-            if (! preg_match('/^(Deprecated|Warning):/', $line)) {
-                continue;
-            }
-
-            unset($testOutput[$key]);
-        }
-
-        return implode("\n", $testOutput);
+        return implode(
+            "\n",
+            array_filter(
+                explode("\n", $testOutput),
+                static fn (string $line) => ! preg_match('/^(Deprecated|Warning|PHP Warning):/', $line),
+            ),
+        );
     }
 }


Test patch:
diff --git a/test/assets/valid-php-with-warnings.sh b/test/assets/valid-php-with-warnings.sh
new file mode 100755
index 0000000..ee3eaf8
--- /dev/null
+++ b/test/assets/valid-php-with-warnings.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+# These should be implicitly filtered out by PIE
+echo "PHP Warning:  PHP Startup: Unable to load dynamic library 'redis' (tried: /path/to/redis (dlopen(/path/to/redis, 0x0009): [...] in Unknown on line 0"
+echo "Warning: PHP Startup: Unable to load dynamic library 'pdo_mysql' (tried: /path/to/pdo_mysql (/path/to/pdo_mysql: cannot open shared object file: No such file or directory), /path/to/pdo_mysql.so (/path/to/pdo_mysql.so: undefined symbol: mysqlnd_debug_std_no_trace_funcs)) in Unknown on line 0"
+echo "Deprecated: Function unsafe_function() is deprecated since 1.5, use safe_replacement() instead in example.php on line 9"
+
+# This is the expected output of PIE:
+echo "PHP";
diff --git a/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php b/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php
index b3c9a86..89be56e 100644
--- a/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php
+++ b/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php
@@ -48,7 +48,8 @@ use const PHP_RELEASE_VERSION;
 #[CoversClass(PhpBinaryPath::class)]
 final class PhpBinaryPathTest extends TestCase
 {
-    private const FAKE_PHP_EXECUTABLE = __DIR__ . '/../../../assets/fake-php.sh';
+    private const FAKE_PHP_EXECUTABLE     = __DIR__ . '/../../../assets/fake-php.sh';
+    private const VALID_PHP_WITH_WARNINGS = __DIR__ . '/../../../assets/valid-php-with-warnings.sh';
 
     public function testNonExistentPhpBinaryIsRejected(): void
     {
@@ -83,6 +84,16 @@ final class PhpBinaryPathTest extends TestCase
         PhpBinaryPath::fromPhpBinaryPath(self::FAKE_PHP_EXECUTABLE);
     }
 
+    public function testWarningsAndDeprecationsAreFiltered(): void
+    {
+        if (Platform::isWindows()) {
+            self::markTestSkipped('Bash script does not run on Windows.');
+        }
+
+        $phpBinary = PhpBinaryPath::fromPhpBinaryPath(self::VALID_PHP_WITH_WARNINGS);
+        self::assertSame(self::VALID_PHP_WITH_WARNINGS, $phpBinary->phpBinaryPath);
+    }
+
     public function testVersionFromCurrentProcess(): void
     {
         $phpBinary = PhpBinaryPath::fromCurrentProcess();