跳转到内容

NextPDF Artisan 启动与发现

Artisan 是一个纯粹的 PSR-4 函数库,没有 service provider、没有包(bundle),也没有 framework(框架)自动发现清单。它的类一旦可被自动加载,它就「启动」了;所谓发现机制就是 Composer 的 PSR-4 映射表,仅此而已。

该包的 composer.json 声明了两个 PSR-4 根目录——NextPDF\Artisan\src/Artisan/,以及 NextPDF\Parser\src/Parser/。这里没有 extra.laravel、没有 Symfony bundle 类,也没有 CodeIgniter registrar。启动时不会扫描、注册或挂载任何东西。

集成点在 nextpdf/core 一侧。Document(通过 HasTextOutput concern)对外提供 writeHtmlChrome(),并会在运行期执行一次针对 NextPDF\Parser\PdfReaderNextPDF\Artisan\PageImporterclass_exists() 检查。当这两者都能通过自动加载器 resolve(解析)时,就能使用 Chrome 路径。当它们无法解析时,core 会抛出版面异常,而不是触发致命错误。因此,所谓「发现」就是:Artisan 的类是否已经挂到自动加载器上?这个问题由 Composer 回答——完全不涉及任何 framework 机制。

这是有意为之的设计。这个桥接是 core 引擎跨越包边界取用的一项能力,而不是由 framework 管理的服务。这样一来,Artisan 在 Laravel、Symfony、CodeIgniter、CLI 脚本或队列 worker 中都能以相同方式使用,因为这些都不是前置条件。

no

yes

Composer autoload (PSR-4)

Application constructs Document

Document::setChromeRendererConfig(config)

Document::writeHtmlChrome(html)

class_exists PdfReader

and PageImporter?

core raises layout exception

resolve ChromeHtmlRenderer

render → parse → import Form XObject

Diagram

这里没有启动内核(bootstrap kernel)、没有命令注册,也没有延迟 provider 阶段。第一次调用 writeHtmlChrome() 就是整个生命周期的入口点。

Artisan 没有 DI 容器,也不会注册任何绑定。各组件都是通过构造函数注入组装的普通对象:创建一个 ChromeRendererConfig,将其传给 ChromeHtmlRenderer,并按需注入一个 PSR-3 logger 和一个自定义的 HtmlSecurityPolicyInterface。在宿主容器中,请自行将 ChromeHtmlRenderer 注册为单例——示例请见 /integrations/artisan/production-usage/.

NextPDF 的部分能力(Premium 电子发票合约)通常通过 framework 容器解析。Artisan 也会在无容器环境中运行——CLI 工具、独立脚本、自定义执行器——因此它附带了 EInvoiceServiceFactory

方法返回值何时为 null
makeEmbedder()EmbedderInterface(Pro 版)未安装 Pro 层
makeValidator()ValidatorInterface(Enterprise 版)未安装 Enterprise 层
makeDefaultProfile()ProfileInterface(EN16931,Pro 版)未安装 Pro 层
makeSchematronRunner()SchematronRunnerInterface(Enterprise 版)未安装 Enterprise 层

每次调用都会返回一个 全新 的实例(一次性使用语义——embed 与 validate 调用各自拥有一个可变的 XML 解析(parse)上下文,不得共享状态)。这个工厂(factory)是为少数没有容器的场景提供的便利手段,而不是 service locator。建议做法仍然是在构造时组装对象,并将它们作为构造函数参数传入。这种「找不到就返回 null」的行为与 framework 封装包一致,因此同一份调用端代码无论是否安装 Premium 都能运行。来源:src/Artisan/EInvoiceServiceFactory.php;集成测试位于 tests/Integration/Artisan/EInvoiceServiceFactoryIntegrationTest.php

这里没有配置文件层叠机制。配置就是你传给 ChromeRendererConfig 的内容:

  1. 明确传入的构造函数参数,或者
  2. 通过 ChromeRendererConfig::fromArray() 从宿主提供的数组创建(采用 snake-case 键;未设置的键会回退到构造函数默认值;chrome_binary 只有在值为非空字符串时才会应用)。

解析后的配置在 renderer(渲染器)的整个生命周期内不可变。每个键的说明请见 /integrations/artisan/configuration 一节。

  • 「这个桥接能被发现吗?」——如果 class_exists(\NextPDF\Artisan\PageImporter::class)true,说明 Composer 的自动加载器已加载它;core 就会使用 Chrome 路径。
  • 「它启动了吗?」——这里没有可能失败的启动过程;缺少的依赖包会在第一次调用 writeHtmlChrome() 时以类型化异常的形式暴露,映射关系整理于 /integrations/artisan/troubleshooting/.
  • 无容器的 Premium 检查——EInvoiceServiceFactory::makeEmbedder() === null 表示未安装 Pro 层;开源渲染路径不受影响。
  • 集成指南:/integrations/artisan/integration/
  • 概览:/integrations/artisan/overview/
  • 配置:/integrations/artisan/configuration/
  • 生产环境使用:/integrations/artisan/production-usage/
  • 疑难解答:/integrations/artisan/troubleshooting/