puli/symfony-bundle
Puli Symfony Bundle integrates Puli resource management with Symfony, enabling logical resource paths, centralized asset discovery, and URL generation across bundles and packages. It simplifies referencing and publishing resources in Symfony applications.
Install the dependency alongside the core library and its Composer bridge:
composer require puli/symfony-bundle puli/puli puli/composer-plugin
Enable the bundle in app/AppKernel.php:
$bundles = [
// ...
new Puli\SymfonyBundle\PuliBundle(),
];
Create a puli.json manifest at the project root that maps your Puli paths and namespaces to real directories:
{
"version": "1.0",
"paths": {
"/app/config": "app/config",
"/app/views": "app/Resources/views"
},
"namespaces": {
"app\\": "src/App"
}
}
Run the discovery/build step so the bundle sees the mappings:
php app/console puli build
First use case — replace file-system paths in framework configuration with Puli paths. Start with a Twig template reference inside a Controller or in config.yml:
# app/config/config.yml
twig:
paths:
"%kernel.root_dir%/../src/App/Resources/views": app_views
becomes, in a template:
{% include '/app/views/layout.html.twig' %}
Look first at the PuliBundle service definitions (puli, puli.filesystem, puli.discovery) — that's where the extension points are. The canonical day-to-day win is referencing resources by Puli path (/app/views/…) or namespace (Acme\Blog:views/…) instead of computing __DIR__-relative paths.
Path aliases in framework config. Use Puli paths directly in routing.yml, twig.yml, translation and validation resources instead of %kernel.root_dir% arithmetic. The bundle swaps Symfony's FileLocator for a Puli-aware locator, so standard @Bundle/... notation keeps working while /app/... and Acme\Blog/... notation become available.
Package discovery via puli.json. Each Composer package that wants to expose resources ships its own puli.json. puli build merges them into a repository the bundle consumes at runtime. This lets you treat any vendor package's assets as a named Puli path.
Twig loader integration. The bundle registers a Twig loader backed by puli.filesystem, so {% include '/app/views/partial.twig' %} resolves through Puli. Use this to share templates across bundles by path rather than by bundle name.
Injecting the filesystem service. In a controller or service:
$fs = $this->get('puli.filesystem');
$file = $fs->get('/app/config/services.yml');
echo $file->getContents();
puli.filesystem is a Puli ResourceRepository; use find(), contains(), get() for uniform access across local files, PHARs, and stream wrappers.
URLs for remote/embedded resources. Map puli:// URLs in puli.json (e.g., a CDN or a phar:// archive) and read them via the same repository API. This is the natural pattern for consuming assets that aren't in the local filesystem.
Console workflow. Wire puli build into your deploy script after composer install; wire puli build --flush (or remove .puli/) whenever puli.json files change in dev. In production, cache the built repository and treat it as read-only.
Typed resources. Declare puli types (e.g., twig, php) in the manifest so the bundle's loaders can filter resources by type when iterating a path — handy for building find('/app/views')->getPaths() style discovery in custom commands.
This package is effectively abandoned. The last release is 2016 and the upstream Puli project was discontinued. It caps out around Symfony 2.8/3.0 and PHP 5.x–7.0. Do not introduce it into a modern Symfony application — treat this assessment as documentation for legacy maintenance only.
puli build is easy to forget. If a puli.json changed but you didn't rebuild, the bundle serves stale mappings and throws "resource not found" for files that clearly exist. Add it to your dev bootstrap and clear Symfony's cache together with .puli/.
Order of Composer operations. puli/composer-plugin performs discovery during composer install/update. If you install a package and skip the plugin (e.g., --no-plugins), its resources are invisible until you run puli discover + puli build manually.
Path vs namespace collisions. /app/views (path) and app\views (namespace) are different key spaces; a typo in the leading slash or backslash silently yields an unrelated or missing resource. Prefer paths (/...) for framework config and namespaces only when you actually want package-style addressing (Vendor\Package:sub/path).
Cache and debugging. The bundle's discovered repository lives under .puli/ (configurable). When debugging resolution, puli ls / puli find from the console show exactly what the locator sees — do that before blaming Symfony's cache warmer.
Extension points. Override the puli service's ResourceRepository (via a compiler pass) if you need a custom repository backend (e.g., a database or S3). Register additional Twig/translation loaders similarly — the bundle exposes services rather than hard-coded loaders.
Framework version pinning. Constrain symfony/symfony to <3.x in composer.json before installing; the bundle's service definitions rely on deprecated ContainerBuilder APIs that break on later versions.
Escape hatch. If the bundle fights you, call puli/puli directly from a hand-registered service and skip the Symfony integration — the library's value (uniform resource access) survives without the bundle, and you avoid the abandoned bundle's container wiring.
How can I help you explore Laravel packages today?