waffle-commons / utils
Utils component for Waffle framework.
Requires
- php: ^8.5
- waffle-commons/contracts: 0.1.0-beta2.1
Requires (Dev)
- carthage-software/mago: ^1.29
- cyclonedx/cyclonedx-php-composer: ^6.2
- php-mock/php-mock-phpunit: ^2.15
- phpunit/phpunit: ^12.5
- vimeo/psalm: ^6.16
This package is auto-updated.
Last update: 2026-05-30 19:47:03 UTC
README
Waffle Utils Component
Release:
v0.1.0-beta2ย |ยCHANGELOG.md
Stateless, pure-function helpers shared across the Waffle ecosystem. The package intentionally has no I/O dependencies and no per-process state โ every helper here is safe to use across FrankenPHP worker requests without reset.
๐ Beta-1 change
The former Waffle\Commons\Utils\Trait\ReflectionTrait has been removed and decomposed into three single-responsibility final readonly services (Beta-1 Phase 1 architectural pass โ Single Responsibility over trait-based reuse). Consumers inject the service they need instead of mixing in a trait.
๐ฆ Installation
composer require waffle-commons/utils
๐งฑ Surface
| Class | Role |
|---|---|
Waffle\Commons\Utils\Service\ClassParser |
Tokenizer-based class introspection. className(string $path): string reads a PHP file with token_get_all() (no regex, no eval) and returns the fully qualified class/interface/trait/enum name, or '' if none. Used by routing's RouteDiscoverer / ControllerFinder. |
Waffle\Commons\Utils\Service\AttributeReader |
newAttributeInstance(object $target, string $attribute): object resolves an attribute instance from a target, falling back to a zero-arg instance when the target carries no matching attribute (preserving the former trait's contract). |
Waffle\Commons\Utils\Service\ReflectionInspector |
Object-shape inspection: isFinal(), isInstance(), getProperties(), getMethods(). |
The package grows only when a helper is genuinely shared across more than one component.
๐ ClassParser
Reads a PHP file with token_get_all() (no regex, no eval) and returns the fully qualified class/interface/trait/enum name found inside, or an empty string if none is present.
use Waffle\Commons\Utils\Service\ClassParser; $parser = new ClassParser(); $fqcn = $parser->className('/path/to/UserController.php'); // 'App\Controller\UserController'
The implementation handles:
- Bracketed (
namespace App { โฆ }) and statement (namespace App;) namespace forms. - PHP 8.x
final,readonly,abstractmodifiers in front ofclass/interface/trait/enum. - Anonymous classes โ they are skipped (returns the first non-anonymous declaration).
๐ PHP 8.5 surface
All three services are final readonly class with strict types and explicit return types throughout. They hold no mutable state and are safe to reuse across FrankenPHP worker requests.
๐งญ Architectural boundary (mago guard)
An active dependency perimeter is enforced on every CI run by vendor/bin/mago guard (bundled into composer mago; zero baselines). The rules live in mago.toml under [guard.perimeter] โ a forbidden use statement fails the build, not a reviewer.
Production code under Waffle\Commons\Utils may depend only on:
Waffle\Commons\Utils\**โ itselfWaffle\Commons\Contracts\**โ the shared contracts package, the only Waffle dependency permittedPsr\**โ PSR interfaces@global+Psl\**โ PHP core and the PHP Standard Library
Test code under WaffleTests\Commons\Utils is unrestricted (@all). Structural rules are guarded too: interfaces must be named *Interface, Exception\** classes must end in *Exception, and any Enum\** namespace may hold only enum declarations.
Contract-first, component-agnostic by construction: components compose through waffle-commons/contracts, never directly through one another.
๐งช Testing
docker exec -w /waffle-commons/utils waffle-dev composer tests
๐ License
MIT โ see LICENSE.md.