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

Metadata Puli File Locator Laravel Package

mi/metadata-puli-file-locator

A Laravel package that locates files and resources through Puli metadata, resolving package paths and repository mappings from a single, consistent API. It simplifies cross-package resource discovery and integrates Puli’s path resolution into your PHP application.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is glue: it implements the FileLocatorInterface from mi/metadata (a fork of jms/metadata) by delegating path resolution to a Puli repository. It does not ship a service provider, config, or facade, so treat it as a library you wire yourself. Before writing any application code, open the repo and read src/ — you need to answer two questions from the source, because the README won't tell you:

  1. Which Puli interface it consumes (ResourceRepository, UriLocator, etc.) — this determines how much of Puli you must bootstrap.
  2. Whether it only resolves paths or also parses/validates MovingImage metadata payloads — the latter significantly increases your maintenance surface.

Minimal first run (standalone PHP, then Laravel):

composer require mi/metadata-puli-file-locator mi/metadata
composer require puli/repository            # runtime resolution
composer require --dev puli/cli puli/discovery puli/manager  # only if using puli.json

A workable "hello world" is: bind one logical path in a Puli repository, construct the package's locator with the repository, and call findFileForClass() for a known metadata class. If that returns a readable path, you're operational; if Puli won't install on your PHP version, stop here (see Gotchas).

First real use case: package-relative metadata discovery — a driver asks the locator "where is the metadata file for App\Foo?", and the answer is a Puli path like /app/src/App/Foo mapped to a physical location via a binding in your Puli repository config, not a hard-coded string.

Implementation Patterns

Wrap it behind an owned interface. Do not let JMS\Metadata\Driver\FileLocator* or Puli types leak into application code. Define a narrow contract and implement it once:

interface MetadataFileResolver {
    public function findFileFor(string $class): ?string;
    public function allKnownClasses(): array;
}

The concrete adapter is the only file that imports Puli or this package. Swapping to Symfony's file-locator or Laravel Storage later becomes a one-file change instead of a codebase-wide refactor.

Binding map, not hard-coded paths. In your Puli repository bootstrap, register bindings per package root:

$repo->add('/app', __DIR__ . '/../src/App');
$repo->add('/vendor', __DIR__ . '/../vendor');

Then metadata paths derive from the logical namespace, and the same code runs in dev, CI, and prod without environment-specific path strings.

Compose with a metadata driver, don't replace it. The locator exists to feed a driver (XML/YAML/annotation reader) that needs to know where class metadata lives. Register it as the driver's locator argument in a service provider:

$this->app->singleton(FileLocatorInterface::class, function ($app) {
    return new PuliFileLocator($app->make(ResourceRepository::class));
});

Keep the driver config separate — this package only handles "where", not "what format".

Namespace-to-path convention. findFileForClass() and getAllClassNames() imply a convention: class FQCN → package-relative path → file. Pin that convention in a single ClassNameToPath helper so the locator, tests, and any custom drivers agree. Without this, getAllClassNames() and findFileForClass() drift and you get silent mismatches.

Testing pattern. Build a tiny in-memory Puli repository in your test bootstrap with fixture files, and assert resolved paths, not resolved file contents. This isolates the integration from the actual Puli discovery cache and lets you run tests without a full puli.json.

Laravel integration. No service provider ships with the package — you write it. Register both the Puli repository (as a singleton, warmed at boot, not lazily per request) and the locator. Do not resolve() this in a controller; resolve once in a provider or a job-scoped service.

Gotchas and Tips

Puli is dead — verify Composer resolution first. Puli's constraint graph is pinned to PHP 5.x/7.x-era releases. On PHP 8.2+ expect --ignore-platform-reqs, a fork, or a polyfill, none of which are supportable long term. Run composer require --dry-run in a branch before writing any code. A clean resolve is a genuine gate, not a formality. If it fails, this package is not adoptable regardless of how clean the API looks.

Watch webmozart/* conflicts. Puli pulls webmozart/assert, webmozart/path-util, and friends; modern Symfony and some Laravel packages also pull these. Because Puli is frozen, version collisions are unresolvable without forking. These manifest as non-deterministic Composer breaks as other packages update — budget for pinning.

Discovery cache staleness is the worst failure mode. Puli's discovery layer builds and caches a repository index. After a deploy or file move, the cache can silently return a wrong (stale) path, which is worse than a hard exception. Warm the cache at deploy time across all workers; never lazy-populate in the request path.

Error semantics differ. Unresolvable paths throw Puli repository exceptions rather than returning null or throwing FileNotFoundException. If your code assumes "file not found" semantics, you'll see uncaught repository exceptions in production logs. Wrap the adapter and translate.

getAllClassNames() vs. findFileForClass() divergence. These are two different views of the same discovery data. If your bindings or conventions change and only one is tested, the other can return stale or partial sets — a common source of "class not found at compile time but found at runtime" bugs. Assert both in tests.

Puli path prefix semantics. Puli paths are absolute from the virtual root (/app/...). A binding without a leading slash silently doesn't match, and the error is either "not found" or a fall-through to another binding. When debugging resolution, dump the repository's bindings first — 90% of "it can't find the file" cases are a missing or overlapping binding, not a locator bug.

Log the resolved physical path once. During roll-out, log the resolved physical path (behind a debug flag) for every lookup. This is the fastest way to catch a misconfigured binding or a discovery cache issue before it becomes an incident.

Fork and vendor before you commit. Given zero stars, zero dependents, and a single-vendor origin, plan on a fork under your org. MIT licensing means this is legally free; the operational cost is that you now own the code. Assign a named owner, or don't adopt it.

Don't let it parse metadata. If you find it does more than path resolution (checks the mi/metadata fork's format version, etc.), treat MovingImage format drift as a maintenance obligation you inherit. Confirm this in src/ before the spike ends.

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.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk