PHPackages                             twigstan/twigstan - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. twigstan/twigstan

ActiveLibrary[Templating &amp; Views](/categories/templating)

twigstan/twigstan
=================

TwigStan is a static analyzer for Twig templates powered by PHPStan

0.1.4(6mo ago)27412.1k↑74.2%17[22 issues](https://github.com/twigstan/twigstan/issues)[16 PRs](https://github.com/twigstan/twigstan/pulls)MITPHPPHP ^8.3CI passing

Since Nov 23Pushed 6mo ago8 watchersCompare

[ Source](https://github.com/twigstan/twigstan)[ Packagist](https://packagist.org/packages/twigstan/twigstan)[ GitHub Sponsors](https://github.com/twigstan)[ RSS](/packages/twigstan-twigstan/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (7)Dependencies (33)Versions (21)Used By (0)

 [![Logo](https://avatars.githubusercontent.com/u/179125187?s=200&v=4)](https://avatars.githubusercontent.com/u/179125187?s=200&v=4)
 **TwigStan** is a static analyzer for [Twig](https://twig.symfony.com) templates powered by [PHPStan](https://phpstan.org).

 [![Screenshot](https://raw.githubusercontent.com/twigstan/twigstan/main/screenshot.png)](https://raw.githubusercontent.com/twigstan/twigstan/main/screenshot.png)

 [![Latest Stable Version](https://camo.githubusercontent.com/be6e0e092ad29cccbce0b1398354ed03a1f4de83148f715ea95f92f2fc15bd83/68747470733a2f2f706f7365722e707567782e6f72672f747769677374616e2f747769677374616e2f763f7374796c653d666f722d7468652d6261646765)](https://packagist.org/packages/twigstan/twigstan) [![PHP Version Require](https://camo.githubusercontent.com/9c89bae21405f3dcfe5a3b72254c67b70cb1c8d320ab112712aa5ad12d25702d/68747470733a2f2f706f7365722e707567782e6f72672f747769677374616e2f747769677374616e2f726571756972652f7068703f7374796c653d666f722d7468652d6261646765)](https://packagist.org/packages/twigstan/twigstan) [![Total Downloads](https://camo.githubusercontent.com/20ff6be25775f40853298d1f70221183c6740d22e8dc9a27f8aa68047107d308/68747470733a2f2f706f7365722e707567782e6f72672f747769677374616e2f747769677374616e2f646f776e6c6f6164733f7374796c653d666f722d7468652d6261646765)](https://packagist.org/packages/twigstan/twigstan) [![License](https://camo.githubusercontent.com/01d2aad8ee5e5796480e0d2c6d9538ae9560ca6d76ab5172f56705f5e176ea7d/68747470733a2f2f706f7365722e707567782e6f72672f747769677374616e2f747769677374616e2f6c6963656e73653f7374796c653d666f722d7468652d6261646765)](https://packagist.org/packages/twigstan/twigstan)

---

Caution

This is very experimental

Note

This project requires a significant amount of my time and effort to develop. If you find it valuable, please consider [sponsoring me](https://github.com/sponsors/twigstan) to support its continued growth and give it a star! ⭐️ Your support is truly appreciated! 🙏

Introduction
============

[](#introduction)

TwigStan uses Twig to compile templates to PHP code. It then optimizes the compiled PHP code slightly, allowing PHPStan to analyze it better. It then reports any errors back to the original template and line number.

The process consists of the following steps:

Template Context Collecting
---------------------------

[](#template-context-collecting)

We use PHPStan to search the PHP codebase for places where a Twig template is rendered. We collect the context that is passed to the template.

- [ContextFromReturnedArrayWithTemplateAttributeCollector](src/PHPStan/Collector/ContextFromReturnedArrayWithTemplateAttributeCollector.php) and [ContextFromControllerRenderMethodCallCollector](src/PHPStan/Collector/ContextFromControllerRenderMethodCallCollector.php) search for controllers that render a Twig template.
- [ContextFromTwigRenderMethodCallCollector](src/PHPStan/Collector/ContextFromTwigRenderMethodCallCollector.php) search for `Twig\Environment::render` calls.

Compilation
-----------

[](#compilation)

The [TwigCompiler](src/Processing/Compilation/TwigCompiler.php) loads the template and converts it into a Twig AST (Abstract Syntax Tree). The AST is optimized by running [several Twig NodeVisitors](src/Processing/Compilation/TwigVisitor). The AST is then compiled into PHP using Twig's default compiler. The compiled PHP code is loaded and converted into a PHP AST. We inject the previously collected template context as PHPDocs into the template. On the PHP AST, we run [various PHP NodeVisitors](src/Processing/Compilation/PhpVisitor). The goal is no longer to render the template but to analyze it. This means we can remove elements that are not relevant to us. The PHP AST is then dumped back into PHP code and saved to disk as a compilation result.

In the next steps, we will use these PHP files.

Flattening
----------

[](#flattening)

The next step is to flatten the Twig templates. Templates can [extend](https://twig.symfony.com/doc/3.x/tags/extends.html) other templates. The child template can choose to override blocks or not, and the parent template can also extend another template. Variables set in a parent template should be available in the child template.

The [TwigFlattener](src/Processing/Flattening/TwigFlattener.php) processes all the compilation results. It reads the Twig metadata to identify the parent(s) and defined blocks. It takes the logic in the parent template (set variables, etc.) from the `doDisplay`method and copies it into the child template's `doDisplay` block.

The same is done for the block hierarchy. It understands which blocks are overridden. The child template will eventually have all blocks defined.

While flattening, the original filename and line numbers are preserved. This is important because later on, we want to trace errors back to their original location.

After the flattening process is finished, the PHP AST is again dumped to disk as a flattening result.

Block Context Collecting
------------------------

[](#block-context-collecting)

Now that we have a flat template, we can analyze the context for every block.

We use PHPStan to run the [BlockContextCollector](src/PHPStan/Collector/BlockContextCollector.php). This collector gathers the context before rendering every block or parent block call.

Scope Injection
---------------

[](#scope-injection)

Now that we know the context before every block call in the template, we can inject this knowledge as PHPDocs into the flattened template.

Analysis
--------

[](#analysis)

Every template is now flattened and has defined context types.

We ask PHPStan to run the analysis on these files.

While this analysis is running, we also collect `{% include %}` usage in the templates. Since we know the context at this point, we collect it.

If we found included templates, we will repeat the full analysis process for these templates. This means that we go back to the Compilation step.

The [AnalysisResultFromJsonReader](src/PHPStan/Analysis/AnalysisResultFromJsonReader.php) processes the results from PHPStan.

For every error in the flattened PHP code, it tries to find the original Twig file and line number. It filters out a few errors that are false positives. It also collapses errors that are already reported higher in the hierarchy. When an error is reported in a parent template, it should only be reported once, instead of every time it's flattened in a child template.

### Installation

[](#installation)

```
composer require --dev twigstan/twigstan:^0.1
```

Then run TwigStan and it will explain what to do next:

```
vendor/bin/twigstan
```

Usage
-----

[](#usage)

Make sure that you configure your `phpPaths` to point to the PHP codebase that renders the Twig templates.

This will make sure that TwigStan can collect the template context from the PHP codebase.

Use the `dump_type` tag to debug if types are properly resolved.

### Marking templates abstract

[](#marking-templates-abstract)

When you have a template that is not rendered directly, but is used as a parent template, you can mark it as abstract.

The benefit of this is that it will not be analyzed as a standalone template, but only as part of the template hierarchy.

You can mark a template as abstract by adding a comment at the top of the file:

```
{# @twigstan-abstract #}
```

When you try to render an abstract template, TwigStan will report an error.

### Debugging

[](#debugging)

You can dump the type of a variable by using:

```
{% dump_type variableName %}
```

If you want to dump the types for the whole context (everything that's available), you can do:

```
{% dump_type %}
```

### Adding type hints

[](#adding-type-hints)

TwigStan supports [the new `{% types %}` tag](https://twig.symfony.com/doc/3.x/tags/types.html) that was introduced in Twig 3.13.

If your types are not automatially resolved from where they are rendered, you manually type each and every variable like this:

```
{% types { variableName: 'type' } %}
```

Note

Keep in mind that TwigStan tries to resolve all these types automatically by collecting the context from the PHP codebase. Don't blindly start adding `{% types %}` tags. First investigate if TwigStan can resolve the types automatically.

The type can be a valid PHPDoc expression. For example:

```
{% types { name: 'string|null' } %}
```

Next to using multiple `{% types %}` tags, you can also define multiple types in a single line:

```
{% types {
    name: 'string',
    users: 'array',
} %}
```

If you want to indicate that a variable is optional, you can do it as follows:

```
{% types {
    isEnabled?: 'bool',
} %}
```

Note

Starting from Twig version 4 you [no longer have to escape backslashes](https://github.com/twigphp/Twig/pull/4199) in fully qualified class names.

They use TwigStan
-----------------

[](#they-use-twigstan)

- MicroSymfony ([PR](https://github.com/strangebuzz/MicroSymfony/pull/95), [GitHub action output](https://github.com/strangebuzz/MicroSymfony/pull/95/checks))

Credits &amp; Inspiration
-------------------------

[](#credits--inspiration)

- [Ondřej Mirtes](https://github.com/ondrejmirtes) for creating PHPStan and providing guidance to create TwigStan.
- [Tomas Votruba](https://github.com/tomasvotruba) for creating and blogging about [Twig PHPStan Compiler](https://github.com/deprecated-packages/twig-phpstan-compiler); and for creating [Bladestan](https://github.com/bladestan/bladestan).
- [Jan Matošík](https://github.com/HonzaMatosik) for creating a [phpstan-twig proof of concept](https://github.com/driveto/phpstan-twig).
- [Jeroen Versteeg](https://github.com/drjayvee) for creating [TwigQI](https://github.com/alisqi/TwigStan) and discussing ideas.
- [Markus Staab](https://github.com/staabm) for improving PHPStan for specific use cases in TwigStan.

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance66

Regular maintenance activity

Popularity44

Moderate usage in the ecosystem

Community23

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 78.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~58 days

Recently: every ~87 days

Total

7

Last Release

188d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/104180?v=4)[Ruud Kamphuis](/maintainers/ruudk)[@ruudk](https://github.com/ruudk)

---

Top Contributors

[![ruudk](https://avatars.githubusercontent.com/u/104180?v=4)](https://github.com/ruudk "ruudk (223 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (31 commits)")[![VincentLanglet](https://avatars.githubusercontent.com/u/9052536?v=4)](https://github.com/VincentLanglet "VincentLanglet (14 commits)")[![raffaelecarelle](https://avatars.githubusercontent.com/u/15015792?v=4)](https://github.com/raffaelecarelle "raffaelecarelle (6 commits)")[![kkevindev](https://avatars.githubusercontent.com/u/34477826?v=4)](https://github.com/kkevindev "kkevindev (1 commits)")[![AJenbo](https://avatars.githubusercontent.com/u/204594?v=4)](https://github.com/AJenbo "AJenbo (1 commits)")[![staabm](https://avatars.githubusercontent.com/u/120441?v=4)](https://github.com/staabm "staabm (1 commits)")[![stefandoorn](https://avatars.githubusercontent.com/u/4903082?v=4)](https://github.com/stefandoorn "stefandoorn (1 commits)")[![tacman](https://avatars.githubusercontent.com/u/619585?v=4)](https://github.com/tacman "tacman (1 commits)")[![OskarStark](https://avatars.githubusercontent.com/u/995707?v=4)](https://github.com/OskarStark "OskarStark (1 commits)")[![COil](https://avatars.githubusercontent.com/u/177844?v=4)](https://github.com/COil "COil (1 commits)")[![evertharmeling](https://avatars.githubusercontent.com/u/308513?v=4)](https://github.com/evertharmeling "evertharmeling (1 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (1 commits)")

---

Tags

static analysistwig

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/twigstan-twigstan/health.svg)

```
[![Health](https://phpackages.com/badges/twigstan-twigstan/health.svg)](https://phpackages.com/packages/twigstan-twigstan)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.7k509.9M17.0k](/packages/laravel-framework)[vimeo/psalm

A static analysis tool for finding errors in PHP applications

5.8k77.5M6.7k](/packages/vimeo-psalm)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[symfony/maker-bundle

Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.

3.4k111.1M568](/packages/symfony-maker-bundle)[drupal/core

Drupal is an open source content management platform powering millions of websites and applications.

19562.3M1.3k](/packages/drupal-core)[infection/infection

Infection is a Mutation Testing framework for PHP. The mutation adequacy score can be used to measure the effectiveness of a test set in terms of its ability to detect faults.

2.2k26.2M1.8k](/packages/infection-infection)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
