Shopware 6: Don’t make assumptions about paths and were your plugin lives
Fabian Blechschmidt
We have a template which generates documents for us. Unfortunately it broke with the following error:
php.CRITICAL: Uncaught Error: Failed opening required '/var/www/customer-deploy/shared/custom/plugins/NameOfThePlugin/src/Twig/../../../../../vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_2d_include.php' (include_path='.:/usr/share/php') {"exception":"[object] (Error(code: 0): Failed opening required '/var/www/customer-deploy/shared/custom/plugins/NameOfThePlugin/src/Twig/../../../../../vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_2d_include.php' (include_path='.:/usr/share/php') at /var/www/customer-deploy/shared/custom/plugins/NameOfThePlugin/src/Twig/TwigExtension.php:95)"} []
In short, the plugin tries to find vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_2d_include.php. But it is looking in shared/vendor and not in /vendor.
Why is this?
Because we use deployer to throw code on the server and the custom/plugins directory is shared between the releases. In other words, our server look like this:
.
├── current -> releases/20260129012927
├── htdocs -> /var/www/share/example.org/current/public
├── releases
│ ├── 20251023075935
│ └── 20260129012927
└── shared
├── custom
└── plugins
└── var
Due to the fact that the plugin file is including the tcpdf example this way:
require_once __DIR__ . '/../../../../../vendor/tecnickcom/tcpdf/examples/barcodes/tcpdf_barcodes_2d_include.php';
It breaks, because the relative path is wrong. I assume, even if we have installed it via composer, it would still break.
There are at least three ways to install a plugin:
- Put it in
custom/plugins (install via ZIP upload or from the store)
- Put it in
custom/static-plugins
- Install it via composer and it ends up in
vendor
So even if we don’t use deployer, this relative link only works for 1 + 2.
TL;DR
Don’t do relative links into vendor because it doesn’t work in all circumstances.
So, How Then?
Get the root directory from Symfony and pass it to the service, like so.
<?php declare(strict_types=1);
namespace Swag\MyPlugin\Service;
final class VendorDirLocator
{
public function __construct(private readonly string $projectDir) {}
public function getProjectDir(): string
{
return $this->projectDir;
}
public function getVendorDir(): string
{
$vendor = $this->projectDir . '/vendor';
$real = realpath($vendor);
if ($real === false) {
throw new \RuntimeException('Project vendor directory not found at: ' . $vendor);
}
return $real;
}
}
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="Swag\MyPlugin\Service\VendorDirLocator">
<argument>%kernel.project_dir%</argument>
</service>
</services>
</container>
Other articles from this category