ZF2 Remove anonymous function from event manager - events

In my module's onBootstrap function I have attached an anonymous function that is hooking into the dispatch.error event (for logging purposes):
$eventManager->attach('dispatch.error', function($e) {
//Code here
});
I am now setting up Unit Tests and do not want the logging function to run on test requests.
How do I remove this anonymous function from the event manager?
From the documentation:
attach
[snip]
The method returns an instance of Zend\Stdlib\CallbackHandler; this
value can later be passed to detach() if desired.
and
detach
[snip]
Scans all listeners, and detaches any that match $listener so that they will no longer be triggered.
I have therefore altered my attach to:
$logCallBack = $eventManager->attach('...
To what do I save the callback to so that I can remove it in my unit test?
$this->getApplication()->getEventManager()->detach(???);

Assign the handler returned and later use it to detach it
$callBackHandler = $eventManager->attach('dispatch.error', function($e) {
//Code here
});
$eventManager->detach($callBackHandler);`

I have added to the callback to the $_SERVER global variable:
$logCallBack = $eventManager->attach(['dispatch.error', 'render.error'], function($e) {
//...
}
$_SERVER['logCallBack'] = $logCallBack;
Then where I want to unregister them:
$callBacks = $_SERVER['logCallBack'];
if(!is_array($callBacks)) {
$callBacks = [$callBacks];
}
foreach($callBacks as $callback) {
$this->getApplication()->getEventManager()->detach($callback);
}

Related

v8: can't get calling function name in a functioncallback

I want to make a log of every function called when i run a js script.
So i want to make a callback for all the functions in javascript like this:
global->Set(v8::String::NewFromUtf8(isolate, "print"), v8::FunctionTemplate::New(isolate, LogName));
global->Set(v8::String::NewFromUtf8(isolate, "eval"), v8::FunctionTemplate::New(isolate, LogName));
global->Set(v8::String::NewFromUtf8(isolate, "unescape"), v8::FunctionTemplate::New(isolate, LogName));
I define my function like this:
void LogName(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::String::Utf8Value str_caller(args.Callee());
printf("%s", str_caller);
}
This is printed when unescape is called: function unescape() { [native code] }
But if do:
object = v8::Handle<v8::Object>::Cast(context->Global()->Get(v8::String::NewFromUtf8(isolate, "String")));
object->Set(v8::String::NewFromUtf8(isolate, "fromCharCode"), v8::FunctionTemplate::New(isolate, LogName)->GetFunction());
This is printed when String.fromCharCode is called: function () { [native code] }
Why in the second example i don't have the functions name, like for example "fromCharCode" ?
I'm still pretty new to V8 but have run into this exact same problem. I've found only one solution so far. I'm not sure if it is ideal, but there are no other solutions so here goes...
Notice the functions where getting the name works are where you are adding a FunctionTemplate value to an ObjectTemplate (that is presumably then used as the global template parameter when you create the Context). Also notice in the ones that don't work you are trying to add a Function to the global Object for that existing Context, and this is when getting the Callee name fails (returns a blank string).
The only solution I've found so far is to keep a persistent handle to the ObjectTemplate you create for global scope, add the FunctionTemplate to that when you go to register your new function, and then create a new Context that uses that modified ObjectTemplate. After this then calls to the function will return the Callee name as desired.
To try to illustrate this with some code:
Isolate *gIsolate;
Persistent<ObjectTemplate> gTemplate;
Persistent<Context> gContext;
// Adds a new function to the global object template
void AddFunction(const char *name, FunctionCallback func)
{
// Get global template
Local<ObjectTemplate> global = ObjectTemplate::New(gIsolate, gTemplate);
// Add new function to it
global->Set(String::NewFromUtf8(gIsolate, name), FunctionTemplate::New(gIsolate, func));
}
void FirstTimeInit()
{
gIsolate = Isolate::New();
HandleScope handle_scope(gIsolate);
Handle<ObjectTemplate> global = ObjectTemplate::New(gIsolate);
// Store global template in persistent handle
gTemplate.Reset(gIsolate, global);
// Register starting functions
AddFunction( "print", LogName );
AddFunction( "eval", LogName );
AddFunction( "unescape", LogName );
// Create context
Handle<Context> context = Context::New(gIsolate, NULL, global);
gContext.Reset(gIsolate, context);
}
void AddOtherFunction()
{
AddFunction( "fromCharCode", LogName );
Local<ObjectTemplate> global = ObjectTemplate::New(gIsolate, gTemplate);
// Create a new context from the modified global template
Local<Context> newContext = Context::New(gIsolate, nil, global);
// Update persistent context handle to reference the new one
gContext.Reset(gIsolate, newContext);
}

Jasmine test event with asynchronous call

The issue is to test the event handlers with asynchronous internal methods, which is executed by SDK like facebook.
the plain test is:
describe('Listens to someevent', function () {
it('and triggers anotherevent', function () {
var eventSpy = spyOnEvent(document, 'anotherevent');
var data = {
param1: 'param1',
param2: 'param2',
}
this.component.trigger('somevent', data);
runs(function() {
expect(eventSpy).toHaveBeenTriggeredOn(document);
});
});
});
when someevent is triggered with options, the component handler is fired:
this.handler = function (e, data) {
SDK.apicall(data, function (err, response) {
if (!err) {
doSomething();
}
// trigger data event
that.trigger(document, 'anotherevent');
});
}
;
};
In jasmine 1.3 and before, a runs without a preceding waitsFor still executes immediately. It's really the waitsFor that makes the spec asynchronous. This has changed in 2.0.
Alternatively, if you don't really want to call the external API during your tests. You can use something like jasmine-ajax (docs). This will allow you to return the ajax call immediately in test with whatever response you want to test with. This has the advantage of making your specs faster (there's no waiting for the API), and make your specs less dependent on the API being up when they run.

Fallback callback when calling unavailable function

Is it possible to set a fallback callback which is called when the user wants to call a function which does not exists? E.g.
my_object.ThisFunctionDoesNotExists(2, 4);
Now I want that a function is getting called where the first parameter is the name and a stack (or something like that) with the arguments passed. To clarify, the fallback callback should be a C++ function.
Assuming your question is about embedded V8 engine which is inferred from tags, you can use harmony Proxies feature:
var A = Proxy.create({
get: function (proxy, name) {
return function (param) {
console.log(name, param);
}
}
});
A.hello('world'); // hello world
Use --harmony_proxies param to enable this feature. From C++ code:
static const char v8_flags[] = "--harmony_proxies";
v8::V8::SetFlagsFromString(v8_flags, sizeof(v8_flags) - 1);
Other way:
There is a method on v8::ObjectTemplate called SetNamedPropertyHandler so you can intercept property access. For example:
void GetterCallback(v8::Local<v8::String> property,
const v8::PropertyCallbackInfo<v8::Value>& info)
{
// This will be called on property read
// You can return function here to call it
}
...
object_template->SetNamedPropertyHandler(GetterCallback);

How to register events from within a module in Yii?

I'm trying to register events from within a submodule in Yii.
It just doesn't seem to work.
The init method is definitely called.
class TestModule extends CWebModule
{
public function init()
{
$this->setImport(array(
'test.models.*',
'test.components.*',
));
Yii::app()->onBeginRequest = array($this, 'onBeginRequest');
}
public function onBeginRequest($event) {
die('Request!');
}
public function beforeControllerAction($controller, $action)
{
if (parent::beforeControllerAction($controller, $action))
{
return true;
}
else
return false;
}
}
To register an event you can do:
$this->getEventHandlers($eventName)->add($eventHandler);
Where $eventHandler is the name of the callback you want to define for the $eventName event.
You can also do it with the following way:
$this->attachEventHandler($eventName, $eventHandler);
I solved the problem myself.
The problem was, that i was actually too late for onBeginRequest (Request was alrdy processed).
So what i did was writing a component with Event Handlers for onBeginRequest and onEndRequest, registering the event handlers in config/main.php and call my Module from this Component.
I basically had to proxy all these events.

Symfony2 Properly Hook in Events from Services

I have a class which is used to generate navigation from a variety of interconnected bundles. I have a Navigation service to accomplish this.
In order to connect this service with the other bits of Navigation, I want to allow the other bundles to define their own services which then listen to the event listener and add their navigation items at the proper time.
The problem is, I can't figure out how to have a service listen to an event without first calling that service manually in order to create it.
Any ideas?
To give a more concrete idea, I have something like this:
// Set up as a service in the bundle.
class Navigation {
// ...
protected $dispatcher; // event dispatcher passed in to service
// ...
public function generateNavigation() {
$items = array();
// add some items
$event = new NavigationEvent($items); // custom event
$this->eventDispatcher->dispatchEvent('navigation_event', $event);
}
}
// Set up as a service in some secondary bundle.
class NavigationWorker {
/**
* #param $dispatcher Same instance as Navigation
*/
public function __construct(EventDispatcher $dispatcher) {
$dispatcher->addListener('navigation_event', array($this, 'doSomething'));
}
}
With this set up, it should work if the NavigationWorker is called at some point and is constructed, but I can't always call them directly, so it is never constructed and the listener is never added.
The way I currently do it is to pass all of the NavigationWorkers to Navigation and have it add their listener, but this is very ugly.
See the Event Listener Documentation. Make NavigationWorker and event listener and it won't need to be explicitly constructed.
I'm changing the answer to this because while that set me on the right path, it wasn't the complete answer. That article really only allows you to hook in to pre-defined kernel events. I however needed my own, so I started working back from there.
In the end, I ended up creating my own tags, a compiler pass to process those tasks. I also added my own extension of EventDispatcher, though that wasn't super-necessary (you could just use the normal one).
Here is what the file solution looked like.
Configuration:
parameters:
my_bundle.navigation.event.class: My\Bundle\DependencyInjection\NavigationEvent
my_bundle.event_dispatcher.class: My\Bundle\DependencyInjection\EventDispatcher
my_bundle.navigation.class: My\Bundle\DependencyInjection\NavigationGenerator
my_bundle.navigation_listener1.class: My\Bundle\DependencyInjection\NavigationListener
my_bundle.navigation_listener2.class: My\Bundle\DependencyInjection\NavigationListener
services:
my_bundle.event_dispatcher:
class: %my_bundle.event_dispatcher.class%
my_bundle.navigation:
class: %my_bundle.navigation.class%
arguments:
- #my_bundle.event_dispatcher
my_bundle.navigation_listener1.class:
class: %my_bundle.navigation_listener1.class%
tags:
- { name: my_bundle.event_listener, event: my_bundle.navigation.generate, method: onGenerateNavigation }
my_bundle.navigation_listener2.class:
class: %my_bundle.navigation_listener2.class%
tags:
- { name: my_bundle.event_listener, event: my_bundle.navigation.generate, method: onGenerateNavigation }
CompilerPass:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class EventListenerCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('my_bundle.event_dispatcher')) {
return;
}
$definition = $container->getDefinition(
'my_bundle.event_dispatcher'
);
$taggedServices = $container->findTaggedServiceIds(
'my_bundle.event_listener'
);
foreach ($taggedServices as $id => $tagAttributes) {
foreach ($tagAttributes as $attributes) {
$definition->addMethodCall(
'addListener',
array($this->getEventString($attributes['event'], $container), array(new Reference($id), $attributes['method']))
);
}
}
}
protected function getEventString($str, ContainerBuilder $container)
{
preg_match('/(.*)\.([^.]*)$/', $str, $matches);
$parameterName = $matches[1];
$constName = strtoupper($matches[2]);
$eventClass = $container->getParameter($parameterName . '.event.class');
if (!$eventClass) {
throw new Exception('Unable to find parameter: ' . $eventClass . '.event.class');
}
// Return the value of the constant.
return constant($eventClass . '::' . $constName);
}
Add a function like this to your compiler class (something like MyBundleBundle).
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new EventListenerCompilerPass());
}
Now the EventListener will have added listeners for each of those events. You than just implement everything else exactly as you would expect (Navigation dispatches events which it listens too). You can than hook in new event listeners from any bundle, and they don't even need to share a common class/interface.
This also works for any custom event, as long as the object which has the constant for the event is registered in the parameters with ".event.class" at the end (so my_bundle.navigation.generate looks for the parameter my_bundle.navigation.event.class, uses that class and the constant GENERATE).
Hopefully that'll help anyone else looking to do something similar.

Resources