Skip to content
docsv0.8.0

CapabilityMatcher

The ONE criterion for "does this `provides` satisfy this `requires`". It exists because the family had four, and they disagreed. Measured in `docs/library/settlement-q-p17.md`: | comparador | identidad | `oneOf` | `contractVersion` | `\` inicial | |-----------------------------|--------------------|---------|-------------------|-------------| | `GraphResolver` (motor) | `id` | sí | **sí** | no | | `CapabilityGraphChecker` | `id` + `interface` | sí | no | no | | `CapabilityGraphValidator` | **sólo** interface | **no** | no | sí | | `PluginInspection` | `id` + `interface` | **no** | no | no | The inspector was strictly weaker than the engine — not a second opinion, a worse one — and it was the surface offered as a diagnostic tool. Two components that decide whether an app boots cannot have two laws. ## Identity `php:` is an explicit, strippable annotation, NOT something inferred from how a string looks. Deducing the scheme from syntax (a `\` means a class) would turn spelling into meaning, and a capability id like `crm.oauth.google.v1` has no backslash and is not a class. So: - `php:Acme\Thing` and `Acme\Thing` are the SAME identity — that is the migration alias between the canonical form and the legacy bare FQCN it replaces, declared here on purpose rather than guessed per call site; - a leading `\` never distinguishes (`\Acme\Thing` ≡ `Acme\Thing`); - `env:DATABASE_URL` lives in its own namespace and never collides with a PHP identity; - anything else is an OPAQUE id, kept verbatim. No claim is made about what it names. A `provides` record offers TWO identities — its `id` and its `interface` — because {@see \Milpa\ValueObjects\Capability\CapabilityProvision} carries both and the family declares requirements against either. ## Contract compatibility, and the unknown version Identity and compatibility are separate questions, and this class keeps them separate: {@see identitiesOffered()} answers the first, {@see satisfies()} answers both. An absent `contractVersion` means UNKNOWN — never `0.0.0`. The legacy wrapper {@see \Milpa\ValueObjects\Capability\CapabilityProvision::fromInterface()} pins `0.0.0` so the record can be constructed, but `0.0.0` is a real version that a `^1.0` constraint rejects *for being too old*, which is a different statement from *nobody said*. Here an unknown version satisfies `*` and nothing else — same verdict the engine already produced, an honest reason.

CapabilityMatcher::identityMatches()

public function identityMatches(array|string $provision, array|string $requirement): bool

Whether a `provides` entry NAMES the thing a `requires` entry asks for, saying nothing about versions. This is the pre-boot question — "does *some* plugin provide this at all" — and keeping it separate from {@see satisfies()} is deliberate: the layering between an identity check before boot and a range check in the resolver was never the defect. The defect was that each layer used a DIFFERENT identity law, so one accepted what the other rejected. Both layers now ask this one.

Parameters

Parameters of identityMatches()
NameTypeDescription
$provision(string | array<string, mixed>)
$requirement(string | array<string, mixed>)

CapabilityMatcher::satisfies()

public function satisfies(array|string $provision, array|string $requirement): bool

Whether a `provides` entry satisfies a `requires` entry: identities must match AND the declared contract version must satisfy the constraint. This is the resolver's question. A consumer that only needs to know whether the capability exists at all wants {@see identityMatches()}.

Parameters

Parameters of satisfies()
NameTypeDescription
$provision(string | array<string, mixed>)
$requirement(string | array<string, mixed>)

CapabilityMatcher::identitiesOffered()

public function identitiesOffered(array|string $entry): array

The canonical identities a `provides` entry offers: the bare string itself, or a record's `id` and `interface`.

Parameters

Parameters of identitiesOffered()
NameTypeDescription
$entry(string | array<string, mixed>)

CapabilityMatcher::identitiesAccepted()

public function identitiesAccepted(array|string $entry): array

Every canonical identity that satisfies one `requires` entry: its own, plus any `oneOf` alternatives. `oneOf` is the reason the inspector and the validator used to report violations the engine never had: a requirement the engine resolves through an alternative must not fail anywhere else.

Parameters

Parameters of identitiesAccepted()
NameTypeDescription
$entry(string | array<string, mixed>)

CapabilityMatcher::constraintOf()

public function constraintOf(array|string $entry): string

The semver constraint a `requires` entry imposes: its `constraint` field, or the range packed into the identity itself (`some.capability@^1.0` — the third syntax, which host profiles use), or `*`.

Parameters

Parameters of constraintOf()
NameTypeDescription
$entry(string | array<string, mixed>)

CapabilityMatcher::contractVersionOf()

public function contractVersionOf(array|string $entry): ?string

The contract version a `provides` entry declares, or NULL when nobody said. Null is not `0.0.0`: see the class docblock.

Parameters

Parameters of contractVersionOf()
NameTypeDescription
$entry(string | array<string, mixed>)

CapabilityMatcher::contractIsCompatible()

public function contractIsCompatible(?string $contractVersion, string $constraint): bool

Whether a declared contract version satisfies a constraint. An unknown version (null) satisfies only the unconstrained `*`.

Parameters

Parameters of contractIsCompatible()
NameTypeDescription
$contractVersion?string
$constraintstring