Skip to content

Events Manager


Overview

The purpose of this component is to intercept the execution of components in the framework by creating hooks. These hooks allow developers to obtain status information, manipulate data, or change the flow of execution during the process of a component. The component consists of a Phalcon\Events\Manager that handles event propagation and execution of events. The manager contains various Phalcon\Events\Event objects, which contain information about each hook/event.

<?php

use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

$eventsManager = new EventsManager();

$eventsManager->attach(
    'db:afterQuery',
    function (Event $event, $connection) {
        echo $connection->getSQLStatement();
    }
);

$connection = new DbAdapter(
    [
        'host'     => 'localhost',
        'username' => 'root',
        'password' => 'secret',
        'dbname'   => 'invo',
    ]
);

$connection->setEventsManager($eventsManager);
$connection->query(
    'SELECT * FROM products p WHERE p.status = 1'
);

Naming Convention

Phalcon events use namespaces to avoid naming collisions. Each component in Phalcon occupies a different event namespace, and you are free to create your own as you see fit. Event names are formatted as component:event. For example, as Phalcon\Db occupies the db namespace, its afterQuery event's full name is db:afterQuery.

When attaching event listeners to the events manager, you can use component to catch all events from that component (e.g. db to catch all the Phalcon\Db events) or component:event to target a specific event (eg. db:afterQuery).

Manager

The Phalcon\Events\Manager is the main component that handles all the events in Phalcon. Different implementations in other frameworks refer to this component as a handler. Regardless of the name, the functionality and purpose are the same.

The component stores listeners in priority-sorted arrays keyed by event type. Each listener is registered with a priority (default 100); when the matching event fires, the manager iterates the queue in order. Priorities are disabled by default — call enablePriorities(true) to honor them.

The methods exposed by the manager are:

public function addSubscriber(Subscriber $subscriber): void
Registers an event subscriber. The subscriber's getSubscribedEvents() map is parsed and each entry is attached through the regular listener pipeline. See Subscribers.

final public function attach(
    string $eventType, 
    mixed $handler, 
    int $priority = self::DEFAULT_PRIORITY
): void
Attaches a listener to the events manager. The handler is an object or a callable.

public function arePrioritiesEnabled(): bool
Returns if priorities are enabled.

public function clearSubscribers(): void
Removes every registered subscriber and detaches each listener they contributed. Listeners attached via attach() are untouched.

public function collectResponses(bool $collect): void
Tells the event manager whether to collect responses returned by every registered listener in a single fire call.

public function detach(string $eventType, mixed $handler): void
Detaches the listener from the events manager. When the last listener for an event type is removed, the event-type key is dropped entirely so hasListeners() reports the truth.

public function detachAll(string $type = null): void
Removes all events from the events manager. With a $type argument, removes only the queue for that event type.

public function enablePriorities(bool $enablePriorities): void
Sets whether priorities are honored when dispatching (default false).

final public function fire(
    string $eventType, 
    object $source, 
    mixed $data = null, 
    bool $cancelable = true
): mixed
Fires an event causing the active listeners to be notified. Returns the last non-null listener return value (or the stopping listener's return value when $event->stop() is called).

public function fireAll(
    string $eventType, 
    object $source, 
    mixed $data = null, 
    bool $cancelable = true
): array
Fires an event and returns every listener's return value as an indexed array. Independent of collectResponses(). See All Responses.

final public function fireQueue(array $queue, EventInterface $event): mixed
Internal handler to call a queue of events. Kept at this signature for backward compatibility with direct callers; the framework's own fire() path uses a private typed dispatch helper instead.

public function getListeners(string $type): array
Returns all the attached listeners of a certain event type.

public function getResponses(): array
Returns the responses collected by the last fire() call when collectResponses(true) is in effect.

public function getSubscribers(): array
Returns the list of registered subscriber instances.

public function halt(): void
Engages the manager-level kill switch — see Kill Switch.

public function hasListeners(string $type): bool
Checks whether a certain event type has listeners.

public function isCollecting(): bool
Returns whether the manager is currently collecting responses.

public function isHalted(): bool
Returns whether the manager-level kill switch is engaged.

public function isStopOnFalse(): bool
Returns whether the stop-on-false short-circuit is enabled — see Stop on False.

public function isStrict(): bool
Returns whether strict mode is enabled — see Strict Mode.

public function isValidHandler(mixed $handler): bool
Checks whether the handler is an object or a callable.

public function removeSubscriber(Subscriber $subscriber): void
Removes a previously registered subscriber. Idempotent.

public function resume(): void
Clears the manager-level kill switch set by halt().

public function setStopOnFalse(bool $flag): void
Enables the opt-in per-event short-circuit on a false listener return — see Stop on False.

public function setStrict(bool $strict): void
Toggles strict mode — see Strict Mode.

Usage

If you are using the Phalcon\Di\FactoryDefault DI container, the Phalcon\Events\Manager is already registered for you with the name eventsManager. This is a global events manager. However, you are not restricted to use only that one. You can always create a separate manager to handle events for any component that you require.

The following example shows how you can create a query logging mechanism using the global events manager:

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Events\Event;
use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

$container     = Di::getDefault();
$eventsManager = $container->get('eventsManager');

$eventsManager->attach(
    'db:afterQuery',
    function (Event $event, $connection) {
        echo $connection->getSQLStatement();
    }
);

$connection = new DbAdapter(
    [
        'host'     => 'localhost',
        'username' => 'root',
        'password' => 'secret',
        'dbname'   => 'invo',
    ]
);

$connection->setEventsManager($eventsManager);
$connection->query(
    'SELECT * FROM products p WHERE p.status = 1'
);

or if you want a separate events manager:

<?php

use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

$eventsManager = new EventsManager();
$eventsManager->attach(
    'db:afterQuery',
    function (Event $event, $connection) {
        echo $connection->getSQLStatement();
    }
);

$connection = new DbAdapter(
    [
        'host'     => 'localhost',
        'username' => 'root',
        'password' => 'secret',
        'dbname'   => 'invo',
    ]
);

$connection->setEventsManager($eventsManager);
$connection->query(
    'SELECT * FROM products p WHERE p.status = 1'
);

In the above example, we are using the events manager to listen to the afterQuery event produced by the db service, in this case, MySQL. We use the attach method to attach our event to the manager and use the db:afterQuery event. We add an anonymous function as the handler for this event, which accepts a Phalcon\Events\Event as the first parameter. This object contains contextual information regarding the event that has been fired. The database connection object as the second. Using the connection variable we print out the SQL statement. You can always pass a third parameter with arbitrary data specific to the event, or even a logger object in the anonymous function so that you can log your queries in a separate log file.

WARNING

You must explicitly set the Events Manager to a component using the setEventsManager() method in order for that component to trigger events. You can create a new Events Manager instance for each component, or you can set the same Events Manager to multiple components as the naming convention will avoid conflicts

Handlers

The events manager wires a handler to an event. A handler is a piece of code that will do something when the event fires. As seen in the above example, you can use an anonymous function as your handler:

<?php

use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Adapter\Pdo\Mysql as DbAdapter;

$eventsManager = new EventsManager();
$eventsManager->attach(
    'db:afterQuery',
    function (Event $event, $connection) {
        echo $connection->getSQLStatement();
    }
);

$connection = new DbAdapter(
    [
        'host'     => 'localhost',
        'username' => 'root',
        'password' => 'secret',
        'dbname'   => 'invo',
    ]
);

$connection->setEventsManager($eventsManager);
$connection->query(
    'SELECT * FROM products p WHERE p.status = 1'
);

You can also create a listener class, which offers more flexibility. In a listener, you can listen to multiple events and even extend Phalcon\Di\Injectable which will give you fill access to the services of the Di container. The example above can be enhanced by implementing the following listener:

<?php

namespace MyApp\Listeners;

use Phalcon\Logger;
use Phalcon\Config;
use Phalcon\Db\AdapterInterface;
use Phalcon\Di\Injectable;
use Phalcon\Events\Event;

/**
 * Class QueryListener
 *
 * @property Config $config
 * @property Logger $logger
 */
class QueryListener extends Injectable
{
    public function beforeQuery(Event $event, AdapterInterface $connection)
    {
        if ($this->config->path('app.logLevel') > 1) {
            $this->logger->info(
                sprintf(
                    '%s - [%s]',
                    $connection->getSQLStatement(),
                    json_encode($connection->getSQLVariables())
                )
            );
        }
    }

    public function rollbackTransaction(Event $event)
    {
        if ($this->config->path('app.logLevel') > 1) {
            $this->logger->warning($event->getType());
        }
    }
}

Attaching the listener to our events manager is very simple:

<?php

$eventsManager->attach(
    'db',
    new QueryListener()
);

The resulting behavior will be that if the app.logLevel configuration variable is set to greater than 1 (representing that we are in development mode), all queries will be logged along with the actual parameters that were bound to each query. Additionally, we will log every time we have a rollback in a transaction.

Another handy listener is the 404 one:

<?php

namespace MyApp\Listeners\Dispatcher;

use Phalcon\Logger;
use Phalcon\Di\Injectable;
use Phalcon\Events\Event;
use Phalcon\Mvc\Dispatcher;
use MyApp\Auth\Adapters\AbstractAdapter;

/**
 * Class NotFoundListener
 *
 * @property AbstractAdapter $auth
 * @property Logger          $logger
 */
class NotFoundListener extends Injectable
{
    public function beforeException(
        Event $event, 
        Dispatcher $dispatcher, 
        \Exception $ex
    ) {
        switch ($ex->getCode()) {
            case Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
            case Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
                $dispatcher->setModuleName('main');
                $params = [
                    'namespace'  => 'MyApp\Controllers',
                    'controller' => 'session',
                    'action'     => 'fourohfour',
                ];

                /**
                 * 404 not logged in
                 */
                if (true !== $this->auth->isLoggedIn()) {
                    $params['action'] = 'login';
                }

                $dispatcher->forward($params);

                return false;
            default:
                $this->logger->error($ex->getMessage());
                $this->logger->error($ex->getTraceAsString());

                return false;
        }
    }
}

and attaching it to the events manager:

<?php

$eventsManager->attach(
    'dispatch:beforeException',
    new NotFoundListener(),
    200
);

First, we attach the listener to the dispatcher component and the beforeException event. This means that the events manager will fire only for that event calling our listener. We could have just changed the hook point to dispatcher so that we are able in the future to add more dispatcher events in the same listener.

The beforeException function accepts the $event as the first parameter, the $dispatcher as the second, and the $ex exception thrown from the dispatcher component. Using those, we can then figure out if a handler (or controller) or an action was not found. If that is the case, we forward the user to a specific module, controller, and action. If our user is not logged in, then we send them to the login page. Alternatively, we just log the exception message in our logger.

The example demonstrates clearly the power of the events manager, and how you can alter the flow of the application using listeners.

Subscribers

Subscribers group multiple listeners for a single class behind a static getSubscribedEvents() method, similar to the pattern in Symfony's EventDispatcher. The events manager parses that map once at registration time and attaches each entry through the regular listener pipeline — no reflection at fire time.

Implement the Phalcon\Contracts\Events\Subscriber contract on the class. Each entry in getSubscribedEvents() accepts three shapes:

  • 'event:name' => 'methodName' — plain method name
  • 'event:name' => ['methodName', priority] — method with priority
  • 'event:name' => [['methodA', priorityA], ['methodB', priorityB]] — multiple listeners for the same event
<?php

namespace MyApp\Listeners;

use Phalcon\Contracts\Events\Subscriber;
use Phalcon\Events\Event;
use Phalcon\Mvc\Model;

class AuditSubscriber implements Subscriber
{
    public static function getSubscribedEvents(): array
    {
        return [
            'model:beforeSave'   => 'recordIntent',
            'model:afterSave'    => ['logSave', 150],
            'model:beforeDelete' => [
                ['markForArchive', 200],
                ['notifyAuditor', 100],
            ],
        ];
    }

    public function recordIntent(Event $event, Model $model): void
    {
        // ...
    }

    public function logSave(Event $event, Model $model): void
    {
        // ...
    }

    public function markForArchive(Event $event, Model $model): void
    {
        // ...
    }

    public function notifyAuditor(Event $event, Model $model): void
    {
        // ...
    }
}

Register and manage subscribers through the dedicated API:

<?php

use MyApp\Listeners\AuditSubscriber;
use Phalcon\Events\Manager as EventsManager;

$eventsManager = new EventsManager();
$subscriber    = new AuditSubscriber();

$eventsManager->addSubscriber($subscriber);

// Inspect what is registered.
$registered = $eventsManager->getSubscribers();

// Remove a specific subscriber, or remove every one in a single call.
$eventsManager->removeSubscriber($subscriber);
$eventsManager->clearSubscribers();

addSubscriber() reads getSubscribedEvents() once and caches the resulting map per class name, so registering several instances of the same subscriber class does not re-invoke the static method. removeSubscriber() is idempotent — removing an instance that was never added (or already removed) is a no-op. clearSubscribers() detaches every listener contributed by every registered subscriber; listeners attached via attach() are untouched.

NOTE

Subscribers are keyed internally by spl_object_id(), so re-adding the same instance is a no-op. Registering two distinct instances of the same subscriber class is allowed and attaches the listeners twice.

Events: Trigger

You can create components in your application that trigger events to an events manager. Listeners attached to those events will be invoked when the events are fired. In order to create a component that triggers events, we need to implement the Phalcon\Events\EventsAwareInterface.

Custom Component

Let's consider the following example:

<?php

namespace MyApp\Components;

use Phalcon\Di\Injectable;
use Phalcon\Events\EventsAwareInterface;
use Phalcon\Events\ManagerInterface;

/**
 * @property ManagerInterface $eventsManager
 * @property Logger           $logger
 */
class NotificationsAware extends Injectable implements EventsAwareInterface
{
    protected $eventsManager;

    public function getEventsManager()
    {
        return $this->eventsManager;
    }

    public function setEventsManager(ManagerInterface $eventsManager)
    {
        $this->eventsManager = $eventsManager;
    }


    public function process()
    {
        $this->eventsManager->fire('notifications:beforeSend', $this);

        $this->logger->info('Processing.... ');

        $this->eventsManager->fire('notifications:afterSend', $this);
    }
}

The above component implements the Phalcon\Events\EventsAwareInterface and as a result, it uses the getEventsManager and setEventsManager. The last method is what does the work. In this example we want to send some notifications to users and want to fire an event before and after the notification is sent.

We chose to name the component notification and the events are called beforeSend and afterSend. In the process method, you can add any code you need in between the calls to fire the relevant events. Additionally, you can inject more data in this component that would help with your implementation and processing of the notifications.

Custom Listener

Now we need to create a listener for this component:

<?php

namespace MyApp\Listeners;

use Phalcon\Events\Event;
use Phalcon\Logger;

/**
 * @property Logger $logger
 */
class MotificationsListener
{
    /**
     * @var Logger
     */
    private $logger;

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
    }

    public function afterSend(
        Event $event, 
        NotificationsAware $component
    ) {
        $this->logger->info('After Notification');
    }

    public function beforeSend(
        Event $event, 
        NotificationsAware $component
    ) {
        $this->logger->info('Before Notification');
    }
}

Putting it all together

<?php

use MyApp\Components\NotificationAware;
use MyApp\Listeners\MotificationsListener;
use Phalcon\Events\Manager as EventsManager;

$eventsManager = new EventsManager();
$component     = new NotificationAware();

$component->setEventsManager($eventsManager);

$eventsManager->attach(
    'notifications',
    new NotificationsListener()
);

$component->process();

When process is executed, the two methods in the listener will be executed. Your log will then have the following entries:

[2019-12-25 01:02:03][INFO] Before Notification
[2019-12-25 01:02:03][INFO] Processing...
[2019-12-25 01:02:03][INFO] After Notification

Custom Data

Additional data may also be passed when triggering an event using the third parameter of fire():

<?php

$data = [
    'name'     => 'Darth Vader',
    'password' => '12345',
];

$eventsManager->fire('notifications:afterSend', $this, $data);

In a listener the third parameter also receives data:

<?php

use Phalcon\Events\Event;

$data = [
    'name'     => 'Darth Vader',
    'password' => '12345',
];

$eventsManager->attach(
    'notifications',
    function (Event $event, $component, $data) {
        print_r($data);
    }
);

$eventsManager->attach(
    'notifications',
    function (Event $event, $component) {
        print_r($event->getData());
    }
);

Propagation

An events manager can have multiple listeners attached to it. Once an event fires, all listeners that can be notified for the particular event will be notified. This is the default behavior but can be altered if need be by stopping the propagation early:

<?php

use Phalcon\Events\Event;

$eventsManager->attach(
    'db',
    function (Event $event, $connection) {
        if ('2019-01-01' < date('Y-m-d')) {
            $event->stop();
        }
    }
);

In the above simple example, we stop all events if today is earlier than 2019-01-01.

Cancellation

By default, all events are cancelable. However, you might want to set a particular event to not be cancelable, allowing the particular event to fire on all available listeners that implement it.

<?php

use Phalcon\Events\Event;

$eventsManager->attach(
    'db',
    function (Event $event, $connection) {
        if ($event->isCancelable()) {
            $event->stop();
        }
    }
);

In the above example, if the event is cancelable, we will stop propagation. You can set a particular event to not be cancelable by utilizing the fourth parameter of fire():

<?php

$eventsManager->fire('notifications:afterSend', $this, $data, false);

The afterSend event will no longer be cancelable and will execute on all listeners that implement it.

WARNING

You can stop the execution by returning false in your event (but not always). For instance, if you attach an event to dispatch:beforeDispatchLoop and your listener returns false the dispatch process will be halted. This is true if you only have one listener listening to the dispatch:beforeDispatchLoop event which returns false. If two listeners are attached to the event and the second one that executes returns true then the process will continue. If you wish to stop any subsequent events from firing, you will have to issue a stop() in your listener on the Event object — or enable Stop on False so the manager treats a false return as a hard cancel.

Stop on False

setStopOnFalse(true) enables an opt-in per-event short-circuit. When the flag is on and the fire's cancelable argument is also true, a listener returning literal false halts the dispatch loop for that event and pins the fire() return value as false. Later listeners attached to the same event do not run, and a subsequent listener returning true cannot revive the chain.

<?php

use Phalcon\Events\Manager as EventsManager;

$eventsManager = new EventsManager();
$eventsManager->setStopOnFalse(true);

$eventsManager->attach(
    'orders:beforePay',
    function ($event, $order) {
        return $order->isValid();
    }
);

$eventsManager->attach(
    'orders:beforePay',
    function ($event, $order) {
        // Skipped entirely if the first listener returned false.
        return $order->reserveStock();
    }
);

$result = $eventsManager->fire('orders:beforePay', $order);
// $result === false when the first listener returns false

Default is off, preserving the historical last-wins return-value behavior so existing code is unaffected. isStopOnFalse() reports the current state. The flag is independent of halt() and $event->stop() — it only governs how the dispatch loop reacts to a literal false listener return.

Kill Switch

halt() engages a manager-level kill switch that survives across fire() calls. Once halted, every subsequent fire(), fireAll(), and fireQueue() call returns immediately (null or []) without dispatching, until resume() clears the flag. Use this when a listener needs to abort all downstream event activity for the rest of a request — a failed authorization check, a fatal configuration error, or a circuit breaker tripping.

<?php

use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

$eventsManager = new EventsManager();

$eventsManager->attach(
    'app:beforeRequest',
    function (Event $event, $app) use ($eventsManager) {
        if (true !== $app->shouldContinue()) {
            $eventsManager->halt();
        }
    }
);

// After the listener trips halt(), no other event fires.
$eventsManager->fire('app:beforeRequest', $app);

$eventsManager->isHalted();                       // true
$eventsManager->fire('app:somethingElse', $app);  // returns null, no dispatch

// Lift the kill switch when normal operation should resume.
$eventsManager->resume();

halt() is distinct from $event->stop(). stop() only halts the current dispatch chain on the Event instance; halt() survives across fire() boundaries on the manager itself.

Priorities

When attaching listeners you can set a specific priority. Setting up priorities when attaching listeners to your events manager defines the order in which they are called:

<?php

use Phalcon\Events\Manager as EventsManager;

$eventsManager = new EventsManager();

$eventsManager->enablePriorities(true);

$eventsManager->attach(
    'db', 
    new QueryListener(), 
    150
);
$eventsManager->attach(
    'db', 
    new QueryListener(), 
    100
);
$eventsManager->attach(
    'db', 
    new QueryListener(), 
    50
); 

NOTE

In order for the priorities to work enablePriorities() has to be called with true to enable them. Priorities are disabled by default

WARNING

A high priority number means that the listener will be processed before those with lower priorities

Responses

The events manager can also collect any responses returned by each event and return them back using the getResponses() method. The method returns an array with the responses:

<?php

use Phalcon\Events\Manager as EventsManager;

$eventsManager = new EventsManager();

$eventsManager->collectResponses(true);

$eventsManager->attach(
    'custom:custom',
    function () {
        return 'first response';
    }
);

$eventsManager->attach(
    'custom:custom',
    function () {
        return 'second response';
    }
);

$eventsManager->fire('custom:custom', $eventsManager, null);

print_r($eventsManager->getResponses());

The above example produces:

[
    0 => 'first response',
    1 => 'second response',
]

NOTE

In order for responses to be collected, collectResponses() has to be called with true to enable collection.

All Responses

fireAll() returns every listener's return value as an indexed array in a single call — without enabling collectResponses() and without depending on getResponses():

<?php

use Phalcon\Events\Manager as EventsManager;

$eventsManager = new EventsManager();

$eventsManager->attach(
    'reports:collect',
    function () {
        return 'metrics';
    }
);

$eventsManager->attach(
    'reports:collect',
    function () {
        return 'audit';
    }
);

$results = $eventsManager->fireAll('reports:collect', $context);
// $results === ['metrics', 'audit']

fireAll() stashes the caller's $this->responses state on entry and restores it on exit, so a fireAll() call from inside a collect-mode fire() does not pollute the outer accumulator. Nested fire() calls also stash and restore their outer accumulator on a per-call basis, eliminating cross-fire response clobbering.

Strict Mode

Strict mode throws Phalcon\Events\Exception when an event is fired with no matching listeners. Useful in development for catching typos in event names that would otherwise dispatch silently.

<?php

use Phalcon\Events\Exception;
use Phalcon\Events\Manager as EventsManager;

$eventsManager = new EventsManager();
$eventsManager->setStrict(true);

try {
    $eventsManager->fire('typo:eventName', $source);
} catch (Exception $ex) {
    echo $ex->getMessage();
    // "No listeners attached for event typo:eventName"
}

setStrict(true) toggles the flag; isStrict() reports the current state. Default is false, so existing application code is unaffected. Strict mode applies to both fire() and fireAll().

Exceptions

Any exceptions thrown in the Events component will be of type Phalcon\Events\Exception. You can use this exception to selectively catch exceptions thrown only from this component.

<?php

use Phalcon\Events\EventsManager;
use Phalcon\Events\Exception;

try {

    $eventsManager = new EventsManager();

    $eventsManager->attach('custom:custom', true);
} catch (Exception $ex) {
    echo $ex->getMessage();
}

Controllers

Controllers act as listeners already registered in the events manager. As a result, you only need to create a method with the same name as a registered event, and it will be fired.

For instance, if we want to send a user to the /login page if they are not logged in, we can add the following code in our master controller:

<?php

namespace MyApp\Controller;

use Phalcon\Logger;
use Phalcon\Dispatcher;
use Phalcon\Http\Response;
use Phalcon\Mvc\Controller;
use MyApp\Auth\Adapters\AbstractAdapter;

/**
 * Class BaseController
 *
 * @property AbstractAdapter $auth
 * @property Logger          $logger
 * @property Response        $response
 */
class BaseController extends Controller
{
    public function beforeExecuteRoute(Dispatcher $dispatcher)
    {
        /**
         * Send them to the login page if no identity exists
         */
        if (true !== $this->auth->isLoggedIn()) {
            $this->response->redirect(
                '/login',
                true
            );

            return false;
        }

        return true;
    }
}
Execute the code before the router, so we can determine if the user is logged in or not. If not, forward them to the login page.

Models

Similar to Controllers, Models also act as listeners already registered in the events manager. As a result, you only need to create a method with the same name as a registered event, and it will be fired.

In the following example, we are using the beforeCreate event, to automatically calculate an invoice number:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

use function str_pad;

/**
 * Class Invoices
 *
 * @property string $inv_created_at
 * @property int    $inv_cst_id
 * @property int    $inv_id
 * @property string $inv_number
 * @property string $inv_title
 * @property float  $inv_total
 */
class Invoices extends Model
{
    /**
     * @var int
     */
    public $inv_cst_id;

    /**
     * @var string
     */
    public $inv_created_at;

    /**
     * @var int
     */
    public $inv_id;

    /**
     * @var string
     */
    public $inv_number;

    /**
     * @var string
     */
    public $inv_title;

    /**
     * @var float
     */
    public $inv_total;

    public function beforeCreate()
    {
        $date     = date('YmdHis');
        $customer = substr(
            str_pad(
                $this->inv_cst_id, 6, '0', STR_PAD_LEFT
            ),
            -6
        );

        $this->inv_number = 'INV-' . $customer . '-' . $date;
    }
}

Contracts

The canonical contracts for the Events component live under the Phalcon\Contracts\Events\ namespace:

Contract Replaces
Phalcon\Contracts\Events\Event Phalcon\Events\EventInterface
Phalcon\Contracts\Events\EventsAware Phalcon\Events\EventsAwareInterface
Phalcon\Contracts\Events\Manager Phalcon\Events\ManagerInterface
Phalcon\Contracts\Events\Stoppable new — PSR-14-shaped mirror
Phalcon\Contracts\Events\Subscriber new — Symfony-style subscribers

The legacy Phalcon\Events\*Interface types are kept as thin extensions of their canonical counterparts and are marked @deprecated. Existing implementors and typehints continue to work; new code should target the canonical contracts directly.

Stoppable Events

Phalcon\Contracts\Events\Stoppable mirrors PSR-14's StoppableEventInterface with a single isPropagationStopped(): bool method. Phalcon\Events\Event implements it and routes the call through the same internal stopped flag as isStopped(), so calling $event->stop() flips both accessors and a PSR-14-aware library reading isPropagationStopped() sees the same state.

NOTE

Phalcon\Events\Event is declared final to enable C-level direct dispatch on its per-fire getters. If you previously subclassed it, build a sibling class that implements Phalcon\Contracts\Events\Event instead.

Custom Manager

The Phalcon\Contracts\Events\Manager contract must be implemented to create your own events manager replacing the one provided by Phalcon. The legacy Phalcon\Events\ManagerInterface alias is also accepted for backward compatibility.

<?php

namespace MyApp\Events;

use Phalcon\Contracts\Events\Manager as ManagerInterface;

class EventsManager implements ManagerInterface
{
    /**
     * @param string          $eventType
     * @param object|callable $handler
     */
    public function attach(string $eventType, $handler);

    /**
     * @param string          $eventType
     * @param object|callable $handler
     */
    public function detach(string $eventType, $handler);

    /**
     * @param string $type
     */
    public function detachAll(string $type = null);

    /**
     * @param string $eventType
     * @param object $source
     * @param mixed  $data
     * @param mixed  $cancelable
     * 
     * @return mixed
     */
    public function fire(
        string $eventType, 
        $source, 
        $data = null, 
        bool $cancelable = false
    );

    /**
     * @param string $type
     *
     * @return array
     */
    public function getListeners(string $type): array;

    /**
     * @param string $type
     *
     * @return bool
     */
    public function hasListeners(string $type): bool;
}

List of Events

The events available in Phalcon are:

Component Event Parameters
ACL acl:afterCheckAccess Acl
ACL acl:beforeCheckAccess Acl
Application application:afterHandleRequest Application, Controller
Application application:afterStartModule Application, Module
Application application:beforeHandleRequest Application, Dispatcher
Application application:beforeSendResponse Application, Response
Application application:beforeStartModule Application, Module
Application application:boot Application
Application application:viewRender Application, View
Cache cache:afterSet Cache
Cache cache:afterGet Cache
Cache cache:afterHas Cache
Cache cache:afterIncrement Cache
Cache cache:afterDecrement Cache
Cache cache:afterDelete Cache
Cache cache:beforeSet Cache
Cache cache:beforeGet Cache
Cache cache:beforeHas Cache
Cache cache:beforeIncrement Cache
Cache cache:beforeDecrement Cache
Cache cache:beforeDelete Cache
CLI dispatch:beforeException Console, Exception
Console console:afterHandleTask Console, Task
Console console:afterStartModule Console, Module
Console console:beforeHandleTask Console, Dispatcher
Console console:beforeStartModule Console, Module
Console console:boot Console
Db db:afterQuery Db
Db db:beforeQuery Db
Db db:beginTransaction Db
Db db:createSavepoint Db, Savepoint Name
Db db:commitTransaction Db
Db db:releaseSavepoint Db, Savepoint Name
Db db:rollbackTransaction Db
Db db:rollbackSavepoint Db, Savepoint Name
Dispatcher dispatch:afterBinding Dispatcher
Dispatcher dispatch:afterCallAction Dispatcher
Dispatcher dispatch:afterDispatch Dispatcher
Dispatcher dispatch:afterDispatchLoop Dispatcher
Dispatcher dispatch:afterExecuteRoute Dispatcher
Dispatcher dispatch:afterInitialize Dispatcher
Dispatcher dispatch:beforeDispatch Dispatcher
Dispatcher dispatch:beforeCallAction Dispatcher
Dispatcher dispatch:beforeDispatchLoop Dispatcher
Dispatcher dispatch:beforeException Dispatcher, Exception
Dispatcher dispatch:beforeExecuteRoute Dispatcher
Dispatcher dispatch:beforeForward Dispatcher, array (MVC Dispatcher)
Dispatcher dispatch:beforeNotFoundAction Dispatcher
Loader loader:afterCheckClass Loader, Class Name
Loader loader:beforeCheckClass Loader, Class Name
Loader loader:beforeCheckPath Loader
Loader loader:pathFound Loader, File Path
Micro micro:afterBinding Micro
Micro micro:afterHandleRoute Micro, return value mixed
Micro micro:afterExecuteRoute Micro
Micro micro:beforeException Micro, Exception
Micro micro:beforeExecuteRoute Micro
Micro micro:beforeHandleRoute Micro
Micro micro:beforeNotFound Micro
Model model:afterCreate Model
Model model:afterDelete Model
Model model:afterFetch Model
Model model:afterSave Model
Model model:afterUpdate Model
Model model:afterValidation Model
Model model:afterValidationOnCreate Model
Model model:afterValidationOnUpdate Model
Model model:beforeDelete Model
Model model:beforeCreate Model
Model model:beforeSave Model
Model model:beforeUpdate Model
Model model:beforeValidation Model
Model model:beforeValidationOnCreate Model
Model model:beforeValidationOnUpdate Model
Model model:notDeleted Model
Model model:notSaved Model
Model model:onValidationFails Model
Model model:prepareSave Model
Model model:validation Model
Models Manager modelsManager:afterInitialize Manager, Model
Request request:afterAuthorizationResolve Request, ['server' => Server array]
Request request:beforeAuthorizationResolve Request, ['headers' => [Headers], 'server' => [Server]]
Response response:afterSendHeaders Response
Response response:beforeSendHeaders Response
Router router:afterCheckRoutes Router
Router router:beforeCheckRoutes Router
Router router:beforeCheckRoute Router, Route
Router router:beforeMount Router, Group
Router router:matchedRoute Router, Route
Router router:notMatchedRoute Router, Route
Storage storage:afterSet Storage
Storage storage:afterGet Storage
Storage storage:afterHas Storage
Storage storage:afterIncrement Storage
Storage storage:afterDecrement Storage
Storage storage:afterDelete Storage
Storage storage:beforeSet Storage
Storage storage:beforeGet Storage
Storage storage:beforeHas Storage
Storage storage:beforeIncrement Storage
Storage storage:beforeDecrement Storage
Storage storage:beforeDelete Storage
View view:afterCompile Volt
View view:afterRender View
View view:afterRenderView View
View view:beforeCompile Volt
View view:beforeRender View
View view:beforeRenderView View, View Engine Path
View view:notFoundView View, View Engine Path
Volt compileFilter Volt, [name, arguments, function arguments]
Volt compileFunction Volt, [name, arguments, function arguments]
Volt compileStatement Volt, [statement]
Volt resolveExpression Volt, [expression]