8000 [HttpFoundation] Make ResponseHeaderBag::makeDisposition static by fabpot · Pull Request #28807 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[HttpFoundation] Make ResponseHeaderBag::makeDisposition static #28807

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

Merged
merged 1 commit into from
Oct 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8000
Diff view
51 changes: 51 additions & 0 deletions src/Symfony/Component/HttpFoundation/HeaderUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
*/
class HeaderUtils
{
const DISPOSITION_ATTACHMENT = 'attachment';
const DISPOSITION_INLINE = 'inline';

/**
* This class should not be instantiated.
*/
Expand Down Expand Up @@ -143,6 +146,54 @@ public static function unquote(string $s): string
return preg_replace('/\\\\(.)|"/', '$1', $s);
}

/**
* Generates a HTTP Content-Disposition field-value.
*
* @param string $disposition One of "inline" or "attachment"
* @param string $filename A unicode string
* @param string $filenameFallback A string containing only ASCII characters that
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO $fallback would be enough in this context

* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @return string A string suitable for use as a Content-Disposition field-value
*
* @throws \InvalidArgumentException
*
* @see RFC 6266
*/
public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = ''): string
{
if (!\in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
}

if ('' === $filenameFallback) {
$filenameFallback = $filename;
}

// filenameFallback is not ASCII.
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
}

// percent characters aren't safe in fallback.
if (false !== strpos($filenameFallback, '%')) {
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
}

// path separators aren't allowed in either.
if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
}

$params = array('filename' => $filenameFallback);
if ($filename !== $filenameFallback) {
$params['filename*'] = "utf-8''".rawurlencode($filename);
}

return $disposition.'; '.self::toString($params, ';');
}

private static function groupParts(array $matches, string $separators): array
{
$separator = $separators[0];
Expand Down
44 changes: 2 additions & 42 deletions src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,51 +251,11 @@ public function clearCookie($name, $path = '/', $domain = null, $secure = false,
}

/**
* Generates a HTTP Content-Disposition field-value.
*
* @param string $disposition One of "inline" or "attachment"
* @param string $filename A unicode string
* @param string $filenameFallback A string containing only ASCII characters that
* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @return string A string suitable for use as a Content-Disposition field-value
*
* @throws \InvalidArgumentException
*
* @see RFC 6266
* @see HeaderUtils::makeDisposition()
*/
public function makeDisposition($disposition, $filename, $filenameFallback = '')
Copy link
Member

Choose a reason for hiding this comment

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

shall we deprecate then?

Copy link
Member Author

Choose a reason for hiding this comment

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

not sure it's worth it.

{
if (!\in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) {
throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
}

if ('' == $filenameFallback) {
$filenameFallback = $filename;
}

// filenameFallback is not ASCII.
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
}

// percent characters aren't safe in fallback.
if (false !== strpos($filenameFallback, '%')) {
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
}

// path separators aren't allowed in either.
if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
}

$params = array('filename' => $filenameFallback);
if ($filename !== $filenameFallback) {
$params['filename*'] = "utf-8''".rawurlencode($filename);
}

return $disposition.'; '.HeaderUtils::toString($params, ';');
return HeaderUtils::makeDisposition((string) $disposition, (string) $filename, (string) $filenameFallback);
}

/**
Expand Down
49 changes: 49 additions & 0 deletions src/Symfony/Component/HttpFoundation/Tests/HeaderUtilsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,53 @@ public function testUnquote()
$this->assertEquals('foo "bar"', HeaderUtils::unquote('"foo \"\b\a\r\""'));
$this->assertEquals('foo \\ bar', HeaderUtils::unquote('"foo \\\\ bar"'));
}

/**
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionInvalidDisposition()
{
HeaderUtils::makeDisposition('invalid', 'foo.html');
}

/**
* @dataProvider provideMakeDisposition
*/
public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
{
$this->assertEquals($expected, HeaderUtils::makeDisposition($disposition, $filename, $filenameFallback));
}

public function provideMakeDisposition()
{
return array(
array('attachment', 'foo.html', 'foo.html', 'attachment; filename=foo.html'),
array('attachment', 'foo.html', '', 'attachment; filename=foo.html'),
array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'),
array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'),
array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'),
array('attachment', 'föö.html', 'foo.html', 'attachment; filename=foo.html; filename*=utf-8\'\'f%C3%B6%C3%B6.html'),
);
}

/**
* @dataProvider provideMakeDispositionFail
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionFail($disposition, $filename)
{
HeaderUtils::makeDisposition($disposition, $filename);
}

public function provideMakeDispositionFail()
{
return array(
array('attachment', 'foo%20bar.html'),
array('attachment', 'foo/bar.html'),
array('attachment', '/foo.html'),
array('attachment', 'foo\bar.html'),
array('attachment', '\foo.html'),
array('attachment', 'föö.html'),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,26 +250,6 @@ public function testGetCookiesWithInvalidArgument()
$bag->getCookies('invalid_argument');
}

/**
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionInvalidDisposition()
{
$headers = new ResponseHeaderBag();

$headers->makeDisposition('invalid', 'foo.html');
}

/**
* @dataProvider provideMakeDisposition
*/
public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
{
$headers = new ResponseHeaderBag();

$this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback));
}

public function testToStringDoesntMessUpHeaders()
{
$headers = new ResponseHeaderBag();
Expand All @@ -284,41 +264,6 @@ public function testToStringDoesntMessUpHeaders()
$this->assertEquals(array('text/html'), $allHeaders['Content-type']);
}

public function provideMakeDisposition()
{
return array(
array('attachment', 'foo.html', 'foo.html', 'attachment; filename=foo.html'),
array('attachment', 'foo.html', '', 'attachment; filename=foo.html'),
array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'),
array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'),
array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'),
array('attachment', 'föö.html', 'foo.html', 'attachment; filename=foo.html; filename*=utf-8\'\'f%C3%B6%C3%B6.html'),
);
}

/**
* @dataProvider provideMakeDispositionFail
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionFail($disposition, $filename)
{
$headers = new ResponseHeaderBag();

$headers->makeDisposition($disposition, $filename);
}

public function provideMakeDispositionFail()
{
return array(
array('attachment', 'foo%20bar.html'),
array('attachment', 'foo/bar.html'),
array('attachment', '/foo.html'),
array('attachment', 'foo\bar.html'),
array('attachment', '\foo.html'),
array('attachment', 'föö.html'),
);
}

public function testDateHeaderAddedOnCreation()
{
$now = time();
Expand Down
0