Конвейерная модель
Spec: ISO 32000-2, §7.5 ISO 32000-2 §7.5 Evidence: Code-backed
Документ NextPDF не создаётся за один непрозрачный шаг. Он проходит через небольшое число явных этапов: фасад фиксирует намерение, слой содержимого превращает его в модель, а писатель сериализует эту модель в соответствующий стандарту PDF. На этой странице описана эта структура и объясняется, почему она подходит движку.
Почему это важно
Заголовок раздела «Почему это важно»Файл PDF сам по себе имеет многослойную структуру — заголовок, тело объектов, таблицу перекрёстных ссылок и трейлер, — и писатель должен собрать всё это согласованно. Если движок, который строит такой файл, устроен как одна запутанная процедура, каждое изменение ставит под угрозу любой результат. Тогда единственный способ получить уверенность — полностью отрисовать документы и проверить их визуально, а это медленно, запоздало и неубедительно.
Явный конвейер меняет эту ситуацию. У каждого этапа есть одна задача и типизированная граница, поэтому вы можете анализировать изменение и тестировать его на том этапе, которого оно касается, а не только в самом конце файла. Эта архитектура — прежде всего выбор в пользу тестируемости и расширяемости.
Если коротко
Заголовок раздела «Если коротко»- Публичная точка входа — это фасад Document: текучий, одноразовый, безопасный для воркеров построитель, который фиксирует, что вам нужно, а не как это сериализуется.
- Фасад делегирует работу примерно двум десяткам узкоспециализированных трейтов-обязанностей (вывод текста, рисование, страницы, безопасность, навигация и так далее): по одной обязанности на каждый трейт, вместо одного гигантского класса.
- Содержимое поступает одним из двух путей: прямое рисование (графические примитивы) или движок HTML/CSS. Оба создают одну и ту же внутреннюю модель документа.
- Выделенный писатель PDF сериализует эту модель, выбирая стратегию PDF 1.4 / 1.7 / 2.0. Создание корректной структуры файла происходит здесь и больше нигде.
- Долгоживущее состояние (реестры шрифтов и изображений) привязано к процессу и является общим; состояние отдельного запроса (документ) создаётся заново и никогда не используется повторно. Эта граница явная, что и делает среды выполнения воркеров безопасными.
Как это устроено в NextPDF
Заголовок раздела «Как это устроено в NextPDF»Проще всего увидеть эту модель, проследив путь документа от вызова до байтов.
- Document facade Fluent, use-once builder; records intent via concern traits.
- Content production Direct drawing or the HTML/CSS engine — both build one document model.
- Document model Accumulated pages, content, and resources held as typed state.
- PDF writer Serialises the model; selects a PDF 1.4 / 1.7 / 2.0 strategy.
- Conforming PDF Header, object body, cross-reference table, trailer.
Два архитектурных решения делают это чем-то большим, чем просто схема.
Фасад собран из частей, а не монолитен. Document не реализует каждую возможность сам; он делегирует каждую область выделенному трейту-обязанности — вывод текста, рисование, страницы, безопасность, типографика, навигация, транзакции и так далее. Место нового метода документа — в трейте, владеющем соответствующей областью, а не в самом фасаде. Класс, который вы вызываете, остаётся небольшим, а обязанности — разделёнными.
Писатель единолично владеет структурой файла. Формирование содержимого решает, какие метки и объекты существуют; писатель решает, как они становятся корректным файлом PDF, в том числе какая стратегия версии применяется. Это разделение закреплено как архитектурное правило: код макета и содержимого не порождает итоговую структуру файла, а писатель не принимает решений о макете. Преимущество в том, что вопрос «является ли результат корректным PDF?» проверяется ровно в одном месте.
Граница времени жизни — часть модели, а не позднее дополнение. Реестры шрифтов и изображений живут в течение всего срока жизни процесса и являются общими для всех запросов; документ, его контекст отрисовки и писатель создаются для каждого запроса и затем уничтожаются. В среде выполнения воркера это различие отделяет безопасное повторное использование от порчи данных между запросами. Именно поэтому оно закреплено в архитектуре, а не оставлено на дисциплину.
Что говорят факты
Заголовок раздела «Что говорят факты»Эта страница имеет статус Evidence: Code-backed . Эти этапы отражают реальную структуру основного репозитория:
- Фасад и его делегирование — это
src/Core/Document.phpплюс трейты-обязанности вsrc/Core/Concerns/(вывод текста, вывод, рисование, страницы, безопасность, типографика, навигация, транзакции и другие — каждый с единственной обязанностью). - Два пути содержимого — это движок HTML/CSS (
src/Html/) и прямое рисование (src/Graphics/), и оба наполняют внутреннюю модель. - Сериализация и стратегия версии PDF находятся в
src/Writer/(PdfWriter.php, с явными классами стратегий PDF 1.4 / 1.7 / 2.0). - Граница между временем жизни процесса и временем жизни запроса — это безопасный для воркеров подход, зафиксированный в обзоре архитектуры и показанный в поставляемом примере фабрики воркеров: он использует общие
FontRegistryиImageRegistryдля разных запросов, но каждыйDocumentсоздаёт заново.
Конечный результат задан форматом. Результат работы писателя должен включать заголовок, тело объектов, таблицу перекрёстных ссылок и трейлер согласно Spec: ISO 32000-2, §7.5 ISO 32000-2 §7.5 . Когда эта обязанность сосредоточена на одном этапе, остальная часть движка может заниматься содержимым, а не сборкой структуры файла.
Практический пример
Заголовок раздела «Практический пример»Задача фасада — сделать так, чтобы намерение выражалось как намерение. Путь содержимого и писатель остаются невидимыми в месте вызова:
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone(); // facade$doc->setTitle('Quarterly Report'); // metadata concern$doc->addPage(); // pages concern$doc->setFont('helvetica', 'B', 16); // typography concern$doc->cell(0, 12, 'Summary', newLine: true); // text-output concern$doc->writeHtml('<p>Generated in-process.</p>'); // HTML content path$doc->save(__DIR__ . '/report.pdf'); // writer stageКаждый вызов попадает в свою отдельную обязанность. Два разных пути содержимого наполняют одну и ту же модель. Ровно один этап — save() — превращает модель в байты файла. Ничто в месте вызова не должно знать, как строится таблица перекрёстных ссылок.
Распространённое заблуждение
Заголовок раздела «Распространённое заблуждение»Частое неверное толкование состоит в том, что «конвейер» подразумевает потоковый push-API, который вы соединяете этап за этапом, как канал Unix. Это не так. Конвейер здесь — это архитектурная декомпозиция: этапы с единственными обязанностями и типизированными границами. Вы по-прежнему работаете через текучий фасад. Этапы — это то, как движок построен и протестирован, а не транспорт, который вы собираете вручную.
Связанная ошибка — считать, что фасад и есть движок. Это точка входа. Основная работа распределена между трейтами-обязанностями, двумя путями содержимого и писателем. Именно это распределение и означает, что изменение одной возможности не ставит под угрозу любой результат.
Ограничения и границы
Заголовок раздела «Ограничения и границы»На этой странице описывается структура конвейера, а не внутренний API какого-либо отдельного этапа. Точный перечень трейтов-обязанностей, правила выбора стратегии писателя и поля модели содержимого определяются кодом и справочником, а не этим описанием. Число трейтов — это деталь реализации, которая может меняться без изменения модели. На этой странице не рассматриваются внутренние этапы движка HTML (отдельная тема) и поведение писателя в части потоковой обработки и памяти (тоже отдельная тема). Структурные утверждения верны на дату проверки этой страницы; авторитетным источником являются src/Core/, src/Html/, src/Graphics/ и src/Writer/ основного репозитория.
Конвейерная модель одинакова во всех редакциях; редакции добавляют возможности внутри этапов, а не новые этапы:
| Edition | Availability |
|---|---|
| Core | Core реализует полный конвейер фасад → содержимое → писатель. |
| Pro | Pro добавляет возможности внутри существующих этапов, а не новые этапы. |
| Enterprise | Enterprise добавляет возможности внутри существующих этапов, а не новые этапы. |
Связанная документация
Заголовок раздела «Связанная документация»- Память и потоковая обработка — как этап писателя удерживает потребление памяти в заданных пределах.
- Конвейер HTML — внутренние этапы пути HTML-содержимого.
- Строгие типы повсюду — типизированные границы, которые делают каждый этап тестируемым независимо.
Глоссарий
Заголовок раздела «Глоссарий»- Фасад — публичная точка входа
Document: текучий, одноразовый построитель, который фиксирует намерение и делегирует работу трейтам-обязанностям. - Трейт-обязанность — узкоспециализированный PHP-трейт, составляющий фасад; каждый владеет одной функциональной областью (вывод текста, рисование, страницы, безопасность и так далее).
- Путь содержимого — один из двух способов, которыми содержимое попадает в модель: прямое рисование или движок HTML/CSS.
- Модель документа — внутреннее типизированное накопление страниц, содержимого и ресурсов в движке до сериализации.
- Этап писателя — компонент, который сериализует модель в корректный PDF, выбирая стратегию PDF 1.4 / 1.7 / 2.0.
- Безопасный для воркеров — спроектированный так, что состояние с временем жизни процесса используется совместно безопасно, тогда как состояние отдельного запроса создаётся заново и никогда не используется повторно.