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!
электрокарниз двухрядный цена http://elektrokarnizy50.ru/ .
Hello there! I know this is kinda off topic but I’d figured I’d ask.
Would you be interested in trading links or maybe guest authoring a blog post or vice-versa?
My website addresses a lot of the same topics as yours and
I feel we could greatly benefit from each other. If you might be interested feel free to send me an email.
I look forward to hearing from you! Wonderful blog by the
way!
Write more, thats all I have to say. Literally, it seems as though you relied on the
video to make your point. You clearly know what youre talking about, why
throw away your intelligence on just posting videos to your site when you could
be giving us something informative to read?
New AI generator nsfw ai art of the new generation: artificial intelligence turns text into stylish and realistic image and videos.
New AI generator ai nsfw generator of the new generation: artificial intelligence turns text into stylish and realistic image and videos.
pokies 111 http://pokies11.com .
thepokies net111 pokies11.com .
denticore ingredients
DentiCore is a superb oral supplement. My teeth are stronger, and my gum
tissues are healthier. The all-natural active ingredients and probiotics
job wonders. I can’t imagine my oral treatment
routine without DentiCore. Extremely suggest!
thepokies111 http://pokies11.com/ .
New AI generator nsfw ai generator of the new generation: artificial intelligence turns text into stylish and realistic image and videos.
игровые автоматы с бездепозитным бонусом Бездепозитный бонус – это шанс сорвать куш, не вкладывая ни копейки. Он позволяет испытать азарт, риск и радость победы, не испытывая страха потери собственных средств. Это особенно ценно для начинающих игроков, которые еще не готовы рисковать большими суммами. Бездепозитный бонус в казино
1win uganda 1win uganda
Наша компания предлагает быстро и выгодно купить диплом, который выполнен на оригинальной бумаге и заверен мокрыми печатями, штампами, подписями. Наш документ способен пройти любые проверки, даже при использовании специфических приборов. zdshi-tula.ru/forum/?PAGE_NAME=topic_new&FID=1#postform
Наша компания предлагает выгодно приобрести диплом, который выполнен на оригинальном бланке и заверен печатями, водяными знаками, подписями. Документ пройдет любые проверки, даже при помощи специального оборудования. itjobsbd.com/employer/diplomirovans
Howdy! I’m at work surfing around your blog from my new iphone!
Just wanted to say I love reading your blog and look forward to all your posts!
Carry on the outstanding work!
Your style is really unique compared to other folks I have read stuff from.
Thanks for posting when you have the opportunity, Guess I will
just bookmark this web site.
https://b2shop.gl/blacksprut_bs2best.html
1win betting app 1win betting app .
Наша компания предлагает быстро приобрести диплом, который выполнен на оригинальном бланке и заверен печатями, водяными знаками, подписями должностных лиц. Документ способен пройти лубую проверку, даже при использовании специально предназначенного оборудования. [url=http://18plus.fun/read-blog/9785_kupit-diplom-kolledzh.html/]18plus.fun/read-blog/9785_kupit-diplom-kolledzh.html[/url]
бонус на спорт 1win 1win1111.ru
1win welcome bonus 1win welcome bonus
We’re a group of volunteers and opening a new scheme in our community.
Your website provided us with valuable information to work on. You have done a formidable job and our whole community will
be grateful to you.
Woah! I’m really digging the template/theme of this blog.
It’s simple, yet effective. A lot of times it’s very difficult to
get that “perfect balance” between user friendliness and
visual appearance. I must say you’ve done a awesome job with this.
Additionally, the blog loads very fast for me on Opera. Exceptional Blog!
download 1 win apk download 1 win apk .
Peculiar article, totally what I was looking for.
1win официальный вход с компьютера https://1win1110.ru/
1win apk login [url=1win-in1.com]1win apk login[/url] .
how to use 1win casino bonus [url=1win11001.com]1win11001.com[/url]
в каком казино дают деньги за регистрацию без депозита Это возможность получить бонус, не внося депозит, но с условием отыгрыша. Важно внимательно изучить условия вейджера, чтобы оценить, насколько реально вывести выигрыш. Бездепозитный бонус игровые автоматы с выводом денег
dragonslot dragonslotscasinos.mobi .
tonicgreens benefits TonicGreens
has absolutely changed my health and wellness. I really feel extra energised and
much less prone to getting ill. The all-natural ingredients give me assurance, recognizing I’m not placing any unsafe chemicals into my body.
It’s a wonderful item that I now suggest to all my good friends and family members.
If you wish for to get much from this article then you have to apply these techniques to your won webpage.
dragon slots dragonslotscasinos.net .
I am regular reader, how are you everybody? This post posted at this site is truly fastidious.
https://a-bsme.at/bs2web_at.html
скачат мостбет скачат мостбет
The Pokies Australia thepokiesnet101.com .
dragon slots dragon slots .
1win [url=https://1win1113.ru/]https://1win1113.ru/[/url]
Wow, gorgeous website. Thnx …
https://mlodagdynia.pl/pl/
сборные печи барбекю https://modul-pech.ru/
Animal Feed https://pvslabs.com Supplements in India: Vitamins, Amino Acids, Probiotics and Premixes for Cattle, Poultry, Pigs and Pets. Increased Productivity and Health.
1win букмекерская приложение 1win букмекерская приложение
Its like you read my mind! You appear to know a lot about this, like you wrote the book in it or something.
I think that you could do with a few pics to drive the message home a little
bit, but other than that, this is fantastic blog. A fantastic read.
I’ll certainly be back.
This is my first time pay a visit at here and i
am genuinely pleassant to read all at single place.
1вин казино отзывы https://www.1win1114.ru
https://a-bsme.at/bs2best.html
These are truly fantastic ideas in on the topic of blogging.
You have touched some good things here. Any way keep up wrinting.
Hi to all, how is the whole thing, I think every one is getting more from this site, and your views are fastidious in support of new users.
pokiesnet http://www.pokiesnet250.com/ .