PHP hook, building hooks in your application
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!
Inspiring story there. What happened after? Thanks!
Yupoo LV
7 Oct 25 at 1:47 am
купить диплом в архангельске с занесением в реестр [url=http://frei-diplom6.ru/]купить диплом в архангельске с занесением в реестр[/url] .
Diplomi_bcOl
7 Oct 25 at 1:49 am
https://clomicareusa.com/# ClomiCare USA
AnthonyGique
7 Oct 25 at 1:50 am
кухни спб [url=http://kuhni-spb-4.ru/]кухни спб[/url] .
kyhni spb_noer
7 Oct 25 at 1:50 am
купить аттестат школы [url=https://rudik-diplom10.ru]купить аттестат школы[/url] .
Diplomi_fwSa
7 Oct 25 at 1:50 am
купить диплом с проводкой одно [url=https://frei-diplom4.ru]купить диплом с проводкой одно[/url] .
Diplomi_yoOl
7 Oct 25 at 1:52 am
купить диплом косметолога [url=http://rudik-diplom5.ru/]купить диплом косметолога[/url] .
Diplomi_pzma
7 Oct 25 at 1:52 am
купить диплом механик техникум дипломы челябинск ком [url=http://frei-diplom10.ru]купить диплом механик техникум дипломы челябинск ком[/url] .
Diplomi_mvEa
7 Oct 25 at 1:52 am
купить диплом об образовании с занесением в реестр [url=http://www.frei-diplom1.ru]купить диплом об образовании с занесением в реестр[/url] .
Diplomi_aoOi
7 Oct 25 at 1:53 am
кухни на заказ санкт петербург от производителя [url=http://www.kuhni-spb-4.ru]http://www.kuhni-spb-4.ru[/url] .
kyhni spb_cker
7 Oct 25 at 1:55 am
купить диплом в балаково [url=http://www.rudik-diplom14.ru]купить диплом в балаково[/url] .
Diplomi_dfea
7 Oct 25 at 1:55 am
Acamprosat Suchtmedizin
JeremySledE
7 Oct 25 at 1:56 am
купить диплом тренера [url=www.rudik-diplom3.ru/]купить диплом тренера[/url] .
Diplomi_ofei
7 Oct 25 at 1:57 am
как купить диплом техникума цена [url=www.educ-ua7.ru/]www.educ-ua7.ru/[/url] .
Diplomi_ueea
7 Oct 25 at 1:57 am
купить диплом в пскове [url=www.rudik-diplom4.ru]купить диплом в пскове[/url] .
Diplomi_msOr
7 Oct 25 at 1:57 am
купить диплом с занесением в реестр отзывы [url=http://frei-diplom4.ru]купить диплом с занесением в реестр отзывы[/url] .
Diplomi_fgOl
7 Oct 25 at 1:58 am
Продаём велосипеды для повседневных поездок kra 40 at кракен онион тор кракен онион зеркало кракен даркнет маркет
RichardPep
7 Oct 25 at 1:59 am
В «Частном Медике 24» работают круглосуточно, чтобы помощь при запое всегда была рядом.
Детальнее – [url=https://vyvod-iz-zapoya-v-stacionare22.ru/]вывод из запоя в стационаре клиника нижний новгород[/url]
Nathangeash
7 Oct 25 at 2:00 am
https://lesbitovok.ru
HowardGoony
7 Oct 25 at 2:00 am
купить диплом косметолога [url=www.rudik-diplom8.ru]купить диплом косметолога[/url] .
Diplomi_ygMt
7 Oct 25 at 2:01 am
наркологическая служба на дом [url=www.narkolog-na-dom-1.ru]www.narkolog-na-dom-1.ru[/url] .
narkolog na dom_iqkt
7 Oct 25 at 2:01 am
где купить настоящий диплом колледжа [url=http://frei-diplom10.ru]http://frei-diplom10.ru[/url] .
Diplomi_gbEa
7 Oct 25 at 2:03 am
диплом проведенный купить [url=http://frei-diplom6.ru]диплом проведенный купить[/url] .
Diplomi_isOl
7 Oct 25 at 2:03 am
купить диплом колледжа [url=http://educ-ua7.ru/]http://educ-ua7.ru/[/url] .
Diplomi_acea
7 Oct 25 at 2:06 am
В Екатеринбурге сервис «Похмельная Служба / Stop-Alko» предлагает услугу вывода из запоя на дому — врач приезжает оперативно, ставит капельницу и обезболивает состояние.
Детальнее – [url=https://vyvod-iz-zapoya-ekaterinburg27.ru/]вывод из запоя в стационаре екатеринбург[/url]
Michaelordek
7 Oct 25 at 2:06 am
купить диплом в ярославле [url=https://rudik-diplom3.ru]купить диплом в ярославле[/url] .
Diplomi_ibei
7 Oct 25 at 2:06 am
куплю диплом высшего образования [url=https://rudik-diplom4.ru]куплю диплом высшего образования[/url] .
Diplomi_wrOr
7 Oct 25 at 2:06 am
купить диплом без занесения в реестр [url=www.frei-diplom3.ru/]купить диплом без занесения в реестр[/url] .
Diplomi_jdKt
7 Oct 25 at 2:06 am
купить диплом косметолога [url=http://www.rudik-diplom8.ru]купить диплом косметолога[/url] .
Diplomi_gvMt
7 Oct 25 at 2:09 am
кухни на заказ производство спб [url=http://www.kuhni-spb-4.ru]http://www.kuhni-spb-4.ru[/url] .
kyhni spb_fjer
7 Oct 25 at 2:09 am
куплю диплом кандидата наук [url=https://www.rudik-diplom7.ru]куплю диплом кандидата наук[/url] .
Diplomi_sxPl
7 Oct 25 at 2:11 am
сколько стоит купить диплом в одессе [url=http://educ-ua7.ru]http://educ-ua7.ru[/url] .
Diplomi_aeea
7 Oct 25 at 2:11 am
купить диплом в лениногорске [url=https://rudik-diplom3.ru/]https://rudik-diplom3.ru/[/url] .
Diplomi_umei
7 Oct 25 at 2:12 am
купить диплом во владивостоке [url=https://rudik-diplom4.ru]купить диплом во владивостоке[/url] .
Diplomi_znOr
7 Oct 25 at 2:12 am
купить диплом средне техническое [url=http://rudik-diplom2.ru]купить диплом средне техническое[/url] .
Diplomi_uspi
7 Oct 25 at 2:12 am
кухни на заказ питер [url=www.kuhni-spb-4.ru/]кухни на заказ питер[/url] .
kyhni spb_cner
7 Oct 25 at 2:13 am
Quality posts is the important to invite the users to pay a visit the website, that’s what this website is providing.
online casino kostenlos
7 Oct 25 at 2:14 am
большая кухня на заказ [url=https://kuhni-spb-4.ru/]kuhni-spb-4.ru[/url] .
kyhni spb_xver
7 Oct 25 at 2:16 am
Мы обеспечиваем быстрое и безопасное восстановление после длительного употребления алкоголя.
Получить дополнительные сведения – [url=https://vyvod-iz-zapoya-nizhnij-novgorod11.ru/]вывод из запоя на дому круглосуточно нижний новгород[/url]
LarryZem
7 Oct 25 at 2:16 am
купить диплом спб с занесением в реестр [url=frei-diplom3.ru]frei-diplom3.ru[/url] .
Diplomi_azKt
7 Oct 25 at 2:16 am
Minotaurus token’s multi-chain support (ETH, BSC, Polygon) is user-friendly. Presale raise at $6.44M shows demand. Eager for those virtual item acquisitions.
minotaurus ico
WilliamPargy
7 Oct 25 at 2:17 am
Great work! That is the kind of information that should be shared
around the net. Disgrace on the seek engines for now
not positioning this publish higher! Come on over and discuss with my web site .
Thanks =)
ラブドール
7 Oct 25 at 2:19 am
купить диплом в ижевске [url=http://www.rudik-diplom6.ru]купить диплом в ижевске[/url] .
Diplomi_qgKr
7 Oct 25 at 2:19 am
Круглосуточный стационарный вывод из запоя в Воронеже — доступная помощь в любое время. Вы можете обратиться к нам в любое время суток, и мы окажем необходимую медицинскую помощь.
Получить дополнительные сведения – [url=https://vyvod-iz-zapoya-v-stacionare-voronezh22.ru/]вывод из запоя в стационаре воронеж[/url]
JosephZek
7 Oct 25 at 2:20 am
диплом купить с занесением в реестр отзывы [url=http://frei-diplom4.ru]http://frei-diplom4.ru[/url] .
Diplomi_fqOl
7 Oct 25 at 2:21 am
https://amoxdirectusa.shop/# amoxicillin canada price
DavidThink
7 Oct 25 at 2:22 am
I’d like to thank you for the efforts you have put in penning this site.
I really hope to see the same high-grade content from you in the
future as well. In truth, your creative writing abilities has inspired me to get my very own website now 😉
buôn bán nội tạng
7 Oct 25 at 2:22 am
дом престарелых в москве
pansionat-msk011.ru
пансионат для пожилых с инсультом
domprestarelihmskNeT
7 Oct 25 at 2:23 am
Zeemo fournit également des styles de sous-titres spécifiques à chaque plateforme pour maximiser les vues et l’engagement
sur différents canaux.
zeemo.to
7 Oct 25 at 2:24 am
Clomid price: where buy clomid prices – can i purchase cheap clomid prices
Charleshaw
7 Oct 25 at 2:24 am