Skip to main content
A loader is responsible for converting a template name into its source content. Wik/Lex ships with three loaders covering the most common use cases. Each loader implements LoaderInterface, so you can also supply your own.

FileLoader

The default. Searches configured directories using dot-notation.

NamespaceLoader

Adds namespace prefixes (admin::dashboard) that map to separate directory trees.

MemoryLoader

Stores templates in memory. Ideal for unit tests and generated content.

FileLoader

FileLoader is wired in automatically when you call ->paths() or Lexer::fromConfig(). You do not need to instantiate it directly for typical usage.

Dot-notation resolution

A dot in a template name is converted to a directory separator before the .lex extension is appended:
Template nameResolved path
'home'views/home.lex
'layouts.app'views/layouts/app.lex
'admin.partials.nav'views/admin/partials/nav.lex
When multiple directories are configured, Lex searches them in order and returns the first match.
use Wik\Lexer\Lexer;

$lexer = (new Lexer())
    ->paths([
        __DIR__ . '/views',
        __DIR__ . '/views/vendor',
    ]);

// Looks for:
//   views/layouts/app.lex
//   views/vendor/layouts/app.lex
echo $lexer->render('layouts.app', $data);

Cache key

FileLoader generates its cache key as md5(absolutePath . ':' . filemtime). This means the compiled cache is automatically invalidated when the source file changes — without reading its content.

Instantiating directly

You rarely need to create a FileLoader yourself, but it is available if you want to use it standalone with tooling:
use Wik\Lexer\Loader\FileLoader;

$loader = new FileLoader([
    __DIR__ . '/views',
    __DIR__ . '/views/vendor',
]);

$path    = $loader->getPath('layouts.app'); // string|null
$content = $loader->load('layouts.app');    // throws ViewException if not found
$exists  = $loader->exists('layouts.app'); // bool

NamespaceLoader

NamespaceLoader wraps a base FileLoader and adds namespace support. A namespace is a short alias (e.g. admin) that maps to a directory. Template names that contain :: are routed to the matching namespace loader; all other names fall through to the base loader.

Registering namespaces

use Wik\Lexer\Loader\FileLoader;
use Wik\Lexer\Loader\NamespaceLoader;

$base   = new FileLoader([__DIR__ . '/views']);
$loader = new NamespaceLoader($base);

$loader->addNamespace('admin', __DIR__ . '/views/admin');
$loader->addNamespace('mail',  __DIR__ . '/views/mail');

Resolution rules

Template nameResolved path
'admin::dashboard'views/admin/dashboard.lex
'admin::users.index'views/admin/users/index.lex
'mail::welcome'views/mail/welcome.lex
'home'views/home.lex (falls through to base)
// Namespaced template
echo $lexer->render('admin::dashboard', $data);

// Non-namespaced template — falls through to the base FileLoader
echo $lexer->render('home', $data);

Multiple directories per namespace

You can call addNamespace() multiple times with the same prefix to register additional directories for that namespace. Lex searches them in registration order.
$loader->addNamespace('admin', __DIR__ . '/views/admin');
$loader->addNamespace('admin', __DIR__ . '/vendor/acme/admin-views');
The namespace separator is always :: and cannot be changed.

MemoryLoader

MemoryLoader stores template source strings in a plain PHP array. It has no dependency on the filesystem, making it the best choice for unit tests and for templates generated at runtime.

Basic usage

use Wik\Lexer\Loader\MemoryLoader;

$loader = new MemoryLoader();
$loader->set('greeting', 'Hello, {{ $name }}!');
$loader->set('farewell', 'Goodbye, {{ $name }}.');

Using MemoryLoader in tests

MemoryLoader is primarily used to supply template source strings to the compiler during testing. Access the raw source via load() or all() for assertions:
use PHPUnit\Framework\TestCase;
use Wik\Lexer\Loader\MemoryLoader;

class MemoryLoaderTest extends TestCase
{
    public function test_it_stores_templates(): void
    {
        $loader = new MemoryLoader();
        $loader->set('greeting', 'Hello, {{ $name }}!');

        $this->assertTrue($loader->exists('greeting'));
        $this->assertSame('Hello, {{ $name }}!', $loader->load('greeting'));
        $this->assertNull($loader->getPath('greeting')); // memory loader has no path
    }
}

Other MemoryLoader methods

MethodDescription
set(string $name, string $source): voidStore or replace an in-memory template.
remove(string $name): voidRemove a stored template.
all(): arrayReturn all stored templates as name => source.
exists(string $name): boolCheck whether a template is registered.
MemoryLoader::getPath() always returns null because in-memory templates have no filesystem path. The dependency graph does not track in-memory templates, so automatic cache invalidation is not applicable.

Build docs developers (and LLMs) love