Meaning of Event in php - php

I know php and nodejs too,in javascript we have asynchronize programming ,so I understand meaning of event in it.but I saw Event in Yii and Zend 2 and use them too,but I can't understand the meaning of it,how it works in php and what exactly does in it?

First of all, there are no events in PHP
An event is an abstraction for callback functions with their name.
Typically, we'd define them as $eventName => $listener, where $listener is a callback function for the $eventName
What is the difference between events and regular callback functions?
Again - the core point to remember, is that events are callback functions. Nothing more.
The only difference between them, is how we do invoke them.
An event is defined on bootstrap step with its required arguments, but invoked on demand without arguments. While the callback function is invoked with arguments and only
Consider this example,
<?php
$eventManager = new EventManager();
$eventManager->attach('my_event', function(){
print_r(func_get_args());
}, array('foo', 'bar'));
As we have just defined an event, we'd invoke like,
$eventManager->trigger('my_event');
This will output: Array([0] => [foo], [1] => [bar]
That's related to JavaScript!
Since most of us are familiar with JavaScript even-driven architecture, its worth nothing to mention an example of its common usage:
var a = document.getElementsByTagName('a')[0];
a.onclick = function(event) { // <-- We define an event with the event argument
event.preventDefault();
alert('A element was clicked');
}
a.click(); // <-- but we invoke it without arguments
// or If you want a Jquery
$("a").click(function(event){
event.preventDefault();
alert('A element was clicked');
});
$("a").click();
Since in PHP we don't have such event-driven nature, we can replace it with our own class that manage events and takes a full advantage of it.
Why use them?
While events confuse so many people, they are extremely useful.
Imagine you have a Content Management System (CMS), where your users can decide how to handle 404 errors. Say, they can handle with
1) Show a blank page
2) Redirect to /
3) Show a custom message
Without events you would have to do it, like
if ($router->isMatched($request)){
//do dispatch etc
} else {
// Here you start handling 404 errors
switch($config->read('404_way_handle')){
case 'show_blank':
die();
break;
case 'show_msg':
echo 'Some custom message';
break;
case 'redirect':
// do redirect
break;
}
}
With an event you can simplify the readability and keep the code more maintainable:
if ($router->isMatched($request)){
// do dispatch
} else {
$eventManager->trigger('404_handler');
}
while 404_handler itself looks like
$eventManager->attach('404_handler', function(){
switch($config->read('404_way_handle')){
case 'show_blank':
die();
break;
case 'show_msg':
echo 'Some custom message';
break;
case 'redirect':
// do redirect
break;
}
}, $config);
So let's break it down
1) Events improve readability, which is great for future
2) Events do adhere to the Single-Responsibility Principle, because you can simply inject $eventManager to your classes that need it, while callback functions could break it or could introduce a global state too (Which is bad for unit-testings).
3) There are distinct types of logic - template logic, business logic, error handler logic, data access logic etc etc. Events do simplify your application logic by decoupling business (or another kind) logic from its configuration logic, so that you end up with clear application logic.
You can watch this lecture if you want to know how they do work in Zend Framework 2 (watch it even if you're not familiar with Zend Framework 2)
Events in MVC-related architectures
Since you've been talking about frameworks, its worth nothing to mention, that there could be events in MVC-related architectures too. And since events are callback functions you can abstract common boostrap events in your MVC-like architecture, like this.
$mvcEvent->on(MVC_EVENT::ROUTE_MATCH, function(){
$mvcEvent->on(MVC_EVENT::DISTPATCH, function($content){
echo $mvcEvent->trigger(MVC_EVENT::RENDER, $content);
});
});
Note : In pure MVC theory, there are no events at all. They do acts as helpers, but again - in frameworks you can abstract them and call them "events".

This article helped me understrand EventManager in ZF2:
The Event Manager is the component of the framework which allows you to hook in to named events in your application.
There are an existing set of named events in the framework, such as the dispatch event in controllers. You can also create your own as suits your application’s purpose. That’s step one.
Then, you attach (or listen) to those events. When they fire – or are triggered – your code interrogates the context of the event and responds if needed.
reference: http://www.maltblue.com/tutorial/zend-framework-2-event-manager-a-gentle-introduction

Related

Add data return in all actions with #Template annotation

I need to return default data in all actions in my Symfony project.
For example search form in side bar, viewers counter etc...
So i need to return some default data in all actions
return array(
'form' => $form->createView(),
'short_search' => $shortSearch->createView(),
);
I found Add data to return of all actions in a Symfony controller solution, but it fails when I'm using #Template annotation.
Of course i can call render function from twig, but it seems like it's not fast and good idea.
What component I should override in this case???
The Controllers section of the Symfony Best Practices document advises against using the #Template() annotation, so the easy fix to your problem would be to simply not use #Template().
The reason overriding the base Controller's render method doesn't work is because you're not actually calling it, and neither is the framework. Instead, the SensioFrameworkExtraBundle #Template annotation works by installing an event listener for KernelEvents::VIEW (kernel.view) and (after having used a different event to guess the template name, if necessary), directly uses the templating service to render the response.
In the generic case, what you can do instead is install an event listener on kernel.view with a higher priority, and using the GetResponseForControllerResultEvent event provided to add in your parameters. This event listener might look something like
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$parameters = $event->getControllerResult();
//modify parameters
$event->setControllerResult($parameters);
}
with whatever services necessary to get the additional parameters passed in via dependency injection. You may also want to look at the implementation for #Template's TemplateListener for reference.
The Symfony Cookbook has more information on how to set up event listeners.
In your specific case, you're probably going to be generating your $form and $shortSearch entirely from within that event handler, so at the least, your event handler is going to need at least the form service injected.
In my opinion, this is all largely more trouble than it's worth, and it would be better to just remove the #Template annotation instead. (As a bonus, you'll get a minor performance boost, especially if you disable the annotations entirely, because you won't have the overhead of calling those event listeners on every request.)

Yii: Multiple events best practices

Events in Yii looks great, but several questions still wakes me at night:
If I raise an event and create several PHP event handler classes in chain, can I pass different data between them (like return value)?
Is the event designed for this goal? As far as I see, the event seems to be one-direction way of notification and passing data back is not a common practice, is that correct?
Lets say:
I have 3 handlers : Handler1, Handler2, Handler3 executed in this order. Each Handler concatenates some string data.
Can I pass the concatenated sting between handlers and are the handlers assumed to do this?
In a event chain, is throwing an exception in an event handler a good practice?
You're correct that the event system was primarily designed (or at least: documented) as a read-only notification system. However, it is possible to do what you want by creating your own subclassed Event that defines a public property for the data you want to pass around.
For example, start with a custom event class:
class MyEvent extends \yii\base\Event
{
public $data;
}
Trigger this event:
$event = new MyEvent([
'data' => 'hello world'
]);
$this->trigger('myEvent', $event);
echo "After passing through the entire event chain, data is now: " . $event->data;
And add behaviors (or handlers) that listen to it:
public function onMyEvent($event)
{
$event->data .= ', goodbye world';
}
If all went well, this should end up echo'ing hello world, goodbye world

Working of Zend_Controller_Plugin_ActionStack

I am currently reading "Zend Framework 1.8
Web Application Development" written by "Keith Pope". In that he tells us to use 'ActionStack' so that the Controller for the category top-level menu will be called on every request. The source code for the the plugin is :
class SF_Plugin_Action extends Zend_Controller_Plugin_Abstract
{
protected $_stack;
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$stack = $this->getStack();
// category menu
$categoryRequest = new Zend_Controller_Request_Simple();
$categoryRequest->setControllerName('category')
->setActionName('index')
->setParam(
'responseSegment',
'categoryMain'
);
// push requests into the stack
$stack->pushStack($categoryRequest);
}
public function getStack()
{
if (null === $this->_stack) {
$front = Zend_Controller_Front::getInstance();
if (!$front->hasPlugin(
'Zend_Controller_Plugin_ActionStack'
)) {
$stack = new Zend_Controller_Plugin_ActionStack();
$front->registerPlugin($stack);
} else {
$stack = $front->getPlugin('ActionStack');
}
$this->_stack = $stack;
}
return $this->_stack;
}
}
I have read the code for 'ActionStack' plugin. In the 'postDispatch' function it saves the current request and then in the 'forward' function it changes the current request's controller, action and also set parameters. Then what will happen to the current request ? How it will be executed ?
Also I heard ActionStack is evil. As I am a newbie I didn't understand most of it, as he did not explained it(for newbies). Why ActionStack is evil ?
ActionStack is evil as it promotes a bad practice: tying view-related logic to controllers. Additionally, it has a huge, negative impact on performance.
Typically, ActionStack is used to develop "widgetized" sites. You set up a list of widgets you need, and map them to individual controller actions, and then loop through the stack. The design flaw with this is that you're now executing N different controllers -- when, really, ONE controller is all you should use. Each individual controller should be able to map the incoming request to the necessary view and models. Instead, you're now basically executing an MVC triad simply to get back a bit of content.
The performance implications come from the fact that you now have to store the previous results in memory, and then re-dispatch -- which means running all pre/post dispatch plugins again, potentially having conflicts in state, and more.
The better approach is to have model-aware view helpers. You can use action helpers to marshal the appropriate models and inject the helpers, and then in your view scripts and/or layout, you simply invoke them. This honors an appropriate separation of concerns, and does not have the same performance implications.
(In ZF2, this marshaling is far easier, as you can define factories for individual helpers -- as such, you can simply use them in your view scripts, and not have to do anything special in the controllers at all in order to deliver widgetized content.)
It is the answer to my first question. As the action stack will be executed last (in the post dispatch) the current response object will be holding all content that got rendered for the request the user made, and the action stack will append the data to it. Hence the user will get content that he asked for + content that got rendered due to action stack
In your example. The front controller will start the execution of the current request and fire the routeStartup, routeShutdown and dispatchLoopStartup events. The dispatchLoopStartup event will call your plugin and your plugin will add a request object to the action stack.
Now the front controller will dispatch the current request, set the isDispatched flag of the current request to true and fire the postDispatch event. Now the Action stack plugin will get called. The front controller will pass the current request object to the Action Stack plugin as an argument and the Action stack plugin will update the controller, module, action properties of the current request object and set its isDispatched flag to false (Forward method).
Now the front controller will check the isDispatched flag of the current request object and since it was reset by the Action Stack plugin start the dispatch process again. And now your new request will get executed.
In short the front controller dispatches the current request, the action stack plugin resets the values of current request and the dispatch loop stars again.

PHP MVC design - multiple actions to same url/controller

In a MVC pattern, what's the best way to handle when a single view could have multiple actions of the same type (e.g POST)?
Say for instance in a TODO list application. You might allow a user to create multiple lists. Each list could have multiple items. So a user navigates to site.com/list/1 which shows them all the items on the 1st list (1 is a GET parameter). There are then 2 forms (POST) on this page to allow a user to:
Create a new item
Delete an existing item
Should the bootstrap create a "listcontroller", inspect the POST variables and then call the appropriate method similar to :
$lc = new ListController();
if(strtolower($request->verb) === 'post'):
if(isset($_POST['title'])) :
$data = $lc->newItem($_POST);
$load->view('newitem.php', $data);
else if(isset($_POST['delete']) && isset($_POST['id'])):
$data = $lc->deleteItem($_POST);
$load-view('deleteitem.php', $data);
endif;// End if post title
else:
//GET request here so show view for single list
endif; //
Or is it better to just do something like
$lc = new ListController();
if(isset($_POST)):
//controller handles logic about what function to call
$data = $lc->PostAction($_POST);
// $data could also potentially hold correct view name based on post
$load->view();
else:
//again just show single list
endif;
I'm just struggling how best to have a controller potentially handle multiple different actions, as there's potentially quite a few nested if/else or case statements to handle different scenarios. I know these would have to sit somewhere, but where is cleanest?
I know that there are many frameworks out there, but I'm going through the whole "want to understand best practice" behind it phase. Or is this totally the wrong way to do it? Should the controllers actually be structured differently?
To begin with, I actually really like, how you are dealing with implementation of MVC. None of that rails-like parody, where view is managed inside the controller.
Here is what I think is the root of your problem: you are still using a "dumb view" approach.
View is not supposed to be a synonym for "template". Instead it should be a full object, which has knowledge-of and ability-to deal with multiple templates. Also, in most of MVC-inspired design patterns, the view instances are able to request information from model layer.
In your code the issue can be traced back to view's factory ( the $load->view() method ), which only gets what controller sends it. Instead controller should only change the name of the view, and maybe send something that would change the state of view.
The best solution for you would be to create full-blown view implementation. Such that view itself could request data from model layer and , based on data it received, decide which template(s) to use and whether to require additional information from model layer.
I think you're somewhat on the right track with the latter approach. However, you should not hard code the calling of actions in your bootstrap. The bootstrap should interpret the URL and call the action methods dynamically through the use of a function like call_user_func_array.
Also, I would suggest that you leave the rendering of views up to the action code so the action logic is self sufficient and flexible. That would allow the action to analyse the input for correctness and render errors or views appropriately. Also, you've got the method 'deleteItem' on your controller, but that should really be the work of a model. Perhaps you should read up some more on MVC and try to work with an existing framework to understand the concepts better before you try to implement your own framework (I would suggest the Yii framework for that).
Here's an example of how I think your logic should be implemented in a good MVC framework.
class ListController extends BaseController
{
public function CreateAction($title){
if(ctype_alnum($title))
{
$list = new List();
$list->Title = $title;
if($list->insert())
{
$this->render_view('list/create_successful');
}
else
{
$this->render_view('list/create_failed');
}
}
else
{
$this->render_view('list/invalid_title');
}
}
public function DeleteAction($id){
$list = List::model()->getById($id);
if($list == null)
{
$this->render_view('list/errors/list_not_found');
}
elseif($list->delete())
{
$this->render_view('list/delete_successful');
}
else
{
$this->render_view('list/delete_failed');
}
}
}
here is a great tutorial on how to write your own MVC framework

Magento 1 - Why do controller action predispatch events not fire if the controller is rewritten?

Why do controller action predispatch events not fire if the controller is rewritten? Here is a snippet of store/app/code/core/Mage/Core/Controller/Varien/Action.php:
abstract class Mage_Core_Controller_Varien_Action
{
// [...]
public function preDispatch()
{
// [...]
if ($this->_rewrite()) {
return; // [What is the purpose if this?]
}
// [...]
// [This is where my event needs to be firing, but this code never gets
// executed because the controller is rewritten]
Mage::dispatchEvent(
'controller_action_predispatch_'.$this->getFullActionName(),
array('controller_action'=>$this)
);
}
// [...]
}
I don't know where to start fixing this problem. Anyone out there ever dealt with this before?
No time to test if the the behavior you're describing is accurate, but if it is I imagine it's what happens in the _rewrite function duplicates the actions of other non-event code after that call, and allowing preDispatch to continue after the rewrite would have made "bad things" happen.
In other words, it's a bug in the implementation of controller re-writing that's been ignored, because the preferred way of handling this is now at the routing level. In general, when a system level bug like this makes it into Magento, it tends to stay there, because cart owners start to rely on the broken behavior and scream loudly when anything changes, even if it's a bug fix.
If you can't re-factor your solution as described in the link above, you can still fire the event yourself in the controller class with old fashion Object Oriented Programming. Add the following to your custom controller (the one you're rewriting to)
protected function _rewrite()
{
//call the parent rewrite method so every that needs
//to happen happens
$original_result = parent::_rewrite();
//fire the event ourselve, since magento isn't firing it
Mage::dispatchEvent(
'controller_action_predispatch_'.$this->getFullActionName(),
array('controller_action'=>$this)
);
//return the original result in case another method is relying on it
return $original_result;
}
Hey, I'm not sure (as I'm not very familiar with the inner workings of Magento), but it occurs to me that _rewrite() checks whether a call to this specific action is being redirected (rewritten in a mod_rewrite kind of way) to a different controller/action. In this light, it would make sense that events for the original action would not be fired, since the whole request gets handled by a different action.

Categories