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

Symfony function polyfills get put into the wrong namespace #657

Closed
allejo opened this issue Apr 18, 2022 · 4 comments
Closed

Symfony function polyfills get put into the wrong namespace #657

allejo opened this issue Apr 18, 2022 · 4 comments

Comments

@allejo
Copy link

allejo commented Apr 18, 2022

Bug report

Question Answer
Box version 3.16.0
PHP version 7.4.21
Platform with version macOS
Github Repo allejo/rrlog @ feature/box-cleanup

When my box.json.dist uses the "KevinGH\\Box\\Compactor\\PhpScoper" compactor, any function that Symfony polyfills in the global namespace is written into the wrong namespace. Kinda hard to explain, so let me try my best.

In my project's vendor directory, the InputOption.php file makes use of str_starts_with, which exists in PHP 8.0+ but Symfony makes use of symfony/polyfills-php80 to polyfill this function, allowing it to work as intended.

namespace Symfony\Component\Console\Input;

class InputOption
{
    // ...

    public function __construct(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
    {
        if (str_starts_with($name, '--')) {
            $name = substr($name, 2);
        }

// ...

This works because symfony/polyfills-php80 has a bootstrap file that has this logic in the global namespace:

if (!function_exists('str_starts_with')) {
    function str_starts_with(?string $haystack, ?string $needle): bool { ... }
}

After running Box with PhpScoper, the InputOption.php file ends up with code that looks like,

namespace humbug12345\Symfony\Component\Console\Input;

class InputOption
{
    // ...

    public function __construct(string $name, $shortcut = null, int $mode = null, string $description = '', $default = null)
    {
        if (\str_starts_with($name, '--')) { // <- Notice the `\` at the beginning of `str_starts_with()`
            $name = substr($name, 2);
        }

// ...

And symfony/polyfill-php80's bootstrap file has been updated to,

namespace humbug12345;

if (!function_exists('str_starts_with')) {
    function str_starts_with(?string $haystack, ?string $needle): bool { ... }
}

This leads to the str_starts_with() not being found since InputOption.php is referencing the one in the global namespace and the polyfill is being added to the scoped namespace.

box.json.dist
{
   "main": "bin/rrlog",
   "compression": "GZ",
   "compactors": [
       "KevinGH\\Box\\Compactor\\Php",
       "KevinGH\\Box\\Compactor\\PhpScoper"
   ],
   "datetime": "release-date",
   "git-version": "package_version",
   "output": "rrlog.phar"
}
Output
$ php -d display_errors=on rrlog.phar

Fatal error: Uncaught Error: Call to undefined function str_starts_with() in phar:///Users/allejo/Development/BZFlag/rrlog/rrlog.phar/vendor/symfony/console/Input/InputOption.php on line 20

Error: Call to undefined function str_starts_with() in phar:///Users/allejo/Development/BZFlag/rrlog/rrlog.phar/vendor/symfony/console/Input/InputOption.php on line 20
@Luc45
Copy link

Luc45 commented Nov 9, 2022

Great summary of this bug. I'm also having the exact same issue. This path does not have a namespace originally, and PHP-Scoper adds one:

  • vendor/symfony/polyfill-php80/bootstrap.php

Original:

if (!function_exists('str_starts_with')) {
    function str_starts_with(?string $haystack, ?string $needle): bool { return p\Php80::str_starts_with($haystack ?? '', $needle ?? ''); }
}

In Phar:

namespace _HumbugBox416619033617;

if (!\function_exists('str_ends_with')) {
    function str_ends_with(?string $haystack, ?string $needle) : bool
    {
        return p\Php80::str_ends_with($haystack ?? '', $needle ?? '');
    }
}

This PR should have solved it, but for some reason it's still prefixing it for me: https://github.com/box-project/box/pull/579/files

It seems this applies only to the Box.phar, not the Phars that it generates.

Box version (Phar): Box version 4.1.0@fb068b2 2022-09-25 14:01:25 UTC

@Luc45
Copy link

Luc45 commented Nov 9, 2022

There are a lot of libraries with this issue out there. The way that Box itself handles is by skip scoping the polyfills. Essentially, use this scoper.inc.php:

Removed, see comment below

It seems to me that this opens a breach in the encapsulation of the Phar, but it's the only solution that I could find.

@theofidry
Copy link
Member

theofidry commented Nov 9, 2022

To expand on @Luc45 good explanation, there is this doc entry https://github.com/humbug/php-scoper/blob/main/docs/further-reading.md#polyfills.

Of course could be missing in which case contributions are welcomed.

I'm also aware this is a PITA, no one is happy about it, but maybe I found a way to get rid of that in the next version.

Also thanks for providing a reproducer still @allejo :)

If there is any other problem please feel free to open another issue

@Luc45
Copy link

Luc45 commented Nov 9, 2022

I'm removing my code example in favor of the snippet that @theofidry shared, which is more complete.

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

No branches or pull requests

3 participants