Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2a32508
feat(mvc): introduce typed TemplateScope for presenter templates
WerySkok Oct 6, 2026
b8027b5
feat(phpstan): analyse Latte templates of the basic example
WerySkok Oct 6, 2026
4e85cda
fix(examples): render title block instead of an undefined variable in…
WerySkok Oct 6, 2026
9be45b8
feat(phpstan): collect onStartup/onBeforeRender variables and skip te…
WerySkok Oct 6, 2026
ca6ff91
feat(phpstan): resolve _template overrides and explicit render calls
WerySkok Oct 6, 2026
a4dd941
fix(phpstan): always analyse the default template next to _template o…
WerySkok Oct 6, 2026
f9ddcc3
chore: consume phpstan-latte fork from VCS
WerySkok Oct 8, 2026
06b42ea
fix: keep template scope assignable by presenters with custom constru…
WerySkok Oct 28, 2026
2aec0ad
chore: consume phpstan-latte 0.21.0 from the fork
WerySkok Oct 28, 2026
20bd67a
fix(phpstan-latte): walk parent lifecycle methods for template context
WerySkok Oct 28, 2026
2a64da9
chore: consume phpstan-latte 0.21.1
WerySkok Oct 28, 2026
f80c673
chore: consume phpstan-latte 0.21.2
WerySkok Oct 28, 2026
9c68fcc
fix: resolve explicit renders and _template paths
WerySkok Oct 28, 2026
2c02fda
test(examples): cover explicit renders, overrides and csrfToken
WerySkok Oct 28, 2026
41518f7
chore: isolate hosted dependency lock changes
WerySkok Oct 28, 2026
354fb89
docs: clarify presenter resolver behavior and type class strings
WerySkok Oct 28, 2026
01ec528
test(examples): cover terminating actions and runtime-safe explicit r…
WerySkok Oct 28, 2026
44ec1fd
feat: make PHPStan resolvers available to downstream projects
WerySkok Oct 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,26 @@ The linter compiles every `.latte` file with the standard presenter engine
`{presenter}` tags), so syntax errors and broken HTML nesting fail the check.
Pass several paths to lint multiple directories.

### PHPStan template analysis

The Laravel-like `latte` section in `phpstan.examples.neon` is a complete
example: it analyses presenter templates, layouts and error templates through
`Chandler\PHPStanLatte\ChandlerPresenterTemplateResolver`. To use it in an app:

- add the `efabrica/phpstan-latte` fork as a dev dependency and add its VCS
repository to the **application's** `composer.json` (Composer repositories
are not inherited from dependencies);
- register the resolvers as PHPStan services;
- declare `Chandler\MVC\SimplePresenter` as `earlyTerminatingMethodCalls`, so
actions that only call `redirect()`, `notFound()`, `terminate()`,
`throwError()` or `pass()` do not get a false `latte.cannotResolve`;
- keep `csrfToken` in `latte.globalVariables` when the app does not analyse
`SimplePresenter.php` itself — it is set in `onBeforeRender()`.

The resolver currently ignores `_templatePath`, so themed templates are not
analysed. Do not add theme directories to `paths` while
`reportUnanalysedTemplates` is enabled, or they will be reported as unanalysed.

## Scheduled Tasks (Cron)

Chandler provides a built-in scheduler and runner for background tasks using a fluent PHP API. Tasks are defined in `cron.php` (or discovered from extension `cron.php` files) and executed via your system crontab.
Expand Down
5 changes: 4 additions & 1 deletion chandler/MVC/SimplePresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ abstract class SimplePresenter implements IPresenter
public const REDIRECT_TEMPORARY_PRESISTENT = 7;

protected $mmReader;

/** @var TemplateScope */
protected $template;

protected $errorTemplate = null;

public function __construct()
{
$this->template = (object) [];
$this->template = new TemplateScope();
}

public function getTemplatingEngine(): TemplatingEngine
Expand Down
18 changes: 18 additions & 0 deletions chandler/MVC/TemplateScope.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Chandler\MVC;

use AllowDynamicProperties;
use stdClass;

/**
* Scope of variables assigned with $this->template->foo = ... by a presenter.
*
* Presenters use this object as a bag of template variables; Router reads it
* via SimplePresenter::getTemplateScope() and passes the values to Latte.
* Dynamic properties are a feature here, hence the attribute.
*/
#[AllowDynamicProperties]
class TemplateScope extends stdClass {}
12 changes: 10 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
"support": {
"source": "https://github.com/OpenVK/chandler"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/OpenVK/phpstan-latte"
}
],
"bin": [
"bin/chandler-latte-lint"
],
Expand Down Expand Up @@ -33,7 +39,8 @@
},
"autoload": {
"psr-4": {
"Chandler\\": "chandler/"
"Chandler\\": "chandler/",
"Chandler\\PHPStanLatte\\": "tools/phpstan-latte/"
},
"classmap": [
"chandler/Bootstrap.php"
Expand All @@ -49,6 +56,7 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.90",
"phpstan/phpstan": "^2.2"
"phpstan/phpstan": "^2.2",
"efabrica/phpstan-latte": "^0.21"
}
}
230 changes: 229 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions examples/basic/Web/Presenters/HelloPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,42 @@ public function renderGreet(int|string $name): void
$this->template->name = $name;
$this->template->title = "Hello, $name";
}

/**
* renderExplicit — render a template directly through the Latte engine.
*
* There is intentionally no Hello/Explicit.latte default template:
* the resolver must use the explicit render call and must not report
* "Cannot resolve latte template".
*/
public function renderExplicit(): void
{
$this->getTemplatingEngine()->render(__DIR__ . '/templates/Hello/ExplicitView.latte', [
'message' => 'Rendered explicitly',
]);

// The template is rendered directly; stop Router from trying to
// render the (intentionally absent) Hello/Explicit.latte afterwards.
$this->terminate();
}

/**
* renderOverride — pick a non-default template through _template.
*/
public function renderOverride(): void
{
$this->template->message = 'Rendered through _template';
$this->template->_template = 'Hello/OverrideView.latte';
}

/**
* renderNotFoundOnly — fixture for actions that only terminate.
*
* There is no template; earlyTerminatingMethodCalls must suppress
* latte.cannotResolve for it.
*/
public function renderNotFoundOnly(): void
{
$this->notFound();
}
}
Loading
Loading