Joomla-cms: Plugins are not usable on a CLI Application

Created on 29 May 2017  路  8Comments  路  Source: joomla/joomla-cms

Steps to reproduce the issue

Create an CLI application that uses any plugin.

````
/**

  • @package Joomla.Cli
    *
  • @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
  • @license GNU General Public License version 2 or later; see LICENSE.txt
    */

// We are a valid entry point.
const _JEXEC = 1;

// Load system defines
if (file_exists(dirname(__DIR__) . '/defines.php'))
{
require_once dirname(__DIR__) . '/defines.php';
}

if (!defined('_JDEFINES'))
{
define('JPATH_BASE', dirname(__DIR__));
require_once JPATH_BASE . '/includes/defines.php';
}

// Get the framework.
require_once JPATH_LIBRARIES . '/import.legacy.php';

// Bootstrap the CMS libraries.
require_once JPATH_LIBRARIES . '/cms.php';

// Configure error reporting to maximum for CLI output.
error_reporting(E_ALL);
ini_set('display_errors', 1);

// Load Library language
$lang = JFactory::getLanguage();

// Try the files_joomla file in the current language (without allowing the loading of the file in the default language)
$lang->load('files_joomla.sys', JPATH_SITE, null, false, false)
// Fallback to the files_joomla file in the default language
|| $lang->load('files_joomla.sys', JPATH_SITE, null, true);

/**

  • A command line cron job to attempt to remove files that should have been deleted at update.
    *
  • @since 3.0
    /
    class TestCli extends JApplicationCli
    {
    /
    *

    • Entry point for CLI script

      *

    • @return void

      *

    • @since 3.0

      */

      public function doExecute()

      {

      JPluginHelper::importPlugin('system');

      }

      }

// Instantiate the application object, passing the class name to JCli::getInstance
// and use chaining to execute the application.
JApplicationCli::getInstance('TestCli')->execute();
````

Expected result

Import system plugin

Actual result

````PHP Notice: Trying to get property of non-object in /home/victor/projects/pluginissue/libraries/joomla/session/handler/joomla.php on line 69
PHP Stack trace:
PHP 1. {main}() /home/victor/projects/pluginissue/cli/test.php:0
PHP 2. JApplicationCli->execute() /home/victor/projects/pluginissue/cli/test.php:64
PHP 3. DeletefilesCli->doExecute() /home/victor/projects/pluginissue/libraries/joomla/application/cli.php:142
PHP 4. JPluginHelper::importPlugin() /home/victor/projects/pluginissue/cli/test.php:58
PHP 5. JPluginHelper::load() /home/victor/projects/pluginissue/libraries/cms/plugin/helper.php:163
PHP 6. JFactory::getUser() /home/victor/projects/pluginissue/libraries/cms/plugin/helper.php:293
PHP 7. JSession->get() /home/victor/projects/pluginissue/libraries/joomla/factory.php:236
PHP 8. JSession->start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:486
PHP 9. JSession->_start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:608
PHP 10. JSessionHandlerJoomla->start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:648

Notice: Trying to get property of non-object in /home/victor/projects/pluginissue/libraries/joomla/session/handler/joomla.php on line 69

Call Stack:
0.0020 277664 1. {main}() /home/victor/projects/pluginissue/cli/test.php:0
0.0593 3112056 2. JApplicationCli->execute() /home/victor/projects/pluginissue/cli/test.php:64
0.0593 3112568 3. DeletefilesCli->doExecute() /home/victor/projects/pluginissue/libraries/joomla/application/cli.php:142
0.0602 3168496 4. JPluginHelper::importPlugin() /home/victor/projects/pluginissue/cli/test.php:58
0.0602 3169392 5. JPluginHelper::load() /home/victor/projects/pluginissue/libraries/cms/plugin/helper.php:163
0.0602 3169712 6. JFactory::getUser() /home/victor/projects/pluginissue/libraries/cms/plugin/helper.php:293
0.0646 3448584 7. JSession->get() /home/victor/projects/pluginissue/libraries/joomla/factory.php:236
0.0646 3449216 8. JSession->start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:486
0.0646 3449440 9. JSession->_start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:608
0.0646 3449688 10. JSessionHandlerJoomla->start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:648

PHP Fatal error: Call to a member function get() on null in /home/victor/projects/pluginissue/libraries/joomla/session/handler/joomla.php on line 71
PHP Stack trace:
PHP 1. {main}() /home/victor/projects/pluginissue/cli/test.php:0
PHP 2. JApplicationCli->execute() /home/victor/projects/pluginissue/cli/test.php:64
PHP 3. DeletefilesCli->doExecute() /home/victor/projects/pluginissue/libraries/joomla/application/cli.php:142
PHP 4. JPluginHelper::importPlugin() /home/victor/projects/pluginissue/cli/test.php:58
PHP 5. JPluginHelper::load() /home/victor/projects/pluginissue/libraries/cms/plugin/helper.php:163
PHP 6. JFactory::getUser() /home/victor/projects/pluginissue/libraries/cms/plugin/helper.php:293
PHP 7. JSession->get() /home/victor/projects/pluginissue/libraries/joomla/factory.php:236
PHP 8. JSession->start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:486
PHP 9. JSession->_start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:608
PHP 10. JSessionHandlerJoomla->start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:648

Fatal error: Call to a member function get() on null in /home/victor/projects/pluginissue/libraries/joomla/session/handler/joomla.php on line 71

Call Stack:
0.0020 277664 1. {main}() /home/victor/projects/pluginissue/cli/test.php:0
0.0593 3112056 2. JApplicationCli->execute() /home/victor/projects/pluginissue/cli/test.php:64
0.0593 3112568 3. DeletefilesCli->doExecute() /home/victor/projects/pluginissue/libraries/joomla/application/cli.php:142
0.0602 3168496 4. JPluginHelper::importPlugin() /home/victor/projects/pluginissue/cli/test.php:58
0.0602 3169392 5. JPluginHelper::load() /home/victor/projects/pluginissue/libraries/cms/plugin/helper.php:163
0.0602 3169712 6. JFactory::getUser() /home/victor/projects/pluginissue/libraries/cms/plugin/helper.php:293
0.0646 3448584 7. JSession->get() /home/victor/projects/pluginissue/libraries/joomla/factory.php:236
0.0646 3449216 8. JSession->start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:486
0.0646 3449440 9. JSession->_start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:608
0.0646 3449688 10. JSessionHandlerJoomla->start() /home/victor/projects/pluginissue/libraries/joomla/session/session.php:648
````

System information (as much as possible)

PHP 5.6.23
Ubuntu 16.04.2 LTS x64
Joomla 3.7.1

Additional comments

I have investigated a bit and looks like the issue it is related with the access level applied in Joomla! 3.7.

This is the code that call to the session handler:

$levels = implode(',', JFactory::getUser()->getAuthorisedViewLevels());

This code is inside of load method of the /libraries/cms/plugin/helper.php file.

I think the code needs to check if the application is a child class of JApplicationCli class. If people agreed that is an issue, I will provide a PR that fixes this issue.

Thank you

J3 Issue No Code Attached Yet

Most helpful comment

The plugin helper (or any place getting view levels) shouldn't have different behavior based on the running application. Yes, this is a legitimate issue, but we need to re-evaluate how JFactory::getUser() works from a command line context (the root of the issue), not bypass it.

All 8 comments

The plugin helper (or any place getting view levels) shouldn't have different behavior based on the running application. Yes, this is a legitimate issue, but we need to re-evaluate how JFactory::getUser() works from a command line context (the root of the issue), not bypass it.

Users are not authenticated from a command line context (even JApplicationCli class does not have a login method) so instead of getting the levels right away, we should check if the user is authenticated or not, if it's not, let's check in which context we are (web context or cli context) so we can bypass (or do something else) that levels check.

The issue is trying to start a session from the CLI with null JInputCookie object. If you check that the cookie object is set it will prevent those errors. I don't think this is the permanent fix but hopefully it will help lead to a better solution.

So on the start function in /libraries/joomla/sessions/handler/joomla.php

       public function start()
    {
        $session_name = $this->getName();
        // Get the JInputCookie object
        $cookie = $this->input->cookie;
        if ($cookie)  // Check to see if  JInputCookie object is set
                {
            if (is_null($cookie->get($session_name)))
            {
                $session_clean = $this->input->get($session_name, false, 'string');

                if ($session_clean)
                {
                    $this->setId($session_clean);
                    $cookie->set($session_name, '', time() - 3600);
                }
            }
        }
        return parent::start();
    }

@JeremyAXS The way that i solve this problem is to run authentication plugins first, with an implementation of oauth2 authentication. After that you can run sessions normaly

https://github.com/fastslack/j3-rest-api/blob/master/api/application/web.php#L214

@fastslack I have tried your solution and it does not work, I think the problem is deeper than we think

The PR doesn't really help with this. The problem boils down to a lot of the API being dependent on an authenticated user's ACL and there really isn't a good way to put a non-guest user into play from command line (well, you can do Joomla\CMS\Factory::getSession()->set('user', Joomla\CMS\User\User::getInstance($user)); but it would still have to be at a point before plugins get loaded).

In the new console stuff we'd need to support a --user argument to pass a user ID and process that before loading plugins to have a native "workaround" to the problem (I don't know if I'd ever call it a fix because the arguably correct fixes mean making the API aware of whether it is running in command line or web context and there are very few cases where that should ever apply).

I came here to report this same issue. From my investigation the issue is concerned with at least one of the APIs JFactory::getApplication(), JFactory::getUser(), JFactory::getSession()

Application instance

JFactory::getApplication();

If we can set the Cli application as the active instance then it would not require to detect application name. I did this by doing this in the Cli application instantiation:

$instance = JApplicationCli::getInstance('TestCron');

JFactory::$application = $instance;

$instance->execute();

Authenticated User

JFactory::getUser();

We can support authentication in the Cli application via cli arguments as done with any typical cli application, such as mysql.

php ./cli/test.php --user="username" --password="password"

or

php ./cli/test.php --user="username"

and ask for password from Cli prompt, if username is given but password is omitted.

Session storage

JFactory::getSession();

IMO a session in Cli doesn't make sense, however an application may still rely on it directly or via userState etc.

I am not yet sure whether the current session API would work transparently with Cli or not. However I believe it should. In case it doesn't, we can emulate a session for Cli.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

uglyeoin picture uglyeoin  路  5Comments

brianteeman picture brianteeman  路  4Comments

mbabker picture mbabker  路  4Comments

Hils picture Hils  路  5Comments

wilsonge picture wilsonge  路  4Comments