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!
Hello, i think that i saw you visited my blog thus
i came to “return the favor”.I’m attempting to find things to improve
my website!I suppose its ok to use a few of your ideas!!
Feel free to visit my site: ซื้อหวยออนไลน์ ถูกกฎหมาย
black sprout
1win сайт регистрация http://1win1114.ru
New AI generator free nsfw ai chat of the new generation: artificial intelligence turns text into stylish and realistic image and videos.
легальная медицинская справка
ремонт стиральной машины аристон гарантийный ремонт стиральных машин
оформление новой медкнижки
недорогое продление санкнижки
Hi, its nice article on the topic of media print, we all understand media is a fantastic source of data.
lee bet регистрация
скачать мостбет официальный скачать мостбет официальный
blacksprut darknet
https://www.flickr.com/photos/203068406@N08/with/54587998436/
Piece of writing writing is also a excitement, if you be
acquainted with afterward you can write if not it is difficult to write.
авиатор игра 1win http://1win1112.ru
1win com [url=https://1win11005.com]https://1win11005.com[/url]
Hey There. I discovered your blog using msn. This is a really smartly written article.
I’ll be sure to bookmark it and come back to read more of your useful info.
Thank you for the post. I’ll certainly return.
Hi there, just wanted to say, I enjoyed this blog post.
It was funny. Keep on posting!
курьерская доставка справок
New AI generator nsfw ai video of the new generation: artificial intelligence turns text into stylish and realistic image and videos.
https://b2tor2.cc/blacksprut_zerkalo.html
Hi, every time i used to check webpage posts here early in the dawn, since i love to gain knowledge of more and more.
официальный сайт банда казино
справка для спортивных соревнований
блэкспрут ссылка
I was suggested this website through my cousin. I am now not certain whether or not this put up is written via
him as no one else understand such distinctive
about my trouble. You are incredible! Thank you!
Hi Dear, are you really visiting this web site on a regular basis, if so
after that you will absolutely take fastidious knowledge.
блэк спрут
New AI generator ai chat nsfw of the new generation: artificial intelligence turns text into stylish and realistic image and videos.
блэкспрут вход
New AI generator free nsfw ai of the new generation: artificial intelligence turns text into stylish and realistic image and videos.
ремонт стиральной машины candy ремонт стиральных машин beko
Thanks for sharing your thoughts. I truly appreciate your
efforts and I am waiting for your next post thank you once
again.
https://solo.to/inlex-partners
Discover thousands of the latest and popular films from various genres.
Enjoy high-quality movie streaming experience without any
subscription needed.
Наши специалисты предлагают выгодно заказать диплом, который выполнен на оригинальной бумаге и заверен мокрыми печатями, штампами, подписями. Диплом пройдет лубую проверку, даже при использовании профессионального оборудования. aipair.io/read-blog/4982_kupit-diplom-o-srednem-specialnom-obrazovanii.html
Наша компания предлагает выгодно и быстро купить диплом, который выполнен на оригинальном бланке и заверен печатями, водяными знаками, подписями. Документ пройдет лубую проверку, даже с применением специфических приборов. needingjob.com/employer/diplomirovans
https://b2tsite4.io/bs2best.html
карниз для штор электрический http://www.elektrokarnizy50.ru .
Мы можем предложить дипломы любых профессий по доступным тарифам. Купить диплом в Новоуральске — kyc-diplom.com/geography/novouralsk.html
https://linktr.ee/Inlex_Partners
Howdy! This is my first comment here so I just wanted to give a quick shout out and say I truly
enjoy reading your articles. Can you suggest any other blogs/websites/forums that go over the same subjects?
Thanks a ton!
Мы предлагаем быстро заказать диплом, который выполняется на оригинальном бланке и заверен мокрыми печатями, водяными знаками, подписями. Документ пройдет лубую проверку, даже с применением специального оборудования. [url=http://demo.accesslinkandcsng.com/employer/diplomirovans/]demo.accesslinkandcsng.com/employer/diplomirovans[/url]
Very nice blog post. I definitely appreciate this website.
Keep it up!
Simply wish to say your article is as surprising.
The clearness in your post is just spectacular and i could assume you’re an expert on this subject.
Well with your permission let me to grab your RSS feed to keep updated with forthcoming post.
Thanks a million and please keep up the rewarding work.
https://lisandrarcarmichael.com/
Hey there! I’m at work surfing around your blog from my new iphone 3gs! Just wanted to say I love reading through your blog and look forward to all your posts! Keep up the outstanding work!
https://emaidan.com.ua/linzy-v-fary-pokraschuyut-svitlo-chi-marketynh
Awesome! Its in fact awesome article, I have got much clear idea on the topic of from this
post.
For those interested in the historical background of LNR, this resource offers a detailed overview of the key events, important dates, and the evolution of the republic from its origins to the present day: history of LNR. It’s an excellent starting point for anyone researching the region’s past and present.
Excellent post. I used to be checking constantly this weblog and I am impressed!
Extremely helpful info specially the last part 🙂 I deal with such information much.
I used to be looking for this particular info for a long time.
Thanks and best of luck.
https://2bs-2best.at/https_bs2best_at.html
After exploring a number of the blog articles on your
web page, I really like your way of writing a blog. I added it to my bookmark website
list and will be checking back in the near future.
Take a look at my web site too and tell me what you
think.
I think this is among the most significant info
for me. And i am glad reading your article. But want to remark on few general things, The site style is wonderful, the articles
is really great : D. Good job, cheers