PHP code example of x3p0-dev / x3p0-framework

1. Go to this page and download the library: Download x3p0-dev/x3p0-framework library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

x3p0-dev / x3p0-framework example snippets


namespace Your\Project;

interface Cache {}

final class FileCache implements Cache {}

final class ReportBuilder
{
    // The container injects a Cache implementation automatically.
    public function __construct(private readonly Cache $cache) {}
}

namespace Your\Project;

use X3P0\Framework\Core\ServiceProvider;

final class CacheServiceProvider extends ServiceProvider
{
    // Bind an interface to a concrete, shared across the request.
    protected const SINGLETONS = [
        Cache::class => FileCache::class
    ];

    // Resolve `'cache'` to the same binding as `Cache::class`.
    protected const ALIASES = [
        'cache' => Cache::class
    ];
}

namespace Your\Project;

use X3P0\Framework\Core\Application;

final class Plugin extends Application
{
    protected const PROVIDERS = [
        CacheServiceProvider::class
    ];
}

namespace Your\Project;

use X3P0\Framework\Container\ServiceContainer;

 new Plugin(new ServiceContainer());
}

add_action('plugins_loaded', static function (): void {
    do_action('your/project/register', plugin());
    plugin()->boot();
}, -999);

use X3P0\Framework\Core\ServiceProvider;

final class BlockServiceProvider extends ServiceProvider
{
    // Shared instances. A bare value is self-bound (the class is its own
    // concrete); a key => value pair binds an abstract to a concrete.
    protected const SINGLETONS = [
        BlockRegistry::class,
        Renderer::class => HtmlRenderer::class
    ];

    // New instance on every resolution. Same key conventions as SINGLETONS.
    protected const TRANSIENTS = [
        RequestContext::class
    ];

    // Overridable defaults: registered only if the abstract isn't already
    // bound, so an extension can replace them regardless of load order.
    protected const SINGLETONS_IF = [
        Logger::class => NullLogger::class
    ];
    protected const TRANSIENTS_IF = [
        View::class => PhpView::class
    ];

    // Alias => abstract. Resolving the alias resolves the abstract.
    protected const ALIASES = [
        'blocks' => BlockRegistry::class
    ];

    // Tag => list of abstracts, resolvable together via `tagged()`.
    protected const TAGS = [
        'theme.blocks' => [AlertBlock::class, CalloutBlock::class]
    ];

    // Services resolved and booted during the provider's boot phase. Each
    // must implement `Bootable`. They boot in the order listed.
    protected const BOOTABLE = [
        BlockRegistrar::class
    ];
}

public function register(): void
{
    $this->container->singleton(Connection::class, function (Container $c): Connection {
        return new Connection($c->get(Config::class)->dsn());
    });
}

public function boot(): void
{
    add_action('init', $this->registerBlocks(...));
}

use X3P0\Framework\Container\Container;
use X3P0\Framework\Core\ServiceProvider;

final class ReportServiceProvider extends ServiceProvider
{
    public function __construct(Container $container, private readonly Clock $clock)
    {
        parent::__construct($container);
    }
}

// Shared instance, built once and reused.
$container->singleton(Cache::class, FileCache::class);

// New instance on every resolution.
$container->transient(RequestContext::class);

// A value the container stores and returns as-is (never built or autowired).
$container->instance('config', new Config([...]));

// Alias one identifier to another (followed transitively).
$container->alias('cache', Cache::class);

$container->singletonIf(Logger::class, NullLogger::class);
$container->transientIf(RequestContext::class);

$container->singletonWhen(
    Gateway::class,
    fn (Container $c): bool => $c->get(Config::class)->get('features.stripe'),
    StripeGateway::class
);

$container->transientWhen(RequestContext::class, ! wp_doing_cron());

// Resolve by identifier.
$cache = $container->get(Cache::class);

// Resolve a class, optionally overriding constructor parameters by name.
$report = $container->make(ReportBuilder::class, ['format' => 'pdf']);

// Build a fresh, unshared instance even if the abstract is bound as a
// singleton. The shared instance is left untouched; only this abstract is
// built anew — its dependencies still resolve normally (shared stays shared).
$scratch = $container->build(Cache::class);

// Invoke a callable with its parameters resolved from the container.
$result = $container->call([$controller, 'handle']);
$result = $container->call(SomeController::class . '::handle');

// Get a closure that resolves the service on each call — without the
// consumer needing the container itself.
$makeReport = $container->defer(ReportBuilder::class);
$report = $makeReport();

final class ReportBuilder
{
    public function __construct(
        private readonly Cache $cache,      // resolved by type
        private readonly int $limit = 50,   // default used if not provided
    ) {}
}

$container->make(ReportBuilder::class);

use X3P0\Framework\Container\Attributes\Singleton;

#[Singleton]
final class FileCache implements Cache {}

use X3P0\Framework\Container\Attributes\SingletonWhen;

#[SingletonWhen([FeatureFlags::class, 'cacheIsShared'])]
final class FileCache implements Cache {}

use X3P0\Framework\Container\Attributes\Get;
use X3P0\Framework\Container\Attributes\Make;
use X3P0\Framework\Container\Attributes\Defer;
use X3P0\Framework\Container\Attributes\Tagged;
use X3P0\Framework\Container\Attributes\DeferTagged;
use X3P0\Framework\Container\Attributes\Build;
use X3P0\Framework\Container\Attributes\Param;
use X3P0\Framework\Container\Attributes\NoAutowire;

final class Dashboard
{
    public function __construct(
        // Resolve a specific identifier (a keyed binding or a chosen concrete).
        #[Get('config')] private readonly Config $config,

        // Resolve an identifier with inline constructor overrides, still
        // honoring a matching cached singleton when none are given.
        #[Make(ReportBuilder::class, ['format' => 'pdf'])] private readonly ReportBuilder $reportBuilder,

        // Inject a closure that resolves the service lazily, on demand.
        #[Defer(ReportBuilder::class)] private readonly Closure $makeReport,

        // Inject every service assigned to a tag, already resolved.
        #[Tagged('theme.blocks')] private readonly iterable $blocks,

        // Inject the tagged services as deferred resolvers, keyed by class,
        // so you build only the ones you actually use.
        #[DeferTagged('report.sections')] private readonly array $sections,

        // Build a fresh, unshared instance with inline constructor
        // overrides — a private copy configured right here.
        #[Build(TransientCache::class, ['ttl' => 3600])] private readonly Cache $cache,

        // Resolve a container-backed named parameter set via `setParam()`.
        #[Param('timezone')] private readonly string $timezone,

        // Skip autowiring so the parameter keeps its default instead of the
        // container building the type — here, leaving `$user` null rather
        // than constructing a WP_User.
        #[NoAutowire] private readonly ?WP_User $user = null
    ) {}
}

use X3P0\Framework\Container\Attributes\ContextualAttribute;
use X3P0\Framework\Container\Container;

#[Attribute(Attribute::TARGET_PARAMETER)]
final class CurrentUser implements ContextualAttribute
{
    public function resolve(Container $container): object
    {
        return $container->get(UserRepository::class)->current();
    }
}

$container->whenNeedsParam(Mailer::class, 'apiKey', 'secret');
$container->whenNeedsParam(Mailer::class, 'timeout',
    fn ($container) => $container->get('config')->get('mail.timeout'));

// Cache resolves to FileCache application-wide, but ExportJob gets RedisCache.
$container->singleton(Cache::class, FileCache::class);
$container->whenNeedsType(ExportJob::class, Cache::class, RedisCache::class);

$container->setParam('timezone', 'America/Chicago');
$container->setParam('retryLimit', 3);

$container->hasParam('timezone'); // true
$container->getParam('timezone'); // 'America/Chicago'

use X3P0\Framework\Container\Attributes\Param;

final class ReportBuilder
{
    public function __construct(
        #[Param('timezone')] private readonly string $timezone
    ) {}
}

$container->tag([AlertBlock::class, CalloutBlock::class], 'theme.blocks');

foreach ($container->tagged('theme.blocks') as $block) {
    $block->register();
}

add_action('your/project/register', static function ($app): void {
    $app->container()->singleton(TestimonialBlock::class);
    $app->container()->tag(TestimonialBlock::class, 'theme.blocks');
});

$container->tag(EmailChannel::class, 'channels', ['slug' => 'email']);
$container->tag(SmsChannel::class,   'channels', ['slug' => 'sms']);

$bySlug   = $container->taggedAbstractsWith('channels', 'slug');
$instance = $container->make($bySlug['email']);

use X3P0\Framework\Container\Attributes\Tag;

#[Tag('channels', ['slug' => 'email'])]
final class EmailChannel implements Channel {}

$container->tagFromAttributes(EmailChannel::class);

$container->setTagContract('channels', Channel::class);

// Run after the service is built, to mutate it in place. Runs once per build,
// so a singleton is observed only the first time it's created.
$container->resolving(ReportBuilder::class, function (object $builder, Container $c): void {
    $builder->setTimezone($c->get(Config::class)->timezone());
});

// Wrap or replace the service with something honoring the same contract.
// Decorators stack in registration order. If the service is already
// resolved, the decorator is applied to the stored instance immediately.
$container->decorate(Cache::class, function (Cache $cache, Container $c): Cache {
    return new LoggingCache($cache, $c->get(Logger::class));
});

$container->has(Cache::class);            // resolvable? (bound or an autowirable class)
$container->registered(Cache::class);     // explicitly bound or instance-registered?
$container->resolved(Cache::class);       // already built and cached?
$container->forgetInstance(Cache::class); // drop the cached instance; rebuild next time

final class Plugin extends Application
{
    protected const PROVIDERS = [
        CoreServiceProvider::class,
        AdminServiceProvider::class,
        FrontendServiceProvider::class
    ];
}

$app->register(AdminServiceProvider::class, RestServiceProvider::class);

add_action('plugins_loaded', static function (): void {
    do_action('your/project/register/plugin', plugin()->begin());
    plugin()->boot();
}, -999);

add_action('after_setup_theme', static function (): void {
    do_action('your/project/register/theme', plugin()->begin());
    plugin()->boot();
}, -999);