Chain of Responsibility pattern gives responsibility of a process handling to more than one object(a chain of objects) so that the sender and receiver of the request remains decoupled. The chain of objects pushes the processing to the next step until the processing is complete.
This article demonstrates Chain of Responsibility pattern implementations in PHP. Check the following implementation details and examples.
Implementation
Follow the steps below for Chain of responsibility pattern implementation-
Here is a simple example-
<?php
// Chain of responsibility pattern in PHP
// Abstract step
abstract class Step {
public function __construct(protected ?Step $nextStep) {
}
abstract function execute(): void;
}
// Step 1
class Step1 extends Step {
public function __construct(?Step $nextStep) {
parent::__construct($nextStep);
}
public function execute(): void {
// Perform all required step for this step
echo "Execute all operation for Step1 processingn";
// Call the next step execution if the step is defined
if ($this->nextStep) {
$this->nextStep->execute();
}
}
}
// Step 2
class Step2 extends Step {
public function __construct(?Step $nextStep) {
parent::__construct($nextStep);
}
public function execute(): void {
// Perform all required step for this step
echo "Execute all operation for Step2 processingn";
// Call the next step execution if the step is defined
if ($this->nextStep) {
$this->nextStep->execute();
}
}
}
// Step 3
class Step3 extends Step {
public function __construct(?Step $nextStep) {
parent::__construct($nextStep);
}
public function execute(): void {
// Perform all required step for this step
echo "Execute all operation for Step3 processingn";
// Call the next step execution if the step is defined
if ($this->nextStep) {
$this->nextStep->execute();
}
}
}
// Demo
$steps = new Step1(new Step2(new Step3(null)));
$steps->execute();
Output:
Output will be as below-
Execute all operation for Step1 processing
Execute all operation for Step2 processing
Execute all operation for Step3 processing
Examples
Check the following examples-
Example #1: Caching Data
Here we are implementing 3 caching methods – Redis, Disk cache, and CDN. Based on certain conditions a specific caching method will be used.
Data Struct
<?php
// Data.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityCache;
enum DATA_TYPE {
case DATA;
case JAVASCRIPT;
case CSS;
}
class Data {
public function __construct(
private DATA_TYPE $type,
private string $key,
private string $data
) {
}
public function getType(): DATA_TYPE {
return $this->type;
}
public function getKey(): string {
return $this->key;
}
public function getData(): string {
return $this->data;
}
}
Cache Handler
<?php
// CacheHandler.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityCache;
abstract class CacheHandler {
public function __construct(protected ?CacheHandler $nextCacheHandler) {
}
abstract function handleRequest(Data $data): void;
}
CDN Cache Handler
<?php
// CdnCacheHandler.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityCache;
class CdnCacheHandler extends CacheHandler {
public function __construct(?CacheHandler $nextCacheHandler) {
parent::__construct($nextCacheHandler);
}
public function handleRequest(Data $data): void {
if ($data->getType() == DATA_TYPE::CSS || $data->getType() == DATA_TYPE::JAVASCRIPT) {
echo "Caching file '" . $data->getKey() . "' in CDNn";
} else if ($this->nextCacheHandler != null) {
$this->nextCacheHandler->handleRequest($data);
}
}
}
Redis Cache Handler
<?php
// RedisCacheHandler.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityCache;
class RedisCacheHandler extends CacheHandler {
public function __construct(?CacheHandler $nextCacheHandler) {
parent::__construct($nextCacheHandler);
}
public function handleRequest(Data $data): void {
if ($data->getType() == DATA_TYPE::DATA && strlen($data->getData()) <= 1024) {
echo "Caching file '" . $data->getKey() . "' in Redisn";
} else if ($this->nextCacheHandler != null) {
$this->nextCacheHandler->handleRequest($data);
}
}
}
Disk Cache Handler
<?php
// DiskCacheHandler.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityCache;
class DiskCacheHandler extends CacheHandler {
public function __construct(?CacheHandler $nextCacheHandler) {
parent::__construct($nextCacheHandler);
}
public function handleRequest(Data $data): void {
if ($data->getType() == DATA_TYPE::DATA && strlen($data->getData()) > 1024) {
echo "Caching file '" . $data->getKey() . "' in Diskn";
} else if ($this->nextCacheHandler != null) {
$this->nextCacheHandler->handleRequest($data);
}
}
}
Demo
In the client we can chain the call to the caching classes-
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternChainOfResponsibilityCacheCdnCacheHandler;
use BigBoxCodeDesignPatternChainOfResponsibilityCacheData;
use BigBoxCodeDesignPatternChainOfResponsibilityCacheDATA_TYPE;
use BigBoxCodeDesignPatternChainOfResponsibilityCacheDiskCacheHandler;
use BigBoxCodeDesignPatternChainOfResponsibilityCacheRedisCacheHandler;
$cacheHandler = new DiskCacheHandler(new RedisCacheHandler(new CdnCacheHandler(null)));
$data1 = new Data(DATA_TYPE::DATA, "key1", "ABC320489un3429rn29urn29r82n9jfdn2");
$cacheHandler->handleRequest($data1);
$data2 = new Data(DATA_TYPE::CSS, "key2", ".some-class{border: 1px solid red; margin: 10px}");
$cacheHandler->handleRequest($data2);
Output
Following output will be generated by demo code-
Caching file 'key1' in Redis
Caching file 'key2' in CDN
Example #2: Interview
Here we are demonestring an interview process. Here we have interview with the HR, tech interview, interview with CEO etc.
Interview Interface
<?php
// Interview.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityInterview;
abstract class Interview {
public function __construct(protected ?Interview $nextInterview) {
}
abstract function execute(): void;
}
Phone Interview
<?php
// PhoneInterview.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityInterview;
class PhoneInterview extends Interview {
public function __construct(?Interview $nextInterview) {
parent::__construct($nextInterview);
}
public function execute(): void {
// Ask all questions for phone interview
// Perform any other action required for phone interview
echo "Ask phone interview questionsn";
// Execute the next interview set while creating new struct
if ($this->nextInterview) {
$this->nextInterview->execute();
}
}
}
Technical Interview
<?php
// TechnicalInterview.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityInterview;
class TechnicalInterview extends Interview {
public function __construct(?Interview $nextInterview) {
parent::__construct($nextInterview);
}
public function execute(): void {
// Ask all questions for technical interview
// Perform any other action required for technical interview
echo "Ask technical interview questionsn";
// Execute the next interview set while creating new struct
if ($this->nextInterview) {
$this->nextInterview->execute();
}
}
}
HR Interview
<?php
// HrInterview.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityInterview;
class HrInterview extends Interview {
public function __construct(?Interview $nextInterview) {
parent::__construct($nextInterview);
}
public function execute(): void {
// Ask all questions for hr interview
// Perform any other action required for hr interview
echo "Ask HR interview questionsn";
// Execute the next interview set while creating new struct
if ($this->nextInterview) {
$this->nextInterview->execute();
}
}
}
CEO Interview
<?php
// CeoInterview.php
namespace BigBoxCodeDesignPatternChainOfResponsibilityInterview;
class CeoInterview extends Interview {
public function __construct(?Interview $nextInterview) {
parent::__construct($nextInterview);
}
public function execute(): void {
// Ask all questions for ceo interview
// Perform any other action required for ceo interview
echo "Ask CEO interview questionsn";
// Execute the next interview set while creating new struct
if ($this->nextInterview) {
$this->nextInterview->execute();
}
}
}
Demo
In the client chain the calls. This way we can define the sequence of the interview processing. Finally call the “execute” method for execution.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternChainOfResponsibilityInterviewCeoInterview;
use BigBoxCodeDesignPatternChainOfResponsibilityInterviewHrInterview;
use BigBoxCodeDesignPatternChainOfResponsibilityInterviewPhoneInterview;
use BigBoxCodeDesignPatternChainOfResponsibilityInterviewTechnicalInterview;
$interviews = new PhoneInterview(new TechnicalInterview(new HrInterview(new CeoInterview(null))));
$interviews->execute();
Output
Output will be as below-
Ask phone interview questions
Ask technical interview questions
Ask hr interview questions
Ask ceo interview questions
Source Code
Use the following link to get the source code:
Other Code Implementations
Use the following links to check the Chain of Responsibility pattern implementation in other programming languages.