What is the correct way to stop code execution after sending Response headers, but without using exit()?
I know the script SHOULD return a Response, but how can I force it to be returned from outside of a Controller, for example from a service?
Lets say my service's method does return a Response this way:
return RedirectResponse($url)->send();
But in another place it can return other things. So what I can do is to check what it does actually return:
$result = $myService->doSomething();
if ($result instanceof RedirectResponse) {
return $result;
}
What I want to achieve is to avoid checking result type in every place where I use my service, BUT I would like to see the returned response in Profiler/logs (if I use exit() I can't).
Is there a way to force kernel terminate?
EDIT:
Actually the service is used in event before any controller, so I want to do redirect before any controller execution. So maybe a way to omit controller execution?
A controller is a PHP callable you create that takes information from the HTTP request and creates and returns an HTTP response (as a Symfony Response object).
The only concern for a Controller is to process a request and return a response. Having a Service handle a Response object is probably a bad design choice.
In any case, you could just die/exit your controller and use Kernel events to hook in the Request/Response flow and inspect the returned Response. Probably the terminate event is the right choice http://symfony.com/doc/current/components/http_kernel/introduction.html
Ok, I found a way to do it. All I had to do is to terminate the kernel before exit - it does dispatch all after-response events (like profiling, logging etc.).
$response = new RedirectResponse($url);
$response->send();
$kernel->terminate($request, $response);
exit();
If anyone would found better way do to this, please answer so I can switch the mark.
Related
First code working. See Below
The second code not working. See Below
Anyone can help provide the documentation for this code Redirect::to('/')->send();
The ->send() method should only be used in places where you cannot return a response directly. And even then, it should be used as few times as possible.
Normally, a request will go through a list of middlewares, end up in a controller which will return a response and exit through another list of middlewares. This is the normal way of returning a response.
When you use the send() function, you interrupt the pipeline and send the response back immediately.
Now, the reason only send() works in your case is because you are redirecting from a constructor and a constructor cannot have a return value. Because of this, the second example will never work.
I would encourage you to use a middleware for this check instead. This way the check is reusable and you can return a redirect response without interrupting the pipeline.
I am currently busy with a PSR-7 project with responses and requests.
Currently we are setting up an application in our index.php by doing something like:
$app = new Application();
$app->loadConfiguration(
'../config/global.yml',
);
// Should return the response?
$app->run((new ServerRequestFactory())->createServerRequestFromGlobals());
Here the run method also calls an emit method that is responsible for sending the headers and printing the body of the response.
The request and respons are now linked together in one call which makes it hard to test since you don't want to send the response with the headers straight to PHPUnit.
I have removed the emit call in the chain of the run method and added this to the index after the run method call:
// Send the response.
$app->send();
This way they are decoupled but the downside is I now have to hold a instance of my response in a response property inside my Application.php($app) class.
I want to move the response instance to the response class itself but my co-workers thinks a class should never hold an instance of itself. Yet when I look at frameworks this happens quite a lot. Is he right about this?
What arguments can I make to decouple my request and response besides easier testing?
I am pretty new to unit testing, one of the arguments I have already heard is that I should not test the full application anyways but rather separate components and therefore should not be worried about de-coupling the request and response.
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();
});
If the incoming request was an AJAX request, no redirect will be
generated. Instead, an HTTP response with a 422 status code will be
returned to the browser containing a JSON representation of the
validation errors.
This is not working! I am trying to access the route via an ajax request and it redirects back.
If validation passes, your code will keep executing normally. However, if validation fails, an Illuminate\Contracts\Validation\ValidationException will be thrown. This exception is automatically caught and a redirect is generated to the user's previous location. The validation errors are even automatically flashed to the session!
Now I want to know where does laravel catch this exception so that I can modify it?
This is handled inside the FormRequest class:
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException($this->response(
$this->formatErrors($validator)
));
}
You can override this function in your own Request object and handle a failed validation any way you like.
After been researching for a while I will post my results so anyone with this problem saves a lot of time.
#Faiz, you technically shouldn't change a thing if you want to stick to laravel behavior (I'll always try to follow taylor's recommendations). So, to receive a 422 response code status you need to tell phpunit you will send a XMLHttpRequest. That said, this works on laravel 5
$response = $this->call('POST', $url, [], [], [],
['HTTP_X_Requested-With' => 'XMLHttpRequest']);
More information at Github Issues. Besides, if you look at Symfony\Component\HttpFoundation\Request#isXmlHttpRequest you will find that this header is used by "common JavaScript frameworks" and refers to this link
Haven't tested on the browser yet, but I think this should work too.
In a PHP MVC framework, how can I cleanly and elegantly exit from the current controller/action, but continue normal script execution?
For example, let's say my framework normally follows this outline:
Map URL to Controller/Action
Instantiate Controller, call Action (capturing output)
Do stuff
Render View
At end of Action method, continue normal operation
Process output if necessary
Send output to browser
Now, let's say I want to stop "normal" execution somewhere in the "Do Stuff" step to, say, render a different view, or do a header redirect, and I want to stop processing the rest of the body of the Action, but continue onto the "Process output" step
How can I achieve this the best way? My only ideas are:
//in controller
protected function redirect($url) {
header("Location: $url");
exit();
}
but this entirely skips the rest of the framework's execution, and dumps whatever was in the output buffer straight to the user. An alternative:
//in dispatcher
call_user_func_array(array($controller,$action),$params);
afterwards:
...
//in controller
protected function redirect($url) {
header("Location: $url");
goto afterwards;
}
However, this makes me twitch and goes against everything I've learned, especially because the label it's referencing is in another file completely.
So, is there any other way to achieve this?
Note: The redirect example probably should use the exit() way, because we're just redirecting to another page anyway and don't care about output. I'm looking for a general-use solution.
In your Action method, you can collect all of your output in a string rather than printing it out. Print it out only at the end of the method. If you need to redirect or bail out, then you haven't output anything yet and you can either redirect or return from the method.
Perhaps you could write a custom exception to represent a "Stop normal execution in the 'Do Stuff'" step? It's messy... but it would work.
you need to abstract much more things. dont just output inside a action! dont set headers as there can go so much wrong before. have different handlers. response handler. handle respons and act depending what you respond. dont just output inside actions. if you have errors throw exceptions catch them with a exceptionhadler and so on.
basic controller action
public function view(int $user_id): ResponseHandler {
$this->validate($user_id); //throws exception
if (1 == 2) throw new ControllerInvalidArgumentException();
$view = new View('nameof view');
return new ResponseHandler($view);
}
just have a look at existing frameworks they have very good implemntations of design patterns like laravel or symfony. not sure what you try just dont write your own framework. if you want to learn read and learn from others if you understand whyt they do you will know how to do.