Scape/SPECS.md

136 lines
8.8 KiB
Markdown
Raw Normal View History

# Scape Templates — Product Specification
2026-01-06 23:29:10 +00:00
## Project Name
Scape Templates
2026-01-06 23:29:10 +00:00
## Project Description
A lightweight, standalone PHP template engine designed for simplicity, security, and performance. Scape is built as an **Output Engine first**, focusing on marrying pre-processed data with design rather than performing additional business logic or data manipulation.
2026-01-06 23:29:10 +00:00
## Project Purpose
To provide a modern, framework-agnostic alternative for template rendering that maintains a strict separation of concerns. Scape enforces a "logic-light" philosophy to ensure templates remain readable and focused purely on presentation.
2026-01-06 23:29:10 +00:00
## Project Installation Instructions
Scape can be installed via Composer:
```bash
composer require getphred/scape
2026-01-06 23:29:10 +00:00
```
## Project Features
### 1. File Handling & Configuration
- **Template Extensions**:
- All templates, layouts, and partials must use the `.scape.php` extension.
- **Filter Extensions**:
- Custom filters must use the standard `.php` extension.
- All custom filters must implement the `Scape\Interfaces\FilterInterface` to ensure they provide the necessary transformation methods.
- **Directory Configuration**: Template locations are managed via environment variables:
- `SCAPE_TEMPLATES_DIR`: Main directory for application templates.
- `SCAPE_LAYOUTS_DIR`: Directory for base layouts and parent templates.
- `SCAPE_PARTIALS_DIR`: Directory for reusable snippets/partials.
- `SCAPE_FILTERS_DIR`: Directory for user-defined filters.
- **Dot Notation Pathing**: All internal paths (extends, includes) use dot notation (e.g., `sidebar.login_form`) relative to their respective directories, omitting the file extension.
### 2. Syntax & White-space
- All opening and closing tags are white-space independent (e.g., `{{var}}` is equivalent to `{{ var }}`).
- **Variable Interpolation**:
- `{{ var }}`: Automatically HTML-escaped output (uses `ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5`).
- `{{{ var }}}`: Raw, unescaped output.
- **Data Access**:
- Use dot-notation for accessing class properties/attributes (e.g., `user.name`).
- Use bracket notation for accessing array elements (e.g., `items['title']` or `users[0]`).
### 3. Logic & Control Flow
- **Syntax**: Uses `{( ... )}` for logic tags.
- **White-space Management**: Logic tags `{( ... )}` automatically consume one trailing newline immediately following the closing `)}` tag to prevent unintended vertical spacing in the rendered output.
- **"Logic-Light" Constraints**:
- No generic programming or complex expressions.
- No `if` statements or conditional branching (by design).
- No manual variable assignment within templates.
- **Loops**:
- Only bounded iteration is supported: `foreach`.
- **Grammar**:
- `{( foreach item in collection )} ... {( endforeach )}`
- `{( foreach key, item in collection )} ... {( endforeach )}`
- Every loop provides access to two local integer variables:
- `index`: The current iteration index, 0-indexed (0, 1, 2...).
- `pos`: The current human-readable position, 1-indexed (1, 2, 3...).
- **Positional Rendering**: Special tags are available within loops to handle presentation based on position:
- `{( first )} ... {( endfirst )}`: Renders only on the first iteration.
- `{( inner )} ... {( endinner )}`: Renders on all iterations except the first and last.
- `{( last )} ... {( endlast )}`: Renders only on the last iteration.
### 4. Inheritance & Reusability
- **Syntax**: Uses `{[ ... ]}` for block and inheritance tags.
- **Layouts & Inheritance**:
- Templates can extend layouts from `SCAPE_LAYOUTS_DIR` using `{[ extends 'path' ]}`.
- The `extends` tag must be the very first thing in a template.
- **Blocks**:
- **Placeholder**: Layouts define placeholders using `{[ block 'name' ]} ... {[ endblock ]}`.
- **Override**: Child templates provide content for these placeholders by defining a block with the same name.
- **Parent Content**: Within an override block, the child can render the layout's default content using the `{[ parent ]}` tag.
- **Default Content**: If a child template does not provide a block, the content within the layout's `block` tags is rendered as a default.
- **Nested Blocks**: Blocks can be nested within other blocks.
- **Partials**:
- Reusable snippets from `SCAPE_PARTIALS_DIR` can be included using `{[ include 'path' ]}`.
- **Encapsulation**: Partials are siloed by default and do not inherit the parent template's variables.
- **Passing Data**:
- Data can be passed as an array: `{[ include 'path' with data_source ]}`.
- The `data_source` can be a local variable, a nested array element (`items['meta']`), a class attribute (`user.profile`), or an inline array declaration (e.g., `['user' => user, 'id' => 1]`).
- The array keys from the source are expanded into individual local variables within the partial.
- The full parent context can be passed explicitly: `{[ include 'path' with context ]}`.
- **Nesting**: Partials can include other partials. To prevent infinite recursion, the engine enforces a maximum nesting depth (default: 20).
### 5. Extensibility
- **Filters**:
- Used with variable interpolation to transform data.
- **Piping**: Supports chaining multiple filters: `{{ var | lower | ucfirst }}`.
- **Arguments**: Supports passing simple arguments (strings, numbers, or other variables): `{{ price | currency('USD') }}`.
- **The `FilterInterface`**: All filters must implement `Scape\Interfaces\FilterInterface`:
```php
public function transform(mixed $value, array $args = []): mixed;
```
- **Loading**: Filters must be pre-loaded at the top of the template.
- **Internal Libraries**: Engine-provided filters are loaded using the `uses` keyword:
- Syntax: `{( uses namespace:library )}` (e.g., `{( uses filters:string )}`).
- **Custom Filters**: User-defined filters are loaded from `SCAPE_FILTERS_DIR` using `load_filter`.
- Syntax: `{( load_filter('path') )}` where path is dot-notated.
### 6. Security & Error Handling
- **Contextual Escaping**: Standard `{{ }}` interpolation ensures XSS protection.
- **Missing Assets**: If a layout or partial is missing, the engine looks for a user-provided `404.scape.php` in `SCAPE_TEMPLATES_DIR`. If not found, it renders a built-in "Template 404" placeholder.
- **Exceptions**: The engine throws specific exceptions within the `Scape\Exceptions` namespace:
- `TemplateNotFoundException`: Main template, layout, or partial missing.
- `SyntaxException`: Malformed tags or disallowed logic (e.g. `if`).
- `FilterNotFoundException`: Target of `uses` or `load_filter` missing.
- `PropertyNotFoundException`: (Debug only) Accessing undefined key/property.
- `RecursionLimitException`: Partials exceed nesting limit (default 20).
- **Variable Access**:
- **Debug Mode**: Accessing a non-existent object property or array key throws a `PropertyNotFoundException`.
- **Production Mode**: Accessing a non-existent property or key fails silently and renders an empty string.
### 7. Performance
- **AST Caching**: The engine caches the parsed Abstract Syntax Tree (AST) of templates to speed up subsequent renders.
- **Cache Location**: Cache files are stored locally in the project directory under `.scape/cache`.
- **No Compiling**: Scape does not compile templates into raw PHP files; it interprets the cached AST directly.
- **Cache Modes**:
- **Development**: Engine checks file modification times (`mtime`) to invalidate the cache when a template changes.
- **Production**: Engine skips `mtime` checks and serves the cached AST directly for maximum performance.
### 8. Host Integration (IoC)
- **The `host` Namespace**: Scape provides a reserved `host` namespace that can be used with the `uses` keyword or as a filter/function prefix.
- **Provider Registration**: Host frameworks (e.g., Phred) can register custom providers to handle calls in this namespace.
- **Use Cases**: Used for framework-level features like feature flags (Flagpole), routing, or translations without creating a hard dependency within the engine.
- **Default Behavior**: If no provider is registered, calls to the `host` namespace return the input value unchanged or `null`.
### 9. Runtime API
- **The `Scape\Engine` Class**: The primary entry point for the library.
- **Configuration Precedence**: Programmatic Config > Environment Variables > Defaults.
- **Rendering**:
- Method: `public function render(string $template, array $data = []): string`
- The `$template` argument uses dot notation.
- **Mode Control**: The engine operating mode (`debug` vs `production`) can be set via the `SCAPE_MODE` environment variable or explicitly during instantiation.
## Project Dependencies
- **PHP**: ^8.2
- **PHPUnit**: ^10.0 (Development)