1. Go to this page and download the library: Download splitstack/invariants library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
splitstack / invariants example snippets
use Splitstack\Invariants\Invariant;
// Touchless rule: the closure captures the local value, no subject needed.
$email = User::find($id)->email;
Invariant::make(
rule: fn () => $email !== '' && str_contains($email, '@'),
message: 'Email must be valid',
)->assert(); // throws StrictViolationException on failure (policy: HydrationPolicy::Strict by default)
// Touched rule: reads properties off a subject.
Invariant::make(
rule: fn ($v) => $v >= 0,
message: 'Balance cannot be negative',
touches: ['balance'],
)->assert($account); // reads $account->balance
use Splitstack\Invariants\Concerns\AssertsInvariants;
use Splitstack\Invariants\Contracts\EnforcesInvariants;
use Splitstack\Invariants\Invariant;
class Account implements EnforcesInvariants
{
use AssertsInvariants; // adds assertInvariants(), quarantine(), isQuarantined()
public function __construct(public int $balance = 0) {}
protected function balanceNonNegative(): Invariant
{
return Invariant::make(
rule: fn ($v) => $v >= 0, // $v will resolve to $this->balance
message: 'Balance cannot be negative',
touches: ['balance'],
);
}
}
(new Account(balance: -5))->assertInvariants();
// throws InvariantViolationException: "Invariant [balanceNonNegative] violated: ..."
// $rule closure's `$v` resolves to `$this->balance` which is -5, so the rule fails.
use Splitstack\Invariants\HydrationPolicy;
Invariant::make(
rule: fn ($v) => ctype_upper($v[0] ?? ''),
message: 'Name must be capitalized',
default: ucfirst($model->name),
touches: ['name'],
policy: HydrationPolicy::AutoCorrect,
)->assert($model);
use Splitstack\Invariants\Concerns\AssertsInvariants;
use Splitstack\Invariants\Contracts\EnforcesInvariants;
use Splitstack\Invariants\HydrationPolicy;
use Splitstack\Invariants\Invariant;
class Account implements EnforcesInvariants
{
use AssertsInvariants;
public function __construct(public int $balance = 0) {}
protected function balanceNonNegative(): Invariant
{
return Invariant::make(
rule: fn ($v) => $v >= 0,
message: 'Balance cannot be negative',
touches: ['balance'],
policy: HydrationPolicy::Quarantine,
);
}
}
$account = new Account(balance: -5);
$account->assertInvariants(); // no throw
$account->isQuarantined(); // true
// You decide what the flag means, e.g. drop tainted rows from a batch:
$safe = array_filter($accounts, fn ($a) => ! $a->isQuarantined());
protected function balanceNonNegative(): Invariant
{
return Invariant::make(
rule: fn ($v) => $v >= 0,
message: 'Balance cannot be negative',
default: 0,
touches: ['balance'],
policy: HydrationPolicy::AutoCorrect,
);
}
$account = new Account(balance: -5);
$account->assertInvariants(); // balance is now 0, no exception thrown
use Splitstack\Invariants\StateGuard;
class Account
{
protected function balanceNonNegative(): StateGuard
{
return StateGuard::make(
rule: fn () => $this->balance >= 0,
message: 'Balance cannot be negative',
);
}
public function substractFunds(int $amount): void
{
$this->balance -= $amount;
// assert-throw ...
$this->balanceNonNegative()->assert();
//... or check without throwing
if ($this->balanceNonNegative()->checks()) {
// continue with the operation
}
}
}
// Can use $touches
$stateGuard = StateGuard::make(
rule: fn ($v) => $v >= 0,
message: 'Balance cannot be negative',
touches: ['balance'],
);
// Can use hydration policies like AutoCorrect or Quarantine
function nonNegative(): StateGuard
{
return StateGuard::make(
rule: fn ($v) => $v >= 0,
message: 'Balance cannot be negative',
touches: ['balance'],
policy: HydrationPolicy::AutoCorrect,
default: 0,
);
}
$this->assert($entity); // will correct instead of throwing
// Can use quarantine to isolate invalid state without throwing
$stateGuard = StateGuard::make(
rule: fn ($v) => $v >= 0,
message: 'Balance cannot be negative',
touches: ['balance'],
policy: HydrationPolicy::Quarantine,
);
$stateGuard->assert($subject); // will quarantine instead of throwing
use Splitstack\Invariants\Attributes\DispatchesEvents;
use Splitstack\Invariants\Attributes\InvariantEvent;
#[DispatchesEvents(App\EventBus::class, via: 'dispatch')] // via defaults to 'dispatch'
class ApprovalRequestEntity implements EnforcesInvariants
{
use AssertsInvariants;
#[InvariantEvent(['id', 'status', 'applicantUserId'])]
protected function statusRequiresInProgress(): Invariant
{
return Invariant::make(
rule: fn ($v) => $this->status === ApprovalRequestStatus::IN_PROGRESS,
message: 'Not in progress',
touches: ['status'],
policy: HydrationPolicy::Quarantine,
);
}
}
protected function statusRequiresInProgress(): Invariant
{
return Invariant::make(
rule: fn ($v) => $v === ApprovalRequestStatus::IN_PROGRESS,
message: 'Not in progress',
touches: ['status'],
policy: HydrationPolicy::Quarantine,
event: new App\Events\ApprovalStalled(id: $this->id, status: $this->status),
);
}
use Splitstack\Invariants\Support\EventDispatcher;
// e.g. in a Laravel service provider
EventDispatcher::resolveUsing(fn (string $class) => app($class));
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.