PHP SDK
PHP SDK verstka/sdk помогает реализовать интеграцию Verstka на PHP backend: открыть редактор, принять callback после сохранения, скачать ZIP, сохранить медиа, обновить HTML/JSON и вернуть ответ Verstka.
Перед этой страницей лучше прочитать общий flow: ОбзорИнтеграция
Установка
composer require verstka/sdkТребования:
| Требование | Значение |
|---|---|
| PHP | 8.2+ |
| Расширения | ext-json, ext-hash, ext-zip |
| HTTP client | Guzzle 7 |
Конфигурация
Обычно достаточно указать apiKey, apiSecret и публичный callbackUrl вашего сайта:
use Verstka\Sdk\Config\VerstkaConfig;
$config = new VerstkaConfig(
apiKey: 'verstka-api-key',
apiSecret: 'verstka-api-secret',
callbackUrl: 'https://site.example/verstka/callback',
);callbackUrl должен быть доступен Verstka backend снаружи. На этот URL Verstka придет при сохранении публикации в редакторе.
Дополнительные настройки можно задать явно:
$config = new VerstkaConfig(
apiKey: 'verstka-api-key',
apiSecret: 'verstka-api-secret',
callbackUrl: 'https://site.example/verstka/callback',
apiUrl: 'https://api.r2.verstka.org/integration',
maxContentSize: 200 * 1024 * 1024,
requestTimeout: 60.0,
downloadTimeout: 120.0,
debug: false,
);apiUrl по умолчанию уже равен https://api.r2.verstka.org/integration.
1. Открыть редактор из админки
В админке лучше сделать кнопку обычной ссылкой, которая открывается в новой вкладке и ведет на route вашего backend:
<a
href="/admin/verstka/edit?post=123"
target="_blank"
>
Редактировать в Verstka
</a>Backend route проверяет права пользователя, загружает статью из CMS, получает editorUrl через SDK и делает redirect на этот URL. Ниже пример в стиле plain PHP; в Symfony, Laravel или другом framework верните redirect своим способом.
use Verstka\Sdk\Client\VerstkaClient;
use Verstka\Sdk\Config\VerstkaConfig;
function openVerstkaEditor(int $articleId, User $user, VerstkaConfig $config): void
{
$article = ArticleRepository::find($articleId);
if (!UserPolicy::canEdit($user, $article)) {
throw new RuntimeException('Access denied');
}
$client = new VerstkaClient($config);
$editorUrl = $client->getEditorUrl(
materialId: (string) $article->id,
vmsJson: $article->vms_json,
metadata: [],
);
header('Location: ' . $editorUrl, true, 302);
exit;
}Если статья открывается впервые, vmsJson можно не передавать или передать null. Если статья уже редактировалась в Verstka, передайте последний сохраненный vmsJson, чтобы редактор открыл актуальную версию публикации.
2. Сохранить файлы через StorageAdapter
SDK умеет скачать ZIP и найти файлы публикации, но не знает, где ваш сайт хранит медиа. Это решает StorageAdapter.
StorageAdapter нужно написать на стороне сайта. SDK вызывает его методы, а ваша реализация решает, куда положить файл и какой публичный URL вернуть.
Adapter получает временный файл, сохраняет его в ваше публичное хранилище uploads, CDN, S3 bucket или другое место и возвращает публичный URL.
use Verstka\Sdk\Storage\StorageAdapter;
final class CmsStorage implements StorageAdapter
{
public function saveMedia(
string $filename,
string $tempPath,
string $materialId,
array $metadata,
): string {
return $this->saveToCdn("articles/$materialId/$filename", $tempPath);
}
public function saveFontFile(
string $filename,
string $tempPath,
string $materialId,
array $metadata,
): string {
return $this->saveToCdn("verstka/fonts/$filename", $tempPath);
}
public function saveFontsManifest(
string $filename,
string $tempPath,
string $materialId,
array $metadata,
): string {
return $this->saveToCdn("verstka/fonts/$filename", $tempPath);
}
}Каждый метод должен вернуть URL, который будет доступен читателям сайта. SDK подставит эти URL в vmsHtml, vmsJson['assets'][*]['clientUrl'], vms_fonts.css и дерево fonts.
saveToCdn в примере - условный метод вашего приложения, который сохраняет файл и возвращает публичный URL.
Для локальной разработки можно использовать LocalStorageAdapter, но в production обычно нужен adapter под хранилище вашего сайта.
3. Принять callback после Save
Когда пользователь нажимает Save в Verstka, backend сайта получает callback. Внутри callback SDK проверяет подпись, скачивает ZIP, сохраняет медиа через StorageAdapter, заменяет временные URL файлов на публичные и вызывает ваш onFinalize.
В onFinalize нужно сохранить обновленные vmsHtml и vmsJson в базу данных сайта.
use Verstka\Sdk\Client\VerstkaClient;
use Verstka\Sdk\Finalize\ContentFinalizeContext;
use Verstka\Sdk\Finalize\ContentFinalizeResult;
$client = new VerstkaClient($config);
$storage = new CmsStorage();
$result = $client->processMaterialCallback(
callbackData: $requestPayload,
signature: $request->headers->get('X-Verstka-Signature', ''),
storage: $storage,
onFinalize: function (ContentFinalizeContext $ctx): ContentFinalizeResult {
ArticleRepository::saveVerstkaContent(
materialId: $ctx->materialId,
vmsHtml: $ctx->vmsHtml,
vmsJson: $ctx->vmsJson,
);
return new ContentFinalizeResult(success: true, vmsJson: $ctx->vmsJson);
},
);
return json_response($result->toResponse());ContentFinalizeContext содержит:
| Поле | Назначение |
|---|---|
materialId | ID материала из CMS. |
metadata | Metadata из callback. |
vmsJson | JSON публикации с обновленными clientUrl. |
vmsHtml | HTML публикации с замененными временными URL. |
savedMediaUrls | Словарь {filename: public_url}. |
Важно сохранить обновленный vmsJson из callback. При следующем открытии редактора его нужно передать в getEditorUrl.
4. Обработать callback шрифтов
Если в Verstka используется свой набор шрифтов сайта, после изменения шрифтов придет отдельное событие site_fonts_updated.
Его обрабатывает processFontsCallback. Если используете один callback route для всех событий, выбирайте обработчик по $requestPayload['event'].
use Verstka\Sdk\Finalize\FontsFinalizeContext;
use Verstka\Sdk\Finalize\FontsFinalizeResult;
$result = $client->processFontsCallback(
callbackData: $requestPayload,
signature: $request->headers->get('X-Verstka-Signature', ''),
storage: $storage,
onFinalize: function (FontsFinalizeContext $ctx): FontsFinalizeResult {
SiteSettings::set('verstka_fonts_css_url', $ctx->cssUrl);
SiteSettings::set('verstka_fonts_json_url', $ctx->jsonUrl);
return new FontsFinalizeResult(success: true, fonts: $ctx->fonts);
},
);
return json_response($result->toResponse());onFinalize для fonts можно не передавать, если сайту достаточно того, что SDK сохранил файлы и вернул Verstka дерево fonts с публичными URL.
5. Использовать framework adapters
Если вы не хотите вручную писать callback route, используйте готовый адаптер для вашего framework. Он регистрирует один callback endpoint и сам выбирает обработку по event: article_saved или site_fonts_updated.
| Framework | Что дает SDK |
|---|---|
| Symfony | Bundle, DI configuration, CallbackController, exception subscriber. |
| Laravel | Service provider, config publish, auto route POST /verstka/callback, exception renderer. |
Metadata
metadata - это необязательный массив или JSON-строка, которую сайт передает при открытии редактора. Verstka сохранит эти данные в сессии и вернет в callback.
Metadata удобно использовать для данных, которые помогут сайту обработать сохранение:
- ID пользователя в CMS;
- ID раздела, бренда или проекта;
- флаги окружения;
- короткоживущий токен для дополнительной проверки прав;
- данные для basic авторизации или bearer token;
- конфигурация Custom Containers.
$editorUrl = $client->getEditorUrl(
materialId: (string) $article->id,
vmsJson: $article->vms_json,
metadata: [
'editor_user_id' => (string) $user->id,
'section_id' => (string) $article->section_id,
'customContainers' => [],
],
);SDK автоматически добавляет version: "php_<sdk-version>".
Не используйте metadata как произвольное хранилище секретов, приватных ключей или персональных данных, которые не нужны callback-обработчику. Полное описание: metadata.
PreSave hooks
processMaterialCallback и processFontsCallback принимают опциональный onPreSave. Hook вызывается после проверки подписи, но до скачивания ZIP. Это место для проверки прав, блокировок и лимитов, основываясь на метаданных callback.
use Verstka\Sdk\Finalize\ContentPreSaveContext;
use Verstka\Sdk\Finalize\PreSaveDecision;
$result = $client->processMaterialCallback(
callbackData: $requestPayload,
signature: $signature,
storage: $storage,
onFinalize: $onFinalize,
onPreSave: function (ContentPreSaveContext $ctx): PreSaveDecision {
if (!UserPolicy::canSave($ctx->metadata['timeLimitedAuthToken'] ?? null, $ctx->materialId)) {
return new PreSaveDecision(allow: false, reason: 'Access denied');
}
return new PreSaveDecision(allow: true);
},
);Если allow=false, SDK не скачивает ZIP, файлы не пишутся, Verstka получает rc: 0.
Callback authorization
Если callback URL сайта защищен Basic Auth или Bearer token, передайте служебные ключи в metadata при открытии редактора.
Basic Auth:
metadata: [
'webhook_auth_user' => 'callback-user',
'webhook_auth_password' => 'callback-password',
],Bearer token:
metadata: [
'webhook_auth_user' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
],Verstka добавит заголовок Authorization к исходящему callback. Подробнее: Callback Authorization.
Справочник методов
| Метод | Назначение |
|---|---|
VerstkaClient::getEditorUrl(...) | Открывает сессию через POST /session/open и возвращает URL редактора. |
VerstkaClient::processMaterialCallback(...) | Обрабатывает article_saved: подпись, ZIP, медиа, onFinalize. |
VerstkaClient::processFontsCallback(...) | Обрабатывает site_fonts_updated: подпись, fonts ZIP, font files, manifests. |
StorageAdapter::saveMedia(...) | Сохраняет файл из vms_media/* и возвращает публичный URL. |
StorageAdapter::saveFontFile(...) | Сохраняет файл шрифта и возвращает публичный URL. |
StorageAdapter::saveFontsManifest(...) | Сохраняет vms_fonts.css или vms_fonts.json и возвращает URL. |
LocalStorageAdapter | Файловая reference-реализация storage adapter. |
SignatureService::signMaterial(...) | Строит HMAC для material_id:url. |
SignatureService::verifySignature(...) | Проверяет HMAC. |
UrlBuilder::buildAuthorizedContentUrl(...) | Добавляет api_key и material_id к content_url. |