Начало работы: PHP
Пакет php/ (Composer, PHP 8.1+) рендерит статичные компоненты на сервере: та же разметка, что у React, плюс valyro-ui/styles.css и атрибуты темы — без Node и React в рантайме
1. Подключите пакет
Проверено на PHP 8.5.9 с Composer 2 (код рассчитан на PHP 8.1+). Пакет ui-kit/ui-kit пока не опубликован в Packagist: подключите его из репозитория как path-репозиторий. Для локалей, кроме en-US, нужно расширение intl (без него числа форматируются как в en-US).
{
"repositories": [{ "type": "path", "url": "../valyro-ui/php" }],
"require": { "ui-kit/ui-kit": "@dev" }
}
2. Стили и тема
Возьмите valyro-ui/styles.css из пакета (npm run build:core → dist/core/styles.css) и положите к статике. Theme::htmlAttributes() ставит data-theme/data-mode на <html>; режим system отдаёт light и маленький скрипт в <head> (Theme::head()), который до первой отрисовки переключает на тёмный по настройке ОС. Для CSP передайте nonce третьим аргументом. В стилях нет @font-face: для темы vercel подключите ещё fonts/geist.css и fonts/geist-mono.css, скопировав их вместе с папками шрифтов, как в гайде HTML + CSS.
<?php
require __DIR__ . '/../vendor/autoload.php';
use UiKit\Html;
use UiKit\Theme;
use UiKit\Ui;
?>
<!doctype html>
<html lang="en" <?= Theme::htmlAttributes('glass', 'system') ?>>
<head>
<meta charset="utf-8">
<?= Theme::head('/valyro-ui.css', 'system') ?>
</head>
<body>
<?= Ui::HeroSection([
'title' => 'Hello from PHP',
'subtitle' => $_GET['name'] ?? 'Same markup as the React components.',
'primaryAction' => ['label' => 'Get started', 'href' => '/start', 'icon' => Ui::icon('Zap')],
]) ?>
<?= Ui::Card([],
Ui::CardHeader([], Ui::CardTitle([], 'Project'), Ui::CardDescription([], 'Production')),
Ui::CardContent([], Ui::Badge(['color' => 'green', 'dot' => true], 'Ready')),
Ui::CardFooter([], Ui::Button(['asChild' => true, 'variant' => 'primary'], Html::el('a', ['href' => '/open'], 'Open'))),
) ?>
</body>
</html>
3. API компонентов
Пропсы — как у React: те же имена и значения по умолчанию (className или class, style — массивом). Контент: строки (экранируются), числа, массивы, другие компоненты, Html::el() и Html::raw() — только для доверенной разметки. Колбэки (formatValue, ratingLabel, ellipsisLabel) — callable. Иконки: Ui::icon('ArrowRight', ['size' => 14]). Любой компонент по имени: Ui::create('Badge', [...]). Все строки экранируются (& < > " '), обработчики on* и опасные имена атрибутов отбрасываются, URL со схемами вроде javascript:/data: заменяются на #.
4. Что работает без JavaScript
Статичные компоненты: Button, IconButton, ButtonGroup, Badge, Avatar и AvatarGroup, Separator, Skeleton и SkeletonText, Spinner, ProgressBar, Breadcrumbs, TextInput, Textarea, Card и его части, StatsCard (со спарклайном), Alert, EmptyState, Timeline, HeroSection, FeatureGrid, Testimonials, LogoCloud, CTASection, Footer. Accordion, Tabs, FAQSection и PricingTable PHP рендерит в начальном состоянии (то, что React отдаёт с сервера), а переключение — дело React: смонтируйте компонент поверх разметки, как это делает плагин WordPress. MarkdownPreview, TokenUsage и UsageQuota в PHP не перенесены.
5. Тесты
npm run php:fixtures рендерит набор пропсов через renderToStaticMarkup в php/tests/fixtures/components.json, PHPUnit сравнивает вывод PHP побайтно. Два осознанных отличия нормализуются: id из useId (React считает их по позиции в дереве, PHP — по порядку на странице) и javascript:-ссылки (React подставляет код с ошибкой, PHP — #). Там же тесты на XSS.
cd php
composer install
vendor/bin/phpunit