Skip to content

ktav — PHP bindings

Packagist CI License: MIT OR Apache-2.0 Playground

Languages: English · Русский · 简体中文

Playground: convert JSON / YAML / TOML / INI ⇄ Ktav in your browser at ktav-lang.github.io.

PHP bindings for the Ktav configuration format. Thin wrapper around the reference Rust parser, loaded at runtime through the PHP FFI extension — no PHP extension to compile, no PECL install. Plain Composer dependency, the native binary is fetched on first call.

Requires PHP 7.4+ with ext-ffi enabled (default in CLI; web SAPIs need ffi.enable=1 in php.ini).

Install

composer require ktav-lang/ktav

Quick start

Parse — read typed values straight off the array

use Ktav\Ktav;

$src = <<<KTAV
service: web
port: 8080
ratio: 0.75
tls: true
tags: [
    prod
    eu-west-1
]
db.host: primary.internal
db.timeout: 30
KTAV;

$cfg = Ktav::loads($src);

$service   = $cfg['service'];        // string
$port      = $cfg['port'];           // int
$ratio     = $cfg['ratio'];          // float
$tls       = $cfg['tls'];            // bool
$tags      = $cfg['tags'];           // array<string>
$dbHost    = $cfg['db']['host'];     // string
$dbTimeout = $cfg['db']['timeout'];  // int

Build & render — construct a document in code

$doc = [
    'name'  => 'frontend',
    'port'  => 8443,
    'tls'   => true,
    'ratio' => 0.95,
    'upstreams' => [
        ['host' => 'a.example', 'port' => 1080],
        ['host' => 'b.example', 'port' => 1080],
    ],
    'notes' => null,
];
$text = Ktav::dumps($doc);

A complete runnable example lives in examples/basic.php.

API

Method Purpose
Ktav::loads(string $src): mixed Parse a Ktav document.
Ktav::loadsStrict(string $src): mixed Parse with strict numeric spelling checks.
Ktav::dumps(array $value): string Render an associative array as Ktav text.
Ktav::format(string $src): string Normalise a document's spelling, keeping comments.
Ktav::nativeVersion(): string Version of the loaded ktav_cabi.

Formatting

Ktav::format() takes Ktav source text and returns Ktav source text. It normalises structure to canonical form (§ 5.9) while keeping the trivia the canonical writer drops:

echo Ktav::format("## the server\nserver: {host: a, port: 80}\n");
// ## the server
// server: {
//     host: a
//     port: 80
// }

Every comment survives verbatim — Ktav has no trailing comments (§ 3.4: a comment owns a whole line), so attachment is unambiguous. Blank lines survive as a grouping hint, but a run of two or more collapses to exactly one and blank padding just inside a bracket is dropped, which makes the transform a fixed point: formatting already-formatted text changes nothing. Key order is never changed — canonical form has no sorting rule, and reordering keys would make review diffs worse.

Errors

KtavException is thrown on any parse or render failure. Beyond a human-readable getMessage(), it carries the nine structured fields of the core's error envelope:

try {
    Ktav::loadsStrict("a: 1.10\n");
} catch (KtavException $e) {
    $e->getError();        // "LossyScalar"
    $e->getBody();         // "1.10"      — as written
    $e->getCanonical();    // "1.1"       — as it would be stored
    $e->getSpecSection();  // "§3.6/§5.2"
    $e->getSpan();         // ["start" => 0, "end" => 7]
}

The full set is getError(), getReason(), getErrorLine(), getLineText(), getSpan(), getPath(), getBody(), getCanonical(), getSpecSection(). Absent information is null, never a missing accessor, so a caller can read any field without checking the error class first.

getPath() is an array of exact decoded key segments, never a joined string: a key literally named a.b is one segment and cannot be confused with a two-segment path.

Two writer rejections are named apart — "UnrepresentableAt" when the writer can say which node is at fault (it fills getPath() too), and "Unrepresentable" when it cannot. The reason code is the same in both, so matching on getReason() is enough when you only need to know that a write was refused.

getErrorLine() rather than getLine(), because PHP declares Exception::getLine() final.

Type mapping

Ktav PHP
null null
true / false bool
bare integer int if it fits, else string (PHP has no native bigint — wrap your own GMP / BCMath if you need arithmetic).
bare decimal float
other scalar string
[ ... ] sequential array
{ ... } associative array (insertion order preserved)

To emit an arbitrary-precision integer, wrap the digit string yourself: ['big' => ['$i' => '9999999999999999999']] — same envelope used on the wire between PHP and the native side.

Key escaping

Since spec 0.6.4 a literal . or : inside a key segment is written with a backslash:

a\.b: v        # key is the single segment "a.b" → ["a.b" => "v"]
a\:b: v        # key contains a colon          → ["a:b" => "v"]
x.y\.z: v      # split on the first dot only   → ["x" => ["y.z" => "v"]]

A literal backslash in a key is \\.

How the native library is resolved

On first call:

  1. KTAV_LIB_PATH env var, if set.
  2. User cache<userCache>/ktav-php/v<version>/<asset>, downloaded on a previous call.
  3. GitHub Release download — fetched once from github.com/ktav-lang/php/releases/download/v<version>/<asset> and cached under (2). Requires network on first call after install.

<userCache> is %LOCALAPPDATA% on Windows, ~/Library/Caches on macOS, $XDG_CACHE_HOME or ~/.cache on Linux.

Runtime support

  • PHP 7.4 / 8.0 / 8.1 / 8.2 / 8.3+. Tested on the LTS lines on every CI run.
  • Prebuilt binaries for: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64, windows/arm64.
  • Linux distros must use glibc 2.17+ (zigbuild baseline). Alpine (musl) support is planned.

License

MIT OR Apache-2.0 — see LICENSE-MIT and LICENSE-APACHE.

Other Ktav implementations

  • spec — specification + conformance suite
  • rust — reference Rust crate (cargo add ktav)
  • csharp — C# / .NET (dotnet add package Ktav)
  • golang — Go (go get github.com/ktav-lang/golang)
  • java — Java / JVM (io.github.ktav-lang:ktav on Maven Central)
  • js — JS / TS (npm install @ktav-lang/ktav)
  • python — Python (pip install ktav)

About

PHP bindings for Ktav — a plain configuration format with three rules, zero indentation, and zero quoting. FFI over the reference Rust crate — no PHP extension to compile, prebuilt binaries for Linux/macOS/Windows.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages