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!
I’ve read some just right stuff here. Certainly value bookmarking
for revisiting. I surprise how so much effort you put to make one of these excellent informative web site.
mostbet ro’yxatdan o’tish [url=mostbet4004.ru]mostbet4004.ru[/url]
What’s up to every , since I am in fact eager of reading this web
site’s post to be updated regularly. It contains pleasant data.
Запой – это не просто временное ухудшение самочувствия, а состояние, при котором организм подвергается значительному токсическому воздействию алкоголя. Длительное злоупотребление приводит к накоплению вредных веществ, что может вызвать:
Ознакомиться с деталями – нарколог на дом рязань
Pretty nice post. I just stumbled upon your blog and wished
to say that I’ve really enjoyed browsing
your blog posts. In any case I will be subscribing to your rss feed and I hope
you write again very soon!
ставки на спорт мостбет https://www.mostbet4013.ru
В этом интересном тексте собраны обширные сведения, которые помогут вам понять различные аспекты обсуждаемой темы. Мы разбираем детали и факты, делая акцент на важности каждого элемента. Не упустите возможность расширить свои знания и взглянуть на мир по-новому!
Изучить вопрос глубже – https://westernedge.org.au/introducing-calibanst
888starz apk download 888starz apk download .
В Сочи решение есть — наркологическая клиника. Здесь помогают людям выйти из запоя без страха и осуждения. Всё анонимно, грамотно и с заботой о каждом пациенте.
Получить дополнительные сведения – запой нарколог на дом
????? 888starz ????? 888starz .
В этой информационной статье вы найдете интересное содержание, которое поможет вам расширить свои знания. Мы предлагаем увлекательный подход и уникальные взгляды на обсуждаемые темы, побуждая пользователей к активному мышлению и критическому анализу!
Детальнее – https://stylewesternbd.com/hello-world
I have been browsing online greater than three hours these days,
yet I by no means found any interesting article like yours.
It’s lovely value enough for me. Personally, if all web owners and
bloggers made just right content material as you did, the net can be much more helpful than ever before.
Первый шаг в лечении — это тщательный осмотр специалиста. Наряду с измерением жизненно важных показателей (пульс, артериальное давление, температура) врач проводит сбор анамнеза, выясняя длительность запоя, тип употребляемого алкоголя и наличие сопутствующих заболеваний. Эти данные помогают оценить степень интоксикации и подобрать индивидуальный план терапии, что является ключевым для дальнейшей эффективной детоксикации.
Подробнее можно узнать тут – http://vyvod-iz-zapoya-yaroslavl0.ru
Этот информативный текст выделяется своими захватывающими аспектами, которые делают сложные темы доступными и понятными. Мы стремимся предложить читателям глубину знаний вместе с разнообразием интересных фактов. Откройте новые горизонты и развивайте свои способности познавать мир!
Разобраться лучше – http://tashiro-s.com/blog.php?itemid=53
Своевременное вмешательство позволяет снизить концентрацию токсинов, нормализовать обмен веществ и предотвратить необратимые повреждения, что делает срочный вызов нарколога на дому жизненно необходимым.
Исследовать вопрос подробнее – нарколог на дом круглосуточно в рязани
888starz opinie [url=www.888starz888. website/]888starz opinie[/url] .
мобильное приложение регистрация вход mostbet app приложении рейтинг 4 8 http://mostbet4014.ru/
Этот информативный текст отличается привлекательным содержанием и актуальными данными. Мы предлагаем читателям взглянуть на привычные вещи под новым углом, предоставляя интересный и доступный материал. Получите удовольствие от чтения и расширьте кругозор!
Разобраться лучше – https://instaprint.es/ofertas
1win crash http://www.1win3007.com
Лечение в наркологической клинике в Воронеже проводится поэтапно, с учётом состояния пациента, длительности зависимости и наличия сопутствующих заболеваний.
Получить дополнительные сведения – наркологическая клиника воронеж.
Сразу после вызова нарколог прибывает на дом для проведения тщательного первичного осмотра. На этом этапе специалист собирает краткий анамнез, измеряет жизненно важные показатели – пульс, артериальное давление, температуру – и оценивает степень алкогольной интоксикации. Полученные данные служат основой для составления индивидуального плана лечения, позволяющего подобрать оптимальные методы детоксикации.
Изучить вопрос глубже – вывод из запоя цена ярославль.
Hey there I am so happy I found your blog page, I really
found you by accident, while I was browsing on Google for something else,
Anyways I am here now and would just like to say thanks for a remarkable post and a all round thrilling blog (I also
love the theme/design), I don’t have time to read
through it all at the minute but I have saved it and also included your RSS feeds, so when I have time I will be back to read much more,
Please do keep up the great work.
Эта публикация завернет вас в вихрь увлекательного контента, сбрасывая стереотипы и открывая двери к новым идеям. Каждый абзац станет для вас открытием, полным ярких примеров и впечатляющих достижений. Подготовьтесь быть вовлеченными и удивленными каждый раз, когда продолжите читать.
Ознакомиться с деталями – https://blog.outdoorhq.com/rayonier-matariki-forests
Сразу после поступления вызова специалист прибывает на дом для проведения первичного осмотра. На данном этапе нарколог:
Подробнее – https://narcolog-na-dom-ryazan00.ru/
Этот обзорный материал предоставляет информационно насыщенные данные, касающиеся актуальных тем. Мы стремимся сделать информацию доступной и структурированной, чтобы читатели могли легко ориентироваться в наших выводах. Познайте новое с нашим обзором!
Ознакомиться с деталями – https://news.xopom.com/185071
кредит под птс спецтехники
avtolombard-pts65.ru/ekb.html
деньги под залог автомобиля авто остается
Комплексный подход к выводу из запоя на дому в Ярославле включает несколько основных этапов, которые позволяют обеспечить оперативное и безопасное лечение:
Изучить вопрос глубже – срочный вывод из запоя
Сразу после поступления вызова специалист прибывает на дом для проведения первичного осмотра. На данном этапе нарколог:
Детальнее – http://www.domen.ru
В обзорной статье вы найдете собрание важных фактов и аналитики по самым разнообразным темам. Мы рассматриваем как современные исследования, так и исторические контексты, чтобы вы могли получить полное представление о предмете. Погрузитесь в мир знаний и сделайте шаг к пониманию!
Углубиться в тему – https://flamanphysiotherapy.com/2022/01/18/hello-world
1win 1win
Крайне рекомендую https://athreebo.tv/2020/11/07/%e3%81%be%e3%82%8b29%e5%ba%97%e9%95%b7%e3%83%bb%e8%8d%89%e5%b1%b1%e8%b2%b4%e6%b4%8b%e3%81%ae%e6%80%9d%e3%81%84/
Информационный портал https://comart.com.ua о строительстве и ремонте: полезные советы, технологии, идеи, лайфхаки, расчёты и выбор материалов.
Новости Украины https://useti.org.ua в реальном времени. Всё важное — от официальных заявлений до мнений экспертов.
Архитектурный портал https://skol.if.ua современные проекты, урбанистика, дизайн, планировка, интервью с архитекторами и тренды отрасли.
Всё о строительстве https://ukrainianpages.com.ua просто и по делу. Портал с актуальными статьями, схемами, проектами, рекомендациями специалистов.
Hello, just wanted to say, I enjoyed this post. It was inspiring.
Keep on posting!
It’s a shame you don’t have a donate button! I’d certainly donate to this fantastic blog!
I suppose for now i’ll settle for book-marking and adding your RSS feed to my Google
account. I look forward to fresh updates and will talk about this
blog with my Facebook group. Talk soon!
mostbet скачать [url=http://mostbet4013.ru/]http://mostbet4013.ru/[/url]
I used to be able to find good information from your articles.
мостбет вывести деньги http://www.mostbet4003.ru
В этой информационной статье вы найдете интересное содержание, которое поможет вам расширить свои знания. Мы предлагаем увлекательный подход и уникальные взгляды на обсуждаемые темы, побуждая пользователей к активному мышлению и критическому анализу!
Разобраться лучше – https://khabarjordar.com/essay-helpers
автоломбард займ под птс
avtolombard-pts65.ru/kazan.html
кредит в залог автомобиля
оригинальные цветочные горшки оригинальные цветочные горшки .
Tremendous issues here. I’m very satisfied to see your article.
Thanks a lot and I am having a look forward to touch you.
Will you kindly drop me a e-mail?
Thank you for the good writeup. It actually used to be a leisure account it.
Look complex to far added agreeable from you!
However, how can we keep in touch?
sweet bonanza xmas free play sweet bonanza xmas free play
скачать mostbet https://mostbet4013.ru/
Новости Украины https://useti.org.ua в реальном времени. Всё важное — от официальных заявлений до мнений экспертов.
Архитектурный портал https://skol.if.ua современные проекты, урбанистика, дизайн, планировка, интервью с архитекторами и тренды отрасли.
Информационный портал https://comart.com.ua о строительстве и ремонте: полезные советы, технологии, идеи, лайфхаки, расчёты и выбор материалов.