Identifier
Foundation Identifier provides injectable contracts for string identifiers and a default ULID implementation. Generated ULIDs are canonical 26-character uppercase strings that combine a millisecond timestamp with secure randomness.
ULIDs work well for identifiers that must be portable across databases or systems while remaining roughly sortable by creation time.
Installation
Section titled “Installation”Install the split package:
Prepare the application
Section titled “Prepare the application”Identifier services are registered through the shared application provider list:
Configuration
Section titled “Configuration”Register the identifier provider
Section titled “Register the identifier provider”In src/App.php, add the Foundation provider before features that generate or validate ULIDs:
The provider registers the ULID generator and validator as shared services. The supplied generator uses the system clock and secure randomness.
Generate a ULID
Section titled “Generate a ULID”Create src/Job/Job_Creator.php and inject UlidGenerator. Registering IdentifierProvider supplies the implementation:
A generated value looks like 01ARYZ6S410000000000000000.
Validate external ULIDs
Section titled “Validate external ULIDs”In src/Job/Job_Request.php, use UlidValidator at input boundaries before passing an external identifier into application behavior:
Validation accepts canonical uppercase ULIDs only. Lowercase values, invalid lengths, ambiguous characters such as I, L, O, and U, and timestamps outside the ULID range are rejected.
Understand ordering and exposure
Section titled “Understand ordering and exposure”The first ten ULID characters encode creation time in milliseconds, so sorting canonical ULID strings groups identifiers by generation time.
Customization
Section titled “Customization”Choose the contract your feature needs
Section titled “Choose the contract your feature needs”Use the narrowest contract that describes the feature:
| Contract | Use when |
|---|---|
Ulid\Contracts\UlidGenerator |
The stored or exchanged identifier must be a ULID |
Contracts\IdentifierGenerator |
The feature needs a unique string but should not choose its format |
For format-independent features, bind IdentifierGenerator to the application’s chosen strategy. IdentifierProvider supplies the ULID-specific binding used in the example above.
If the application chooses ULIDs as its default, create src/Identifier/Provider.php:
Register both providers in src/App.php, in that order:
The callback aliases the broad contract to the configured ULID singleton, so both contracts resolve the same generator.
A service that accepts the application’s chosen format can now import Contracts\IdentifierGenerator and inject it in place of Ulid\Contracts\UlidGenerator. Its call to $this->generator->generate() stays the same. Services whose storage or external API requires ULIDs should keep the ULID-specific contract.
Testing
Section titled “Testing”Replace ULID generation
Section titled “Replace ULID generation”For the Job_Creator above, use a fixture that returns one known ULID. Create tests/Support/Fixtures/Identifier/Fixed_Ulid_Generator.php:
After registering IdentifierProvider in the test container, bind the fixture before resolving the service under test:
Use UlidValidator when a test only needs to confirm that production generation returns a valid ULID. Avoid asserting an exact value from the system clock and secure entropy.
For a feature using the optional IdentifierGenerator strategy, implement and bind that contract in its fixture instead. Match the contract injected by the service under test.