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

Exporting closure containing self leads to fatal error #26

Open
boesing opened this issue Dec 8, 2022 · 2 comments
Open

Exporting closure containing self leads to fatal error #26

boesing opened this issue Dec 8, 2022 · 2 comments

Comments

@boesing
Copy link

boesing commented Dec 8, 2022

Hey there,

I have the following (reduced) example of our application which uses this component to dump the application config to a filesystem cache:

<?php

use Brick\VarExporter\VarExporter;

require __DIR__ . '/vendor/autoload.php';

$dataProvider = new class {
    public const CONFIGURATION_ENTRY = 'data-provider-specific-config';

    public function getConfig(): array
    {
        return [
            self::CONFIGURATION_ENTRY => [
                'foo' => 'bar',
            ],
            'factories' => [
                'MyService' => static function (array $config): object {
                    $configForMyDataProvider = $config[self::CONFIGURATION_ENTRY];

                    return new class ($configForMyDataProvider['foo']) {

                        public string $foo;

                        public function __construct(string $foo)
                        {
                            $this->foo = $foo;
                        }
                    };
                },
            ],
        ];
    }
};

$config = array_merge(
    [],
    $dataProvider->getConfig(),
    // Other data providers providing stuff
);

$content = "<?php\n" . VarExporter::export(
    $config,
    VarExporter::ADD_RETURN | VarExporter::CLOSURE_SNAPSHOT_USES
);

file_put_contents('cached-config.php', $content);

$configFromFilesystem = require 'cached-config.php';

($configFromFilesystem['factories']['MyService'])($configFromFilesystem);

So the exporter generates this code:

<?php
return [
    'data-provider-specific-config' => [
        'foo' => 'bar'
    ],
    'factories' => [
        'MyService' => static function (array $config) : object {
            $configForMyDataProvider = $config[self::CONFIGURATION_ENTRY];
            return new class($configForMyDataProvider['foo'])
            {
                public string $foo;
                public function __construct(string $foo)
                {
                    $this->foo = $foo;
                }
            };
        }
    ]
];

Overall, no real problem and everything works until the closure is being used to instantiate the service. PHP gives us the follow fatal error:

Fatal error: Uncaught Error: Cannot access self:: when no class scope is active in /project/cached-config.php:8

I wonder if it is possible to convert the self reference to some kind of FQCN so that we do not have to care about using self or not.

I am willing to contribute a fix. Just wanted to raise the issue first.

Feel free to give me some feedback whenever you find some time.
Thanks!

@boesing
Copy link
Author

boesing commented Dec 8, 2022

Okay, did my first researches and it seems that this library is not the problem.
Might need to create an issue in nikic/php-parser.

@boesing
Copy link
Author

boesing commented Dec 16, 2022

I got feedback from Nikita and it seems that it should be possible somehow from within this tool:
nikic/PHP-Parser#903 (comment)

I will see if I can prepare a patch once I find some time. But as always, there are other things with higher priorities (I ended up fixing the issue by just referencing the class by myself).

So any help would be highly appreciated here.

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

1 participant