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!
1 x bet giri? [url=1xbet-giris-6.com]1xbet-giris-6.com[/url] .
1xbet giris_jzsl
24 Oct 25 at 4:11 am
My spouse and I absolutely love your blog and find almost all of your post’s to be just what I’m
looking for. Do you offer guest writers to write content for you?
I wouldn’t mind publishing a post or elaborating on most of the subjects you write regarding here.
Again, awesome weblog!
Houston, Texas end dump trailer hauling service
24 Oct 25 at 4:11 am
Ремонт двигателей Митсубиси [url=www.dzen.ru/a/aPkGhHYePCku9uSe/]www.dzen.ru/a/aPkGhHYePCku9uSe/[/url] .
Kapitalnii remont benzinovih dvigatelei v Moskve_vfEn
24 Oct 25 at 4:12 am
kraken 2025
kraken
JamesDaync
24 Oct 25 at 4:12 am
купить диплом в оренбурге [url=http://rudik-diplom9.ru]купить диплом в оренбурге[/url] .
Diplomi_uqei
24 Oct 25 at 4:13 am
1xbet giri? yapam?yorum [url=https://1xbet-giris-9.com]1xbet giri? yapam?yorum[/url] .
1xbet giris_hron
24 Oct 25 at 4:16 am
https://www.lenotoplenie.ru официальный источник актуальных бонусных предложений
CollinDwets
24 Oct 25 at 4:17 am
1win lucky jet скачать [url=https://1win5519.ru/]https://1win5519.ru/[/url]
1win_kg_fuEr
24 Oct 25 at 4:17 am
I believe this is one of the such a lot significant info for me.
And i am happy reading your article. However
want to statement on some general things, The web site style is perfect, the articles is really great : D.
Just right activity, cheers
https://warnaqq88.com
24 Oct 25 at 4:17 am
1xbet t?rkiye giri? [url=http://1xbet-giris-2.com]http://1xbet-giris-2.com[/url] .
1xbet giris_ewPt
24 Oct 25 at 4:17 am
1 x bet [url=https://1xbet-9.com]https://1xbet-9.com[/url] .
1xbet_lxSn
24 Oct 25 at 4:18 am
приложение 1 вин [url=https://1win5518.ru/]приложение 1 вин[/url]
1win_kg_nukl
24 Oct 25 at 4:20 am
1xbet lite [url=http://www.1xbet-giris-4.com]http://www.1xbet-giris-4.com[/url] .
1xbet giris_rbSa
24 Oct 25 at 4:21 am
1xbet giri? yapam?yorum [url=https://www.1xbet-giris-6.com]https://www.1xbet-giris-6.com[/url] .
1xbet giris_pisl
24 Oct 25 at 4:21 am
1xbet giri?i [url=www.1xbet-giris-5.com]1xbet giri?i[/url] .
1xbet giris_niSa
24 Oct 25 at 4:22 am
Hi all, here every one is sharing these kinds of knowledge, thus it’s pleasant to
read this webpage, and I used to visit this website everyday.
fastest payout online casinos
24 Oct 25 at 4:23 am
modernlifestylezone – Fast shipping and item arrived in excellent condition, very pleased today.
Dayna Oesterle
24 Oct 25 at 4:23 am
kraken сайт
kraken сайт
JamesDaync
24 Oct 25 at 4:23 am
1xBet промокод инструкции по активации бонусных предложений и правила участия
CollinDwets
24 Oct 25 at 4:24 am
1xbet yeni giri? [url=https://1xbet-giris-8.com/]1xbet yeni giri?[/url] .
1xbet giris_ouPn
24 Oct 25 at 4:24 am
uniquedecorstore – Love how they curate unique pieces that add character to rooms.
Gaylene Koinzan
24 Oct 25 at 4:24 am
1 x bet [url=https://1xbet-giris-4.com]https://1xbet-giris-4.com[/url] .
1xbet giris_vsSa
24 Oct 25 at 4:25 am
related webpage
PHP hook, building hooks in your application – Sjoerd Maessen blog at Sjoerd Maessen blog
related webpage
24 Oct 25 at 4:25 am
кракен обмен
кракен ios
kraken официальный
24 Oct 25 at 4:27 am
1xbet giri? adresi [url=1xbet-giris-7.com]1xbet giri? adresi[/url] .
1xbet giris_jvKn
24 Oct 25 at 4:27 am
kraken сайт
kraken вход
кракен официальный сайт
24 Oct 25 at 4:28 am
HerenGezondheid: veilige online medicijnen Nederland – officiële Sildenafil webshop
Jesuskax
24 Oct 25 at 4:28 am
Thanks for some other informative web site. The place else may just
I get that kind of info written in such a perfect way?
I have a venture that I am simply now working on,
and I’ve been on the glance out for such information.
dump truck services for land clearing
24 Oct 25 at 4:29 am
1вин бет [url=www.1win5518.ru]1вин бет[/url]
1win_kg_uikl
24 Oct 25 at 4:29 am
купить диплом в люберцах [url=www.rudik-diplom15.ru]купить диплом в люберцах[/url] .
Diplomi_qjPi
24 Oct 25 at 4:32 am
1xbet resmi giri? [url=https://www.1xbet-giris-1.com]https://www.1xbet-giris-1.com[/url] .
1xbet giris_ajkt
24 Oct 25 at 4:33 am
лаки джет скачать [url=1win5519.ru]лаки джет скачать[/url]
1win_kg_qrEr
24 Oct 25 at 4:33 am
1xbet lite [url=http://www.1xbet-giris-7.com]http://www.1xbet-giris-7.com[/url] .
1xbet giris_tgKn
24 Oct 25 at 4:33 am
Приветственные бонусы также различаются по формату. Часть бонусов доступны новым клиентам. При регистрации на 1xBet, введите промокод и заберите удвоенный стартовый бонус в размере 32500 рублей.Букмекерская контора 1xBet предлагает своим клиентам играть и делать ставки с использованием бонусных средств. Это увеличивает вовлечённость к играм и повышает надежность игры.Действующий код 1xBet можно получить по этой ссылке — https://efaflex.ru/include/pages/?promokod_pri_registracii_6.html.
JesusJuize
24 Oct 25 at 4:34 am
1xbet yeni giri? adresi [url=https://1xbet-giris-9.com/]1xbet yeni giri? adresi[/url] .
1xbet giris_ezon
24 Oct 25 at 4:34 am
1x giri? [url=https://1xbet-giris-4.com]https://1xbet-giris-4.com[/url] .
1xbet giris_opSa
24 Oct 25 at 4:34 am
1xbet com giri? [url=http://www.1xbet-giris-2.com]http://www.1xbet-giris-2.com[/url] .
1xbet giris_gcPt
24 Oct 25 at 4:35 am
Hi there, just became alert to your blog through Google, and found that it’s really informative.
I am gonna watch out for brussels. I’ll appreciate if you continue this in future.
A lot of people will be benefited from your writing.
Cheers!
seo
24 Oct 25 at 4:35 am
http://herengezondheid.com/# betrouwbare online apotheek
Hermanereli
24 Oct 25 at 4:36 am
1x bet giri? [url=http://www.1xbet-giris-1.com]http://www.1xbet-giris-1.com[/url] .
1xbet giris_rdkt
24 Oct 25 at 4:36 am
kraken обмен
кракен маркетплейс
кракен android
24 Oct 25 at 4:37 am
1вин скачать официальный сайт [url=http://1win5519.ru/]http://1win5519.ru/[/url]
1win_kg_gaEr
24 Oct 25 at 4:37 am
кракен тор
кракен vk4
JamesDaync
24 Oct 25 at 4:37 am
1xbet yeni adresi [url=https://1xbet-giris-8.com]1xbet yeni adresi[/url] .
1xbet giris_xvPn
24 Oct 25 at 4:41 am
Spot on with this write-up, I absolutely believe this
website needs a great deal more attention. I’ll probably be back again to read through more, thanks for the advice!
https://garudamudacendikia.sch.id/hajartrus/?92=
24 Oct 25 at 4:41 am
I don’t know if it’s just me or if perhaps everybody else encountering issues with your website.
It appears as though some of the written text in your posts are running off the screen. Can somebody
else please provide feedback and let me know if this
is happening to them as well? This might be a problem with my web browser because I’ve had this happen before.
Appreciate it
Funny Feet Reactions
24 Oct 25 at 4:42 am
1xbet tr giri? [url=www.1xbet-giris-10.com]www.1xbet-giris-10.com[/url] .
1xbet giris_deka
24 Oct 25 at 4:42 am
financialgrowthplan – Strong resource for anyone serious about building financial stability and growth.
Dusti Eblin
24 Oct 25 at 4:42 am
1xbet giri? g?ncel [url=www.1xbet-giris-4.com/]www.1xbet-giris-4.com/[/url] .
1xbet giris_dcSa
24 Oct 25 at 4:45 am
1xbet ?ye ol [url=1xbet-giris-1.com]1xbet-giris-1.com[/url] .
1xbet giris_ggkt
24 Oct 25 at 4:46 am