Skip to content

Extension points

Contract has two kinds of interfaces. Some, like ContainerInterface or ConnectionInterface, describe classes you consume: you type hint against them and let a Raxos package provide the implementation. Others are meant to be implemented by you, so you can plug custom behavior into a Raxos package. This page is about the second kind.

Small interfaces you implement

Extension point interfaces are intentionally small, usually one or two methods, so implementing one is a quick way to hook into a package without subclassing its internals. Common examples:

Why the interface lives here

Because the interface lives in Contract rather than in the package that consumes it, your implementation does not need to depend on that package's concrete classes at all. You depend on the small contract, the package depends on the same contract, and the two meet without being coupled.

The consuming package usually needs to know which of your classes to call. That link is made with an attribute you attach to your implementation or to the property that uses it, for example the #[Caster] attribute on a model column.

Example: a custom ORM caster

CasterInterface is a typical extension point. raxos/database calls into it, your application supplies the implementation, and the #[Caster] attribute wires the two together.

php
<?php
declare(strict_types=1);

namespace App\Casters;

use Raxos\Contract\Database\Orm\CasterInterface;
use Raxos\Database\Orm\Model;

final readonly class MoneyCaster implements CasterInterface
{
    public function decode(string|float|int|null $value, Model $instance): mixed
    {
        return $value === null ? null : (int) $value;
    }

    public function encode(mixed $value, Model $instance): string|float|int|null
    {
        return $value === null ? null : (string) $value;
    }
}

See the CasterInterface reference for the full method contract, and the extension point references for the router and message bus equivalents.