Ast: семантическое дерево документа и сериализация
Краткий обзор
Заголовок раздела «Краткий обзор»Модуль Ast предоставляет семантическое абстрактное синтаксическое дерево документа (AST) для движка. Он моделирует документ как типизированную иерархию узлов: Document, Section, Heading, Paragraph, List, Table, Figure, Code и FormField. Модель фиксирует ограничивающие прямоугольники и якоря цитирования и сериализуется в версионированный формат JavaScript Object Notation (JSON). Слой тегирования доступности использует это дерево для построения дерева структуры.
Стабильность: экспериментальная. Это внутренняя часть модели. Её классы не гарантируют публичный программный интерфейс (API), зафиксированный для версии. Набор узлов и атрибуты узлов могут измениться. Схема сериализации версионируется независимо (
AstDocument::CURRENT_SCHEMA_VERSION = '1.0.0'). Сериализатор обнаруживает и отклоняет несовместимую схему, поэтому сохранённый AST JSON остаётся стабильным контрактом даже при изменении API в памяти.
Установка
Заголовок раздела «Установка»composer require nextpdf/core:^3Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Здесь AST представляет логическую структуру документа. Это не синтаксическое дерево парсера для конкретного входного формата. AstDocument — это контейнер. Он содержит корневой AstNode (который должен быть NodeType::Document), версию схемы, хэш исходного файла Portable Document Format (PDF) и число страниц. Он отклоняет некорректное построение, в том числе пустую версию схемы, число страниц меньше одной или неверный тип корня.
AstNode — это рекурсивный узел. NodeType перечисляет семантические виды. Узел содержит дочерние узлы, необязательный BoundingBox, необязательное текстовое содержимое и атрибуты, проверяемые с помощью NodeAttributeSchema. API узла поддерживает неизменяемое порождение. withBboxAndText() возвращает новый узел. deepClone() копирует поддерево. NodeId — это объект-значение, задающий идентичность. CitationAnchor связывает узел с местоположением в источнике для отслеживаемости. AstNodeCollection — это набор, реализующий Countable/IteratorAggregate, с фильтрацией через ofType().
AstSerializer — это граница сохранения. serialize() записывает AstDocument в JSON. deserialize() считывает его обратно. canDeserialize() и extractSchemaVersion() позволяют проверить совместимость перед разбором, поэтому несоответствие схемы становится обнаруживаемым состоянием, а не повреждённой загрузкой. AstDocument::estimateTokenCount() помогает оценить объём содержимого для последующей обработки, ограниченной по числу токенов.
Состав API
Заголовок раздела «Состав API»| Класс | Ключевые члены | Назначение |
|---|---|---|
AstDocument | toJson(), nodeCount(), estimateTokenCount(), CURRENT_SCHEMA_VERSION | Корневой контейнер; проверяет тип корня и схему |
AstNode | addChild(), children(), childCount(), totalNodeCount(), withBboxAndText(), deepClone() | Рекурсивный семантический узел |
NodeType (enum) | Document, Heading, Table, Figure, FormField, … | Семантический вид узла |
AstNodeCollection | add(), count(), isEmpty(), ofType(), toArray() | Итерируемый набор узлов с фильтрацией по типу |
AstSerializer | serialize(), deserialize(), canDeserialize(), extractSchemaVersion() | Версионированное сохранение в JSON |
BoundingBox | toArray(), equals() | Объект-значение геометрии (сравнение с эпсилоном) |
NodeId / CitationAnchor | toString(), equals(), toArray() | Идентичность узла и якорь отслеживаемости в источнике |
NodeAttributeSchema | проверка атрибутов | Схема для атрибутов узла |
Запустите composer docs:generate-api-php -- --module=Ast, чтобы сгенерировать полную таблицу PHPDoc.
Пример кода — быстрый старт
Заголовок раздела «Пример кода — быстрый старт»Соберите небольшое дерево, а затем сериализуйте его.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Ast\AstNode;use NextPDF\Ast\AstSerializer;use NextPDF\Ast\NodeType;
$root = new AstNode(NodeType::Document);$heading = new AstNode(NodeType::Heading);$root->addChild($heading);$root->addChild(new AstNode(NodeType::Paragraph));
echo "Nodes: {$root->totalNodeCount()}\n";
$json = (new AstSerializer())->serialize(/* an AstDocument wrapping $root */);Пример кода — рабочая среда
Заголовок раздела «Пример кода — рабочая среда»Выполните полный цикл чтения-записи сохранённого AST с защитными проверками. Проверяйте совместимость схемы перед десериализацией недоверенного JSON.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Ast\AstDocument;use NextPDF\Ast\AstSerializer;use Psr\Log\LoggerInterface;
final readonly class AstStore{ public function __construct( private AstSerializer $serializer, private LoggerInterface $logger, ) {}
public function load(string $json): ?AstDocument { if (!$this->serializer->canDeserialize($json)) { $this->logger->warning('AST JSON schema incompatible; rejected.', [ 'found_schema' => $this->serializer->extractSchemaVersion($json), 'expected' => AstDocument::CURRENT_SCHEMA_VERSION, ]);
return null; }
return $this->serializer->deserialize($json); }}Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»AstDocumentтребует, чтобы корневым узлом былNodeType::Document. При построении дерева с любым другим корнем выбрасывается исключение.AstNode::withBboxAndText()иdeepClone()возвращают новые экземпляры. Доступные мутаторы узла (addChild()) изменяют узел. Вспомогательные методы порождения — нет. Учитывайте, какой метод вызываете.- Всегда перед
deserialize()вызывайтеcanDeserialize()для JSON из внешних источников. Несоответствие версии схемы — это обнаруживаемое, ожидаемое состояние. estimateTokenCount()— это оценка для определения объёма последующей обработки, а не точный подсчёт токенизатора. Не считайте её авторитетной.BoundingBox::equals()— это сравнение с эпсилоном (по умолчанию 0.001). Точное равенство чисел с плавающей точкой не является контрактом.
Производительность
Заголовок раздела «Производительность»Построение и обход дерева выполняются за O(n) по числу узлов. Сериализация линейна относительно размера дерева. Профиль воспроизводимости — bitwise. Одно и то же дерево сериализуется в одни и те же байты JSON, благодаря чему схема остаётся стабильным контрактом сохранения. Эталонная нагрузка по умолчанию остаётся значительно ниже бюджета 1500 мс по времени / 64 МБ по пиковому потреблению.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»AstSerializer::deserialize() разбирает JSON, который может быть сохранён или передан. Сначала проверьте совместимость с помощью canDeserialize(). Относитесь к текстовому содержимому и атрибутам десериализованного дерева как к недоверенным строкам при возврате в приложение или отображении. Сам модуль не выполняет input/output (I/O) и не встраивает внешние данные. См. модель угроз движка в /modules/core/security/.
Соответствие
Заголовок раздела «Соответствие»Этот модуль не содержит нормативных заявлений о спецификации PDF. Семантический AST — это внутренняя абстракция движка. Он не реализует стандартизированную модель документа, разделы которой нужно цитировать. Там, где AST используется для тегирования доступности, соответствие вывода стандартам PDF/UA и tagged-PDF документируется и проверяется в /modules/core/accessibility/ и /modules/core/conformance/, а не здесь.
См. также
Заголовок раздела «См. также»- Модуль Accessibility — использует AST для построения дерева структуры.
- Модуль Inspect — анализирует макет и структуру.
- Модуль HTML — предоставляет источник структуры документа.
- Обзор соответствия