Symfony 3. Cookie auto deletes immediately after create - symfony

I create cookie in EventSubscriber:
function onKernelResponse(FilterResponseEvent $event)
{
$response = new Response();
$cookie_data = 'test';
$cookie = new Cookie('test_cookie', $cookie_data, strtotime('now +1 year'));
$response->headers->setCookie($cookie);
$response->send();
}
then I watch to Application tab in Chrome and press F5.
this cookie appears for 0.5 sec and automatically remove
what i am doing wrong?

I hope this helps somebody.
I found problem: nginx send missed static files like .jpg to application.
When i fix nginx vhost file - cookies starts to set correctly.

Related

Symfony3 add locale in deeplink

I create a new site in symfony3 following the getting started section in the official symfony documentation in https://symfony.com/doc/current/setup.html
Everything is working ok.. if I put mydomain.com as the URL, the framework add /en or the correct local.
My question is if there is a way that if the user do a deeplink to mydomain.com/blog the framework found that the local is not present so it can add and transform the url to mydomain.com/en/blog
I'm not adding the code as it is the default one. Let me know if you need it.
There are multiple ways to do this. Probably the easiest is to have an EventSubscriber or -Listener that catches request without a locale and then handles adding that information. Since you based your project on the demo application you might want to look at their solution: https://github.com/symfony/demo/blob/master/src/EventSubscriber/RedirectToPreferredLocaleSubscriber.php
The steps to perform in your event handler are roughly these:
Listen to kernel.request event
Return early based on some criteria, e.g. homepage, a cookie with the language is set, or something else
Detect the language either by getting the default locale or determining from your available locales and the browser header which language fits best (see: https://github.com/willdurand/Negotiation#language-negotiation)
Redirect, add the locale as attribute to request, write the currently set language to a cookie, or whatever else you need to do to change the route
Thanks to #dbrumann I get to this solution... For sure it can be improve to use less code but it just did the trick.
I updated the onKernelRequest method in RedirectToPreferredLocaleSubscriber class
public function onKernelRequest(GetResponseEvent $event): void
{
$request = $event->getRequest();
$path = explode('/',$request->getPathInfo());
$hasLocale = false;
foreach ($this->locales as $key => $l) {
if($l == $path[1]){
$hasLocale = true;
}
}
if(!$hasLocale){
// Ignore sub-requests and all URLs but the homepage
if (!$event->isMasterRequest() || '/' !== $request->getPathInfo()) {
$preferredLanguage = $request->getPreferredLanguage($this->locales);
if ($preferredLanguage !== $this->defaultLocale) {
$url = "";
foreach ($path as $key => $p) {
if($key > 0){
$url .= "/" . $p;
}
}
//print_r('/' . $preferredLanguage . $url);exit;
$response = new RedirectResponse('/' . $preferredLanguage . $url);
$event->setResponse($response);
}
}
else{
// Ignore requests from referrers with the same HTTP host in order to prevent
// changing language for users who possibly already selected it for this application.
if (0 === mb_stripos($request->headers->get('referer'), $request->getSchemeAndHttpHost())) {
return;
}
$preferredLanguage = $request->getPreferredLanguage($this->locales);
if ($preferredLanguage !== $this->defaultLocale) {
$response = new RedirectResponse($this->urlGenerator->generate('homepage', ['_locale' => $preferredLanguage]));
$event->setResponse($response);
}
}
}
}

How to use Nginx X-Accel with Symfony?

I would want to use Nginx X-Accel with Symfony, for the moment I've this code.
$request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect');
$request->headers->set('X-Accel-Mapping', '/var/www/html/files/=/protected-files/');
$request->headers->set('X-Accel-Limit-Rate', '1k');
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse($file->getAbsolutePath());
$response->headers->set('Content-Disposition', 'attachment;filename="'.$filename.'"');
$response->headers->set('Cache-Control', 'no-cache');
return $response;
And the Nginx Conf:
location /protected-files {
internal;
alias /var/www/html/files;
}
To test the code (know if the file is really served by Nginx), I've add a X-Accel-Limit-Rate on 1ko/s, but a 2Mo file is downloaded instantly, then I'm sure, it doesn't work fine.
I've find this part of code on internet, because the Symfony doc, doesn't really explain how to use it... (http://symfony.com/doc/current/components/http_foundation.html#serving-files)
Why I need to return a BinaryResponse with the file, like without Nginx X-Sendfile, and add the X-Sendfile, X-Accel properties in the resuqest ? I just return the response, no the request, how it can work ?
Finally, I move the X-Accel part from $request to $response, and just set X-Accel-Redirect header.
If we want limit the download speed, we can use $request->headers->set('X-Accel-Limit-Rate', 10000);, it works well, the number is in bytes.
Then I've change the $response->headers->set('Content-Disposition', 'attachment;filename="'.$filename.'"'); to $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename);
The final code is:
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse($file->getAbsolutePath());
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$filename
);
$response->headers->set('X-Accel-Redirect', '/protected-files/path/to/file');
return $response;
And in Nginx:
location /protected-files/ {
internal;
alias /var/www/html/files/;
}

Return an image from a controller action in symfony

I need to access an image by providing its name in the url path, i tried to use this code but the image is not showing
/**
*
* #Route("images/{imgname}",name="workflow_image")
*/
public function WorkflowImageAction(Request $request,$imgname){
$filepath = $this->get('kernel')->getRootDir().'/../web/images/workflow/'.$imgname;
$file = readfile($filepath);
$headers = array(
'Content-Type' => 'image/png',
'Content-Disposition' => 'inline; filename="'.$file.'"');
return $file;
}
if you are serving a static file, you can use a BinaryFileResponse:
use Symfony\Component\HttpFoundation\BinaryFileResponse;
$file = 'path/to/file.txt';
$response = new BinaryFileResponse($file);
return $response;
More info about Serving Files in Symfony2 in the doc.
Hope this help
Are you sure, it's a good idea to share image through php?
You can write some rules for folder web/image/workflow in your server (nginx or apache).
Share them through php is bad idea.
Nginx/apache can do it very fast, not using RAM (php read full image in RAM).
Also, nginx/apache can cache this image.
All the answers here are outdated.
I would suggest not using BinaryFileResponse or using file_get_contents since they would read the whole file and place it in your memory.
Please use StreamedResponse provided at Symfony\Component\HttpFoundation\StreamedResponse.
$imageFilePath = dirname(__FILE__)."/../../var/tmp/bean.jpg";
$streamedResponse = new StreamedResponse();
$streamedResponse->headers->set("Content-Type", 'image/png');
$streamedResponse->headers->set("Content-Length", filesize($imageFilePath));
$streamedResponse->setCallback(function() use ($imageFilePath) {
readfile($imageFilePath);
});
return $streamedResponse;

Response returned only after kernel.terminate event

My understanding of kernel.terminate is that it triggers after the response has been returned to the client.
In my testing tough, this does not appear to be the case. If I put a sleep(10) in the function that's called on kernel.terminate. the browser also waits for 10 seconds. The processing seems to be happening before the response is sent.
I have the following in config:
calendar:
class: Acme\CalendarBundle\Service\CalendarService
arguments: [ #odm.document_manager, #logger, #security.context, #event_dispatcher ]
tags:
- { name: kernel.event_subscriber }
My subscriber class:
class CalendarService implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
'kernel.terminate' => 'onKernelTerminate'
);
}
public function onKernelTerminate()
{
sleep(10);
echo "hello";
}
}
UPDATE
This appears to be related to Symfony not sending a Content-Length header. If I generate that, the response return properly.
// app_dev.php
...
$kernel = new AppKernel('dev', true);
$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
// --- START EDITS ---
$size = strlen($response->getContent());
$response->headers->set('Content-Length', $size);
$response->headers->set('Connection', 'close');
// ---- END EDITS ----
$response->send();
$kernel->terminate($request, $response);
This issue turned out to be very specific to my setup (Nginx, PHP-FCGI, Symfony).
There were a handful of issues in play that caused the issue:
Symfony does not include a Content-Length nor Connection: close header
PHP-FCGI does not support the fastcgi_finish_request function
Nginx buffers the response from PHP-FCGI because Gzip is on
The solution was to switch from PHP-FCGI to PHP-FPM in order to get support for fastcgi_finish_request. Symfony internally calls this before executing the kernel terminate logic thereby definitively closing the connection.
Another way to solve this would be to turn off Gzip on Nginx, but this wasn't really an option for me.

Symfony2 Templating without request

I'm trying to send an email from a ContainerAwareCommand in Symfony2. But I get this exception when the email template is render by:
$body = $this->templating->render($template, $data);
Exception:
("You cannot create a service ("templating.helper.assets") of an inactive scope ("request").")
I found in github that this helper need the request object. Anybody knows how can I to instance the Request object?
You need to set the container into the right scope and give it a (fake) request. In most cases this will be enough:
//before you render template add bellow code
$this->getContainer()->enterScope('request');
$this->getContainer()->set('request', new Request(), 'request');
The full story is here. If you want to know the details read this issue on github.
The problem arises because you use asset() function in your template.
By default, asset() relies on Request service to generate urls to your assets (it needs to know what is the base path to you web site or what is the domain name if you use absolute asset urls, for example).
But when you run your application from command line there is no Request.
One way to fix this it to explicitely define base urls to your assets in config.yml like this:
framework:
templating:
assets_base_urls: { http: ["http://yoursite.com"], ssl: ["http://yoursite.com"] }
It is important to define both http and ssl, because if you omit one of them asset() will still depend on Request service.
The (possible) downside is that all urls to assets will now be absolute.
Since you don't have a request, you need to call the templating service directly like this:
$this->container->get('templating')->render($template, $data);
Following BetaRide's answer put me on the right track but that wasn't sufficient. Then it was complaining: "Unable to generate a URL for the named route "" as such route does not exist."
To create a valid request I've modified it to request the root of the project like so:
$request = new Request();
$request->create('/');
$this->container->enterScope('request');
$this->container->set('request', $request, 'request');
You might need to call a different route (secured root?), root worked for me just fine.
Symfony2 Docs
Bonus addition:
I had to do so much templating/routing in cli through Symfony2 commands that I've updated the initializeContainer() method in AppKernel. It creates a route to the root of the site, sets the router context and fakes a user login:
protected function initializeContainer()
{
parent::initializeContainer();
if (PHP_SAPI == 'cli') {
$container = $this->getContainer();
/**
* Fake request to home page for cli router.
* Need to set router base url to request uri because when request object
* is created it perceives the "/portal" part as path info only, not base
* url and thus router will not include it in the generated url's.
*/
$request = Request::create($container->getParameter('domain'));
$container->enterScope('request');
$container->set('request', $request, 'request');
$context = new RequestContext();
$context->fromRequest($request);
$container->get('router')->setContext($context);
$container->get('router')->getContext()->setBaseUrl($request->getRequestUri());
/**
* Fake admin user login for cli. Try database read,
* gracefully print error message if failed and continue.
* Continue mainly for doctrine:fixture:load when db still empty.
*/
try {
$user = $container->get('fos_user.user_manager')->findUserByUsername('admin');
if ($user !== null) {
$token = $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->getContainer()->get('security.token_storage')->setToken($token);
}
} catch (\Exception $e) {
echo "Fake Admin user login failed.\n";
}
}
}
You might not need the last $container->get('router')->getContext()->setBaseUrl($request->getRequestUri()); part, but I had to do it because my site root was at domain.com/siteroot/ and the router was stripping /siteroot/ away for url generation.

Resources