adachsoft/prompt-template-lib

PHP library for rendering function-based prompt templates with extensible SPI and safe file access

Installs: 1

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Forks: 0

pkg:composer/adachsoft/prompt-template-lib

v0.1.0 2025-12-15 21:04 UTC

This package is not auto-updated.

Last update: 2025-12-16 08:46:06 UTC


README

Small PHP library for rendering function-based prompt templates with a clear, layered architecture (PublicApi / Spi / Internal). It lets you define template tags that call PHP functions (SPI implementations) with arguments, for example:

Hello <user_name>, today is <datetime('Y-m-d')>.
Please analyze: <file_read('docs/task.md')>

The engine parses the template, resolves tags using registered functions, and returns the rendered string. File-based operations are secured with normalized, safe paths to protect against directory traversal.

Requirements

  • PHP >= 8.3
  • Composer (for installation and autoloading)

Dev tools (optional, already declared in composer.json):

  • phpunit/phpunit
  • phpstan/phpstan
  • friendsofphp/php-cs-fixer

Installation

composer require adachsoft/prompt-template-lib

The package is framework-agnostic and can be used in any PHP application that supports Composer autoloading.

Core concepts

1. Public API

Public entry point is the engine interface and its default implementation:

  • AdachSoft\PromptTemplateLib\PublicApi\PromptEngineInterface – public contract used in type hints.
  • AdachSoft\PromptTemplateLib\PublicApi\PromptEngine – default implementation of the interface.

You normally do not instantiate this class manually. Use the factory instead:

  • AdachSoft\PromptTemplateLib\PublicApi\PromptEngineFactory
use AdachSoft\PromptTemplateLib\PublicApi\PromptEngineFactory;
use AdachSoft\PromptTemplateLib\PublicApi\PromptEngineInterface;

$engine = PromptEngineFactory::createDefault(__DIR__); // PromptEngineInterface

createDefault() wires all internal components and registers standard functions (see below).

2. SPI (Service Provider Interface)

To add your own function (tag), implement the SPI interface:

  • AdachSoft\PromptTemplateLib\Spi\PromptFunctionInterface
use AdachSoft\PromptTemplateLib\Spi\PromptFunctionInterface;

final class UserNameFunction implements PromptFunctionInterface
{
    public function getName(): string
    {
        // Name must follow the engine naming rules (see below).
        return 'user_name';
    }

    public function execute(array $arguments): string
    {
        // In a real application this could read from a user context, session, etc.
        return 'Example User';
    }
}

Then register the function in the engine:

$engine->registerFunction(new UserNameFunction());

You can now use <user_name> in templates.

Function naming rules

Every implementation of PromptFunctionInterface must return a name that satisfies the following rules:

  • at least 3 characters long,
  • contains only lowercase letters a-z, digits 0-9 and underscore (_),
  • cannot start or end with underscore (_).

If a function is registered with a name that violates these rules, the engine throws InvalidFunctionNameException.

3. Internal layer (implementation details)

The Internal layer is not part of the public contract and may change between versions. It contains:

  • Internal\Registry\FunctionRegistry – registry that maps function names to PromptFunctionInterface implementations.
  • Internal\Core\TemplateParser – parser responsible for finding tags in the template, parsing arguments and delegating calls to registered functions.

These classes are marked as @internal and should not be used directly from client code.

Standard functions

The library ships with two example functions, registered automatically by PromptEngineFactory::createDefault():

  • DateTimeFunction – tag: <datetime> or <datetime('Y-m-d')>

    • Without arguments it returns the current date/time in DATE_ATOM format.
    • With a single argument it uses that argument as the format string for DateTimeImmutable::format().
  • FileReadFunction – tag: <file_read('relative/path.txt')>

    • Reads contents of a file from a base directory configured in the factory.
    • Uses adachsoft/normalized-safe-path under the hood to normalize and validate paths.
    • Any attempt to escape the base directory (path traversal such as ../..) results in an exception.

Quick start

1. Basic usage

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use AdachSoft\PromptTemplateLib\PublicApi\PromptEngineFactory;
use AdachSoft\PromptTemplateLib\Spi\PromptFunctionInterface;

// Create engine with standard functions <datetime> and <file_read(...)>
$engine = PromptEngineFactory::createDefault(__DIR__);

// Register custom function <user_name>
$engine->registerFunction(new class implements PromptFunctionInterface {
    public function getName(): string
    {
        return 'user_name';
    }

    public function execute(array $arguments): string
    {
        return 'Example User';
    }
});

$template = "Hello <user_name>, today is <datetime('Y-m-d')>.\n" .
    "Prompt: <file_read('README.md')>";

echo $engine->render($template);

2. Using in your application

In a real project you would typically:

  1. Configure the base directory for file_read via configuration/env.
  2. Use dependency injection to construct PromptEngineInterface (or call PromptEngineFactory::createDefault() at your composition root).
  3. Register your own PromptFunctionInterface implementations during bootstrap.

Error handling

The library uses a focused set of dedicated exceptions in the PublicApi\Exception namespace:

  • PromptTemplateException – base class for all library-specific errors.
  • InvalidFunctionNameException – a prompt function was registered with a name that violates the naming rules.
  • InvalidFunctionArgumentException – a prompt function received invalid arguments (wrong count, type or value).
  • UnknownFunctionException – template references a tag name that is not registered in the engine.
  • TemplateSyntaxException – syntax error in template tags or arguments.
  • FunctionExecutionException – wrapper thrown by the engine when a function throws an unexpected exception.

Implementations of PromptFunctionInterface SHOULD:

  • throw InvalidFunctionArgumentException when the parsed arguments coming from a template tag are invalid,
  • throw other PromptTemplateException subclasses for domain-specific problems (I/O errors, missing resources, security violations),
  • let unexpected Throwable bubble up; it will be wrapped in FunctionExecutionException by the parser.

Testing

The repository includes a PHPUnit test suite covering:

  • Public API (PromptEngineInterface, PromptEngine, PromptEngineFactory)
  • Internal parser (TemplateParser) and registry (FunctionRegistry)
  • Standard functions (DateTimeFunction, FileReadFunction)

Run tests with:

vendor/bin/phpunit

License

This library is released under the MIT License.