ZF2 second round of bootstrapping - php

How would I be able to subscribe to events in a way such that a second round of bootstrapping would be possible in one of my modules, making sure all modules have had their onBootstrap() methods already called?
I have already tried subscribing to the same onBootstrap() event from within my Module's onBootstrap(), but with a lower priority. That didn't work; apparently the events that are to be triggered are determined before triggering any, and therefore you cannot subscribe to the same event that is currently being triggered and expect it to work.
I also wanted to try to subscribe to loadModules.post within init(), and then subscribe to EVENT_BOOTSTRAP, but I realized I couldn't find any way to access $mvcEvent, and in turn, $application, and in turn, the application event manager where the subscription needs to take place on.

I had to do exactly this just the other day.
You're right that you can't attach in the onBootstrap event, you need to attach a listener in the init method instead, and you need to use the shared manager to listen to the MvcEvent being triggered by the application
public function init(ModuleManager $modules)
{
// attach to the end of the bootstrap event
$modules->getEventManager()
->getSharedManager()
->attach('Zend\Mvc\Application', MvcEvent::EVENT_BOOTSTRAP, function ($e) {
// do something after everything else has bootstrapped
}, -1000);
}

Related

Log contextual data (without specifying it explicitly)

I'm working on a multi-tenant app where I need to log a lot more data than what I pass to the log facade. What I mean is, every time I do this...
Log::info('something happened');
I get this:
[2017-02-15 18:12:55] local.INFO: something happened
But I want to get this:
[2017-02-15 18:12:55] [my ec2 instance id] [client_id] local.INFO: something happened
As you can see I'm logging the EC2 instance ID and my app's client ID. I'm of course simplifying this as I need to log a lot more stuff in there. When I consume and aggregate these logs, having these extra fields make them incredibly handy to figure what things went wrong and where.
In the Zend Framework, I usually subclass the logger and add these extra fields in my subclass but I'm not sure how I can do that with Laravel. I can't find where the logger is instantiated so that I can plug my custom logger in (if that is even the way to go in Laravel).
So, I'm not asking how to get the EC2 instance ID and the other stuff, I'm only asking what the proper way to "hot wire" the Laravel logger is to be able to plug this in.
Just an idea... the logger in Laravel is really a Monolog instance... You could push a handler on it and do whatever processing you want for each entry... like so...
<?php
$logger->pushProcessor(function ($record) {
$record['extra']['dummy'] = 'Hello world!';
return $record;
});
As per the Laravel doc you can hook up into the monolog config at boot...
Custom Monolog Configuration
If you would like to have complete control over how Monolog is
configured for your application, you may use the application's
configureMonologUsing method. You should place a call to this method
in your bootstrap/app.php file right before the $app variable is
returned by the file:
$app->configureMonologUsing(function ($monolog) {
$monolog->pushHandler(...);
});
return $app;
So instead just push a processor on the $monolog instance passed to the hook...
Just an idea, I have not tried this in Laravel but used Monolog before...

Symfony Kernel events vs Custom events

In my controller after response I have to do some work. What is better to use:
1. Listen to kernel.terminate event
or
2. Dispatch my custom event
?
Why kernel.terminate?
As you can see, by calling $kernel->terminate after sending the
response, you will trigger the kernel.terminate event where you can
perform certain actions that you may have delayed in order to return
the response as quickly as possible to the client (e.g. sending
emails).
But on the other hand is it ok to check every request in my subscriber?
kernel.terminate happens after the response is sent, and can be useful for some "heavy" operations you can perform after the client has received the response. There are a few downsides however, mainly that if something goes wrong, there is no way to give the appropriate feedback to the user (for example to try again or to report a problem). Additionally, not all errors may be logged (see https://github.com/symfony/symfony/issues/19078).
Since you want to publish jobs to a Gearman queue, I would suggest avoiding using kernel.terminate, since typically publishing a job does not involve significant resources, and should be possible to do before sending the response. So you could trigger your custom event, or perhaps even avoid the event dispatcher completely by doing a more explicit call in your controller.
You won't be able to have your own event doing work after the response without using kernel.terminate. Because this is the only action that may occur after the response. We can confirm this by having a look at the front controller app.php:
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
As a note, kernel.terminate will work only if you use PHP-FPM. Otherwise, no solution outside of using some message queue.
Finally, a common pattern is to dynamically add a listener on kernel.terminate. From inside your controller, assuming you need to call my_service:
$myService = $this->get('my_service');
$this->get('event_dispatcher')->addListener('kernel.terminate', function (Event $event) use (myService) {
$myService->doSomething();
});

How to test in functional test if event has been dispatched

I am testing a controller action using a functional test in Symfony. In this test I am doing something like this:
$client->request(
'PUT',
'/api/nodes/',
$data
);
Afterwards I would like to test if a certain event has been dispatched. I already tried to enable the profiler previously (and set the config accordingly) and check the data in the EventDataCollector:
$client->enableProfiler();
$client->request(
'PUT',
'/api/nodes/' . $data[0]['id'] . '?webspace=sulu_io&language=en',
$data[0]
);
/** #var EventDataCollector $eventDataCollector */
$eventDataCollector = $client->getProfile()->getCollector('events');
This works as expected, but the problem is that the $eventDataCollector only contains data about the events for which some listeners have actually been executed. Fortunately there is an event listener executed in this specific case, but I would like that to work also without any event listeners attached, since I can't say for sure that this situation will continue to be like that.
So my question is if there is a way to test if a event is dispatched, which is save, even if there wasn't a event listener attached.
You could register an event listener/subscriber in your test environment only. Its sole purpose would be to enable you to inspect if the event was fired.
Yagni. Functional tests should be based on the specifications, e.g. sending some data to PUT /api/nodes/ HTTP/1.1 should result with something (ideally) valuable for API consumers. Some data manipulations, I suppose. The test should confirm the output matches expectations for specific data permutations.
Event listening is an internal implementation of your black box and is not subject of functional testing. It should be tested in isolation. Enabling profiler, you basically change the system under test, and end up testing something that only partially related to the production code.

What's the difference between Commands and Events in the context of Laravel 5?

So Laravel 5 was finally released yesterday with the final implementation of the command bus but I was wandering, what's the real difference in using a command bus over event mechanisms that we have in the previous releases?
Ok, I see the reason that it can be used to create commands from Request objects which is pretty useful but beyond that it seems to behave in a similar way even down to the whole queuing functionality for events now?
Can you please provide examples of use cases and where the pros and cons of either are?
Commands are things about to happen right now. i.e. "CreateUser"
Events are things that have just occured right now - i.e. "UserSuccessfullyCreated"
The differences appear minor - but have some key differences.
Commands must be specifically called/dispatched. I.e. if you want to
do CommandX - you must call CommandX somewhere.
Events respond to an event firing anywhere in your application.
The great thing is multiple event handling classes can respond to the
same event.
Lets do an example to illustrate it best. Lets say we create a user, and we want to send them a welcome email and also update our newsletter list.
In a Command Scenario would would do
AdminController {
function create() {
Bus::dispatch(new CreateUser(Auth::user());
}
}
then in our CommandClass - we would do
public function handle(CreateUser $auth)
{
// 1. Create the user here
// 2. Send welcome email
// 3. Update our newsletter
}
But if we use events - we would do something like this in our CommandClass
public function handle(CreateUser $auth)
{
// 1. Create the user here
Event::fire(new UserWasCreated($user));
}
then we can create as many events as we want to listen to that event and do something:
EventClassA
Event::listen('UserWasCreated', function($event)
{
// 2. Send welcome email
});
EventClassB
Event::listen('UserWasCreated', function($event)
{
// 3. Update our newsletter
});
The great thing is separation of concerns. The command "createuser" now does not need to worry itself about what happens after a user is created. It just needs to CreateUser.
Also - if we want to add another function after a user signs up - say enter them in a lotto draw - you can just add another Event Class and add a new event listener.
EventClassC
Event::listen('UserWasCreated', function($event)
{
// 4. Register them in lotto
});
Notice how we didnt need to touch the command CreateUser class code at all? This provides a true separation concerns of classes in a OOP style approach.
I just want to share my understanding of this concept on top of the correct answer:
The main difference is that Commands can change a Model state, while Events just react to a state change.
COMMANDS:
Commands in Laravel represent the implementation of the Command design pattern.
The main adventages of Commands:
The can be accessed from anywhere
They are very easy to read by any other developer
To create a Command in Laravel 5:
You need to generate a command DTO (which can implement the SelfHandling interface). Using php artisan make:command {command-name}
Example: php artisan make:command Course/PostCourseCommand
The naming convention for commands: speak the business language and add postfix Command to it
To call (dispatch) the command from you controller, you can use:
$this->dispatch(new PostCourseCommand())
or
Bus::dispatch(new PostCourseCommand());
Side Note:
The "dispatch from request” feature is a nice way to skip passing the variables to the command constructor one by one, instead it will resolve this for you:
Example:
$test_request = Request::create('/test', 'GET', [
'name' => 'Mahmoud',
'nickname' => 'Mega'
]);
$result = Bus::dispatchFrom(
CreateCourse::class, $test_request
);
Finally:
You can separate the handler function and it’s logic from the command DTO to the Handlers directory, to do so:
Generate a command handler via artisan
art handler:command --command="Course/PoatCourseCommand"
remove the SelfHandling interface from the Command class so it will search for a handler to handle it.
EVENTS:
Events in Laravel represent the implementation of the Observer design pattern.
To create an Event in Laravel 5:
use artisan: art make:event {event-name}
Example: art make:event LogSomething
generate an event handler for that event
art handler:event LogSomething --event="LogSomething"
register the event and it’s handler in the event service provider (app/Providers/EventServiceProvider.php)
Example:
protected $listen = [
\Zzz\Events\LogSomething::class => [ // event.name
\Zzz\Handlers\Events\LogSomething::class, //EventListener
],
],
To call (fire) an Event:
use:
Event::fire(New LogSomething());
or you can use the event helper
event(New LogSomething());
Side Note:
alternatively you can generate an event by simply registering the event in the service provider then running this command.
php artisan event:generate << this will automatically add the two classes for you
Also you can listen to an event without creating an event handler or registering a lister in the listeners array, by just going to the event service prover and inside the boot function writing your event and its action (NOT RECOMMENDED). Example:
Event::listen('XXX\Events\DoSomethingElse', function($event)
{
dd('handle me :)');
});
Finally: you can queue an event or even subscribe to multiple events from within the class itself..

Correct ZF2 Redirect behaviour

I have been using ZF2 for a few months now and I am confused about how the controller redirect is supposed to work. Is it supposed to immediately stop processing the current action, and send the redirect request? Or is it supposed to complete processing of the current action, then perform the redirect?
Pretty fundamental question, huh?
Back in ZF1, I am pretty sure the redirection took place immediately (unlike forward(), which was stored up until the current action was completed). I assumed it was the same case in ZF2 and so far that has been my experience, however today suddenly I find that controllers are storing the redirect up, and sending it at the end of the current action.
Here's an example:
public function testAction()
{
$this->redirect()->toUrl('/info');
echo 'Hello';
die();
}
In this case, the action will echo 'Hello' and then die.
I think that this is probably the normal course of events and that I have just (by sheer fluke) not noticed it before today. I just want to be sure though, before I go back and alter all my controllers. (The alternative explanation is that somewhere in my config I am destroying/overriding the redirect plugin).
In Zend Framework 2.*, execution is never halted (except for a particular upload progress handler and some locations in the console component).
Therefore, you have to stop your controller from dispatching manually:
public function testAction()
{
return $this->redirect()->toUrl('/info');
echo 'Hello'; // will never be executed
}
To be more precise, as of this callback (used when triggering a Zend\Mvc\MvcEvent::EVENT_DISPATCH), any listener to the dispatch event of an application that returns a ResponseInterface causes a "short-circuit" to the application finish event.
Short-circuiting (in the Zend\Mvc\Application) basically causes subsequent events to be skipped and forces the application to directly trigger the Zend\Mvc\MvcEvent::EVENT_FINISH event, therefore echoing the response (happens in a listener of the finish event).
In this particular controller action, the call to the $this->redirect()->toUrl('...') helper produces a Zend\Http\Response, and since we directly return it, the short-circuit is triggered.

Categories