hiqdev/composer-config-plugin

Is it possible to use $params in anonymous functions?

Closed this issue · 9 comments

Composer.json

    "extra": {
        "config-plugin": {
            "params": [
                "config/params.php",
                "?config/params-local.php"
            ],
            "web": [
                "config/web.php"
            ]
        }
    }

params.php

return [
    'app.name' => 'My Application',
];

web.php

return [
    'app' => function () use($params) {
        return [
            'name' => $params['app.name']
        ];
    },
    'appName' => $params['app.name'],
];

index.php

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

$config = require hiqdev\composer\config\Builder::path('web');

$appClosure = $config['app'];
$app = call_user_func($appClosure);
var_dump($app, $config['appName']);

Got:

php ./public/index.php
PHP Notice: Undefined variable: params in /home/razon/Projects/php/composer-config/vendor/hiqdev/composer-config-plugin-output/web.php on line 10
array(1) {
["name"]=>
NULL
}
string(14) "My Application"

BTW, the generated web.php missing use language structure

$baseDir = dirname(dirname(dirname(__DIR__)));

defined('COMPOSER_CONFIG_PLUGIN_BASEDIR') or define('COMPOSER_CONFIG_PLUGIN_BASEDIR', $baseDir);

$_ENV = array_merge((array) require __DIR__ . '/dotenv.php', (array) $_ENV);

return array (
  'app' => function () {
        return [
            'name' => $params['app.name']
        ];
    },
  'appName' => 'My Application',
);

Please, show what do you want in practice, cause your example doesn't look practical.

I am trying to access parameters in an anonymous function.

Such as logger configuration(DI), I want to make FileTarget's levels to be mergeable via params.php and params-local.php:

    'logger' => function (yii\di\Container $container) {
        $logger = new Yiisoft\Log\Logger([
            $container->get(Yiisoft\Log\FileTarget::class),
        ]);

        return $logger;
    },
    Yiisoft\Log\FileTarget::class => function (yii\di\Container $container) use($params) {
        /** @var yii\base\Aliases $aliases */
        $aliases = $container->get('aliases');
        $target = new Yiisoft\Log\FileTarget(
            $aliases->get($params['console.logger.target.file.logFile']),
            $container->get(Yiisoft\Log\FileRotator::class)
        );
        $target->levels = $params['logger.target.file.levels'];
        //...
        return $target;
    },
    Yiisoft\Log\FileRotator::class => [
        '__class' => Yiisoft\Log\FileRotator::class,
    ],

params.php

return [
    'logger.target.file.levels' => ['warning', 'error'],
];

params-local.php

return [
    'logger.target.file.levels' => ['info', 'debug'],
];

You can get params from the app object similarly you get aliases:

$target->levels = $container->get('app')->params['logger.target.file.levels'];

But it looks like a good case to improve Yii DI for these objects could be configured with arrays only without closures.

Something like this:

    'logger' => [
        '__class' => Logger::class,
        '__construct' => [
            [Reference::to('file-target')],
        ],
    ],
    'file-target' => [
        '__class' => Yiisoft\Log\FileTarget::class,
        '__construct' => [
            Reference::aliasTo('@logger/logFile'),
            Reference::to(Yiisoft\Log\FileRotator::class),
        ],
        'levels' => $params['logger.target.file.levels'],
    ],

I'll look into it on a weekend.

Your solution look good to me:)

EDIT:
I could not find the method Reference::aliasTo(yiisoft/di), am I missing something?

Hi, I found another issue about exporting Closure, here is an example of yii-demo.

I've read the code of Helper::dumpClosure, but have no idea how to handle it so far.

It may can be fixed by two ways:

  1. Defines full qualified name in closure, such as return new Yiisoft\Log\Logger([]), simple, but strange.
  2. Dump the use lines.

Reproduce:

$ ./vendor/bin/yii serve
$ curl http://localhost:8080
[Tue Jul  2 13:52:29 2019] PHP Fatal error:  Uncaught Error: Class 'Logger' not found in /home/razon/Projects/yiisoft/yii-demo/vendor/hiqdev/composer-config-plugin-output/web.php:478
Stack trace:
#0 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/factory/src/Definitions/CallableDefinition.php(19): {closure}(Object(yii\di\Container), Array)
#1 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/di/src/Container.php(128): Yiisoft\Factory\Definitions\CallableDefinition->resolve(Object(yii\di\Container), Array)
#2 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/di/src/Container.php(116): yii\di\Container->buildInternal('logger', Array)
#3 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/di/src/Container.php(84): yii\di\Container->build('logger', Array)
#4 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/factory/src/Definitions/Reference.php(45): yii\di\Container->get('logger')
#5 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/di/src/Container.php(128): Yiisoft\Factory\Definitions\Reference->resolve(Object(yii\di\Container), Array)
# in /home/razon/Projects/yiisoft/yii-demo/vendor/hiqdev/composer-config-plugin-output/web.php on line 478
[Tue Jul  2 13:52:29 2019] [::1]:48260 [500]: / - Uncaught Error: Class 'Logger' not found in /home/razon/Projects/yiisoft/yii-demo/vendor/hiqdev/composer-config-plugin-output/web.php:478
Stack trace:
#0 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/factory/src/Definitions/CallableDefinition.php(19): {closure}(Object(yii\di\Container), Array)
#1 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/di/src/Container.php(128): Yiisoft\Factory\Definitions\CallableDefinition->resolve(Object(yii\di\Container), Array)
#2 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/di/src/Container.php(116): yii\di\Container->buildInternal('logger', Array)
#3 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/di/src/Container.php(84): yii\di\Container->build('logger', Array)
#4 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/factory/src/Definitions/Reference.php(45): yii\di\Container->get('logger')
#5 /home/razon/Projects/yiisoft/yii-demo/vendor/yiisoft/di/src/Container.php(128): Yiisoft\Factory\Definitions\Reference->resolve(Object(yii\di\Container), Array)
# in /home/razon/Projects/yiisoft/yii-demo/vendor/hiqdev/composer-config-plugin-output/web.php on line 478

Dump the use lines is good but how? :)

I have no idea so far, it maybe impossible :(

That's actually a problem not about anonymous functions but about $params is not defined in configs at all.

Initialization of $params was added in configs.
So this problem is fixed. Just use as you expected from the very beginning:

In web.php:

return [
    'app' => function () use ($params) {
        return [
            'name' => $params['app.name']
        ];
    },
    'appName' => $params['app.name'],
];

Closing this issue. Please open a new one in case of any problems.