tourze/operation-system-enum
A small PHP enum package for representing operating systems consistently across your codebase. Use it to identify, compare, and label OS values like Windows, macOS, Linux, Android, and iOS in Laravel or plain PHP projects, with a simple typed API that avoids loose strings and magic constants.
This package provides a PHP enum (and likely small helper methods) for representing operating systems, so you don't hand-roll string constants like 'windows', 'linux', 'macos' across jobs, migrations, and API payloads.
Minimal steps:
composer require tourze/operation-system-enum. It is almost certainly a pure-PHP enum with no service provider, config file, or migrations to publish — confirm by checking composer.json for extra.laravel before running vendor:publish.src/ — typically one file, e.g. OperationSystem or OperatingSystem). Read the case names and, if backed, the backing values. Those values are the contract you'll persist in the database and expose over the API, so decide early whether you accept them verbatim.use Tourze\OperationSystemEnum\OperationSystem;
final class DeployAgent
{
public function __construct(public readonly OperationSystem $os) {}
public function command(): string
{
return match ($this->os) {
OperationSystem::WINDOWS => 'agent.exe',
OperationSystem::LINUX, OperationSystem::MACOS => './agent',
};
}
}
The 1.0.0 bump from 0.0.2 is most likely an API-stability declaration rather than a feature drop — the release notes only link a diff. Before adopting, skim that changelog for renamed cases or changed backing values, since those are the only breaking changes a package like this can realistically ship.
Eloquent casts. If the enum is backed, cast the column directly:
protected function casts(): array
{
return ['os' => OperationSystem::class];
}
Store the backing value in a string column (nullable if the OS is discovered asynchronously, which it usually is — agents report in after the record exists). Add a OperationSystem::cases()-driven migration comment or a check constraint if you need DB-level safety.
Validation. In Laravel 9+, prefer Rule::enum(OperationSystem::class) over in: lists so adding a case doesn't require touching every FormRequest:
'os' => ['required', Rule::enum(OperationSystem::class)],
Detection at the boundary. The enum should be constructed in exactly one place — a small resolver that maps php_uname('s'), a User-Agent header, or an agent's self-reported string onto a case. Everything downstream receives the enum, never a string. This keeps the mapping (including unmapped/unknown OS handling) in one file and makes the package easy to swap out.
Branching. Use match on the enum rather than if chains; it's exhaustive-checked at the type level once the enum is fully covered. For per-OS lookup tables (install paths, shell flavors, package managers), key a config array by $os->value and resolve lazily — this keeps the logic out of the enum and out of your models.
API surface. Return the enum directly from resources when you want the backing value in JSON (BackedEnum serializes to its value automatically); if your public API must stay decoupled from the package's naming, map explicitly to your own DTO strings instead of relying on implicit serialization.
UPDATE migration rather than trusting casts to handle old rows — an unmapped backing value throws ValueError on hydration, not a null.null vs unknown. Check whether the package has an explicit UNKNOWN/OTHER case. If it doesn't, don't invent one by casting — keep the column nullable and treat null as "not yet detected," so you can distinguish "we don't know yet" from "it's something we don't support."in_array($os, ['linux']) is always false, and == comparisons across enum instances behave unexpectedly if you mix cases from this enum with another OS enum. Use === against cases, or $os->value when crossing a string boundary.Rule::enum() on older Laravel. If you're below 9.x, fall back to Rule::in(array_column(OperationSystem::cases(), 'value')) — build it from cases() so it can't drift.->value and rehydrate with OperationSystem::from() (or tryFrom() at untrusted boundaries). from() throws on unknown input — that's what you want for internal calls, not for user input.^1.0 in composer.json and read the full changelog diff once; the release notes here are just a link, so the actual scope of change is unverified from the notes alone.How can I help you explore Laravel packages today?