Skip to content
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
Diff view
1 change: 1 addition & 0 deletions .github/workflows/composer-require-checker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
composer-require-checker:
uses: yiisoft/actions/.github/workflows/composer-require-checker.yml@master
with:
config: composer-require-checker.json
os: >-
['ubuntu-latest']
php: >-
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
- Enh #57: Simplify code of `HeaderValueHelper::getSortedValueAndParameters()` (@vjik)
- Bug #56: Add missed `ext-mbstring` dependency (@vjik)
- Enh #58: Add return value to closure for `preg_replace_callback()` in `HeaderValueHelper::getParameters()` (@vjik)
- Enh #59: Implement file name transliteration and remove `yiisoft/strings` dependency (@vjik)

## 1.2.0 November 09, 2021

- New #26: Add `HeaderValueHelper` that has static methods to parse header value parameters (@devanych)

## 1.1.1 February 10, 2021

- Chg: Update yiisoft/strings dependency (@samdark)
- Chg: Update `yiisoft/strings` dependency (@samdark)

## 1.1.0 December 28, 2020

Expand Down
20 changes: 20 additions & 0 deletions composer-require-checker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"symbol-whitelist" : [
"null", "true", "false",
"static", "self", "parent",
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", "mixed", "never",
"transliterator_transliterate"
],
"php-core-extensions" : [
"Core",
"date",
"json",
"hash",
"pcre",
"Phar",
"Reflection",
"SPL",
"standard"
],
"scan-files" : []
}
4 changes: 1 addition & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
],
"require": {
"php": "7.4.* || 8.0 - 8.4",
"ext-mbstring": "*",
"yiisoft/strings": "^2.0"
"ext-mbstring": "*"
},
"require-dev": {
"maglnet/composer-require-checker": "^3.8 || ^4.3",
Expand All @@ -55,7 +54,6 @@
},
"config": {
"sort-packages": true,
"bump-after-update": "dev",
"allow-plugins": {
"infection/extension-installer": true,
"composer/package-versions-deprecated": true
Expand Down
11 changes: 1 addition & 10 deletions src/ContentDispositionHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Yiisoft\Http;

use InvalidArgumentException;
use Yiisoft\Strings\Inflector;

use function in_array;

Expand Down Expand Up @@ -63,7 +62,7 @@
{
if (!in_array($type, [self::INLINE, self::ATTACHMENT])) {
throw new InvalidArgumentException(
'Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE . '".'

Check warning on line 65 in src/ContentDispositionHeader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ public static function value(string $type, ?string $fileName = null): string { if (!in_array($type, [self::INLINE, self::ATTACHMENT])) { - throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE . '".'); + throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE); } $header = $type; if ($fileName === null) {

Check warning on line 65 in src/ContentDispositionHeader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "Concat": @@ @@ public static function value(string $type, ?string $fileName = null): string { if (!in_array($type, [self::INLINE, self::ATTACHMENT])) { - throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE . '".'); + throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . '".' . self::INLINE); } $header = $type; if ($fileName === null) {

Check warning on line 65 in src/ContentDispositionHeader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ public static function value(string $type, ?string $fileName = null): string { if (!in_array($type, [self::INLINE, self::ATTACHMENT])) { - throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE . '".'); + throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . '".'); } $header = $type; if ($fileName === null) {

Check warning on line 65 in src/ContentDispositionHeader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "Concat": @@ @@ public static function value(string $type, ?string $fileName = null): string { if (!in_array($type, [self::INLINE, self::ATTACHMENT])) { - throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE . '".'); + throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . self::INLINE . '" or "' . '".'); } $header = $type; if ($fileName === null) {

Check warning on line 65 in src/ContentDispositionHeader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ public static function value(string $type, ?string $fileName = null): string { if (!in_array($type, [self::INLINE, self::ATTACHMENT])) { - throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE . '".'); + throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . self::INLINE . '".'); } $header = $type; if ($fileName === null) {

Check warning on line 65 in src/ContentDispositionHeader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "Concat": @@ @@ public static function value(string $type, ?string $fileName = null): string { if (!in_array($type, [self::INLINE, self::ATTACHMENT])) { - throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE . '".'); + throw new InvalidArgumentException('Disposition type must be either "' . '" or "' . self::ATTACHMENT . self::INLINE . '".'); } $header = $type; if ($fileName === null) {

Check warning on line 65 in src/ContentDispositionHeader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ public static function value(string $type, ?string $fileName = null): string { if (!in_array($type, [self::INLINE, self::ATTACHMENT])) { - throw new InvalidArgumentException('Disposition type must be either "' . self::ATTACHMENT . '" or "' . self::INLINE . '".'); + throw new InvalidArgumentException('Disposition type must be either "' . '" or "' . self::INLINE . '".'); } $header = $type; if ($fileName === null) {
);
}

Expand All @@ -74,15 +73,7 @@
}

$fileName = str_replace(['%', '/', '\\'], '_', $fileName);

$fallbackName = (new Inflector())->toTransliterated($fileName, Inflector::TRANSLITERATE_LOOSE);
$fallbackName = str_replace("\r\n", '_', $fallbackName);
/**
* @var string $fallbackName We use valid regular expression, so `preg_replace()` always returns string.
*/
$fallbackName = preg_replace('/[^\x20-\x7e]/u', '_', $fallbackName);
$fallbackName = str_replace('"', '\\"', $fallbackName);

$fallbackName = FallbackNameCreator::create($fileName);
$utfName = rawurlencode($fileName);

$header .= "; filename=\"{$fallbackName}\"";
Expand Down
63 changes: 63 additions & 0 deletions src/FallbackNameCreator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Http;

use function extension_loaded;

/**
* @internal
*/
final class FallbackNameCreator
{
/**
* @var string[] Fallback map for transliteration used when `intl` isn't available.
*/
private const TRANSLITERATION_MAP = [
'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'AE', 'Ç' => 'C',
'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
'Ð' => 'D', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ő' => 'O',
'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ű' => 'U', 'Ý' => 'Y', 'Þ' => 'TH',
'ß' => 'ss',
'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'ae', 'ç' => 'c',
'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',
'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ő' => 'o',
'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ű' => 'u', 'ý' => 'y', 'þ' => 'th',
'ÿ' => 'y',
];

/**
* The rule is loose, letters will be transliterated with the characters of Basic Latin Unicode Block.
*
* @see https://unicode.org/reports/tr15/#Normalization_Forms_Table
*/
private const TRANSLITERATOR = 'Any-Latin; Latin-ASCII; [\u0080-\uffff] remove';

/**
* @param string $fileName The file name to be transliterated. Should be a valid UTF-8 string.
*/
public static function create(string $fileName): string
{
$fallbackName = self::transliterate($fileName);
$fallbackName = str_replace("\r\n", '_', $fallbackName);
/**
* @var string $fallbackName We use valid regular expression, so `preg_replace()` always returns a string.
*/
$fallbackName = preg_replace('/[^\x20-\x7e]/u', '_', $fallbackName);
return str_replace('"', '\\"', $fallbackName);
}

private static function transliterate(string $fileName): string
{
if (!extension_loaded('intl')) {

Check warning on line 53 in src/FallbackNameCreator.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.2-ubuntu-latest

Escaped Mutant for Mutator "LogicalNot": @@ @@ } private static function transliterate(string $fileName): string { - if (!extension_loaded('intl')) { + if (extension_loaded('intl')) { return strtr($fileName, self::TRANSLITERATION_MAP); } /**
return strtr($fileName, self::TRANSLITERATION_MAP);

Check warning on line 54 in src/FallbackNameCreator.php

View check run for this annotation

Codecov / codecov/patch

src/FallbackNameCreator.php#L54

Added line #L54 was not covered by tests
}

/**
* @var string We assume that `$fileName` are valid UTF-8 strings and transliterator is valid, so
* `transliterator_transliterate()` never returns `false`.
*/
return transliterator_transliterate(self::TRANSLITERATOR, $fileName);
}
}
Loading