PHPackages                             shojiku/shojiku - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. shojiku/shojiku

ActiveLibrary[Templating &amp; Views](/categories/templating)

shojiku/shojiku
===============

Deterministic PDF documents from a YAML template plus your data

v0.2.0(today)02↑2900%MITPHPPHP ^8.3

Since Aug 2Pushed todayCompare

[ Source](https://github.com/kengos/shojiku-php)[ Packagist](https://packagist.org/packages/shojiku/shojiku)[ Docs](https://shojiku.pages.dev)[ RSS](/packages/shojiku-shojiku/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (3)Used By (0)

Shojiku for PHP
===============

[](#shojiku-for-php)

PHP bindings for [Shojiku](https://shojiku.pages.dev) — a document engine that turns a YAML template plus your data into a deterministic PDF. The package is pure PHP and renders nothing on its own: it drives the `shojiku` command-line binary, which you install separately.

Install
-------

[](#install)

```
composer require shojiku/shojiku
```

**No dependencies and no extension to compile.** The binary is the other half, and the package never downloads an executable — that is deliberate. Take it from the [GitHub release](https://github.com/kengos/shojiku/releases/latest) (a per-platform archive plus the shared `packs` archive), `cargo install shojiku-cli`, or the Docker image; the [quickstart](https://github.com/kengos/shojiku/blob/main/docs/quickstart.md)covers them.

The SDK looks for the binary in the `SHOJIKU_BIN` environment variable first, then the path you configure, then `PATH`.

Usage
-----

[](#usage)

```
use Shojiku\Client;
use Shojiku\LocalPem;

$client = new Client(templates: 'app/templates');

$result = $client->generate('receipt_ja', [
    'customer' => ['name' => 'Yamada Shoji K.K.'],
    'items' => [['name' => 'Consulting', 'qty' => 1, 'price' => 120000]],
]);

if ($result->success()) {
    $result->artifact()->write('receipt.pdf');
} else {
    foreach ($result->failure()->diagnostics() as $d) {
        error_log($d->message());
    }
}
```

Signing is a separate step over the rendered document:

```
$signed = $result->artifact()->sign(new LocalPem(key: 'signer.key', cert: 'signer.crt'));
if ($signed->success()) {
    $signed->artifact()->write('receipt-signed.pdf');
}
```

And verification answers with the whole report — including what it did **not** check, which is a field rather than a footnote:

```
$result = $client->artifact(file_get_contents('receipt-signed.pdf'))
    ->verify(anchors: 'ca.crt');

if ($result->success()) {
    echo 'valid; not checked: ', implode(', ', $result->report()->notChecked()), "\n";
}
```

Nothing throws in the normal flow: every operation returns a result you query — `success()`, the artifact, and the engine's diagnostics (an overflowing box, an unknown field) on a success as well as a failure. A failure carries a trace: which step failed, and its structured cause, so it is data you log and inspect rather than an exception you catch. A signature that does not verify is a **failed** result, so a caller who checks only `success()` is never told a forgery is fine.

Exceptions are reserved for programmer misuse — a template name that is not a string, both forms of the same material at once, unwrapping a result you did not check — plus the two the environment can produce: a missing binary (`BinaryNotFoundException`, which names the install channels) and one too old to report what it did (`IncompatibleEngineException`).

Templates the application already holds
---------------------------------------

[](#templates-the-application-already-holds)

`generate()` resolves a template NAME against the configured root, with containment rules that reject paths, traversal, and every Windows spelling of one. When the sources come from object storage, a database or a heredoc instead, hand them over as bytes:

```
$result = $client->generateSource(
    template: $yamlYouFetched,
    definitions: $definitionsYouFetched,
    params: $params,
);
```

That argument is source TEXT: a path-shaped value is a template that fails to parse, never a file this package opens. Root containment does not apply here, because there is no root to be contained by.

Configuration
-------------

[](#configuration)

```
Shojiku\Configuration::configure([
    'templates' => 'app/templates',
    'lang' => 'ja-JP',
    'strict' => true,
    'providers' => ['invoice' => new LocalPem(key: '/etc/shojiku/signer.key', cert: '/etc/shojiku/signer.crt')],
]);
```

An explicit constructor argument beats this, which beats the `SHOJIKU_TEMPLATE_ROOT` / `SHOJIKU_FONT_DIR` / `SHOJIKU_LOCALE_DIR`environment; `SHOJIKU_BIN` is the deliberate exception and beats both, because where the engine lives is a deployment decision. `env: false`turns every one of those lookups off — in this process **and** in the engine child, which would otherwise read them itself.

`strict: true` is the other exception, and the only place configuration beats a call site: it refuses the bytes entrance, signs only documents this client rendered from its own template root, and takes signing material only as the name of a registered provider. Verification is never restricted.

Signing with a key this process never holds
-------------------------------------------

[](#signing-with-a-key-this-process-never-holds)

When the private key lives in a cloud KMS, an HSM or a smartcard, use `ExternalSigner` instead. Shojiku hands out the bytes a signature has to cover; your code signs them wherever the key is and hands the signature back, so the key never enters your application:

```
$provider = new Shojiku\ExternalSigner(
    sign: fn (string $toBeSigned): string => $kms->sign($keyId, $toBeSigned),
    cert: 'signer.crt',
    algorithm: Shojiku\Algorithm::EcdsaP256Sha256,
);
$signed = $artifact->sign($provider);
```

The call site does not change — which provider you pass is the only difference, and a provider registered by name works the same way under a strict client. This package ships no cloud client of its own: the callback is whichever client your application already uses.

Two details worth getting right. The bytes you are handed are the CMS **signed attributes**, not the document's digest — a service that signs a digest must hash *these* bytes with SHA-256 itself. And the signature is that operation's raw output: PKCS#1 v1.5 bytes for `rsa-pkcs1-sha256`, an ASN.1 DER sequence for `ecdsa-p256-sha256`, which is what AWS KMS and Google Cloud KMS both return unchanged.

A failure inside your own code is *not* swallowed into a failed result: an outage at your key service is not a fact about the document.

Requirements
------------

[](#requirements)

PHP 8.3 or newer, and the `shojiku` binary installed as described above.

Development
-----------

[](#development)

Development happens in the [monorepo](https://github.com/kengos/shojiku), where this package is `sdk/php`. Gates run in a container, so no PHP toolchain is needed locally:

```
make verify:sdk:php
```

`make test:sdk:php` and `make lint:sdk:php` are the faster slices.

Documentation
-------------

[](#documentation)

- [Template reference](https://github.com/kengos/shojiku/blob/main/docs/engine/README.md) — how to write the YAML the engine renders
- [SDK policy](https://github.com/kengos/shojiku/blob/main/docs/agents/sdk.md) — the lifecycle contract every Shojiku SDK implements

License
-------

[](#license)

Licensed under any of [Apache-2.0](https://github.com/kengos/shojiku/blob/main/LICENSE-APACHE), [MIT](https://github.com/kengos/shojiku/blob/main/LICENSE-MIT), or [BSD-3-Clause](https://github.com/kengos/shojiku/blob/main/LICENSE-BSD), at your option.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance100

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~6 days

Total

2

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/968151?v=4)[Kengo Suzuki](/maintainers/kengos)[@kengos](https://github.com/kengos)

---

Top Contributors

[![kengos](https://avatars.githubusercontent.com/u/968151?v=4)](https://github.com/kengos "kengos (8 commits)")

---

Tags

pdftemplateinvoicedocumentreceipt

### Embed Badge

![Health badge](/badges/shojiku-shojiku/health.svg)

```
[![Health](https://phpackages.com/badges/shojiku-shojiku/health.svg)](https://phpackages.com/packages/shojiku-shojiku)
```

###  Alternatives

[phpoffice/phpword

PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)

7.6k40.7M262](/packages/phpoffice-phpword)[anourvalar/office

Generate documents from existing Excel &amp; Word templates | Export tables to Excel (Grids)

23999.6k](/packages/anourvalar-office)[mayaram/laravel-ocr

Laravel OCR &amp; Document Data Extractor - A powerful OCR and document parsing engine for Laravel

762.7k](/packages/mayaram-laravel-ocr)[icircle/docx-template-in-php

Create Templates in MS Word docx format and use them Creating Business documents using PHP

4322.9k](/packages/icircle-docx-template-in-php)[tomatophp/filament-docs

Manage your documents and contracts all in one place with template builder

452.7k](/packages/tomatophp-filament-docs)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
