vimex/storage-utils-bundle
A Symfony bundle offering utility helpers for working with storage in PHP applications. Small and easy to integrate, it bundles common storage-related tasks so you can wire them into your Symfony project with less boilerplate. Best suited to projects already using Symfony that need lightweight stora...
First, the framing that matters: vimex/storage-utils-bundle is a Symfony bundle, not a Laravel package. It ships a Bundle subclass, a DI extension, and YAML/XML service definitions — none of which Laravel's container understands. There is no service provider, no config publish tag, no facade.
Second, the reality check. Zero stars, zero dependents, last release 2018-06-20. That release predates Laravel 6, PHP 7.4, and Symfony 5. Nothing in the ecosystem exercises it, so there is no community evidence it works against any modern Symfony component version Laravel pulls in. Treat this as reference source code to read and copy, not as a dependency to composer require into a production app.
If you're evaluating it anyway, this is the order of operations:
Vendor it without autoloading it into your container.
composer require vimex/storage-utils-bundle --no-scripts
Expect constraint friction: the 2018 release almost certainly pins symfony/*: ^3.4|^4.0 and php: ^7.1, which will conflict with a current Laravel. Use --ignore-platform-reqs only to read it, or better, clone the repo to a scratch directory.
Read the entry points in this order. This is where the actual behavior lives:
VimexStorageUtilsBundle.php — usually one or two lines, tells you nothing except the namespace root.DependencyInjection/VimexStorageUtilsExtension.php — this is the real map. It loads the service config and, critically, shows you the alias mappings that tell you what the public service IDs are.Resources/config/services.yml (or .xml) — constructor arguments and tags. Anything tagged as a compiler pass participant or a tagged collector will not run under Laravel, so cross those out now.DependencyInjection/Configuration.php — the config tree. This is the schema you must hand-translate into a Laravel config file.First use case: prove the utility class works standalone. Ignore the bundle plumbing entirely. Instantiate the core storage utility directly in php artisan tinker with arguments you construct by hand. If it needs the Symfony filesystem, request stack, or a KernelInterface, that's your signal the class is not portable and only the leaf helpers are worth keeping.
If it survives step 3, copy it. Move the handful of classes you actually use into app/Support/Storage/, rewrite the namespace, and delete the composer dependency. Given the adoption numbers, this is the only defensible path for anything long-lived.
Adapter-first wrapping. Whatever the bundle exposes, do not let its types leak into your domain code. Define your own interface and put the bundle (or copied classes) behind it:
// app/Contracts/StorageUtility.php
interface StorageUtility
{
public function put(string $path, $contents): string;
public function url(string $path): string;
}
// app/Support/Storage/LegacyStorageUtilityAdapter.php
final class LegacyStorageUtilityAdapter implements StorageUtility
{
public function __construct(private \Vimex\StorageUtilsBundle\...\Util $inner) {}
public function put(string $path, $contents): string
{
return $this->inner->store($path, $contents);
}
public function url(string $path): string
{
return $this->inner->publicUrl($path);
}
}
Binding it in a service provider costs one line, and the day you replace it with league/flysystem or Laravel's own Storage facade, the swap is contained.
Manual config translation. Symfony's Configuration.php tree maps almost 1:1 onto a Laravel config array — rootNode → top-level keys, scalarNode → scalar, arrayNode with prototype → list. Publish it yourself:
// config/storage_utils.php
return [
'adapter' => env('STORAGE_UTILS_ADAPTER', 'local'),
'root' => env('STORAGE_UTILS_ROOT', storage_path('app/utils')),
];
Then resolve constructor arguments in your provider rather than hoping for autowiring — Laravel will autowire by type hint, but the bundle's services are typically defined with scalar arguments, which Laravel cannot infer.
Constructor-injection over container lookups. If you bridge the bundle, bind it as a singleton and inject the interface. Resist app(SomeBundleClass::class) sprinkled through controllers; with an unmaintained package you want every touchpoint greppable in one provider.
Characterization tests before you trust it. Write tests that pin current behavior for the operations you depend on — path normalization, filename collision handling, mime detection. Since the upstream has no test signal worth relying on, your suite becomes the contract. This also makes the eventual migration mechanical rather than archaeological.
Integration tip: if the utility touches the filesystem, point it at a dedicated disk root and assert on it with Storage::fake()-style isolation where possible. Old Symfony-oriented code frequently calls realpath(), is_writable(), and mkdir() on absolute paths that are hostile to in-memory or S3 fakes — verify before you build a test suite around it.
The DI compiler passes are dead code under Laravel. Symfony bundles routinely register tagged services, CompilerPassInterface implementations, and parameter resolvers. None of that executes. If the bundle's functionality depends on a compiler pass — say, collecting adapters into an aggregate — you will silently get an empty collection rather than a loud failure. Grep the bundle for CompilerPass and addCompilerPass before you assume anything works.
Autowiring mismatches. Laravel resolves by type-hint; Symfony service definitions resolve by service ID and explicit arguments. A class with a string $rootPath constructor argument will throw Unresolvable dependency resolving [Parameter #0 [ <required> string $rootPath ]]. Always define arguments explicitly.
Abandonment specifics to check:
each(), and often relies on mcrypt — all fatal or deprecated on PHP 8.2+.unserialize(), archive extraction, or user-supplied paths, that is an unaudited attack surface.composer audit will report nothing, because nothing is filed. Absence of advisories is not absence of bugs.Debugging under Symfony (if you're testing it in its native habitat first). Use bin/console debug:container --show-private | grep -i vimex to find real service IDs, and debug:config vimex_storage_utils to see the resolved tree. This is the fastest way to learn the actual public API before translating it.
Watch for kernel/request coupling. Anything taking RequestStack, KernelInterface, or %kernel.root_dir% as an argument is bound to Symfony's request lifecycle and directory conventions. Those parameters have no Laravel equivalent; you must substitute storage_path() / base_path() and, for request-scoped services, decide between scoped bindings and plain singletons.
Extension points, ranked by usefulness: (1) the leaf utility classes — usually portable; (2) config values — translatable; (3) tagged service collections — needs manual reimplementation as a Laravel tagged-binding pattern; (4) event subscribers and compiler passes — effectively non-portable, rewrite from scratch.
The honest recommendation. For a package with 0 stars, 0 dependents, and a seven-year-old last release, the leverage is in reading the implementation to understand the problem, not in depending on it. Laravel's Storage facade plus league/flysystem covers the same ground with active maintenance, a real security process, and zero bridge code. Copy the one or two algorithms that solve a genuine edge case, credit the source in a comment, and move on.
How can I help you explore Laravel packages today?