01/12/2018 - PHP
In the case of Chain of responsibility pattern, the request is passed to a chain of objects where it either gets processed by one or some or all of them. In general, the order of the chain is not important but sometimes it can be based on the business requirements. In example below, the order is important and all the objects in chain process the request. It authenticates the user by checking the credentials first, then check if the use is active or not then if the user has required permissions to use our service. For more information read Chain-of-responsibility pattern. Note: See more examples towards the end of the post.
declare(strict_types=1);
abstract class AbstractChain
{
/** @var self */
private $next;
public function linkNext(self $next): self
{
$this->next = $next;
return $next;
}
public function check(User $user): bool
{
return $this->next ? $this->next->check($user) : true;
}
}
declare(strict_types=1);
class CredentialChain extends AbstractChain
{
private $validCredentials = [
['username' => 'hello', 'password' => '123123'],
['username' => 'inanzzz', 'password' => '321321'],
['username' => 'inanzzz', 'password' => '123123']
];
public function check(User $user): bool
{
foreach ($this->validCredentials as $validCredential) {
if (
$user->getUsername() === $validCredential['username'] &&
$user->getPassword() === $validCredential['password']
) {
return parent::check($user);
}
}
throw new Exception('Invalid credentials.');
}
}
declare(strict_types=1);
class StatusChain extends AbstractChain
{
public function check(User $user): bool
{
if ($user->isActive()) {
return parent::check($user);
}
throw new Exception('Invalid status.');
}
}
declare(strict_types=1);
class RoleChain extends AbstractChain
{
private $validRoles = [
'STUDENT',
'LECTURER',
'ADMIN',
];
public function check(User $user): bool
{
foreach ($this->validRoles as $validRole) {
if ($user->getRole() === $validRole) {
return parent::check($user);
}
}
throw new Exception('Invalid role.');
}
}
declare(strict_types=1);
class User
{
private $username;
private $password;
private $role;
private $isActive;
public function __construct(string $username, string $password, string $role, bool $isActive)
{
$this->username = $username;
$this->password = $password;
$this->role = $role;
$this->isActive = $isActive;
}
public function getUsername(): string
{
return $this->username;
}
public function getPassword(): string
{
return $this->password;
}
public function getRole(): string
{
return $this->role;
}
public function isActive(): bool
{
return $this->isActive;
}
}
declare(strict_types=1);
class Authenticate
{
private $chain;
public function __construct(AbstractChain $chain)
{
$this->chain = $chain;
}
public function login(User $user): bool
{
return $this->chain->check($user);
}
}
declare(strict_types=1);
require_once 'AbstractChain.php';
require_once 'CredentialChain.php';
require_once 'StatusChain.php';
require_once 'RoleChain.php';
require_once 'User.php';
require_once 'Authenticate.php';
$chain = new CredentialChain();
$chain
->linkNext(new StatusChain())
->linkNext(new RoleChain());
try {
(new Authenticate($chain))
->login(
new User('inanzzz', '123123', 'ADMIN', true)
);
echo 'Success'.PHP_EOL;
} catch (Exception $e) {
echo $e->getMessage().PHP_EOL;
}
# Success
new User('inanzzz', '123123', 'ADMIN', true)
# Invalid credentials.
new User('inanzzzA', '123123', 'ADMIN', true)
new User('inanzzz', '1231230', 'ADMIN', true)
# Invalid status.
new User('inanzzz', '123123', 'ADMIN', false)
# Invalid role.
new User('inanzzz', '123123', 'UUU', true)
In this example, only one handler processes the data.
abstract class AbstractChain
{
/** @var self */
private $next;
public function addNext(self $next): self
{
$this->next = $next;
return $next;
}
public function handle(string $type): ?string
{
return $this->next ? $this->next->handle($type) : null;
}
}
class BikeHandler extends AbstractChain
{
public function handle(string $type): ?string
{
echo 'BikeHandler has run'.PHP_EOL;
if ($type === 'bike') {
return $type;
}
return parent::handle($type);
}
}
class CarHandler extends AbstractChain
{
public function handle(string $type): ?string
{
echo 'CarHandler has run'.PHP_EOL;
if ($type === 'car') {
return $type;
}
return parent::handle($type);
}
}
class BusHandler extends AbstractChain
{
public function handle(string $type): ?string
{
echo 'BusHandler has run'.PHP_EOL;
if ($type === 'bus') {
return $type;
}
return parent::handle($type);
}
}
// Client
$bikeHandler = new BikeHandler();
$carHandler = new CarHandler();
$busHandler = new BusHandler();
$bikeHandler->addNext($carHandler)->addNext($busHandler);
echo $bikeHandler->handle('bike').PHP_EOL;
// Result
BikeHandler has run
bike
In this example, only relevant handlers process the data.
abstract class AbstractChain
{
/** @var self */
private $next;
public function addNext(self $next): self
{
$this->next = $next;
return $next;
}
public function handle(Car $car): Car
{
return $this->next ? $this->next->handle($car) : $car;
}
}
class TypeHandler extends AbstractChain
{
public function handle(Car $car): Car
{
echo 'TypeHandler has run'.PHP_EOL;
if ($car->type === '4x4') {
$car->canRace = true;
return $car;
}
return parent::handle($car);
}
}
class OriginHandler extends AbstractChain
{
public function handle(Car $car): Car
{
echo 'OriginHandler has run'.PHP_EOL;
if ($car->origin === 'Germany') {
$car->canRace = true;
return $car;
}
return parent::handle($car);
}
}
class BelongHandler extends AbstractChain
{
public function handle(Car $car): Car
{
echo 'BelongHandler has run'.PHP_EOL;
if ($car->belongsTo === 'Boss') {
$car->canRace = true;
return $car;
}
return parent::handle($car);
}
}
class Car
{
public $type;
public $origin;
public $belongsTo;
public $canRace = false;
}
// Client
$car = new Car();
$car->type = 'invalid';
$car->origin = 'Germany';
$car->belongsTo = 'Boss';
$typeHandler = new TypeHandler();
$originHandler = new OriginHandler();
$belongHandler = new BelongHandler();
$typeHandler->addNext($originHandler)->addNext($belongHandler);
$typeHandler->handle($car);
print_r($car);
// Result
TypeHandler has run
OriginHandler has run
Car Object
(
[type] => invalid
[origin] => Germany
[belongsTo] => Boss
[canRace] => 1
)
In this example, all the handlers process the data (this is same as the main example above).
abstract class AbstractChain
{
/** @var self */
private $next;
public function addNext(self $next): self
{
$this->next = $next;
return $next;
}
public function handle(int $number): int
{
return $this->next ? $this->next->handle($number) : $number;
}
}
class AddHandler extends AbstractChain
{
public function handle(int $number): int
{
echo 'AddHandler has run'.PHP_EOL;
$number = $number + 1;
return parent::handle($number);
}
}
class RemoveHandler extends AbstractChain
{
public function handle(int $number): int
{
echo 'RemoveHandler has run'.PHP_EOL;
$number = $number - 2;
return parent::handle($number);
}
}
class MultiplyHandler extends AbstractChain
{
public function handle(int $number): int
{
echo 'MultiplyHandler has run'.PHP_EOL;
$number = $number * 3;
return parent::handle($number);
}
}
// Client
$addHandler = new AddHandler();
$removeHandler = new RemoveHandler();
$multiplyHandler = new MultiplyHandler();
$addHandler->addNext($removeHandler)->addNext($multiplyHandler);
echo $addHandler->handle(3).PHP_EOL;
// Result
AddHandler has run
RemoveHandler has run
MultiplyHandler has run
6