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

Fatal error: Uncaught Error: Interface 'Stringable' not found in #1010

Open
reinos opened this issue Apr 17, 2024 · 8 comments · May be fixed by #1035
Open

Fatal error: Uncaught Error: Interface 'Stringable' not found in #1010

reinos opened this issue Apr 17, 2024 · 8 comments · May be fixed by #1035
Milestone

Comments

@reinos
Copy link

reinos commented Apr 17, 2024

Bug report

Question Answer
PHP-Scoper version master branch due to this possible fix
PHP version 8.2
Platform with version MacOS

After running php-scoper it seems that some interfaces are no scoped.

See the source of file /vendor/symfony/polyfill-php80/PhpToken.php

namespace Symfony\Polyfill\Php80;

class PhpToken implements \Stringable
{
....
}

and file vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php

if (\PHP_VERSION_ID < 80000) {
    interface Stringable
    {
        /**
         * @return string
         */
        public function __toString();
    }
}

are scoped like

namespace ReinosMapsScoped\Symfony\Polyfill\Php80;

class PhpToken implements \Stringable <!-- NOTICE THIS, its not scoped
{
...
}
if (\PHP_VERSION_ID < 80000) {
    interface Stringable
    {
        /**
         * @return string
         */
        public function __toString();
    }
    \class_alias('ReinosMapsScoped\\Stringable', 'Stringable', \false);
}

The issue seems that class PhpToken implements \Stringable is not scoped to class PhpToken implements \ ReinosMapsScoped\Stringable

Now it throws an PHP error Fatal error: Uncaught Error: Interface 'Stringable' not found in..... and with adding manually the scoped namespace it fixed the error.

Is this a bug in PHPscoper or a configuration issue? 🤔

@theofidry
Copy link
Member

It you're using the latest tagged version it is a known issue, if you're using the master branch however it should be fixed.

You notice you have a class alias statement added:

    \class_alias('ReinosMapsScoped\\Stringable', 'Stringable', \false);

You should also have a corresponding class_exists() statement in your scoper-autoload.php so \Stingable should be a registered symbol by the time PhpToken is loaded.

If you still have the problem on master please provide a reproducer so that I can look into it

@reinos
Copy link
Author

reinos commented May 3, 2024

Hi @theofidry, thanks for your reply!

So I did some digging and I found the issue.

The file scoper-autoload.php generate this for me.

humbug_phpscoper_expose_class('Reinos_maps_upd', 'ReinosMapsScoped\Reinos_maps_upd');
humbug_phpscoper_expose_class('Reinos_maps', 'ReinosMapsScoped\Reinos_maps');
humbug_phpscoper_expose_class('Reinos_maps_ext', 'ReinosMapsScoped\Reinos_maps_ext');
humbug_phpscoper_expose_class('Reinos_maps_ACT', 'ReinosMapsScoped\Reinos_maps_ACT');
humbug_phpscoper_expose_class('Reinos_maps_mcp', 'ReinosMapsScoped\Reinos_maps_mcp');
humbug_phpscoper_expose_class('ComposerAutoloaderInitc47e9d714b1dba70cb6cf7603e380961', 'ReinosMapsScoped\ComposerAutoloaderInitc47e9d714b1dba70cb6cf7603e380961');
humbug_phpscoper_expose_class('GooglePolylineBadInput', 'ReinosMapsScoped\GooglePolylineBadInput');
humbug_phpscoper_expose_class('GoogleTraitBadInputTest', 'ReinosMapsScoped\GoogleTraitBadInputTest');
humbug_phpscoper_expose_class('GooglePolylineHacking', 'ReinosMapsScoped\GooglePolylineHacking');
humbug_phpscoper_expose_class('GoogleTraitHackingTest', 'ReinosMapsScoped\GoogleTraitHackingTest');
humbug_phpscoper_expose_class('JsonException', 'ReinosMapsScoped\JsonException');
humbug_phpscoper_expose_class('PhpToken', 'ReinosMapsScoped\PhpToken');
humbug_phpscoper_expose_class('ValueError', 'ReinosMapsScoped\ValueError');
humbug_phpscoper_expose_class('Attribute', 'ReinosMapsScoped\Attribute');
humbug_phpscoper_expose_class('UnhandledMatchError', 'ReinosMapsScoped\UnhandledMatchError');
humbug_phpscoper_expose_class('Stringable', 'ReinosMapsScoped\Stringable');
humbug_phpscoper_expose_class('Normalizer', 'ReinosMapsScoped\Normalizer');

Indeed, Stringable is there and should be registered. However, the class PhpToken is called first and that class implements the Stringable interface. So at that time when PhpToken is registering, \Stringable is not yet created as alias.

By moving Stringable just before the PhpToken fixed the issue.

humbug_phpscoper_expose_class('Stringable', 'ReinosMapsScoped\Stringable');
humbug_phpscoper_expose_class('PhpToken', 'ReinosMapsScoped\PhpToken');

Any idea how we can fix this?

@theofidry
Copy link
Member

I see, that's an annoying one... We could fix it for this specific case for now. Fixing this more generally looks a bit tricky, doable but tricky.

@theofidry theofidry added this to the 1.0.0 milestone May 3, 2024
@incraigulous
Copy link

@theofidry , do you know of a workaround we could use until this can be fixed? I can confirm that it works if I reorder my exposed classes to this order:

humbug_phpscoper_expose_class( 'Stringable', 'CodeZone\Bible\Stringable' );
humbug_phpscoper_expose_class( 'PhpToken', 'CodeZone\Bible\PhpToken' );

@incraigulous
Copy link

incraigulous commented May 15, 2024

@theofidry , what about a setting to allow for setting a priority of exposed classes?

'exposed-class-priority' => [
     'Stringable' => 0,
     'PhpToken' => 1
]

Just an idea. I'm sure there is a more elegant solution.

@theofidry
Copy link
Member

I'm working on a PoC for it. In terms of effort having another setting is not exactly trivial either, so might as well try to fix it the proper way. I don't think it is that hard in the end, but I was on holiday so didn't get that much time to look into it, I hope to fix this within the week or next week though.

@incraigulous
Copy link

🙏 Thank you @theofidry!

@theofidry theofidry linked a pull request May 18, 2024 that will close this issue
@theofidry
Copy link
Member

Ok I'll need some help here... Could you guys provide a super minimal reproducer?

I identified the source code changes that I need to do, it's not a crazy amount but for the tests it's another story. So I've been trying to implement an e2e test instead to validate my idea first before fixing all the other tests... And I can't make it work.

I did my changes in #1035. There is some source code changes but they actually have no impact yet (I'm tracking the dependencies of the class when aliasing a class, but I do nothing with them for now). The end to end test is in the fixtures/set041-exposed-symbols-hierarchy directory and executed with make e2e_041.

I've tried various things, but I cannot reproduce the issue you describe. I've even added a polyfill-80 structure like to try to reproduce it without having to run an old PHP version but this does not do the trick: for me the StringeableLike interface (which would be the Stringeable of your case) is present in the loader classmap (because it is registered in classmap in the composer.json).

I also notice that PhpToken is namespaced, so I don't get why yours is exposed this way.

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

Successfully merging a pull request may close this issue.

3 participants