usePHP(PSX エンジン)#
Relayer は polidog/use-php の上に構築されています。ルーティング・DI・認証・キャッシュなどのアプリ機構は Relayer が担い、ビュー(描画)は usePHP が担当します。このページは、その描画エンジンである usePHP の要点をまとめます。
PSX とは#
PSX は「JSX 風の構文を書ける PHP」です。.psx ファイルを usePHP がコンパイルし、H:: 呼び出し(ハイパースクリプト)に変換します。
<?php
use Polidog\UsePhp\Html\H;
return fn () => (
<section className="card">
<h1>It works</h1>
<p>これは {date('Y')} 年のページです。</p>
</section>
);上は次と等価です(コンパイル結果のイメージ)。
return fn () => H::section(className: 'card', children: [
H::h1(children: 'It works'),
H::p(children: ['これは ', date('Y'), ' 年のページです。']),
]);- 属性は
className(React 流。classに変換)。{ ... }で PHP 式を埋め込み。 .psx内では JSX を書く。.phpのヘルパクラスは JSX 不可なのでH::を直接使う。
テキストは必ずエスケープされる(XSS 安全)#
Renderer はすべてのテキスト子要素を htmlspecialchars でエスケープします。 生 HTML をそのまま注入する仕組みはありません(テキストは常にエスケープされ、構造だけが本物のタグになる)。
そのため Markdown を表示するときは「HTML 文字列」ではなく H:: の Element ツリーへ変換します(このサイトの App\Docs\Markdown がまさにそれ — 構造は本物のタグ、テキストはエスケープ=安全)。
コンポーネント#
PascalCase のタグはコンポーネント。use で FQCN を解決します。
<?php
namespace App\Components;
use Polidog\UsePhp\Html\H;
return fn (array $props) => (
<div className="card">
<h3>{$props['title']}</h3>
<div>{$props['children'] ?? null}</div>
</div>
);use App\Components\Card;
return fn () => (
<Card title="お知らせ">
<p>本文…</p>
</Card>
);ループや条件は式で書きます。
<ul>
{array_map(fn ($x) => <li key={$x['id']}>{$x['name']}</li>, $items)}
</ul>
{$loggedIn ? (<a href="/logout">Logout</a>) : (<a href="/login">Login</a>)}フック(useState / useEffect / useRouter)#
フックは fc() でラップした関数コンポーネントの中でのみ機能します(コンポーネントのコンテキストが必要)。状態はサーバー側で保持され、保存方法を StorageType で選びます。提供されるフックは useState / useEffect / useRouter(と fc)です。
use Polidog\UsePhp\Storage\StorageType;
use function Polidog\UsePhp\Runtime\{fc, useState, useEffect, useRouter};fc()#
fc(callable $component, ?string $key = null,
StorageType $storageType = StorageType::Session, ?Defer $defer = null): FunctionComponent$key はコンポーネントインスタンスの識別子、$storageType は状態の保持方法、 $defer は遅延描画(後述)用。フックを使うコンポーネントは必ず fc() で包みます。
useState#
useState($initial) は [値, セッター] を返します。セッターは Action を返すので、イベント(onClick など)に結び付けて使います。usephp.js がプログレッシブに強化し、JS 無しでもフォーム POST にフォールバックします。
return fc(function (array $props) {
[$count, $setCount] = useState($props['initial'] ?? 0);
return (
<div>
<span>Count: {$count}</span>
<button onClick={fn () => $setCount($count + 1)}>+</button>
<button onClick={fn () => $setCount(0)}>reset</button>
</div>
);
}, 'counter', StorageType::Snapshot);useEffect#
useEffect(callable $callback, ?array $deps = null): void- 初回(マウント)と、依存値が変わったときに実行。
$deps = null… 毎回の描画で実行 /[]… マウント時のみ /
[$a, $b] … その値が変わったときだけ。
$callbackが クリーンアップ関数を返すと、次回実行前
(およびクリーンアップ時)に呼ばれます。
return fc(function (array $props) {
[$tab, $setTab] = useState('home');
useEffect(function () use ($tab) {
\error_log("tab changed: {$tab}");
return function () {
\error_log('cleanup before next effect');
};
}, [$tab]); // $tab が変わったときだけ
return (
<div>
<button onClick={fn () => $setTab('home')}>Home</button>
<button onClick={fn () => $setTab('about')}>About</button>
<p>active: {$tab}</p>
</div>
);
}, 'tabs', StorageType::Session);useRouter#
現在 URL とルートパラメータを取得します。
$router = useRouter();
$router['currentUrl']; // 現在の URL
$router['params']; // ['id' => '42'] など(動的セグメント)StorageType#
| 種別 | 挙動 |
|---|---|
Session(既定) | サーバーセッションに保持。ページ遷移を跨いで残る |
Memory | ページ読み込みごとにリセット |
Snapshot | 状態を(署名付きで)HTML に埋め込み往復。サーバーはステートレス。本番は USEPHP_SNAPSHOT_SECRET 必須 |
useMemo/useRefなどはありません。提供されるのは上記のフックのみ。フォーム送信の検証などは バリデーション / サーバーアクション を参照。
コンパイルと配布#
.psx はそのままでは動かず、コンパイルが必要です。
vendor/bin/usephp compile src/Components # .psx をコンパイル + manifest 生成
vendor/bin/usephp publish # public/usephp.js を配置- dev(
APP_ENV=dev)はリクエスト時に自動コンパイル。 - 本番はデプロイ時に事前コンパイル必須(デプロイ 参照)。
- キャッシュキーは ソースの realpath の sha1。ビルドと実行時で同じ絶対パス
である必要があります。
エディタのシンタックスハイライト#
.psx は VS Code や Vim/Neovim が標準では認識しないので、放っておくと PHP 扱いになるか、プレーンテキストとして開かれます。polidog/use-php リポジトリには .psx 用のハイライト定義が同梱されていて、入れると以下が効きます。
*.psxのファイルタイプ判定- エディタ標準の PHP 文法による PHP コードのハイライト
- JSX 風タグを上に重ねるルール
- HTML 要素(
<div>,<span>…)と PascalCase のコンポーネントタグ(<Counter />) - 自己閉鎖タグとフラグメント(
<>…</>) - 属性(リテラル文字列と
{ ... }の PHP 式) { ... }の中身は PHP としてハイライト
- HTML 要素(
定義は polidog/use-php リポジトリの editors/ に入っています。インストール手順は各エディタの README を参照してください。
- VS Code —
editors/vscode/(README)。
vsce package で .vsix をビルドして入れるのが推奨。Marketplace への公開は未。
- Neovim / Vim —
editors/nvim/(README)。
lazy.nvim / packer / 手動コピーいずれも可。Vim-script のみで Lua 依存なし。
同梱されていないもの(現時点では scope 外):LSP(型チェック・補完)、 tree-sitter 文法、PSX 対応 PHPStan 拡張、フォーマッタ連携。これらは別リポジトリとして切り出される予定です。
どちらの定義も正規表現ベースなので、
$a < $bのような比較式が稀にタグと誤認されることがあります。踏んだら最小再現とともに use-php 側に issue を立ててください。
ページ単位のアセット#
グローバルに JS/CSS を足さず、必要なルートだけで宣言できます。
return function (PageContext $ctx) {
$ctx->js('https://example.com/highlight.min.js', defer: true);
return fn () => (/* ... */);
};このサイトでもコードハイライト用 highlight.js を、<pre><code> を持つドキュメントページだけで $ctx->js() 読み込みしています(ページ単位のスクリプト 参照)。
エスケープハッチ#
リッチな UI が必要な箇所は React アイランドでクライアント描画に逃がせます(React アイランド 参照)。重い部分は 遅延コンポーネント(#[Defer] / fc(..., defer: ...)、/_defer/{name} を usephp.js が後追い取得)にできます。
Relayer との関係#
あなたのアプリ
└─ Relayer ルーティング / DI / 認証 / キャッシュ / バリデーション / DB
└─ usePHP PSX コンパイル / Element 描画 / 状態 / アイランド / 遅延usePHP は「描画の心臓部」。Relayer はその周りに Web アプリの骨格を足したもの、と捉えると全体像が掴みやすいです。次は ルーティングとページ へ。
変更履歴 (3)
- Add editor syntax highlighting section (VS Code / Neovim) referencing use-php editors/
- バージョン差分表記(vX.Y.Z 追加/破壊的変更/依存バージョン注記)を削除し現在形に整理
- 新規作成