Authorization on its own: deciding whether a requester is allowed to perform an action on a subject. No user, no token and no firewall in the vocabulary, and no authentication stack required for any of it to work.
What it covers
- Voters that answer a question with a grant, a denial, or nothing at all, an abstention being a result of its own.
- Combining algorithms that turn several answers into one, under their XACML names, with an exact correspondence to those of Symfony Security.
- Access policies declared where they apply, as attributes, and composed with
All,AtLeastOneOfandWhen. - Decisions that say why: a verdict, a reason, and who voted which way, which is what makes a refusal diagnosable six months later.
- Entry points onto the same machinery: controllers, URL rules, Twig, console commands, workflow guards, and a profiler panel that shows every question a request asked.
Two packages
spomky-labs/access-control-lib is the library, usable in any PHP application: it requires PHP and an event dispatcher contract, and knows nothing about HTTP. spomky-labs/access-control-bundle integrates it into the Symfony full-stack framework. Both are read-only subtree splits of access-control-framework, where issues and pull requests belong.
Getting started
composer require spomky-labs/access-control-bundle
// config/bundles.php
return [
// ...
AccessControl\Bundle\AccessControlBundle::class => ['all' => true],
];
There is no enabled flag: registering the bundle is the opt-in. In an application that has Symfony Security, nothing you can see changes. Your voters keep being consulted, #[IsGranted] is read, is_granted() answers in your templates, and your security.yaml stays byte for byte what it was. What changed is who decides.
Outside Symfony, the library is one object and needs no container:
$accessControlManager = new AccessControlManager(
strategies: [new PermitOverridesStrategy()],
voters: [new PostVoter()],
);
$decision = $accessControlManager->decide(new AccessRequest($requester, 'EDIT', $post));
One thing worth knowing: there are two migrations
The first is free and happens on day one: install the bundle, change nothing else, and the component decides. The second is moving to its own vocabulary, file by file, at your own pace, or never. A codebase halfway through works, because both vocabularies reach the same voters.
Confusing the two would cost the adoption, so they are documented apart. And what the bridge cannot carry across is refused out loud rather than answered quietly: field level ACL raises instead of returning false, and the same setting declared on both security.* and access_control.* stops the build instead of silently picking one. A guard that lets somebody through is the only unacceptable failure, and it is a silent one.
Where to look next
The documentation is organised by the question you are asking, from the vocabulary to the migration. A demonstration application puts every idea behind a real page, with a test suite that asserts on the decisions rather than on the status codes. The source is on GitHub, under the MIT licence.
A series of articles takes one piece of it a day: the vocabulary, the voters, the combining algorithms, the entry points, the tests and the migration.