Scape/SPECS.md

162 lines
11 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 (with the following defaults when not provided):
- `SCAPE_TEMPLATES_DIR`: Main directory for application templates.
- Default: `./templates`
- `SCAPE_LAYOUTS_DIR`: Directory for base layouts and parent templates.
- Default: `./templates/layouts`
- `SCAPE_PARTIALS_DIR`: Directory for reusable snippets/partials.
- Default: `./templates/partials`
- `SCAPE_FILTERS_DIR`: Directory for user-defined filters.
- Default: `./filters`
- `SCAPE_CACHE_DIR`: Directory for cached AST files.
- Default: `./.scape/cache`
- **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 `{( ... )}` and block/inheritance tags `{[ ... ]}` automatically consume one trailing newline immediately following their closing `)}` or `]}` 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 )}`).
- **filters:string** library includes:
- `lower`: Converts to lowercase.
- `upper`: Converts to uppercase.
- `ucfirst`: Capitalizes the first character.
- `currency(code)`: Formats numeric value as currency (default: 'USD').
- `float(precision)`: Formats numeric value as a float with fixed precision (default: 2).
- `date(format)`: Formats a timestamp or date string (default: 'Y-m-d H:i:s').
- `truncate(length, suffix)`: Truncates string to length (default length: 100, suffix: '...').
- `default(fallback)`: Returns fallback if value is empty.
- `json`: Returns JSON encoded string.
- `url_encode`: Returns URL encoded string.
- `join(glue)`: Joins array elements with glue (default glue: '').
- `first`: Returns first element of a collection.
- `last`: Returns last element of a collection.
- `word_count`: Returns word count of a string.
- `keys`: Returns keys of an associative array.
- **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. **All exceptions must include helpful, context-rich error messages** (e.g., template name, line number, and specific failure reason) to assist in debugging:
- `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) & i18n
- **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.
- **Localization (i18n)**:
- **Philosophy**: Scape is "Simple for Blogs, Powerful for Enterprise." Localization is an **opt-in** feature.
- **Responsibility**: Translation logic and message catalogs reside in the Application/Framework. Scape provides the **access layer**.
- **Usage**: Templates can use `host.translate('key')` or the `|t` filter alias to fetch localized strings on-demand.
- **Portability**: If no host provider is registered, i18n calls return the input key/value unchanged, ensuring templates remain portable across environments.
- **Use Cases**: Used for framework-level features like feature flags (Flagpole), routing, or translations without creating a hard dependency within the engine.
### 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)