Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Twig Extensions Laravel Package

stinger-soft/twig-extensions

Twig Extensions for PHP adds handy filters, functions and tags to the Twig templating engine, making it easier to write expressive templates in Laravel and other PHP projects. A lightweight package that plugs into existing Twig setups with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install the package

composer require stinger-soft/twig-extensions

The package is a plain Twig extension library (Apache-2.0), not a Laravel service provider — it ships filters/functions/tests as a Twig ExtensionInterface implementation. There is nothing to publish and no migrations.

2. Confirm you actually have Twig in Laravel

Twig is not a first-party Laravel view engine. In practice you are using rcrowe/twigbridge, which wires a Twig\Environment into the container:

composer require rcrowe/twigbridge
php artisan vendor:publish --provider="TwigBridge\ServiceProvider"

3. Register the extension

Open config/twigbridge.php and add the extension class to the extensions array:

'extensions' => [
    // ...
    StingerSoft\TwigExtensions\Extension::class, // verify FQCN in composer.json "autoload"
],

4. Where to look first

  • vendor/stinger-soft/twig-extensions/composer.json — real namespace, PHP and twig/twig version constraints (this is the compatibility gate).
  • vendor/stinger-soft/twig-extensions/src/ — the class implementing getFilters(), getFunctions(), getTests(). That file is the documentation; with 0 stars and 0 dependents there is no tutorial ecosystem to lean on.
  • The README for the canonical names — do not assume them from the package title.

5. First use case

The realistic first win is a Blade-to-Twig migration where you need a handful of string/array helpers without writing your own extension class:

{{ article.title|some_string_filter }}
{% if value is some_test %}...{% endif %}

Render a single throwaway template to prove registration before touching production views:

Twig::renderString('{{ "x"|your_filter }}'); // via TwigBridge facade

Implementation Patterns

Register once, globally, then get out of the way. Twig extensions are cheap; the compiled-template cache is what matters. Put the class in config/twigbridge.php under extensions and stop thinking about it. Per-controller registration is possible by resolving Twig\Environment and calling addExtension() early in the request, but it fragments behaviour and defeats template caching across requests.

Use it standalone when you are not in Blade land. The extension is framework-agnostic, which is its main selling point:

$twig = new \Twig\Environment($loader);
$twig->addExtension(new StingerSoft\TwigExtensions\Extension());

This is the pattern for mail renderers, PDF/queue workers, or a artisan command that renders templates outside the HTTP stack.

Compose, don't duplicate. If the package gives you generic string/array filters, build your domain filters on top in an app-owned extension rather than copying logic into templates:

class AppTwigExtension extends \Twig\Extension\AbstractExtension
{
    public function getFilters(): array
    {
        return [
            new \Twig\TwigFilter('money', fn ($v) => number_format($v / 100, 2)),
        ];
    }
}

Register both in twigbridge.php; order matters only for name collisions (last registered wins).

Testing templates. Unit-test rendered output with the extension attached, not the extension internals:

$twig = new \Twig\Environment(new \Twig\ArrayLoader([
    't' => '{{ value|your_filter }}',
]));
$twig->addExtension(new StingerSoft\TwigExtensions\Extension());
$this->assertSame('expected', $twig->render('t', ['value' => 'input']));

This doubles as your compatibility harness — it will fail loudly on the day you upgrade Twig.

Integration tips

  • Clear storage/framework/views (or php artisan view:clear) after changing extension registration; Twig compiles to PHP and will happily serve stale bytecode.
  • In queue workers, restart queue:work after config changes so the environment rebuilds.
  • If you need the same helpers in Blade components during a partial migration, wrap the Twig extension's filter callables in a small helper facade so both engines share one implementation.

Gotchas and Tips

Vendor risk is the headline. Zero stars, zero dependents, and a generic name mean no community backstop. Before adopting: read every filter body, check the twig/twig constraint in composer.json, and pin an exact version ("stinger-soft/twig-extensions": "1.0.3") or vendor the source into app/Support/Twig/. If it is abandoned mid-project, forking is cheap — it is a single extension class plus helpers.

Twig 2 → 3 is the likely breakage point. getFilters()/getFunctions() returning bare callables and Twig_SimpleFilter were removed in Twig 3 in favour of TwigFilter/TwigFunction objects. A low-traffic package may predate that. If it does not install cleanly, add twig/twig to your root composer.json with the version the package supports and check nothing else forces Twig 3.

Escaping and is_safe. Any filter that returns markup must be declared safe in the extension, otherwise {{ html|your_filter }} is escaped and you get <div>. Conversely, |raw in the template is a code smell that usually means the extension forgot ['is_safe' => ['html']]. Audit this deliberately — it is the most common security bug in third-party Twig filters.

Null and type coercion. These helper libraries frequently call mb_strtolower/array_* on the raw value. Twig resolves missing variables to null, so {{ undefined_var|your_filter }} can throw. Guard with {% if x is defined and x is not null %} or wrap the filter in your own null-safe layer.

Name collisions. Generic filter names (slug, truncate, markdown) already exist in Twig core, TwigBridge's own extensions, or twig/extra-bundle. Registration silently overrides. Enumerate $twig->getFilters() once and diff it against what you expect before shipping.

Debugging inside the extension. Twig swallows little. Add {{ dump() }} only with the debug extension enabled (twigbridge has a debug config flag) and APP_DEBUG=true; otherwise use dd() inside the filter callable, which still works because filters are plain PHP invoked at render time.

Extension points are shallow by design. There is no config file and no service provider, so "configuration" means subclassing the extension and overriding getFilters()/getFunctions() to remove or replace entries, or registering a competing filter after it. Treat the class as a sealed unit and prefer your own extension for anything project-specific — that keeps the upgrade path to a fork or a replacement trivial.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect