Chain of responsibility (Sorumluluk zinciri) paterni söz konusu olduğunda gelen istek, liste içindeki bir, birden fazla veya tüm işleyiciler tarafından işlenir. Genel olarak, zincirin sırası önemli değildir ancak bazen duruma göre önem kazanabilir. Aşağıdaki örnekte, sıra önemlidir ve zincirdeki tüm nesneler isteği işler. Örneğimiz sistemi kullanmak isteyen kullanıcının önce kullanıcı adı ve şifresi kontrol ediyor. Doğru ise kullanıcının aktif olup olmadığına bakılır. Aktif ise gerekli izini olup olmadığı kontrol edilir. Eğer varsa sistem kullanılabilir. Daha fazla bilgi için Chain-of-responsibility pattern sayfasını okuyun. Not: Yazının sonuna doğru daha fazla örnek görebilirsiniz.




Zincir classları


AbstractChain


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;
}
}

CredentialChain


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.');
}
}

StatusChain


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.');
}
}

RoleChain


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.');
}
}

Diğer classlar


User


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;
}
}

Authenticate


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);
}
}

Test


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;
}

Sonuç


# 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)

Diğer örnekler


Tek işleyici


Bu örnekte sadece bir tane işleyici veriyi işliyor.


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

Birden fazla işleyici


Bu örnekte birden fazla işleyici veriyi işliyor.


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
)

Tüm işleyiciler


Bu örnekte tüm işleyiciler veriyi işliyor (bu örnek yukarıdaki ana örnekle aynıdır).


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