Search by

super-kernel / annotator

wheakerd

Restores and queries build-generated, package-aware PHP attribute records for SuperKernel.

Package info

github.com/super-kernel/annotator

pkg:composer/super-kernel/annotator

Statistics

Installs: 201

Dependents: 8

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0-alpha.2 2026-11-10 08:54 UTC

README

English | 中文文档

SuperKernel Annotator

super-kernel/annotator restores the native PHP Attributes that skernel build collects from a project and its installed packages, and answers queries about them without scanning sources or loading the annotated classes.

Installation

composer require super-kernel/annotator

The package has no requirement beyond PHP. The records it restores exist only after skernel build has run in the project. super-kernel/di restores them when it creates the root container; without a container, the providers of super-kernel/metadata supply what restoration needs.

What it provides

  • An immutable collector that finds the classes and methods carrying an Attribute, by its exact class or by a parent class or interface.
  • The Attributes declared on one class, method, or property, as instances that were constructed and validated at build time.
  • The skipped-class diagnostics of the build: the classes whose Attributes skernel build could not collect.
  • Restoration of the records generated by skernel build, rejecting records that do not match the installed packages.
  • A publisher that build tools use to write the records.

It does not:

  • scan class files, construct Attributes from source, or decide which classes are collected; skernel build does;
  • read DocBlock annotations; it covers native PHP Attributes only;
  • load or reflect the annotated classes when it answers a query;
  • print the skipped-class diagnostics when an application starts.

Quick start

In an application created from super-kernel/skeleton, declare an Attribute for routes. Save it as src/Routing/Route.php:

<?php

declare(strict_types=1);

namespace Src\Routing;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final readonly class Route
{
    public function __construct(public string $method, public string $path)
    {
    }
}

Use it on a controller. Save the controller as src/Http/PageController.php:

<?php

declare(strict_types=1);

namespace Src\Http;

use Src\Routing\Route;

final class PageController
{
    #[Route('GET', '/')]
    public function home(): string
    {
        return 'Home';
    }

    #[Route('GET', '/about')]
    #[Route('GET', '/about-us')]
    public function about(): string
    {
        return 'About';
    }
}

Ask the collector for every method with a route. Save the script as bin/routes.php:

<?php

declare(strict_types=1);

use Src\Routing\Route;
use SuperKernel\Context\ApplicationContext;
use SuperKernel\Contract\AnnotationCollectorInterface;

require dirname(__DIR__) . '/vendor/autoload.php';

$collector = ApplicationContext::getContainer()->get(AnnotationCollectorInterface::class);

foreach ($collector->getMethodsWithAttribute(Route::class) as $class => $methods) {
    foreach ($methods as $method => $routes) {
        foreach ($routes as $route) {
            echo $route->attribute()->method, ' ', $route->attribute()->path, ' -> ', $class, '::', $method, PHP_EOL;
        }
    }
}

Build the project and run the script:

skernel build
php bin/routes.php

It prints:

GET /about -> Src\Http\PageController::about
GET /about-us -> Src\Http\PageController::about
GET / -> Src\Http\PageController::home

Public surface

Name Kind Purpose
SuperKernel\Contract\AnnotationCollectorInterface Interface Queries the collected Attributes; a SuperKernel container supplies it.
SuperKernel\Contract\AttributeMetadataInterface Interface One collected Attribute: attribute() returns the instance, types() its class and captured parent types.
SuperKernel\Contract\PackageAnnotationsInterface Interface The class, method, and property Attributes of one package.
SuperKernel\Contract\ScanDiagnosticsInterface Interface Returns the skipped-class diagnostics of a collector or a package.
SuperKernel\Annotator\AnnotationCollector Class The collector; new AnnotationCollector($packages) creates one from package records, which is useful in tests.
SuperKernel\Annotator\Cache\AnnotationCache Class read() restores the collector from the records of skernel build.
SuperKernel\Annotator\Cache\AnnotationPublisher Class publish() writes the records; build tools call it.
SuperKernel\Annotator\Model\AttributeMetadata Class The collected Attribute that a build tool creates and publishes.
SuperKernel\Annotator\Model\PackageAnnotations Class The record of one package that a build tool creates and publishes.

Behavior to know

  • Run skernel build after composer install or composer update and after adding or changing an Attribute or an annotated class, or start with skernel run, which builds first. A changed annotated class is not detected; queries answer from the last build.
  • read() raises SuperKernel\Annotator\Exception\AnnotationCacheException when a record is missing or invalid or the records belong to other package versions, for example The annotation package-reference stub does not match current Composer metadata. A container reports it as SuperKernel\Metadata\Exception\BuildRequiredException with the message The generated annotation records cannot be restored. Run `skernel build` to generate the project metadata.
  • A query returns only Attributes declared on the queried class or member; inherited methods and properties are listed under the class that declares them.
  • Methods of a class are listed in the order of their names; Attributes on one class or member keep their declaration order.
  • A class that skernel build could not collect is missing from every query; it appears in the skipped-class diagnostics.

Documentation

The developer documentation describes every query with complete examples, the diagnostics, restoration without a container, publishing records, and the generated files: SuperKernel Annotator.

License

Released under the MIT License. See LICENSE.