Skip to content

[FrameworkBundle] Add file helper to Controller #18502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c2e7917
[FrameworkBundle] Add file helper to Controller
dfridrich Apr 10, 2016
fb665d2
[FrameworkBundle] Fix coding standard in file helper
dfridrich Apr 10, 2016
2da9399
[FrameworkBundle] Fix coding standard in file helper tests
dfridrich Apr 10, 2016
ab19f5a
[FrameworkBundle] Add auto-detect mime type feature in file helper
dfridrich Apr 11, 2016
8dde06e
[FrameworkBundle] Add ability to pass path to file helper
dfridrich Apr 11, 2016
9bbecbc
[FrameworkBundle] Fix typo in file helper
dfridrich Apr 11, 2016
1a58eb9
[FrameworkBundle] Enhance file helper code
dfridrich May 2, 2016
b647e70
[FrameworkBundle] Enhance file helper code (fix line)
dfridrich May 2, 2016
8257508
[FrameworkBundle] Add early return to file helper and make better test.
dfridrich May 2, 2016
50264eb
[FrameworkBundle] Fix file helper coding style
dfridrich May 2, 2016
86acd27
[FrameworkBundle] Remove file helper code comments.
dfridrich May 2, 2016
6fb74b5
[FrameworkBundle] Early exception if file is not readable in file hel…
dfridrich May 2, 2016
ec0aab9
Remove file from string functionality
dfridrich Jun 15, 2016
26b112c
Remove file from string functionality
dfridrich Jun 15, 2016
921c1f5
Remove unused dead code
dfridrich Jun 15, 2016
3823cfa
Remove file helper throws annotation
dfridrich Jun 15, 2016
bc040ac
Add mime type to BinaryFileResponse contructor in file helper
dfridrich Jun 15, 2016
03be5a9
Fix sprint and add test for non existing files
dfridrich Jun 15, 2016
211241d
Remove unnecessary check in file helper
dfridrich Jun 15, 2016
05495e9
Use setContentDisposition method in file helper
dfridrich Jun 15, 2016
3757cb9
Use setContentDisposition method in file helper
dfridrich Jun 15, 2016
63c831d
Simplify file helper
dfridrich Jun 15, 2016
cc7315b
Simplify file helper and remove mimetype test for null content-type h…
dfridrich Jun 15, 2016
85dcb96
Use basename function instead of pathinfo
dfridrich Jun 15, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@

use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
Expand Down Expand Up @@ -123,6 +126,23 @@ protected function json($data, $status = 200, $headers = array(), $context = arr
return new JsonResponse($data, $status, $headers);
}

/**
* Returns a BinaryFileResponse object with original or customized file name and disposition header.
*
* @param File|string $file File object or path to file to be sent as response
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be \SplFileInfo|string to be equal to BinaryFileResponse?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, see #19115

* @param string|null $fileName File name to be sent to response or null (will use original file name)
* @param string $disposition Disposition of response ("attachment" is default, other type is "inline")
*
* @return BinaryFileResponse
*/
protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesnt this new method manifest as a BC break when the subclassing controller already contains a file method?

{
$response = new BinaryFileResponse($file);
$response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFileName() : $fileName);

return $response;
}

/**
* Adds a flash message to the current session for type.
*
Expand Down
128 changes: 128 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
Expand Down Expand Up @@ -209,6 +212,126 @@ public function testJsonWithSerializerContextOverride()
$this->assertEquals('{}', $response->getContent());
}

public function testFile()
{
/* @var ContainerInterface $container */
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$container->set('kernel', $kernel);

$controller = new TestController();
$controller->setContainer($container);

/* @var BinaryFileResponse $response */
$response = $controller->file(new File(__FILE__));
$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains(basename(__FILE__), $response->headers->get('content-disposition'));
}

public function testFileAsInline()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$controller = new TestController();
$controller->setContainer($container);

/* @var BinaryFileResponse $response */
$response = $controller->file(new File(__FILE__), null, ResponseHeaderBag::DISPOSITION_INLINE);

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition'));
$this->assertContains(basename(__FILE__), $response->headers->get('content-disposition'));
}

public function testFileWithOwnFileName()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$controller = new TestController();
$controller->setContainer($container);

/* @var BinaryFileResponse $response */
$fileName = 'test.php';
$response = $controller->file(new File(__FILE__), $fileName);

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains($fileName, $response->headers->get('content-disposition'));
}

public function testFileWithOwnFileNameAsInline()
{
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$controller = new TestController();
$controller->setContainer($container);

/* @var BinaryFileResponse $response */
$fileName = 'test.php';
$response = $controller->file(new File(__FILE__), $fileName, ResponseHeaderBag::DISPOSITION_INLINE);

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition'));
$this->assertContains($fileName, $response->headers->get('content-disposition'));
}

public function testFileFromPath()
{
$controller = new TestController();

/* @var BinaryFileResponse $response */
$response = $controller->file(__FILE__);

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains(basename(__FILE__), $response->headers->get('content-disposition'));
}

public function testFileFromPathWithCustomizedFileName()
{
$controller = new TestController();

/* @var BinaryFileResponse $response */
$response = $controller->file(__FILE__, 'test.php');

$this->assertInstanceOf(BinaryFileResponse::class, $response);
$this->assertSame(200, $response->getStatusCode());
if ($response->headers->get('content-type')) {
$this->assertSame('text/x-php', $response->headers->get('content-type'));
}
$this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition'));
$this->assertContains('test.php', $response->headers->get('content-disposition'));
}

/**
* @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException
*/
public function testFileWhichDoesNotExist()
{
$controller = new TestController();

/* @var BinaryFileResponse $response */
$response = $controller->file('some-file.txt', 'test.php');
}

public function testIsGranted()
{
$authorizationChecker = $this->getMock('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface');
Expand Down Expand Up @@ -494,6 +617,11 @@ public function json($data, $status = 200, $headers = array(), $context = array(
return parent::json($data, $status, $headers, $context);
}

public function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
{
return parent::file($file, $fileName, $disposition);
}

public function isGranted($attributes, $object = null)
{
return parent::isGranted($attributes, $object);
Expand Down