Setting up the application

Autoloading

First of all, application classes have to be autoloaded. Objective PHP exclusively rely on composer autoloader. So, to make your classes autoloadable, just add the appropriate directive in the composer.json file:

"autoload": {
    "psr-4": {
        "App\\": "app/src"
    }
}

Where App should match your application main namespace. Note that the Objective PHP composer.json file already contains such a directive, using Project as namespace. You’re invited to update this setting to match your own standards.

Note

Any change to the autoload section of composer.json needs the command composer dumpautoload to be executed to make changes available to the project.

The Application class

The first class you have to create is the Application class. It should extend ObjectivePHP\Application\AbstractApplication, and be placed in your main namespace.

Application instance will the most important object in your application, since it will allow to link all components by being passed as main argument to all middlewares and more generally all invokables (keep on reading to gert more information about those concepts).

Barely all logic in the Application class lays in the Application::init() method, which is automatically triggered when Application::run() is called.

Defining a workflow

It is very important to understand that Objective PHP, as a framework, doesn’t provide the application with an hardcoded workflow, but with every components and mechanisms that are needed to create a workflow.

Note

While Objective PHP, as a framework, doesn’t provide a workflow, the Objective PHP Starter Kit does!

Declaring Steps

The workflow in Objective PHP is defined by first declaring Steps, then plugging Middlewares to each step. Since several middlewares can be plugged into each step, middlewares are stacked, and will be run one after the other, in the same order they were plugged.

Declaring steps is quite trivial (remember this should take place in Application::init()):

$this->addSteps('first-step', 'second-step' /* , 'step-n' */ );

Yes, a step is nothing more, from the developer point view, than a label. When running the application, steps also are run in the same order they were declared using addSteps().

Plugging Middlewares

Middlewares are basically small chunks of code designed to handle very specific parts of the workflow. Objective PHP extends this definition to Packages (Objective PHP extensions), that are considered as parts of the application.

Note

Most common Middelwares (for handling routing, action excution, rendering, etc.) are provided with the objective-php/application package and are plugged by default in the starter kit Project\Application::init() method.

Basic usage

Middlewares are not plugged directly into the application, but into Steps (see previous section). This allow a simple way to sequence middlewares execution order.

$this->getStep('bootstrap')->plug(
    function(ApplicationInterface $app) {
        /* anonymous function is an invokable,
           and as such can be used as a Middleware */
    }
);

Aliasing

When a middleware is plugged into a Step, it is possible to alias it using the Step::as() method. Aliasing a middleware is useful for handling substitution: when plugging a middleware with a given alias, if another one was previously plugged with the same alias, the former will take the latter place in the stack.

// this plugs the invokable class AnyMiddleware as 'initializer'
$this->getStep('bootstrap')->plug(AnyMiddleware::class)->as('intializer');

// this plugs the another invokable class OtherMiddleware also as 'initializer'
$this->getStep('bootstrap')->plug(OtherMiddleware::class)->as('intializer');

// at execution time, only OtherMiddleware will actually be run

Objective PHP also offers to use aliasing to plug default middlewares only. By aliasing a middleware using Step::asDefault, this middleware will be actually registered only if no other was already plugged using the same alias.

This is used for instance in starter kit to plug default operations middlewares, as router: if a package plugs a router middleware, the default one will simply be ignored:

// register custom router
if($whatever = true)
{
    $this->getStep('route')->plug(CustomerRouter::class)->as('router');
}


// this one will be ignored because CustomerRouter was aliased as router prior to SimpleRouter
$this->getStep('route')->plug(SimpleRouter::class)->asDefault('router');

Note

Later on, aliases will also permit to fetch middleware returned values.

Execution filters

Objective PHP allow the developer to filter middlewares actual execution by providing the Step::plug() method with extra invokables, expected to return a boolean. In this case, the middleware will be run only if all filter invokables return true.

$this->getStep('first-step')->plug(
        Middleware::class,
        function(ApplicationInterface $app) {
            return $app->getEnv() == 'development');
        }
);

This very simple mechanism allow the developer to setup a very flexible and dynamic workflow, with little efforts. For instance, it is possible to activate or not a middleware based on current date, user profile, environment variable, and so on. Since the filters are exepected to return a boolean, they can implement a decision mechanism based on virtually anything.

Objective PHP provides by default several filters, like UrlFilter or ContentTypeFilter, located in Application\Workflow\Filter. Those default filter ease most typical filtering needs:

// AnyMiddleware will be run only if URL matches the "/admin/*" pattern
$this->getStep('action')->plug(AnyMiddleware::class, new UrlFilter('/admin/*'));