Introduction
One of the real challenges in building any type of framework, core or application is making it possible for the developers to hook into the business logic at specific points. Since PHP is not event based, nor it works with interrupts you have to come up an alternative.
The test case
Lets assume we are the main developers of a webshop framework. Programmers can use our framework to build complete webshops. Programmers can manage the orders that are placed on the webshop with the order class. The order class is part of our framework and we don’t want it to be extended by any programmer. However we don’t want to limit to programmers in their possibilities to hook into the orders process.
For example programmers should be able to send an email to the webshopowner if an order changes from one specific delivery status to another. This functionality is not part of the default behavior in our framework and is custom for the progammers webshop implementation.
Like said before, PHP doesn’t provide interrupts or real events so we need to come up with another way to implement hooks into our application. Lets take a look at the observer pattern.
Implementing the Observer pattern
The observer pattern is a design-pattern that describes a way for objects to be notified to specific state-changes in objects of the application.
For the first implementation we can use SPL. The SPL provides in two simple objects:
SPLSubject
- attach (new observer to attach)
- detach (existing observer to detach)
- notify (notify all observers)
SPLObserver
- update (Called from the subject (i.e. when it’s value has changed).
iOrderRef = $iOrderRef;
// Get order information from the database or an other resources
$this->iStatus = Order::STATUS_SHIPPED;
}
/**
* Attach an observer
*
* @param SplObserver $oObserver
* @return void
*/
public function attach(SplObserver $oObserver)
{
$sHash = spl_object_hash($oObserver);
if (isset($this->aObservers[$sHash])) {
throw new Exception('Observer is already attached');
}
$this->aObservers[$sHash] = $oObserver;
}
/**
* Detach observer
*
* @param SplObserver $oObserver
* @return void
*/
public function detach(SplObserver $oObserver)
{
$sHash = spl_object_hash($oObserver);
if (!isset($this->aObservers[$sHash])) {
throw new Exception('Observer not attached');
}
unset($this->aObservers[$sHash]);
}
/**
* Notify the attached observers
*
* @param string $sEvent, name of the event
* @param mixed $mData, optional data that is not directly available for the observers
* @return void
*/
public function notify()
{
foreach ($this->aObservers as $oObserver) {
try {
$oObserver->update($this);
} catch(Exception $e) {
}
}
}
/**
* Add an order
*
* @param array $aOrder
* @return void
*/
public function delete()
{
$this->notify();
}
/**
* Return the order reference number
*
* @return int
*/
public function getRef()
{
return $this->iOrderRef;
}
/**
* Return the current order status
*
* @return int
*/
public function getStatus()
{
return $this->iStatus;
}
/**
* Update the order status
*/
public function updateStatus($iStatus)
{
$this->notify();
// ...
$this->iStatus = $iStatus;
// ...
$this->notify();
}
}
/**
* Order status handler, observer that sends an email to secretary
* if the status of an order changes from shipped to delivered, so the
* secratary can make a phone call to our customer to ask for his opinion about the service
*
* @package Shop
*/
class OrderStatusHandler implements SplObserver
{
/**
* Previous orderstatus
* @var int
*/
protected $iPreviousOrderStatus;
/**
* Current orderstatus
* @var int
*/
protected $iCurrentOrderStatus;
/**
* Update, called by the observable object order
*
* @param Observable_Interface $oSubject
* @param string $sEvent
* @param mixed $mData
* @return void
*/
public function update(SplSubject $oSubject)
{
if(!$oSubject instanceof Order) {
return;
}
if(is_null($this->iPreviousOrderStatus)) {
$this->iPreviousOrderStatus = $oSubject->getStatus();
} else {
$this->iCurrentOrderStatus = $oSubject->getStatus();
if($this->iPreviousOrderStatus === Order::STATUS_SHIPPED && $this->iCurrentOrderStatus === Order::STATUS_DELIVERED) {
$sSubject = sprintf('Order number %d is shipped', $oSubject->getRef());
//mail('secratary@example.com', 'Order number %d is shipped', 'Text');
echo 'Mail sended to the secratary to help her remember to call our customer for a survey.';
}
}
}
}
$oOrder = new Order(26012011);
$oOrder->attach(new OrderStatusHandler());
$oOrder->updateStatus(Order::STATUS_DELIVERED);
$oOrder->delete();
?>
There are several problems with the implementation above. To most important disadvantage is that we have only one update method in our observer. In this update method we don’t know when and why we are getting notified, just that something happened. We should keep track of everything that happens in the subject. (Or use debug_backtrace… just joking, don’t even think about using it that way ever!).
Taking it a step further, events
Lets take a look at the next example, we will extend the Observer implementation with some an additional parameter for the eventname that occured.
Finishing up, optional data
iOrderRef = $iOrderRef;
// Get order information from the database or something else...
$this->iStatus = Order::STATUS_SHIPPED;
}
/**
* Attach an observer
*
* @param Observer_Interface $oObserver
* @return void
*/
public function attachObserver(Observer_Interface $oObserver)
{
$sHash = spl_object_hash($oObserver);
if (isset($this->aObservers[$sHash])) {
throw new Exception('Observer is already attached');
}
$this->aObservers[$sHash] = $oObserver;
}
/**
* Detach observer
*
* @param Observer_Interface $oObserver
* @return void
*/
public function detachObserver(Observer_Interface $oObserver)
{
$sHash = spl_object_hash($oObserver);
if (!isset($this->aObservers[$sHash])) {
throw new Exception('Observer not attached');
}
unset($this->aObservers[$sHash]);
}
/**
* Notify the attached observers
*
* @param string $sEvent, name of the event
* @param mixed $mData, optional data that is not directly available for the observers
* @return void
*/
public function notifyObserver($sEvent, $mData=null)
{
foreach ($this->aObservers as $oObserver) {
try {
$oObserver->update($this, $sEvent, $mData);
} catch(Exception $e) {
}
}
}
/**
* Add an order
*
* @param array $aOrder
* @return void
*/
public function add($aOrder = array())
{
$this->notifyObserver('onAdd');
}
/**
* Return the order reference number
*
* @return int
*/
public function getRef()
{
return $this->iOrderRef;
}
/**
* Return the current order status
*
* @return int
*/
public function getStatus()
{
return $this->iStatus;
}
/**
* Update the order status
*/
public function updateStatus($iStatus)
{
$this->notifyObserver('onBeforeUpdateStatus');
// ...
$this->iStatus = $iStatus;
// ...
$this->notifyObserver('onAfterUpdateStatus');
}
}
/**
* Order status handler, observer that sends an email to secretary
* if the status of an order changes from shipped to delivered, so the
* secratary can make a phone call to our customer to ask for his opinion about the service
*
* @package Shop
*/
class OrderStatusHandler implements Observer_Interface
{
protected $iPreviousOrderStatus;
protected $iCurrentOrderStatus;
/**
* Update, called by the observable object order
*
* @param Observable_Interface $oObservable
* @param string $sEvent
* @param mixed $mData
* @return void
*/
public function update(Observable_Interface $oObservable, $sEvent, $mData=null)
{
if(!$oObservable instanceof Order) {
return;
}
switch($sEvent) {
case 'onBeforeUpdateStatus':
$this->iPreviousOrderStatus = $oObservable->getStatus();
return;
case 'onAfterUpdateStatus':
$this->iCurrentOrderStatus = $oObservable->getStatus();
if($this->iPreviousOrderStatus === Order::STATUS_SHIPPED && $this->iCurrentOrderStatus === Order::STATUS_DELIVERED) {
$sSubject = sprintf('Order number %d is shipped', $oObservable->getRef());
//mail('secratary@example.com', 'Order number %d is shipped', 'Text');
echo 'Mail sended to the secratary to help her remember to call our customer for a survey.';
}
}
}
}
$oOrder = new Order(26012011);
$oOrder->attachObserver(new OrderStatusHandler());
$oOrder->updateStatus(Order::STATUS_DELIVERED);
$oOrder->add();
?>
Now we are able to take action on different events that occur.
Disadvantages
Although this implementation works quite well there are some drawbacks. One of those drawbacks is that we need to dispatch an event in our framework, if we don’t programmers can’t hook into our application. Triggering events everywhere give us a small performance penalty however I do think this way of working gives the programmers a nice way to hook into your application on those spots that you want them to hook in.
Just for the record
Notice that this code is just an example and can still use some improvements, for example: each observer is initialized even it will maybe never be notified, therefore I suggest to make use of lazy in some cases for loading the objects. There are other systems to hook into an application, more to follow!
Excellent blog here! Also your web site loads up
fast! What web host are you using? Can I get your affiliate link to your host?
I wish my site loaded up as fast as yours lol
Do you mind if I quote a couple of your articles as
long as I provide credit and sources back to your website?
My website is in the exact same area of interest as yours and my visitors would genuinely benefit from a lot of the information you provide here.
Please let me know if this okay with you. Regards!
автомобили бу Купить БМВ: Удовольствие от вождения BMW – это бренд, известный своими спортивными автомобилями, предлагающими превосходную управляемость и динамику.
I’ve tried a few different supplements over the years,
but Boostaro actually feels different. More energy,
better workouts, and yes—definitely noticing a difference
where it matters. Appreciate the honest breakdown in this video!
Very good article! We are linking to this great content on our site.
Keep up the good writing.
Психотерапевт Киров. Психолог 555 212 оценок
I simply could not leave your web site before suggesting that I really enjoyed the standard info
an individual provide on your visitors? Is gonna be again steadily in order
to investigate cross-check new posts
We stumbled over here from a different web address and thought
I might check things out. I like what I see so now i am following you.
Look forward to looking at your web page for a second time.
где купить аттестаты за 11 класс http://arus-diplom4.ru/ .
купить диплом в калининграде купить диплом в калининграде .
Календарь стрижек https://novorjev.ru/ .
https://2-bs2best.lat/blacksprut.html
1 win бонус при регистрации 1 win бонус при регистрации
диплом как купить http://arus-diplom4.ru/ .
аттестат за 9 классов купить аттестат за 9 классов купить .
Thank you for the good writeup. It in fact was
a amusement account it. Look advanced to far added agreeable from you!
However, how can we communicate?
wine lover gift Oiseaux et Vin (French for “Birds & Wine”) is Alesya G’s elegant bird collection for wine lovers. Explore bird-themed drinkware and accessories – from chic stemless wine glasses and insulated tumblers to fun phone cases featuring wine-sipping birds. These unique gifts marry French style with humor, perfect for wine enthusiasts and bird lovers alike. Sip and celebrate – shop wine-bird mugs, shirts, and decor now!
купить диплом врача [url=http://www.arus-diplom4.ru]купить диплом врача[/url] .
можно купить диплом высшем образовании [url=www.arus-diplom5.ru/]можно купить диплом высшем образовании[/url] .
мостбет ставки на спорт скачать [url=https://www.mostbet11017.ru]мостбет ставки на спорт скачать[/url]
masbet http://mostbet11018.ru/
First off I want to say superb blog! I had a quick question which
I’d like to ask if you don’t mind. I was interested to find out how
you center yourself and clear your head before writing. I have
had difficulty clearing my thoughts in getting my ideas out.
I truly do enjoy writing but it just seems like the first 10 to 15 minutes are generally lost just trying to figure out how
to begin. Any recommendations or hints? Kudos!
бк. мостбет. [url=www.mostbet11029.ru]бк. мостбет.[/url]
Лунные день сегодня http://pechory-online.ru .
What’s up mates, its enormous article regarding teachingand completely defined,
keep it up all the time.
1win vhod 1win vhod
bird wall art Why don’t birds get zapped? This clever science question inspires our minimalist birds on wires art posters. Designed by singer-artist Alesya G, each premium print features silhouetted birds on a power line with a witty caption. These black-and-white wall art pieces are perfect for modern home decor or a kids’ room. Gift playful science and style – browse our bird-wire art prints for unique wall decor today.
aviator игра 1win официальный сайт https://1win1106.ru/
thank a lot for your site it helps a great deal.
https://koscierski.info
1000 рублей за регистрацию вывод сразу без вложений Тысяча рублей, словно луч надежды, маячит перед новичками онлайн-казино, предлагая начать свой путь в мир азарта без каких-либо финансовых вложений. Это возможность испытать удачу, не рискуя собственными средствами, и почувствовать вкус победы, не расставаясь с кровно заработанными. 1000 рублей за регистрацию вывод сразу без вложений в казино адмирал
Hey! This is my 1st comment here so I just wanted to give
a quick shout out and tell you I really enjoy reading your posts.
Can you suggest any other blogs/websites/forums that
go over the same topics? Thanks!
Online na prawdziwe pieniadze to frajda i szansa na wygrana. Wyplaty w 24h!
https://vulkan-vegas-casino-poland.pro/
mostbet com официальный сайт https://mostbet11030.ru
What’s up Dear, are you really visiting this website
on a regular basis, if so after that you will without doubt take
fastidious know-how.
1 вин ставки на спорт онлайн https://www.1win1105.ru
I loved as much as you’ll receive carried out right here.
The sketch is tasteful, your authored subject matter stylish.
nonetheless, you command get bought an nervousness
over that you wish be delivering the following.
unwell unquestionably come more formerly again as
exactly the same nearly a lot often inside case you shield this hike.
Психотерапевт Челны. Психолог 649 273 оценок
мостбет ставки на исход http://mostbet11017.ru/
психиатр частный прием психиатр частный прием .
Wow, attractive site. Thnx …
https://www.dziennikpowiatowy.pl
диплом в москве купить диплом в москве купить .
аттестат купить за 9 класс аттестат купить за 9 класс .
When some one searches for his required thing, so he/she wishes to
be available that in detail, therefore that thing is maintained over here.
игры на деньги с выводом на карту без вложений [url=www.1win1106.ru]www.1win1106.ru[/url]
школьный аттестат купить [url=http://arus-diplom8.ru]школьный аттестат купить[/url] .
mostbet casino скачать mostbet casino скачать
download mostbet mostbet11029.ru
mostbet вход в личный кабинет mostbet вход в личный кабинет
Searching fօr cost-effective storage container rental?
Container Storage Units ⲟffers top-quality solutions іn Keent аnd Surrey.
Greаt for households and commercial ᥙse alike.
Whether you prefer cryptocurrency for complete anonymity or traditional payment methods with minimal verification, our country-specific guides will point you in the right direction.
Casinos with payouts without providing documents