-
Notifications
You must be signed in to change notification settings - Fork 0
Permissions
InitPHP\Auth\Permission is a small, dependency-free set of named
permissions. It does case-insensitive membership checks, deduplicates
on insertion, and exposes magic accessors that read well in templates.
The class is intentionally not coupled to any adapter — you can stash
the underlying permission list in a SessionAdapter, a CookieAdapter,
your own database, or anywhere else.
use InitPHP\Auth\Permission;
new Permission(); // empty
new Permission(['Editor', 'POST_LIST', 'post_edit']);The constructor:
- Runs each entry through the normalization pipeline.
- Silently skips non-string values.
- Drops duplicates after normalization.
$perm = new Permission(['Editor', 'editor', 'EDITOR']);
$perm->getPermissions(); // ['editor']
$perm = new Permission(['admin', 42, null, ['nested'], 'editor']);
$perm->getPermissions(); // ['admin', 'editor'] — non-strings droppedv1 footgun, fixed: in v1 the constructor stored permissions verbatim while
is()/push()/remove()lower-cased the needle. A mixed-case permission supplied at construction time could never match. v2 normalizes in the constructor the same waypush()does. See the Migration Guide.
$perm = new Permission(['admin', 'editor']);
$perm->is('admin'); // true
$perm->is('viewer'); // false
$perm->is('viewer', 'editor'); // true — any match wins
$perm->is('viewer', 'guest'); // falseis() is any-match, not all-match. To require every name, call it
per name and combine:
$perm->is('admin') && $perm->is('editor');Both methods report the number of names actually changed:
$perm = new Permission(['admin']);
$perm->push('editor', 'viewer'); // returns 2
$perm->push('admin', 'EDITOR'); // returns 0 — both already present (case-insensitive)
$perm->remove('viewer', 'guest'); // returns 1 — viewer removed, guest absent
$perm->remove('viewer'); // returns 0 — already goneremove() reindexes the internal list after deletion, so the
list<string> invariant holds:
$perm = new Permission(['admin', 'editor', 'viewer']);
$perm->remove('editor');
$perm->getPermissions(); // ['admin', 'viewer'] — keys 0,1 not 0,2v1 bug, fixed: v1 left a hole in the array after
unset(), which broke JSON encoding ofgetPermission()output.
$perm = new Permission(['Editor', 'viewer']);
$perm->getPermissions(); // ['editor', 'viewer']The returned list is already normalized (lower-cased, trimmed). It
is also a list<string> (0-indexed, sequential keys), so it round-trips
cleanly through json_encode() / json_decode().
The v1 alias getPermission() survives as a deprecated shim and will
be removed in v3:
$perm->getPermission(); // works, but @deprecated
$perm->getPermissions(); // preferred| Expression | Equivalent to |
|---|---|
$perm->is_admin() |
$perm->is('admin') |
isset($perm->admin) |
$perm->is('admin') |
isset($perm->is_admin) |
$perm->is('admin') (the is_ prefix is stripped) |
unset($perm->is_admin) |
$perm->remove('admin') |
These are convenient inside templates (Twig, Blade, plain PHP). In code that runs through an IDE or PHPStan, prefer the explicit methods so auto-completion and static analysis keep working.
A call that does not start with is_ raises BadMethodCallException:
$perm->doSomething();
// BadMethodCallException: Method InitPHP\Auth\Permission::doSomething() does not exist.The same happens for an empty-prefix call:
$perm->is_(); // false (the suffix is empty, no permission to check)Every name — supplied at construction time, to is(), or to push() /
remove() — passes through the same internal pipeline:
strtolower()trim()
So ' Admin ', 'admin', and 'ADMIN' all refer to the same
permission. The pipeline runs once on the way in and never again — the
internal list always holds the normalized form.
__sleep() keeps only the permission list, so it is safe to drop a
Permission straight into $_SESSION or any other PHP-serialize()
sink:
$_SESSION['perm'] = serialize(new Permission(['Editor', 'viewer']));
// later, in another request
$perm = unserialize($_SESSION['perm']);
$perm->is('editor'); // trueThe serialized blob only contains the
permissionsproperty. Any subclass state you add must be declared on__sleep()overrides; the base class will not pick it up automatically.
-
Forgetting that case-folding happens everywhere. You do not need
to lower-case names yourself before passing them to
is(),push(), or the constructor. v2 takes care of it. -
Reading magic accessors in static analysis. PHPStan and Psalm
cannot see the
is_*methods. Use$perm->is('admin')instead in code that needs to type-check. -
Treating
is()as all-match. It is any-match. Combine calls with&&when you need conjunction. -
Trying to subclass and add behaviour without overriding
__sleep. If you add new properties they must be added to the__sleep()array, or they will not survive a serialize/unserialize cycle.
- Segment — store the permission list alongside the user id in an adapter-backed segment.
- Recipes → Multi-Segment — auth, cart, and CSRF segments side by side.
initphp/auth · MIT License · part of the InitPHP family
Source · Issues · Discussions · Packagist · Contributing · Security Policy
Getting Started
Core Types
Adapters
Reference
Recipes
Migration & Help