zenstruck/class-metadata is framework-agnostic and ships no Laravel service provider, so "getting started" is really three steps: install it, annotate a class, and build a lookup index.
composer require zenstruck/class-metadatause Zenstruck\ClassMetadata\Attribute\ClassAlias;
#[ClassAlias('stripe')]
final class StripeGateway { /* ... */ }
MetadataFactory over the directory you want scanned, pointing it at a cache file:use Zenstruck\ClassMetadata\MetadataFactory;
$factory = MetadataFactory::create(app_path(), storage_path('app/class-metadata.php'));
$factory->for('stripe'); // lookup by alias
$factory->for(StripeGateway::class); // lookup by class
Look first at the attribute classes (Attribute/ClassAlias, Attribute/ClassMetadata) and MetadataFactory — those are basically the entire public surface. The first real use case in a Laravel app is a human-readable registry of "pluggable" classes (payment gateways, export formats, notification channels, workflow steps) where you store 'stripe' in the DB/config rather than a FQCN and resolve the class later.
Note: confirm exact factory method names/signatures against the README — the package is small and the API has shifted across releases.
register()/boot() and bind it; inject where needed. Rebuilding the index per request is the main thing to avoid.php artisan class-metadata:build) and call it from your deploy script or a Composer post-autoload hook; optionally hook cache:clear.'stripe' in DB columns/config, not the class name. You get refactor freedom — rename or move the class without migrating stored values.app($class) so the concrete implementation gets dependency injection instead of being new'd.match, a metadata-backed class registry scales better when each implementation has different constructor dependencies.app/ (or specific namespaces) rather than the whole project to keep build time and the compiled file small.opcache_invalidate() after write, or better, only write during deploy.#[Attribute(Attribute::TARGET_CLASS)] correctly on custom attributes or PHP rejects them.'stripe' — check whether the factory throws or last-wins, and assert uniqueness in a test.for() throws or returns null for a missing class/alias, and handle it at the boundary (validate incoming config values).app/) in setUp, and keep a test asserting every expected alias resolves to the right class.How can I help you explore Laravel packages today?