PHP code example of polidog / relayer

1. Go to this page and download the library: Download polidog/relayer 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/ */

    

polidog / relayer example snippets



declare(strict_types=1);

Relayer::boot(__DIR__ . '/..')->run();

$router = Relayer::boot(__DIR__ . '/..');
$router->setJsPath('/assets/app.js');
$router->addCssPath('/assets/style.css');
$router->run();

// trap: bakes "" if API_TOKEN is unset at compile time
$container->setParameter('app.api_token', $_ENV['API_TOKEN'] ?? '');

// resolves at boot, not at compile — runtime-injected secrets work
$container->setParameter('app.api_token', '%env(API_TOKEN)%');


// src/Pages/users/[id]/page.psx
declare(strict_types=1);

namespace App\Pages\Users;

use App\Service\UserRepository;
use Polidog\UsePhp\Runtime\Element;
use Polidog\Relayer\Router\Component\PageComponent;

final class UserDetailPage extends PageComponent
{
    public function __construct(private readonly UserRepository $users) {}

    public function render(): Element
    {
        $user = $this->users->find($this->getParam('id'));
        return <h1>{$user->name}</h1>;
    }
}


// src/Pages/about/page.psx
return fn() => <main><h1>About</h1></main>;


// src/Pages/users/page.psx
declare(strict_types=1);

use App\Service\UserRepository;
use Polidog\Relayer\Router\Component\PageContext;
use Polidog\UsePhp\Runtime\Element;

return function (PageContext $ctx, UserRepository $users): Closure {
    $ctx->metadata(['title' => 'Users']);

    return function () use ($users): Element {
        $list = $users->all();
        return <ul>{...\array_map(fn($u) => <li>{$u->name}</li>, $list)}</ul>;
    };
};

return function (PageContext $ctx) use ($posts): Closure {
    $post = $posts->find($ctx->params['id']) ?? $ctx->notFound();
    if ($post->isDraft && null === $ctx->user()) {
        $ctx->abort(403);
    }

    return fn (): Element => <article>{$post->title}</article>;
};


// src/Pages/api/users/route.php
declare(strict_types=1);

use App\Service\UserRepository;
use Polidog\Relayer\Http\Request;
use Polidog\Relayer\Http\Response;

return [
    'GET'  => fn (UserRepository $users): Response => Response::json(['users' => $users->all()]),
    'POST' => function (Request $req, UserRepository $users): Response {
        $users->create($req->allPost());
        return Response::json(['ok' => true], 201);
    },
];

return function (PageContext $ctx): Closure {
    $ctx->js('/assets/chart.js', defer: true);

    return fn (): Element => <canvas id="chart"></canvas>;
};

final class Dashboard extends LayoutComponent
{
    public function render(): Element
    {
        $this->addJs('/assets/dashboard.js', module: true);
        return <div>{...$this->getChildren()}</div>;
    }
}


// src/Pages/dashboard/page.psx
declare(strict_types=1);

use Polidog\Relayer\React\Island;
use Polidog\Relayer\Router\Component\PageContext;

return fn (PageContext $ctx) => (
    <section>
        <h1>Dashboard</h1>
        {Island::mount('Chart', ['points' => $ctx->params])}
    </section>
);

$document->addHeadHtml(Island::loaderScript());
$document->addHeadHtml('<script type="module" src="/islands.js"></script>');


// src/Pages/middleware.php
declare(strict_types=1);

use Polidog\Relayer\Http\Request;

return function (Request $request, Closure $next): void {
    if (null === $request->header('x-api-key')) {
        \http_response_code(401);
        echo '{"error":"missing api key"}';
        return; // route never runs
    }
    $next($request);
};


// src/Pages/middleware.php
use Polidog\Relayer\Http\Cors;

return Cors::middleware([
    'origins' => ['https://app.example.com'], // or ['*']
    // methods / headers / credentials / maxAge are optional
]);

public function render(): Element
{
    return (
        <form method="post">
            <input type="hidden" name="_usephp_action" value={$this->action([$this, 'save'])} />
            <input name="title" />
        </form>
    );
}

public function save(array $form): void
{
    // ... handle $form['title']
    header('Location: /dashboard', true, 303); // PRG
    exit;
}


// src/Pages/users/page.psx
declare(strict_types=1);

use App\Service\UserRepository;
use Polidog\Relayer\Router\Component\PageContext;
use Polidog\UsePhp\Runtime\Element;

return function (PageContext $ctx, UserRepository $users): Closure {
    $save = $ctx->action('save', function (array $form) use ($users, $ctx): void {
        $users->create($form['name']);
        $ctx->redirect('/users'); // 303 Post/Redirect/Get; unwinds here
    });

    return function () use ($save, $users): Element {
        return (
            <main>
                <ul>{...\array_map(fn($u) => <li>{$u->name}</li>, $users->all())}</ul>
                <form action={$save}>
                    <input name="name" />
                    <button>save</button>
                </form>
            </main>
        );
    };
};

// list → positional:   handler($form, 42)
$delete = $ctx->action('delete', function (array $form, int $id) use ($repo): void {
    $repo->delete($id);
}, [$user->id]);

// assoc → named args:   handler(formData: $form, id: 42)
$ctx->action('delete', fn (array $formData, int $id) => $repo->delete($id), ['id' => $user->id]);

return function (PageContext $ctx) use ($schema): Closure {
    $errors = [];
    $save = $ctx->action('save', function (array $form) use ($schema, &$errors, $ctx): void {
        $result = $schema->safeParse($form);
        if (!$result->success) { $errors = $result->errors; return; }
        // ... on success: $ctx->redirect('/users') (303 Post/Redirect/Get)
    });

    // $errors is mutated after the action runs → capture by reference
    return function () use ($save, &$errors): Element { /* render $errors */ };
};


// src/PagesConfigurator.php
declare(strict_types=1);

namespace App;

use App\Service\UserRepository;
use App\Service\PdoUserRepository;
use Polidog\Relayer\AppConfigurator as BaseConfigurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class AppConfigurator extends BaseConfigurator
{
    public function configure(ContainerBuilder $container): void
    {
        $container->register(PdoUserRepository::class);
        $container->setAlias(UserRepository::class, PdoUserRepository::class)
            ->setPublic(true);
    }
}

Relayer::boot(__DIR__ . '/..', new App\AppConfigurator(__DIR__ . '/..'))->run();


// src/Components/TodoList.psx
declare(strict_types=1);

namespace App\Components;

use Polidog\Relayer\Db\Database;
use Polidog\Relayer\Relayer;
use Polidog\UsePhp\Runtime\Element;

return function (array $props): Element {
    $db = Relayer::container()->get(Database::class);
    $todos = $db->fetchAll('SELECT id, title, done FROM todos ORDER BY id');

    return (
        <ul>
            {array_map(fn($t) => <li>{$t['title']}</li>, $todos)}
        </ul>
    );
};


// src/Pages/users/page.psx
declare(strict_types=1);

namespace App\Pages\Users;

use App\Service\UserRepository;
use Polidog\UsePhp\Runtime\Element;
use Polidog\Relayer\Router\Component\PageComponent;

final class UsersPage extends PageComponent
{
    public function __construct(private readonly UserRepository $users)
    {
    }

    public function render(): Element
    {
        $users = $this->users->all();
        // ...
    }
}


// src/Pages/signup/page.psx
declare(strict_types=1);

use Polidog\Relayer\Http\Request;
use Polidog\Relayer\Router\Component\PageContext;
use Polidog\UsePhp\Runtime\Element;

return function (PageContext $ctx, Request $req): Closure {
    $errors = [];

    if ($req->isPost()) {
        $email = $req->post('email') ?? '';
        if (!\filter_var($email, \FILTER_VALIDATE_EMAIL)) {
            $errors['email'] = 'Invalid email';
        }
        if ([] === $errors) {
            $ctx->redirect('/thanks'); // 303 Post/Redirect/Get; unwinds here
        }
    }

    return function () use ($errors, $req): Element {
        // ... render form, echoing $req->post('email') back into the input
    };
};


declare(strict_types=1);

namespace App\Auth;

use Polidog\Relayer\Auth\Credentials;
use Polidog\Relayer\Auth\Identity;
use Polidog\Relayer\Auth\UserProvider;

final class PdoUserProvider implements UserProvider
{
    public function __construct(private readonly \PDO $pdo) {}

    public function findByIdentifier(string $identifier): ?Credentials
    {
        $stmt = $this->pdo->prepare(
            'SELECT id, name, password_hash, roles FROM users WHERE email = ?'
        );
        $stmt->execute([\strtolower(\trim($identifier))]);
        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
        if (false === $row) {
            return null;
        }

        return new Credentials(
            identity: new Identity(
                id: (int) $row['id'],
                displayName: (string) $row['name'],
                roles: \json_decode((string) $row['roles'], true) ?: [],
            ),
            passwordHash: (string) $row['password_hash'],
        );
    }
}


// src/Pages/login/page.psx
declare(strict_types=1);

use Polidog\Relayer\Auth\Authenticator;
use Polidog\Relayer\Router\Component\PageContext;
use Polidog\UsePhp\Runtime\Element;

return function (PageContext $ctx, Authenticator $auth): Closure {
    $error = null;

    $login = $ctx->action('login', function (array $form) use ($auth, &$error, $ctx): void {
        $identity = $auth->attempt(
            (string) ($form['email']    ?? ''),
            (string) ($form['password'] ?? ''),
        );

        if (null === $identity) {
            $error = 'Invalid email or password.';

            return;
        }

        $ctx->redirect('/dashboard'); // 303 Post/Redirect/Get; unwinds here
    });

    return function () use ($login, $error): Element {
        // ... render form, surface $error as a single generic message
    };
};


namespace App\Pages;

use Polidog\Relayer\Auth\Auth;
use Polidog\Relayer\Router\Component\PageComponent;

#[Auth] // any authenticated user
final class DashboardPage extends PageComponent { /* ... */ }

#[Auth(roles: ['admin'])] // role-gated; non-admin gets 403
final class AdminPage extends PageComponent { /* ... */ }

#[Auth(redirectTo: '')] // empty redirect -> 401 instead of 302 (JSON / API)
final class ApiEndpoint extends PageComponent { /* ... */ }


// src/Pages/dashboard/page.psx
declare(strict_types=1);

use Polidog\Relayer\Router\Component\PageContext;
use Polidog\UsePhp\Runtime\Element;

return function (PageContext $ctx): Closure {
    $user = $ctx->

return function (PageContext $ctx, ?Identity $user): Closure {
    $ctx->metadata(['title' => $user?->displayName ?? 'Welcome']);

    return fn(): Element => null !== $user
        ? <p>Hi, {$user->displayName}</p>
        : <a href="/login">Sign in</a>;
};

$container->register(NativePasswordHasher::class)
    ->setArguments([\PASSWORD_ARGON2ID]);

#[Auth(redirectTo: '')]            // 401 for an absent/bad token (no redirect)
final class ApiEndpoint extends PageComponent { /* ... */ }

#[Auth(roles: ['admin'])]          // roles read from the token's claims
final class AdminApi extends PageComponent { /* ... */ }


// src/Pages/auth/token/route.php — POST { } with Authorization: Bearer <jwt>
declare(strict_types=1);

use Polidog\Relayer\Auth\Authenticator;
use Polidog\Relayer\Auth\Token\BearerToken;
use Polidog\Relayer\Auth\Token\TokenVerifier;
use Polidog\Relayer\Auth\Token\AuthorizationHeader;
use Polidog\Relayer\Http\Response;

return [
    'POST' => static function (
        TokenVerifier $verifier,
        Authenticator $auth,
        AuthorizationHeader $header,
    ): Response {
        $identity = $verifier->verify(
            BearerToken::parse($header->value()) ?? '',
        );
        if (null === $identity) {
            return Response::json(['error' => 'invalid token'], 401);
        }

        $auth->login($identity);             // rotates the session id

        return Response::json(['user' => $identity->toArray()]);
    },
];


// src/Pages/page.psx
declare(strict_types=1);

namespace App\Pages;

use Polidog\Relayer\Router\Component\PageComponent;
use Polidog\Relayer\Http\Cache;
use Polidog\UsePhp\Runtime\Element;

#[Cache(
    maxAge: 3600,
    sMaxAge: 86400,
    public: true,
    vary: ['Accept-Language'],
    etag: 'home-v1',
)]
final class HomePage extends PageComponent
{
    public function render(): Element { /* ... */ }
}

#[Cache(
    maxAge: 3600,
    public: true,
    vary: ['Accept-Language'],
    etag: 'home-v1',
    etagWeak: true,
    lastModified: '2025-01-15 10:00:00 UTC',
)]
final class HomePage extends PageComponent { /* ... */ }


// src/Pages/feed/page.psx
declare(strict_types=1);

use Polidog\Relayer\Http\Cache;
use Polidog\Relayer\Router\Component\PageContext;
use Polidog\UsePhp\Runtime\Element;

return function (PageContext $ctx): Closure {
    // Lightweight setup: declare cache, read params. NO DB queries here.
    $ctx->cache(new Cache(maxAge: 60, public: true, etagKey: 'feed'));

    return function () use ($ctx): Element {
        // Heavy work goes here — only runs on cache miss.
        // ... query DB, build the page
    };
};

#[Cache(maxAge: 60, public: true, etagKey: 'user-list')]
final class UsersPage extends PageComponent { /* ... */ }

final class UserRepository
{
    public function __construct(private readonly EtagStore $etags) {}

    public function save(User $user): void
    {
        // ... persist
        $this->etags->set('user-list', \sha1((string) \microtime(true)));
    }
}

final class RedisEtagStore implements EtagStore
{
    public function __construct(
        private readonly \Redis $redis,
        private readonly string $prefix = 'etag:',
    ) {}

    public function get(string $key): ?string
    {
        $value = $this->redis->get($this->prefix . $key);
        return \is_string($value) && $value !== '' ? $value : null;
    }

    public function set(string $key, string $etag): void
    {
        $this->redis->set($this->prefix . $key, $etag);
    }

    public function forget(string $key): void
    {
        $this->redis->del($this->prefix . $key);
    }
}

$container->register(RedisEtagStore::class);
$container->setAlias(EtagStore::class, RedisEtagStore::class)->setPublic(true);

use Polidog\Relayer\Db\Database;

final class UserPage extends PageComponent
{
    public function __construct(private readonly Database $db) {}

    public function render(): string
    {
        $user = $this->db->fetchOne(
            'SELECT id, name FROM users WHERE id = :id',
            ['id' => 42],
        );
        // ...
    }
}

$db->transactional(function (Database $tx): void {
    $tx->perform('INSERT INTO orders (user_id) VALUES (?)', [$userId]);
    $tx->perform('UPDATE users SET order_count = order_count + 1 WHERE id = ?', [$userId]);
});

use Polidog\Relayer\Http\Client\HttpClient;

final class WeatherPage extends PageComponent
{
    public function __construct(private readonly HttpClient $http) {}

    public function render(): string
    {
        $res = $this->http->get('https://api.example.com/weather?city=tokyo', [
            'Accept' => 'application/json',
        ]);

        if (!$res->ok()) {
            // 4xx/5xx is not an exception — a normal HttpResponse to branch on
            return 'Could not fetch (' . $res->status . ')';
        }

        $data = $res->json();
        // ...
    }
}

$res = $this->http->request(
    'POST',
    'https://api.example.com/orders',
    ['Content-Type' => 'application/json'],
    json_encode(['item' => 'book']),
);

use Polidog\Relayer\Validation\Validator;

$schema = Validator::object([
    'email' => Validator::string()->trim()->email(),
    'name'  => Validator::string()->trim()->min(1, 'Name is 

$result = $schema->safeParse($_POST);

if ($result->success) {
    $data = $result->data;          // coerced values
} else {
    $errors = $result->errors;      // ['email' => '...', 'address.zip' => '...']
}

$schema = Validator::object([
    'name'     => Validator::string()->trim()->min(1, 'Name is )->min(8, 'Password must be at least 8 characters.'),
]);

$signup = $ctx->action('signup', function (array $form) use ($schema, &$errors): void {
    $result = $schema->safeParse($form);
    if (!$result->success) {
        $errors = $result->errors;   // hand field errors to the view
        return;
    }
    // $result->data is coerced
});

// translations/ja.php
return [
    'home.title'   => 'ようこそ',
    'cart.items'   => '{count}点|{count}点', // pipe = plural forms
    'user'         => ['greeting' => 'こんにちは、{name}さん'],
];

use Polidog\Relayer\I18n\Translator;

return static fn (Translator $t) => h('h1', [], $t->trans('home.title'));
// placeholders:  $t->trans('user.greeting', ['name' => $name])
// plural:        $t->transChoice('cart.items', $count)

use Psr\Log\LoggerInterface;

final class CheckoutPage extends PageComponent
{
    public function __construct(private readonly LoggerInterface $log) {}

    public function render(): string
    {
        $this->log->info('checkout started for {user}', ['user' => $userId]);
        // ...
    }
}

use Polidog\Relayer\Profiler\Profiler;

public function __construct(private readonly Profiler $profiler) {}

// one-shot event
$this->profiler->collect('app', 'cache warmed', ['keys' => 12]);

// timed span (finalized by stop())
$span = $this->profiler->start('app', 'heavy compute');
$result = $this->compute();
$span->stop(['rows' => \count($result)]);

// timed span around a call, finalized for you (returns the call's value,
// records an `error` payload and rethrows if it throws)
$user = $this->profiler->measure('lib', 'sdk.fetchUser',
    fn () => $this->thirdPartySdk->fetchUser($id));

final class TraceableWeatherApi implements WeatherApi
{
    public function __construct(
        private readonly WeatherApi $inner,
        private readonly Profiler $profiler,
    ) {}

    public function forecast(string $city): Forecast
    {
        // Choose the label/payload deliberately — record the city, not an
        // API key the real client might also take.
        return $this->profiler->measure('lib', 'weather.forecast',
            fn () => $this->inner->forecast($city));
    }
}

// config: AppConfigurator::configure()
if ($_ENV['APP_ENV'] === 'dev') {
    $container->register(TraceableWeatherApi::class)
        ->setArguments([new Reference(HttpWeatherApi::class), new Reference(Profiler::class)])
        ->setPublic(true);
    $container->setAlias(WeatherApi::class, TraceableWeatherApi::class)->setPublic(true);
}

your-app/
  .env                 # loaded automatically if present
  composer.json
  config/
    services.yaml      # auto-loaded if present (also services.php / .yml)
  public/
    index.php
  src/
    Pages/             # AppRouter file-based routes live here
      layout.psx
      page.psx
      about/
        page.psx
    AppConfigurator.php # your service registrations (extends Polidog\Relayer\AppConfigurator)