We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Multiple module CLI

I posted a question about how to handle CLI for multiple applications and today I found the solution.

I have two applications and wanted a common cli to execute tasks from them.

  • v1 -> path: apps/kapi/v1
  • admin -> path: apps/kapi/admi

The solution I have uses docopt but it is not required.

Examples:

# Execute v1's MainTask::mainAction()
php cli.php v1 main main --env=local

# Execute admin's MailTask::specialAction($params)
php cli.php admin mail special param1 param2 param3 --env=local

Code as shown below:

<?php
// how to use composer with phalcon https://stackoverflow.com/a/25662855
$loader = require_once(__DIR__ . '/../vendor/autoload.php');

// namespace to module mapping
$moduleNamespaces = array(
    'admin' => 'Kapi\Admin\Tasks',
    'v1'  => 'Kapi\V1\Tasks'
);

$doc = <<<DOC
Phalcon CLI.

Usage:
  cli.php <module> <task> <action> [<params> ...]  [--env=<env>]
  cli.php (-h | --help)

Options:
  -h --help             Show this screen.
  --env=<env>           Set environment [default: local]
DOC;

$module = $task = $action = $env = '';
$params = array();

$docpot = new Docopt();
$args = $docpot->handle($doc);
foreach($args as $k => $v)
{
    if ($k == '--env')
        $env = $v;
    else if (preg_match('/<(.*?)>/', $k, $match))
    {
        $k = array_pop($match);
        $$k = $v;
    }
}

// used in other places of application
define('APP_ENV', $env);

// build arguments for CLI
$arguments['module'] = $module;
$arguments['task'] = $task;
$arguments['action'] = $action;
$arguments['params'] = $params;

class Console extends \Phalcon\CLI\Console
{
    public function __construct()
    {
        $loader = new \Phalcon\Loader();
        $namespaces = require_once(__DIR__ . '/../boot/autoload_namespaces.php');
        $loader->registerNamespaces($namespaces['base']);

        // register the installed modules
        $this->registerModules(array(
            'v1' => array(
                'className' => 'Kapi\V1\Module',
                'path' => __DIR__  . '/../apps/kapi/v1/Module.php'
            ),
            'admin' => array(
                'className' => 'Kapi\Admin\Module',
                'path' => __DIR__ . '/../apps/kapi/admin/Module.php'
            ),
        ));

        $loader->register();
    }

    public function main()
    {
        $di = new \Phalcon\DI\FactoryDefault\CLI();

        // registering a router
        $di->set('router', function(){
            $router = new \Phalcon\CLI\Router();

            return $router;
        });

        // registering a dispatcher
        $di->set('dispatcher', function () use($di) {

            // obtain the standard eventsManager from the DI
            $eventsManager = $di->getShared('eventsManager');

            $eventsManager->attach("dispatch:beforeDispatchLoop", function($event, $dispatcher) {
                $dispatcher->setActionName(\Phalcon\Text::camelize($dispatcher->getActionName()));
            });

            $dispatcher = new \Phalcon\CLI\Dispatcher();
            // bind the EventsManager to the Dispatcher
            $dispatcher->setEventsManager($eventsManager);

            return $dispatcher;
        });

        $this->setDI($di);
    }
}

$console = new Console();
$console->main();
$di = $console->getDI();

/** @var $dispatcher \Phalcon\CLI\Dispatcher */
$dispatcher = $di['dispatcher'];

// https://forum.phalcon.io/discussion/4573/cli-task-not-found-when-in-namespace#C15319
$dispatcher->setDefaultNamespace($moduleNamespaces[$module]);
$dispatcher->setNamespaceName($moduleNamespaces[$module]);

try {
    $console->handle($arguments);
}
catch (\Phalcon\Exception $e) {
    echo $e->getMessage();
}

@attozk Can you please add solution to Phalcon Tips?