コンテンツにスキップ

NextPDF の設計思想

Spec: ISO/IEC 25010 Spec: ISO 32000-2 Evidence: Design principle

このページでは、NextPDF の API に関するすべての判断を照らし合わせるための原則を示します。原則をあえて少数に絞っているのは、暗唱できない原則は、いざというときに適用できないからです。

このページは最初に読むべきページです。ほかの Insider_ ページでは、これらの原則が具体的な場面で機能する様子を示します。このページで原則に名前を付けておくことで、ほかのページの内容も理解しやすくなります。

PDF は歴史が長く、独自の流儀を持ち、推測には容赦しないほど厳格です。署名は、それが対象とするバイトだけを正確に対象とします。フォントは、埋め込まれているか、そうでないかのどちらかです。アーカイブ用プロファイルは、要件を満たすか、数か月後の監査で不機嫌な監査人の前で不合格になるかのどちらかです。

入力が曖昧なとき、ライブラリには選択肢があります。推測して黙っていることもできれば、処理を止めてそのことを伝えることもできます。前者は、デモでは親切に見えます。しかし同時に、何が問題だったのか手がかりが残らない本番障害という代償を払うことになります。NextPDF は後者を選びます。第一印象としての安心感はやや劣りますが、その代わり、根拠をもって説明できる結果を得られます。ソフトウェア品質標準は、このトレードオフをはっきりと名指ししています。フェイルセーフな振る舞いとは、障害発生時に、未定義の状態のまま処理を続けるのではなく、安全な状態へ復帰する製品の能力のことです( Spec: ISO/IEC 25010, §3 )。

NextPDF は、優先順位順に並べた 5 つの原則の上に構築されています。

  1. 明示は暗黙に勝る。 意図が重要なら、それを明示します。エンジンは、署名レベル、出力モード、適合対象を文脈から推論しません。
  2. 早く失敗し、はっきり失敗し、早期に失敗する。 不正な入力は、1 バイトも書き込まれる前に、原因を明確に示すメッセージとともに拒否されます。
  3. エラーは API の一部である。 失敗は具体的で、型付けされ、構造化されたコンテキストを伴います。偶発的なものではなく、設計されたものです。
  4. 境界は発見されるものではなく、明示されるもの。 あらゆる主張は、どこで止まるかを示します。「必要ではあるが十分ではない」は、NextPDF が意図的に用いる表現です。
  5. 何も黙って劣化しない。 エンジンは、完成して見えるが中途半端な成果物を返すことはありません。

それ以外のすべて、つまり流暢なビルダー、使い捨てのドキュメント、厳格な型付けは、これらの原則から派生するものです。

これらの原則は、壁に貼るポスターではありません。ソースコードのなかで具体的な形を取り、互いに補強し合います。

以下の表は、各原則がエンジンのどこで確認できるか、そしてそれが欠けた場合に何を犠牲にするかを対応付けたものです。

原則NextPDF での現れ方反対の選択をした場合の代償
明示は暗黙に勝るsetSignature(certInfo:, level:) は PAdES レベルを必須の名前付き引数として受け取り、「auto」レベルは存在しない要件が求めていないプロファイルで署名され、検証時になって初めて判明する文書
早く失敗し、はっきり失敗するsave() はレンダリング前にストリームラッパーや null バイトを含むパスを拒否する。setSignature() に続けて save() を呼ぶと、未署名のファイルを出力するのではなく例外をスローするパストラバーサルによる書き込み、あるいはアーカイブ内の「未署名なのに署名済みと信じられている」PDF
エラーは API の一部1 つの抽象基底例外と、具体的に型付けされたサブクラス群があり、それぞれがログや APM 向けに構造化された getContext() を公開するありきたりなスタックトレースと、推測に費やす長い午後
境界は明示されるプロセス内の適合性チェックは検出結果を返し、最終判定は独立したバリデーターに委ねられると明言する「例外が出ないのだから適合しているはずだ」という結論を、監査人が覆す事態
何も黙って劣化しないアーカイブ用タイムスタンプの処理経路は、必須の辞書を欠いたプロファイルを出力せず、中途半端に書かれたプロファイルを返すことを拒否する長期検証プロファイルのつもりで、実際にはひそかにそうなっていないプロファイル

これらの原則をあわせて読むと、一つの姿勢が浮かび上がります。エンジンは、自信ありげな「たぶん」よりも、正直な「いいえ」を返すことを選ぶのです。これは悲観主義ではありません。PDF がしばしば法的な成果物であるという認識に基づくものです。誤った法的成果物は、そもそも作られなかった成果物よりも悪いものです。

このページは Evidence: Design principle の記述です。これらの原則は意図的な決定であり、計測ではなく論証によって裏付けられています。原則が外部の分野で名前を持つ場合、その推論が単なる社内の意見にとどまらないよう、このページはそれを根拠として参照します。

  • 「未定義の状態のまま処理を続けるのではなく拒否する」という姿勢は、 Spec: ISO/IEC 25010 §3 の フェイルセーフ 品質特性、つまり障害発生時に自らを安全な状態に置く製品という性質にあたります。同じ系統に属する フォールトトレランス は、障害があってもシステムが意図どおりに動作し続ける度合いのことです。NextPDF は、その努力を障害の隠蔽ではなく、検出と停止に向けます。
  • 「採用前に境界を明示する」という姿勢は、適切度認識性 Spec: ISO/IEC 25010, §3.26 )、すなわちドキュメントと第一印象から適合性を判断できる能力にあたります。
  • これらすべてが PDF において重要となる固有の理由は Spec: ISO 32000-2, §12.8 にあります。署名のバイト範囲は、それが対象とするバイトだけを正確に保護し、それ以上は保護しません。したがって、署名済み文書を「親切心から」書き換えたり、その周辺を推測したりするエンジンは、まったく役に立ちません。

個々の原則は、それぞれのページで実際のエンジンのソースコードに照らして示されています。推測を拒む API機能としてのエラー が、その Evidence: Code-backed 証拠を提示しています。このページが「なぜ」を扱い、それらのページが 「何を」を扱います。

これらの原則は、ごく一般的な使い方の数行のなかに見て取れます。署名の呼び出しは、意図を明示的に示します。エンジンは、誤解を招くものを出力するのではなく、早い段階で拒否します。

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Exception\NotImplementedException;
use NextPDF\Security\Signature\CertificateInfo;
use NextPDF\Security\Signature\SignatureLevel;
$document = Document::createStandalone();
$document->setTitle('Service Agreement 2026-0042');
$document->addPage();
$document->setFont('helvetica', '', 12);
$document->cell(0, 10, 'This agreement is configured for a PAdES signature.', newLine: true);
// Explicit beats implicit: the PAdES level is a required, named argument.
// There is no inferred or "auto" level.
$document->setSignature(
certInfo: new CertificateInfo(
certificate: $certificatePem,
privateKey: $privateKeyPem,
),
level: SignatureLevel::PAdES_B_B,
);
try {
// Fail fast, no silent degradation: rather than emit an UNSIGNED file
// that the caller believes setSignature() signed, the high-level path
// refuses and names the supported route.
$document->save('/srv/output/agreement.pdf');
} catch (NotImplementedException $e) {
// The message identifies the feature and the follow-up, not a stack
// trace: "... is not implemented in this release. <actionable follow-up>"
error_log($e->getMessage());
}

要点は、署名の仕組みそのものではありません。3 つの原則が一つのスニペットのなかに見て取れること、それが要点です。すなわち、意図が明示され(level:)、失敗は早期かつ名前付きで発生し、エンジンは自らの状態について偽る文書を生成することを拒むのです。

もっともよくある読み違いは、これらの原則が NextPDF を「使いにくく」しているというものです。これらが難しくしているのは、誤った使い方です。必須の引数が一つあれば、あとで驚かされる暗黙のデフォルトが一つ減ります。早期の例外が一つあれば、アーカイブ内の壊れた成果物が一つ減ります。この摩擦は、本番環境、監査、法廷のように誤りの代償が大きい場所ではなく、呼び出し箇所や開発段階のように代償の小さい場所に、意図的に置かれています。

もう一つの読み違いは、「方針が明確である」ことを「融通が利かない」ことだと受け取るものです。そんなことはありません。エンジンが方針を持つのは 正しさと意図 についてであって、あなたの文書についてではありません。レイアウト、コンテンツ、フォント、構造は、引き続き完全にあなたが制御します。その方針とは、推測が危険となる場面で、あなたに代わって推測しないということです。

このページは設計意図を示すものです。それ自体が振る舞いの仕様ではありません。これらの原則は、判断がどのように下されるかを述べるものであり、特定のメソッドに関する保証ではありません。各メソッドの厳密なコントラクトは、リファレンスと、その根拠レベルを備えた各メソッド専用の Insider_ ページに記載されています。

これらの原則は、絶対的な物理法則でもありません。これらは優先順位であり、判断を伴って適用されます。2 つの原則が衝突する場合(より厳格な拒否か、より寛容なデフォルトか)、上記の優先順位が決め手となります。特定のモジュールが、根拠のある例外を文書化することもあります。その場合、その例外は暗黙の前提ではなく、明文化されます。

最後に、ここでの根拠の基盤が「設計原則」であるのは、意図によるものです。このページは論証します。ベンチマークは行いません。数値、テスト、または条項による裏付けを必要とする主張は、ここではなく、その根拠を担うページで述べられます。

  • 推測を拒む API — 明示的な意図とフェイルファストの原則を、実際の API に照らして示します。
  • 機能としてのエラー — 設計された側面としての型付き例外階層。
  • PHP 8.4 の基盤 — これらの原則を、願望にとどめず強制可能にする言語機能。
  • 設計原則(根拠レベル) — 主張が意図的な設計上の決定であり、ベンチマークや単一のテストによって計測されたものではなく、意図と裏付けとなる標準から論証されていることを示すページ。
  • フェイルセーフ — ソフトウェア品質特性の一つ。障害発生時に、製品が未定義の状態のまま続行するのではなく、安全な状態へ復帰すること。NextPDF が推測ではなく拒否を選ぶ理由です。
  • フェイルファスト — 処理を続行して後で原因不明のまま失敗するのではなく、不正な入力を可能なかぎり早い時点で、明確な原因とともに拒否すること。
  • PAdES — PDF Advanced Electronic Signatures。PDF 文書に署名するための ETSI プロファイルファミリー(B-B、B-T、B-LT、B-LTA)。ここでは初出時に展開し、詳細は署名関連のページで扱います。
  • 必要ではあるが十分ではない — プロセス内のチェックが実質的なシグナルではあっても適合性の判定ではない場合に用いる、意図的な表現。最終的な権威ある判断は、独立したバリデーターに委ねられます。