Skip to content

Validation ​

djot-php provides built-in validation features to detect issues in your Djot documents. This is useful for linting content, catching errors in CI pipelines, or providing feedback in editors.

The Problem ​

Documents can have issues that don't prevent rendering but indicate problems:

djot
Check the [installation guide][install] for details.

See footnote[^missing] for more info.

[unused]: https://example.com/never-linked

Without validation, these issues go unnoticed until users encounter broken links.

Enabling Warnings ​

Enable warnings to collect non-fatal issues during parsing:

php
use Djot\DjotConverter;

$converter = new DjotConverter(warnings: true);
$html = $converter->convert($content);

if ($converter->hasWarnings()) {
    foreach ($converter->getWarnings() as $warning) {
        echo $warning . "\n";
    }
}

What Warnings Detect ​

IssueExampleCategory
Undefined reference[text][missing]reference
Undefined footnote[^missing]footnote
Unused referenceDefined but never linkedreference
Broken anchor link[link](#nonexistent)anchor
Unattached attribute{.note} text at the start of a paragraphattribute

Strict Mode ​

For fatal errors that should stop processing, enable strict mode:

php
use Djot\DjotConverter;
use Djot\Exception\ParseException;

$converter = new DjotConverter(strict: true);

try {
    $html = $converter->convert($content);
} catch (ParseException $e) {
    echo "Error: " . $e->getMessage() . "\n";
    echo "Line: " . $e->getSourceLine() . "\n";
    echo "Column: " . $e->getSourceColumn() . "\n";
}

What Strict Mode Catches ​

ErrorDescription
Unclosed code fence``` without closing
Unclosed div::: note without :::
Unclosed comment{% comment without %}
Unclosed raw block``` {=html} without closing

Combining Both Modes ​

Use both warnings and strict mode together for comprehensive validation:

php
$converter = new DjotConverter(warnings: true, strict: true);

try {
    $html = $converter->convert($content);

    // Check for non-fatal warnings
    foreach ($converter->getWarnings() as $warning) {
        echo "[WARN] " . $warning . "\n";
    }
} catch (ParseException $e) {
    echo "[ERROR] " . $e->getMessage() . "\n";
}

Working with Warnings ​

The ParseWarning class provides detailed information:

php
foreach ($converter->getWarnings() as $warning) {
    // Basic info
    echo $warning->getMessage();  // "Undefined reference 'missing'"
    echo $warning->getLine();     // 5
    echo $warning->getColumn();   // 12

    // Additional context
    echo $warning->getCategory();   // "reference"
    echo $warning->getSuggestion(); // "Define with [missing]: url"

    // Convert to array (useful for JSON APIs)
    $data = $warning->toArray();
}

Validating Documents ​

Here's a complete example for validating a document or file:

php
use Djot\DjotConverter;
use Djot\Exception\ParseException;

function validateDjot(string $content): array
{
    $errors = [];
    $warnings = [];

    $converter = new DjotConverter(warnings: true, strict: true);

    try {
        $converter->convert($content);

        foreach ($converter->getWarnings() as $warning) {
            $warnings[] = [
                'message' => $warning->getMessage(),
                'line' => $warning->getLine(),
                'column' => $warning->getColumn(),
                'category' => $warning->getCategory(),
                'suggestion' => $warning->getSuggestion(),
            ];
        }
    } catch (ParseException $e) {
        $errors[] = [
            'message' => $e->getMessage(),
            'line' => $e->getSourceLine(),
            'column' => $e->getSourceColumn(),
        ];
    }

    return [
        'valid' => empty($errors),
        'errors' => $errors,
        'warnings' => $warnings,
    ];
}

// Usage
$result = validateDjot(file_get_contents('document.djot'));

if (!$result['valid']) {
    echo "Document has errors:\n";
    foreach ($result['errors'] as $error) {
        printf("  Line %d: %s\n", $error['line'], $error['message']);
    }
}

if ($result['warnings']) {
    echo "Warnings:\n";
    foreach ($result['warnings'] as $warning) {
        printf("  Line %d: %s\n", $warning['line'], $warning['message']);
    }
}

CI/CD Integration ​

Use validation in your build pipeline:

php
#!/usr/bin/env php
<?php
// validate-docs.php

require_once 'vendor/autoload.php';

use Djot\DjotConverter;
use Djot\Exception\ParseException;

$exitCode = 0;
$files = glob('docs/**/*.djot');

foreach ($files as $file) {
    $converter = new DjotConverter(warnings: true, strict: true);

    try {
        $converter->convert(file_get_contents($file));

        foreach ($converter->getWarnings() as $warning) {
            fprintf(STDERR, "%s:%d: warning: %s\n",
                $file, $warning->getLine(), $warning->getMessage());
            $exitCode = 1;
        }
    } catch (ParseException $e) {
        fprintf(STDERR, "%s:%d: error: %s\n",
            $file, $e->getSourceLine(), $e->getMessage());
        $exitCode = 1;
    }
}

exit($exitCode);

Run in CI:

bash
php validate-docs.php || exit 1

Clearing Warnings ​

When reusing a converter instance, clear warnings between documents:

php
$converter = new DjotConverter(warnings: true);

$converter->convert($doc1);
$warnings1 = $converter->getWarnings();

$converter->clearWarnings();

$converter->convert($doc2);
$warnings2 = $converter->getWarnings();

See Also ​

Released under the MIT License.