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

Phalcon\Cli\Console and Namespace support

Hello,

Firt of all this is a long post and it's something that I sould be contributing (make it myself and share) but well, maybe someone else did it, it's pending, or Im doing things the way I'm not suposed to.

Long short story, I need to use Phalcon\Cli\Console with namespaces on a nice orderly module integrated fassion.

So, here it goes:

I'm developing a multi module, multi mode (MVC/Console) Application.

I'm currently having two modules named "base" and "billing".

The base module is for the MVC Application only and the billing module is to be used by both MVC and Console Applications.

To get the two of them working "together" as you don't have a unified instance of Application that handles both things, I've implemented a builder pattern to “build” one or another as follows:

ApplicationBuilder: parent class, has three methods that implements and to be implemented as well for it's children; one to register services, another to register the loaders and the last one is an initialization method. Here is the definition of all the common modules, services and loaders required for both applications. It registers i.e. the logger, database, billing module, etc. There is another method called “build" (the only public) that returns the specialized instance of Phalcon\Application (by it’s children).

MVCBuilder: child of ApplicationBuilder, registers i.e. "view" service, Phalcon\Mvc\Router service, Phalcon\Mvc\Dispatcher, cookie support (and crypt for cookie), registers the "base" module, etc.

ConsoleBuilder: child of ApplicationBuilder, implementing the things only required by this Application, the Phalcon\Cli\Router, the Phalcon\Cli\Dispatcher, etc.

MicroBuilder: hey, if I want to add a Micro Application, I can do it by implementing this.

The idea of implementing the builder pattern here is to delegate the complex building (setting services, loaders, modules and configurations) to a model that you have all the applications together and sharing their components; i.e. if I want to change the Phalcon\Logger\Adapter service to all the applications I only need to change it on the registerServices() method of ApplicationBuilder. You get the idea…

So everything is "nice" and "mostly happy" tied together until I get the following setback:

The framework is not properly adjusted to be working with Console Applications and Namespaces.

Phalcon\Cli\Console->handle requires an array and basically it sends the arguments to Phalcon\Cli\Router->handle phalcon/cli/console.zep#L63 and then sets the dispatcher, etc.

Phalcon\Cli\Router->handle works with both arrays and strings:

Phalcon\Cli\Router\Route->reConfigure executed by it’s constructor it does correctly support namespaces router/route.zep#L89

So if we call Phalcon\Cli\Console->handle with an array it doesn’t work with namespaces.

If we declare a specific route like “billing/:controller/:action” we can match it to i.e.[“module” => “billing”, “namespace” => “My\Billing\Module\TaskNamespace”, “controller” => 1, “action” => 2] it will no be processed by the router as this will require to call Phalcon\Cli\Console->handle(“billing/:controller/:action”) that we cannot do.

The only thing to do that works out of the box without getting much into trouble is to specify the namespace on the task argument i.e.

<?php

$arguments = [“module” => “billing”, “task” => “My\Billing\Module\TaskNamespace\Main”, “action” => “test”];
$console->handle($arguments); /* var $console Phalcon\Cli\Console */

I don’t like that since I would have to specify the namespace of all modules to be added in the future by hand with each action execution.

Another fix is to tell the dispatcher where is the namespace of the controller to execute (the thing that the Phalcon\Cli\Console->handle is not doing)

<?php
// ...
$builder = new ConsoleBuilder();
$app = $builder->build();

$app->getDI()->get(‘dispatcher’)->setNamespace(“My\Billing\Module\TaskNamespace”); //To be set according to which task i’m executing

$arguments = [“module” => “billing”, “task” => “main”, “action” => “test”];
$app->handle($arguments); 

As for what to do to really use namespaces….

The Phalcon\Cli\Router should add namespace support by adding it to it’s default routes, adding a _defaultNamespace property, _namespace property, it’s access methods, and make it work with arrays (with strings it already works as Phalcon\Cli\Router\Route does support them properly)

The Phalcon\Cli\Console->handle:

  • Should set the namespace on the dispatcher as Phalcon\Cli\Dispatcher already supports namespaces as they are implemented on it’s parent.
  • Should allow arrays and strings as parameters to be consistent with Phalcon\Cli\Router.

Can yo add support for Namespaces to Console Applications?

Am I missing something?

Thanks for your time :)

EDIT: Added some styling.



1.3k

https://forum.phalcon.io/discussion/7283/multiple-module-cli

Hi, thanks for the reply, the link you sent me is setting the namespace on the dispatcher as one of the solutions I suggested.

I'm using the same approach, but I added to the registerModule an aditional parameter specifying the namespace of de module tasks, like this:

$application->registerModules([
    'billing' => [
        'className' => 'MyNamespace\\BillingModule',
        'path' => APP_PATH . '/billing/BillingModule.php',
        'tasksNamespace' => 'MyNamespace\\Billing\\Tasks',
    ]
]);

// ...

$dispatcher = $application->getDI()->get('dispatcher'); /* @var $dispatcher \Phalcon\Cli\Dispatcher */
$dispatcher->setNamespaceName($application->getModule($arguments["module"])["tasksNamespace"]);

In this approach the namespace of the module tasks are defined on the module registration and leaves a cleaner code on the bootsrap file.

I think that namespace support in the Phalcon\Cli\Console and their related should be a pretty good addition to avoid this type of workaround, also considering that it's not to much work to add it.

https://github.com/phalcon/cphalcon feel free to do PR here, zephir is really nice language and so hard to learn, it's very similar to php.



1.3k

https://github.com/phalcon/cphalcon feel free to do PR here, zephir is really nice language and so hard to learn, it's very similar to php.

Thanks for the sugestion, I few months I've learn about this framework and the first thing I did before even write a line or install the module was to read about phalcon that lead me to zephyr, then I read about zephyr and learn it's basics.

I'm on my fourth phalcon application and I've been looking directly into the zep code to find some things that may or may not be on the web docs of phalcon but sometimes it's easier to see how it's made.

I've never (ever) contribute directly on a open source comunity and since I mainly work on enterprise environment, I never (ever) used GIT for source control (I use svn). So first I need to get in touch with GIT. Furthermore, I need to learn how compile the source zeps into the phalcon zep to build the module for testing purposes etc. And... shame on me, I've never wrote test cases before.. so it's a lot of to-learn checks before I adventure into posting a PR.

So it may take some time... :/

edited Mar '17

Well i think it's good to learn stuff like this to be honest :) I think contributing into open source can improve your skills.