Problem/Motivation
`FlysystemStreamWrapper` does not implement `getDirectoryPath()`, which causes a fatal error when the `assets://` stream wrapper is overridden with a Flysystem scheme.
Steps to reproduce
1. Install Flysystem 3.00-beta1
2. Configure any Flysystem scheme as the `assets://` override in `settings.php`:
<?php
$settings['flysystem']['assets'] = [
'driver' => 'suzuki_azure_blob', // or any custom driver
'public_url_base' => 'https://example.com/sites/default/files',
'config' => [ ... ],
];
?> `
3. Bootstrap Drupal (any page request or `drush cr`)
Expected result
The `assets://` stream wrapper resolves correctly via Flysystem, and CSS/JS aggregates are written to and served from the configured adapter.
Actual Result
Fatal error at bootstrap:
PHP Fatal error: Uncaught Error: Call to undefined method
Drupal\flysystem\StreamWrapper\FlysystemStreamWrapper::getDirectoryPath()
in /web/core/modules/system/src/Routing/AssetRoutes.php:45
Root cause
`core/modules/system/src/Routing/AssetRoutes.php` (line 45) calls `getDirectoryPath()` on the `assets://` stream wrapper to build the route path for asset serving. This method is defined on `Drupal\Core\StreamWrapper\LocalStream` but is **not part of `StreamWrapperInterface`**.
`FlysystemStreamWrapper` only implements `StreamWrapperInterface` — not `LocalStream` — so the method is missing.
Relevant core code:
// AssetRoutes.php:45
$directory_path = $wrapper->getDirectoryPath();
`getDirectoryPath()` is documented as returning the base directory path used to serve files publicly (e.g. `sites/default/files`). For a Flysystem scheme, this maps naturally to the `public_url_base` path component.
Proposed resolution
Add `getDirectoryPath()` to `FlysystemStreamWrapper`:
<?php
/**
* Returns the base public directory path for this scheme.
*
* Required by core's AssetRoutes (and potentially other subsystems)
* which call getDirectoryPath() on any stream wrapper registered for
* the assets:// or public:// schemes.
*
* For a Flysystem scheme, this is derived from the public_url_base
* configuration — the path component after the host.
*
* @return string
* The public directory path, e.g. 'sites/default/files'.
*/
public function getDirectoryPath(): string {
try {
$scheme = $this->getScheme();
$definition = $this->getFactory()->getDefinition($scheme);
if ($definition->publicUrlBase === NULL) {
return '';
}
// Extract path component from public_url_base.
// e.g. 'https://example.com/sites/default/files' → 'sites/default/files'
$path = parse_url($definition->publicUrlBase, PHP_URL_PATH);
return ltrim((string) $path, '/');
}
catch (\Throwable) {
return '';
}
}
?>Impact
Without this fix it is impossible to use Flysystem for the `assets://` scheme, which is the correct mechanism for serving Drupal CSS/JS aggregates from shared/remote storage in multi-instance deployments (e.g. VMSS, Kubernetes, auto-scaling setups).
Environment
- Drupal 11.x
- Flysystem 3.00-beta1
- PHP 8.4
- Driver: custom Azure Blob Storage adapter (user-assigned Managed Identity)
Work around
Do not override `assets://` with a Flysystem scheme. Keep CSS/JS aggregates on local disk and use a separate Flysystem scheme only for user uploads and media files.
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | fix-assets-scheme-overwrite-3611227.patch | 1.1 KB | j.b |
Issue fork flysystem-3611227
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
j.b commentedComment #3
j.b commentedComment #4
j.b commentedComment #5
lisa.rae commentedComment #7
lisa.rae commentedPatch has been converted to a merge request.
Patch and test are in the merge request, the drupal pipeline returned failure with test only, and passing with patch and test.
Please test and if successful, mark RTBC and I’ll merge.
FYI - I tested against the 3.0.x-dev branch.
Comment #8
lisa.rae commentedComment #10
lisa.rae commented