Truly modular Phalcon application

I have been working with phalcon for pretty long time and I belive I came to some point at which I think I can make it public.

Previously I have made this topic: https://forum.phalconphp.com/discussion/17365/phalcon-is-not-trully-modular-framework-and-how-to-solve-it

Work done so far:

  1. Absolutely no code leaves modules directory
  2. Configuration files are in json (tho it wouldn't be a problem to make them yaml/xml etc)
  3. CLI support with option to add your own task (there are few built in tasks like generating modules, dumping routes etc)
  4. Migrations are inside modules, format is pretty much unchanged
  5. Router works differently than in phalcon, routes are read from configuration file
  6. All modules have option to inject services, initialize code etc.
  7. Modules are not simple arrays anymore, meaning you can get instance of any (or active) module class And much more

This is how app directory looks like: https://i.imgur.com/JhoJK6J.png (I probably should change plural to singular for some directories)

Each module needs to have a short definition:

{
    "name": "core",
    "version": "1.0.0",
    "enabled": true,
    "priority": 0
}

Modules are loaded by priority (i plan to add dependencies to them) Each modules have their own migrations which are executed automatically based on what is set in module_versions in database There is Acl and Auth module which implements basic auth and acl for:

  1. Routes
  2. Entities

Uses twig instead of volt because of few reasons:

  1. Nesting
  2. Namespaces (yes you can include templates from another module by: include '@modulename/some/template.twig'
  3. Overriding templates (crud generates twig template on the fly instead of relaying on templates)

But the biggest change is in routing because in phalcon, only the active module gets loaded. It isnt the case in my app. All modules are being loaded but only active one is executed. And routes are being kept in routes.json:

{
    "inventory": {
        "type": "crud",
        "prefix": "/inventory",
        "controller": "inventory",
        "children": {
            "import_from_tme": {
                "path": "/import-from-the-supplier",
                "action": "importFromTheSupplier"
            },
            "stocktaking": {
                "path": "/stocktaking/export",
                "action": "exportStocktaking"
            }
        }
    },
    "inventory_changelog": {
        "type": "crud",
        "prefix": "/inventory-changelog",
        "controller": "changelog"
    },
    "supplier": {
        "type": "crud",
        "prefix": "/supplier",
        "controller": "supplier"
    }
}

yes you can generate routes by name, there are 3 different types of routes like 'normal', 'crud', 'include'

Long story short: I think I have made phalcon trully modular, it needs some work to make it more proper and if anyone is interested in development of a skeleton let me know

edited 22d ago

I'm interested in evaluating your new research or my upcoming Perch Framework.

At a glance I recommend a few things:

1) Define your module priorities in a list and not in the module itself.

2) Allow enabling/disabling the module in the central list. This will speed up disabled modules and make code reuse easier.

3) Rename cli.php to run and add a shell shebang to it and make it executable (so that it can be run without calling php directly)

I have questions:

1) How do you handle forwarding between modules or do you only allow each module to setup routes?

2) How do you separate Task vs Controller related modules? Can many modules provide tasks or only a single one?

I've been working on my own Phalcon based framework for a long time. My framework has a completely new "command" based CLI system with full GNU style option parsing and help from a single definition. My CLI is more advanced than the MVC part since for that I was focusing more on getting a first class gettext integration between PHP and Webpack. So my project has so far two composer packages, one zephir extension, two NPM packages (one for browser time and one for build time) and a separate stand alone command line tool with cool stuff like JSON schema evaluation of the project directory structure as well as configuration files. I'm using "augments" to splice on tech in a way so that a package can provide drop in functionality. I use this heavily for development so for example the "perch" utility / PHAR executable is a basic skeleton that loads the development augmentation. This allows a normal app to use the development augmentation directly but in development mode only so that its not loaded in production. I'm just about to scoop the fuck out of the devtools webtools to use as an augmentation that automatically works with a route prefix like @webtools. ALso to hook into the very optional webpack stuff. So in short I'm interested in how you are doing your routes setup because that might save me a lot of time. I'm a smart ass so I could probably help you to refine the data structure if its not perfect. If you open up the hood early for me then I can let you into a early look at my tech to see what you can get out of it. Otherwise good luck and may the best framework win.

edited 22d ago

Additionally I want to add that maybe you have the module names backwards. Perhaps you should have the directory name be limited to a simple [a-z] charset and then allow the module name in the json configuration to be different or more expressive. It looks like you are putting the flavor name in the directory name and if I understand that correctly then it seems backwards to me.

  1. That would be a bad idea. I want modules to be autonomic. But maybe a local.config which would override a any module configuration if needed.
  2. Same as first point, a local config that would allow for modules to be overriden.
  3. Minor thing, can be done, can be improved

As for questions:

  1. I didn't yet implement forwarding but this could be done with little effort.
  2. CLI tasks? Any module can have its own task. Like core has https://i.imgur.com/0BG1y5V.png Auth has: https://i.imgur.com/eMC6WMh.png

Basically it finds any *Task.php inside Vendor/Module/Cli and executes it

My CLI is basically the same app as MVC but without http request. Phalcon devtools are not needed in my app. I've implemented generators in CLI like symfony does. For modules/migrations/etc. Im using few symfony components:

    "require": {
        "swiftmailer/swiftmailer": "^5.4",
        "symfony/console": "^3.3",
        "symfony/templating": "^3.3",
        "twig/twig": "^2.4",
        "ext-json": "*",
        "ext-fileinfo": "*",
        "ext-soap": "*"
    }

We can work something out if you are interested. But I don't want webpack. Im using just gulp for everything. My frontend is pure AngularJS with ES6 classes instead of functions. Even gulpfile is a class (which reads javascripts from modules - that needs some improvement as well)

I won't have time for this for a while.