Cms: Cache console commands exception when using Yii's Session Redis Driver

Created on 12 Feb 2018  路  7Comments  路  Source: craftcms/cms

Description

Calling the php craft cache or any other cache/* commands throw an exception.

Exception 'yii\base\UnknownMethodException' with message 'Calling unknown method: craft\console\Request::getIsSecureConnection() in vendor/yiisoft/yii2/base/Component.php:294'

Steps to reproduce

  1. php craft cache

Additional info

  • Craft version: Craft Pro 3.0.0-RC9
  • PHP version: PHP 7.0.27
  • Database driver & version: MySQL 5.7.17
  • Plugins & versions: Amazon S3 1.0.8, Redactor 1.0.1
bug

Most helpful comment

Implemented that idea for RC10 - which will be released later today. So going forward, you can fix this by defining your session component in config/app.web.php:

return [
    'components' => [
        'session' => function() {
            $stateKeyPrefix = md5('Craft.'.craft\web\Session::class.'.'.Craft::$app->id);
            /** @var yii\redis\Session $session */
            $session = Craft::createObject([
                'class' => yii\redis\Session::class,
                'flashParam' => $stateKeyPrefix.'__flash',
                'name' => Craft::$app->getConfig()->getGeneral()->phpSessionName,
                'cookieParams' => Craft::cookieConfig(),
            ]);
            $session->attachBehaviors([craft\behaviors\SessionBehavior::class]);
            return $session;
        },
    ],
];

All 7 comments

I鈥檓 not able to reproduce this; do you have any cache-related settings set in config/general.php or config/app.php?

Thanks Brandon, it's on my side. I've set the session component to use Redis. After removing the following session component, the cache command works perfectly.

$config = [
    'components' => [
        'redis' => [
            'class' => yii\redis\Connection::class,
            'hostname' => getenv('REDIS_SERVER'),
            'port' => getenv('REDIS_PORT'),
            'database' => getenv('REDIS_DATABASE'),
        ],
        'cache' => [
            'class' => yii\redis\Cache::class,
            'redis' => [
                'hostname' => getenv('REDIS_SERVER'),
                'port' => getenv('REDIS_PORT'),
                'database' => getenv('REDIS_DATABASE'),
            ],
        ],
        'session' => function() {
            $stateKeyPrefix = md5('Craft.'.craft\web\Session::class.'.'.Craft::$app->id);
            /** @var yii\redis\Session $session */
            $session = Craft::createObject([
                'class' => yii\redis\Session::class,
                'flashParam' => $stateKeyPrefix.'__flash',
                'name' => Craft::$app->getConfig()->getGeneral()->phpSessionName,
                'cookieParams' => Craft::cookieConfig(),
            ]);
            $session->attachBehaviors([craft\behaviors\SessionBehavior::class]);
            return $session;
        },
    ],
];

I will setup PHP to use Redis sessions instead of overwriting this component.

Ideally we鈥檒l be able to fix this so you can continue using Yii鈥檚 Redis driver.

I鈥檓 not seeing any references to getIsSecureConnection in their code though. Can you search for the error in storage/logs/console.log and post the stack trace that follows?

Here it is, it is in the Craft::cookieConfig() function :

2018-02-12 20:32:53 [-][-][-][error][yii\base\UnknownMethodException] yii\base\UnknownMethodException: Calling unknown method: craft\console\Request::getIsSecureConnection() in /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/base/Component.php:294
Stack trace:
#0 /Volumes/Data/Martin/Work/website/vendor/craftcms/cms/src/Craft.php(103): yii\base\Component->__call('getIsSecureConn...', Array)
#1 /Volumes/Data/Martin/Work/website/config/app.php(57): Craft::cookieConfig()
#2 [internal function]: craft\services\Config->{closure}()
#3 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/di/Container.php(501): call_user_func_array(Object(Closure), Array)
#4 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/BaseYii.php(351): yii\di\Container->invoke(Object(Closure), Array)
#5 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/di/ServiceLocator.php(137): yii\BaseYii::createObject(Object(Closure))
#6 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/base/Module.php(724): yii\di\ServiceLocator->get('session', true)
#7 /Volumes/Data/Martin/Work/website/vendor/craftcms/cms/src/console/Application.php(106): yii\base\Module->get('session', true)
#8 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/console/controllers/CacheController.php(275): craft\console\Application->get('session')
#9 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/console/controllers/CacheController.php(54): yii\console\controllers\CacheController->findCaches()
#10 [internal function]: yii\console\controllers\CacheController->actionIndex()
#11 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#12 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/base/Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#13 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/console/Controller.php(135): yii\base\Controller->runAction('', Array)
#14 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/base/Module.php(528): yii\console\Controller->runAction('', Array)
#15 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/console/Application.php(180): yii\base\Module->runAction('cache', Array)
#16 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/console/Application.php(147): yii\console\Application->runAction('cache', Array)
#17 /Volumes/Data/Martin/Work/website/vendor/yiisoft/yii2/base/Application.php(386): yii\console\Application->handleRequest(Object(craft\console\Request))
#18 /Volumes/Data/Martin/Work/website/craft(24): yii\base\Application->run()
#19 {main}

Thanks :)

Thanks! I see what鈥檚 going on. When the cache command is executed, it will loop through all of the configured application components to find all the ones that implement CacheInterface. In the process your session config closure is called, which is calling Craft::cookieConfig(), which rightly assumes that it鈥檚 a web request.

The same error could have just as easily affected _all_ console requests, had you set your session config to an array that calls Craft::cookieConfig() directly, rather than through a closure.

Craft鈥檚 own config avoids this by having two separate app config files 鈥撀爋ne for web requests and one for console requests 鈥撀爓hich only configure the components that make sense for that request type. Web requests have a session component, but console requests don鈥檛. So the actual Craft bug here is simply that there鈥檚 no (simple) way for developers to do the same, in their project config.

One possible solution: We could start checking for config/app.web.php or config/app.console.php, depending on the request type, in addition to config/app.php. So if you want to override something like the session component, you鈥檇 just do it from config/app.web.php, so it doesn鈥檛 exist at all for console requests.

Implemented that idea for RC10 - which will be released later today. So going forward, you can fix this by defining your session component in config/app.web.php:

return [
    'components' => [
        'session' => function() {
            $stateKeyPrefix = md5('Craft.'.craft\web\Session::class.'.'.Craft::$app->id);
            /** @var yii\redis\Session $session */
            $session = Craft::createObject([
                'class' => yii\redis\Session::class,
                'flashParam' => $stateKeyPrefix.'__flash',
                'name' => Craft::$app->getConfig()->getGeneral()->phpSessionName,
                'cookieParams' => Craft::cookieConfig(),
            ]);
            $session->attachBehaviors([craft\behaviors\SessionBehavior::class]);
            return $session;
        },
    ],
];

Superb! Thanks Brandon, works for me.

Was this page helpful?
0 / 5 - 0 ratings