Перейти к содержимому

Exception: типизированная иерархия исключений

Каждое исключение, выбрасываемое NextPDF, наследуется от единого абстрактного базового класса NextPdfException, поэтому любую ошибку движка можно обработать одним catch. Каждое доменное исключение реализует ContextAwareExceptionInterface и предоставляет структурированные диагностические поля для журналирования и мониторинга производительности приложений (APM) без разбора текста сообщения.

Окно терминала
composer require nextpdf/core:^3

Иерархия состоит из трёх уровней:

RuntimeException (PHP SPL)
└── NextPdfException (abstract; implements ContextAwareExceptionInterface)
├── InvalidConfigException
├── FontNotFoundException
├── FontParsingException
├── ImageProcessingException
├── SignatureException
├── EncryptionException
├── WriterException
├── PageLayoutException
├── HtmlParsingException
├── CompressionException
├── NotImplementedException
├── … (23 domain exceptions total)
└── Strict\StrictModeViolation (abstract)
├── Strict\IncompatibleRenderingModeException
├── Strict\OracleConformanceFailure
└── Strict\UnregisteredCssDeviation

NextPdfException наследуется от RuntimeException из стандартной библиотеки PHP (SPL). Если перехватывать RuntimeException, будут перехвачены и ошибки NextPDF. Перехват NextPdfException сужает обработчик до ошибок движка. Для точечного восстановления перехватывайте листовой класс. Поддерево Strict\ группирует нарушения режима соответствия под абстрактным классом StrictModeViolation, который наследуется от NextPdfException.

Контекст вместо строковых кодов. NextPDF идентифицирует ошибку по её типу PHP, а не по строковому коду ошибки. Классы исключений не определяют константу кода NPDF-####. Вместо этого ContextAwareExceptionInterface::getContext() возвращает array<string, mixed> примитивных полей в snake_case, которые безопасно сериализовать в журнал или полезную нагрузку APM. NextPdfException::getContext() по умолчанию возвращает []. Каждое доменное исключение переопределяет этот метод, добавляя поля для соответствующего сбоя. Например, FontNotFoundException::getContext() возвращает font_name, search_paths и fallback_attempted. WriterException возвращает output_path и writer_state. InvalidConfigException возвращает config_key, given_value и expected_type. Стабильные строковые идентификаторы NEXTPDF_W_* относятся к нефатальному перечислению WarningCode в модуле Support, а не к исключениям.

Возможность реагирования. Docblock каждого доменного исключения указывает, кто может на него отреагировать: разработчик, инфраструктура или вызывающая библиотека. InvalidConfigException — это ошибка разработчика; исправьте конфигурацию. FontNotFoundException — это ошибка разработчика или инфраструктуры; проверьте путь или права доступа к файлу. WriterException — это ошибка инфраструктуры; проверьте диск, права доступа или выходной поток. NotImplementedException — это ошибка вызывающего кода; удалите вызов или закрепите версию выпуска, в которой появляется указанная доработка. Несколько исключений предоставляют именованные конструкторы для точного указания первопричин: SignatureException::ltvCapabilityMissing(), ::tsaRequired() и аналогичные. Операторы видят фактическую причину вместо обобщённого сообщения.

СимволВидКлючевые члены
NextPDF\Contracts\ContextAwareExceptionInterfaceинтерфейсgetContext(): array<string, mixed>
NextPDF\Exception\NextPdfExceptionабстрактный класснаследуется от RuntimeException; getContext() (по умолчанию [])
NextPDF\Exception\InvalidConfigExceptionfinalgetConfigKey(), getGivenValue(), getExpectedType(), getContext()
NextPDF\Exception\FontNotFoundExceptionfinalgetFontName(), getSearchPaths(), wasFallbackAttempted(), getContext()
NextPDF\Exception\SignatureExceptionfinalgetCertInfo(), getSignatureLevel(), getDetail(), getContext(); именованные конструкторы ltvCapabilityMissing(), tsaRequired(), httpClientMissing(), …
NextPDF\Exception\WriterExceptionfinalgetOutputPath(), getWriterState(), getContext()
NextPDF\Exception\PageLayoutExceptionfinalgetPageNumber(), getContext()
NextPDF\Exception\NotImplementedExceptionfinal$feature, $followUp
NextPDF\Exception\Strict\StrictModeViolationabstractнаследуется от NextPdfException
NextPDF\Exception\Strict\IncompatibleRenderingModeExceptionfinalнаследуется от StrictModeViolation

Полный список листовых классов (23): BarcodeEncoderNotFoundException, BarcodeException, CompressionException, ContentStreamBalanceException, CssParserLimitExceededException, CssResolutionBudgetExceededException, EncryptionException, FontNotFoundException, FontParsingException, GraphicsStateBalanceException, HtmlParsingException, ImageProcessingException, InvalidConfigException, LinearizationInvariantException, LinearizationUnimplementedException, MissingShadingResourceException, NotImplementedException, PageLayoutException, PdfRViolationException, SignatureException, TemplateException, UnsupportedAlgorithmException, WriterException.

Чтобы перехватить любую ошибку движка, используйте базовый тип.

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Exception\NextPdfException;
try {
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 10, 'Hello');
$doc->save('out.pdf');
} catch (NextPdfException $e) {
\error_log($e->getMessage());
}

Готовый к запуску пример examples/15-exception-handling.php демонстрирует этот шаблон.

Выполняйте восстановление на уровне листового класса и передавайте структурированный контекст в конвейер журналирования.

<?php
declare(strict_types=1);
use NextPDF\Contracts\ContextAwareExceptionInterface;
use NextPDF\Core\Document;
use NextPDF\Exception\FontNotFoundException;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
function render(Document $doc, LoggerInterface $logger): void
{
try {
$doc->setFont('Brand-Sans', '', 12);
$doc->cell(0, 10, 'Invoice');
$doc->save('invoice.pdf');
} catch (FontNotFoundException $e) {
// Targeted recovery: fall back to a built-in font.
$logger->warning($e->getMessage(), $e->getContext());
$doc->setFont('helvetica', '', 12);
$doc->save('invoice.pdf');
} catch (NextPdfException $e) {
// Any other engine error: structured context, then rethrow.
$context = $e instanceof ContextAwareExceptionInterface
? $e->getContext()
: [];
$logger->error($e->getMessage(), $context);
throw $e;
}
}
  • Порядок перехвата имеет значение. Перечисляйте листовые классы перед NextPdfException; первый catch (NextPdfException) поглощает каждый подкласс.
  • getContext() использует ключи в snake_case, а значения являются примитивами или списками примитивов без вложенных объектов. Полезную нагрузку можно безопасно сериализовать в формат JavaScript Object Notation (JSON).
  • Базовый NextPdfException::getContext() возвращает []. Подкласс, который не переопределяет этот метод, не несёт структурированных полей. В этом случае полагайтесь на getMessage().
  • NextPdfException является абстрактным; его нельзя инстанцировать напрямую. Выбрасывайте конкретный листовой класс.
  • NotImplementedException намеренно привлекает внимание. Оно сигнализирует о намеренно отсутствующей реализации, а не о временном сбое. Не пытайтесь повторить операцию.
  • Исключения Strict\* сигнализируют о нарушении контракта режима соответствия, а не о восстанавливаемой ошибке времени выполнения. Рассматривайте их как дефекты конфигурации или входных данных.
  • Константы строкового кода ошибки не существует. Сопоставляйте ошибки по типу исключения. Передавайте getContext() для машинных потребителей.

При создании исключения происходит одна аллокация объекта и вызов sprintf, который формирует сообщение: O(1). getContext() возвращает небольшой ассоциативный массив, построенный из уже имеющихся полей: O(1) по количеству полей. Исключения возникают на пути сбоя, а не на горячем пути. Эта стоимость пренебрежимо мала по сравнению с работой, которая завершилась сбоем. По умолчанию performance_budget для этой справочной страницы составляет wall_ms: 1500, peak_mb: 64.

Поля контекста могут содержать сведения, полученные из документа. FontNotFoundException включает пути поиска в файловой системе, WriterException включает выходной путь, а InvalidConfigException включает переданное значение. Прежде чем передавать контекст в приёмник журналов с низким уровнем доверия, очистите поля или передайте только список разрешённых ключей, поскольку пути и значения могут раскрыть структуру развёртывания или пользовательский ввод. Сообщения исключений удобочитаемы и могут включать те же сведения. Не показывайте необработанные сообщения конечным пользователям в чувствительном с точки зрения безопасности контексте. SignatureException намеренно включает в сообщение конкретную первопричину, например отсутствующий пакет или пустой Uniform Resource Locator (URL) для Time Stamping Authority (TSA), чтобы операторы могли разобраться в причине без поиска по местам вызова. Эти сведения предназначены для операторов, а не для конечных пользователей.

Этот модуль определяет модель ошибок движка и не содержит нормативной ссылки на стандарт. Исключения, выбрасываемые при нарушениях стандартов, например PdfRViolationException или Strict\OracleConformanceFailure, ссылаются на соответствующий нормативный пункт в модуле, который обнаруживает нарушение, а не здесь.

  • /modules/core/contracts/ContextAwareExceptionInterface: определение
  • /modules/core/observability/ — передача getContext() в APM
  • /modules/core/config/InvalidConfigException, NotImplementedException
  • /modules/core/support/DegradedException; WarningCode (NEXTPDF_W_*)
  • /modules/core/event/InvalidConfigException из addListener()

Глоссарий: контекстно-зависимое исключение · политика деградации