Merge branch '4.4' into 5.2

* 4.4:
  [Finder] Fix gitignore regex build with "**"
  Fixed deprecation warnings about passing null as parameter
  [Security] Keep Bulgarian wording consistent across all texts.
  Migrate configuration file for PHP CS Fixer 2.19/3.0
  [Form] Replace broken ServerParams mock
This commit is contained in:
Fabien Potencier 2021-05-16 15:07:46 +02:00
commit e7692c50bd
25 changed files with 195 additions and 77 deletions

3
.gitignore vendored
View File

@ -1,7 +1,8 @@
vendor/
composer.lock
phpunit.xml
.php_cs.cache
.php-cs-fixer.cache
.php-cs-fixer.php
.phpunit.result.cache
composer.phar
package.tar

View File

@ -4,7 +4,7 @@ if (!file_exists(__DIR__.'/src')) {
exit(0);
}
return PhpCsFixer\Config::create()
return (new PhpCsFixer\Config())
->setRules([
'@PHP71Migration' => true,
'@PHPUnit75Migration:risky' => true,
@ -14,7 +14,7 @@ return PhpCsFixer\Config::create()
])
->setRiskyAllowed(true)
->setFinder(
PhpCsFixer\Finder::create()
(new PhpCsFixer\Finder())
->in(__DIR__.'/src')
->append([__FILE__])
->notPath('#/Fixtures/#')
@ -40,4 +40,5 @@ return PhpCsFixer\Config::create()
// explicit trigger_error tests
->notPath('Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php')
)
->setCacheFile('.php-cs-fixer.cache')
;

View File

@ -158,9 +158,9 @@ if ('disabled' === $getEnvVar('SYMFONY_DEPRECATIONS_HELPER')) {
}
$COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar')
|| ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar 2> NUL`) : `which composer.phar 2> /dev/null`))
|| ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer 2> NUL`) : `which composer 2> /dev/null`))
|| file_exists($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? `git rev-parse --show-toplevel 2> NUL` : `git rev-parse --show-toplevel 2> /dev/null`).\DIRECTORY_SEPARATOR.'composer.phar')
|| ($COMPOSER = rtrim((string) ('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar 2> NUL`) : `which composer.phar 2> /dev/null`)))
|| ($COMPOSER = rtrim((string) ('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer 2> NUL`) : `which composer 2> /dev/null`)))
|| file_exists($COMPOSER = rtrim((string) ('\\' === \DIRECTORY_SEPARATOR ? `git rev-parse --show-toplevel 2> NUL` : `git rev-parse --show-toplevel 2> /dev/null`)).\DIRECTORY_SEPARATOR.'composer.phar')
? ('#!/usr/bin/env php' === file_get_contents($COMPOSER, false, null, 0, 18) ? $PHP : '').' '.escapeshellarg($COMPOSER) // detect shell wrappers by looking at the shebang
: 'composer';

View File

@ -46,7 +46,7 @@ class Cookie
* Sets a cookie.
*
* @param string $name The cookie name
* @param string $value The value of the cookie
* @param string|null $value The value of the cookie
* @param string|null $expires The time the cookie expires
* @param string|null $path The path on the server in which the cookie will be available on
* @param string $domain The domain that the cookie is available
@ -62,7 +62,7 @@ class Cookie
$this->rawValue = $value;
} else {
$this->value = $value;
$this->rawValue = rawurlencode($value);
$this->rawValue = rawurlencode($value ?? '');
}
$this->name = $name;
$this->path = empty($path) ? '/' : $path;

View File

@ -157,7 +157,7 @@ class NodeExtension extends AbstractExtension
{
$element = $node->getElement();
if ($this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) {
if ($element && $this->hasFlag(self::ELEMENT_NAME_IN_LOWER_CASE)) {
$element = strtolower($element);
}

View File

@ -205,7 +205,7 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
if ($value instanceof Definition) {
$class = $value->getClass();
if (isset(self::BUILTIN_TYPES[strtolower($class)])) {
if ($class && isset(self::BUILTIN_TYPES[strtolower($class)])) {
$class = strtolower($class);
} elseif (!$class || (!$this->autoload && !class_exists($class, false) && !interface_exists($class, false))) {
return;

View File

@ -88,10 +88,12 @@ class RegisterServiceSubscribersPass extends AbstractRecursivePass
$serviceMap[$key] = new Reference($type);
}
if (false !== $i = strpos($name, '::get')) {
$name = lcfirst(substr($name, 5 + $i));
} elseif (false !== strpos($name, '::')) {
$name = null;
if ($name) {
if (false !== $i = strpos($name, '::get')) {
$name = lcfirst(substr($name, 5 + $i));
} elseif (false !== strpos($name, '::')) {
$name = null;
}
}
if (null !== $name && !$this->container->has($name) && !$this->container->has($type.' $'.$name)) {

View File

@ -178,7 +178,7 @@ class ResolveBindingsPass extends AbstractRecursivePass
$typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter);
if (\array_key_exists($k = ltrim($typeHint, '\\').' $'.$parameter->name, $bindings)) {
if ($typeHint && \array_key_exists($k = ltrim($typeHint, '\\').' $'.$parameter->name, $bindings)) {
$arguments[$key] = $this->getBindingValue($bindings[$k]);
continue;

View File

@ -47,7 +47,7 @@ abstract class AbstractUriElement
$this->currentUri = $currentUri;
$elementUriIsRelative = null === parse_url(trim($this->getRawUri()), \PHP_URL_SCHEME);
$baseUriIsAbsolute = \in_array(strtolower(substr($this->currentUri, 0, 4)), ['http', 'file']);
$baseUriIsAbsolute = null !== $this->currentUri && \in_array(strtolower(substr($this->currentUri, 0, 4)), ['http', 'file']);
if ($elementUriIsRelative && !$baseUriIsAbsolute) {
throw new \InvalidArgumentException(sprintf('The URL of the element is relative, so you must define its base URI passing an absolute URL to the constructor of the "%s" class ("%s" was passed).', __CLASS__, $this->currentUri));
}

View File

@ -41,8 +41,8 @@ class NumberComparator extends Comparator
*/
public function __construct(?string $test)
{
if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test));
if (null === $test || !preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test ?? 'null'));
}
$target = $matches[2];

View File

@ -68,20 +68,16 @@ class Gitignore
$isAbsolute = false;
}
$parts = array_map(function (string $v): string {
$v = preg_quote(str_replace('\\', '', $v), '~');
$v = preg_replace_callback('~\\\\\[([^\[\]]*)\\\\\]~', function (array $matches): string {
return '['.str_replace('\\-', '-', $matches[1]).']';
}, $v);
$v = preg_replace('~\\\\\*\\\\\*~', '[^/]+(?:/[^/]+)*', $v);
$v = preg_replace('~\\\\\*~', '[^/]*', $v);
$v = preg_replace('~\\\\\?~', '[^/]', $v);
return $v;
}, explode('/', $gitignoreLine));
$regex = preg_quote(str_replace('\\', '', $gitignoreLine), '~');
$regex = preg_replace_callback('~\\\\\[((?:\\\\!)?)([^\[\]]*)\\\\\]~', function (array $matches): string {
return '['.('' !== $matches[1] ? '^' : '').str_replace('\\-', '-', $matches[2]).']';
}, $regex);
$regex = preg_replace('~(?:(?:\\\\\*){2,}(/?))+~', '(?:(?:(?!//).(?<!//))+$1)?', $regex);
$regex = preg_replace('~\\\\\*~', '[^/]*', $regex);
$regex = preg_replace('~\\\\\?~', '[^/]', $regex);
return ($isAbsolute ? '' : '(?:[^/]+/)*')
.implode('/', $parts)
.('' !== end($parts) ? '(?:$|/)' : '');
.$regex
.('/' !== substr($gitignoreLine, -1) ? '(?:$|/)' : '');
}
}

View File

@ -88,9 +88,9 @@ class GitignoreTest extends TestCase
[],
],
[
['/a', 'm/*'],
['a', 'a/b', 'a/b/c', 'm/'],
['aa', 'm', 'b/m', 'b/m/'],
['/a', 'm/*', 'o/**', 'p/**/', 'x**y'],
['a', 'a/b', 'a/b/c', 'm/', 'o/', 'p/', 'xy', 'xuy', 'x/y', 'x/u/y', 'xu/y', 'x/uy', 'xu/uy'],
['aa', 'm', 'b/m', 'b/m/', 'o', 'b/o', 'b/o/', 'p', 'b/p', 'b/p/'],
],
[
['a', '!x'],
@ -173,8 +173,8 @@ class GitignoreTest extends TestCase
],
[
['dir1/**/dir2/'],
['dir1/dirA/dir2/', 'dir1/dirA/dirB/dir2/'],
[],
['dir1/dir2/', 'dir1/dirA/dir2/', 'dir1/dirA/dirB/dir2/'],
['dir1dir2/', 'dir1xdir2/', 'dir1/xdir2/', 'dir1x/dir2/'],
],
[
['dir1/*/dir2/'],
@ -202,37 +202,22 @@ class GitignoreTest extends TestCase
['a/app/cache/file.txt'],
],
[
[
'#IamComment',
'/app/cache/',
],
['#IamComment', '/app/cache/'],
['app/cache/file.txt', 'app/cache/subdir/ile.txt'],
['a/app/cache/file.txt', '#IamComment', 'IamComment'],
],
[
[
'/app/cache/',
'#LastLineIsComment',
],
['/app/cache/', '#LastLineIsComment'],
['app/cache/file.txt', 'app/cache/subdir/ile.txt'],
['a/app/cache/file.txt', '#LastLineIsComment', 'LastLineIsComment'],
],
[
[
'/app/cache/',
'\#file.txt',
'#LastLineIsComment',
],
['/app/cache/', '\#file.txt', '#LastLineIsComment'],
['app/cache/file.txt', 'app/cache/subdir/ile.txt', '#file.txt'],
['a/app/cache/file.txt', '#LastLineIsComment', 'LastLineIsComment'],
],
[
[
'/app/cache/',
'\#file.txt',
'#IamComment',
'another_file.txt',
],
['/app/cache/', '\#file.txt', '#IamComment', 'another_file.txt'],
['app/cache/file.txt', 'app/cache/subdir/ile.txt', '#file.txt', 'another_file.txt'],
['a/app/cache/file.txt', 'IamComment', '#IamComment'],
],
@ -280,6 +265,127 @@ class GitignoreTest extends TestCase
['example/test', 'example/example.txt2', 'example/packages/foo.yaml'],
['example/example.txt', 'example/packages', 'example/packages/'],
],
// based on https://www.atlassian.com/git/tutorials/saving-changes/gitignore
[
['**/logs'],
['logs/debug.log', 'logs/monday/foo.bar'],
[],
],
[
['**/logs/debug.log'],
['logs/debug.log', 'build/logs/debug.log'],
['logs/build/debug.log'],
],
[
['*.log'],
['debug.log', 'foo.log', '.log', 'logs/debug.log'],
[],
],
[
[
'*.log',
'!important.log',
],
['debug.log', 'trace.log'],
['important.log', 'logs/important.log'],
],
[
[
'*.log',
'!important/*.log',
'trace.*',
],
['debug.log', 'important/trace.log'],
['important/debug.log'],
],
[
['/debug.log'],
['debug.log'],
['logs/debug.log'],
],
[
['debug.log'],
['debug.log', 'logs/debug.log'],
[],
],
[
['debug?.log'],
['debug0.log', 'debugg.log'],
['debug10.log'],
],
[
['debug[0-9].log'],
['debug0.log', 'debug1.log'],
['debug10.log'],
],
[
['debug[01].log'],
['debug0.log', 'debug1.log'],
['debug2.log', 'debug01.log'],
],
[
['debug[!01].log'],
['debug2.log'],
['debug0.log', 'debug1.log', 'debug01.log'],
],
[
['debug[a-z].log'],
['debuga.log', 'debugb.log'],
['debug1.log'],
],
[
['logs'],
['logs', 'logs/debug.log', 'logs/latest/foo.bar', 'build/logs', 'build/logs/debug.log'],
[],
],
[
['logs/'],
['logs/debug.log', 'logs/latest/foo.bar', 'build/logs/foo.bar', 'build/logs/latest/debug.log'],
[],
],
[
[
'logs/',
'!logs/important.log',
],
['logs/debug.log'/* must be pruned on traversal 'logs/important.log'*/],
[],
],
[
['logs/**/debug.log'],
['logs/debug.log', 'logs/monday/debug.log', 'logs/monday/pm/debug.log'],
[],
],
[
['logs/*day/debug.log'],
['logs/monday/debug.log', 'logs/tuesday/debug.log'],
['logs/latest/debug.log'],
],
[
['logs/debug.log'],
['logs/debug.log'],
['debug.log', 'build/logs/debug.log'],
],
[
['*/vendor/*'],
['a/vendor/', 'a/vendor/b', 'a/vendor/b/c'],
['a', 'vendor', 'vendor/', 'a/vendor', 'a/b/vendor', 'a/b/vendor/c'],
],
[
['**/vendor/**'],
['vendor/', 'vendor/a', 'vendor/a/b', 'a/b/vendor/c/d'],
['a', 'vendor', 'a/vendor', 'a/b/vendor'],
],
[
['***/***/vendor/*****/*****'],
['vendor/', 'vendor/a', 'vendor/a/b', 'a/b/vendor/c/d'],
['a', 'vendor', 'a/vendor', 'a/b/vendor'],
],
[
['**vendor**'],
['vendor', 'vendor/', 'vendor/a', 'vendor/a/b', 'a/vendor', 'a/b/vendor', 'a/b/vendor/c/d'],
['a'],
],
];
return $cases;

View File

@ -44,7 +44,21 @@ abstract class AbstractRequestHandlerTest extends TestCase
protected function setUp(): void
{
$this->serverParams = $this->getMockBuilder(ServerParams::class)->setMethods(['getNormalizedIniPostMaxSize', 'getContentLength'])->getMock();
$this->serverParams = new class() extends ServerParams {
public $contentLength;
public $postMaxSize = '';
public function getContentLength(): ?int
{
return $this->contentLength;
}
public function getNormalizedIniPostMaxSize(): string
{
return $this->postMaxSize;
}
};
$this->requestHandler = $this->getRequestHandler();
$this->factory = Forms::createFormFactoryBuilder()->getFormFactory();
$this->request = null;
@ -310,14 +324,10 @@ abstract class AbstractRequestHandlerTest extends TestCase
/**
* @dataProvider getPostMaxSizeFixtures
*/
public function testAddFormErrorIfPostMaxSizeExceeded($contentLength, $iniMax, $shouldFail, array $errorParams = [])
public function testAddFormErrorIfPostMaxSizeExceeded(?int $contentLength, string $iniMax, bool $shouldFail, array $errorParams = [])
{
$this->serverParams->expects($this->once())
->method('getContentLength')
->willReturn($contentLength);
$this->serverParams->expects($this->any())
->method('getNormalizedIniPostMaxSize')
->willReturn($iniMax);
$this->serverParams->contentLength = $contentLength;
$this->serverParams->postMaxSize = $iniMax;
$options = ['post_max_size_message' => 'Max {{ max }}!'];
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\TextType', null, $options);

View File

@ -131,7 +131,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
$args = [];
foreach ($parameters as $p) {
/** @var \ReflectionParameter $p */
$type = ltrim($target = ProxyHelper::getTypeHint($r, $p), '\\');
$type = ltrim($target = (string) ProxyHelper::getTypeHint($r, $p), '\\');
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
if (isset($arguments[$r->name][$p->name])) {

View File

@ -164,7 +164,7 @@ class RouterListener implements EventSubscriberInterface
private function createWelcomeResponse(): Response
{
$version = Kernel::VERSION;
$projectDir = realpath($this->projectDir).\DIRECTORY_SEPARATOR;
$projectDir = realpath((string) $this->projectDir).\DIRECTORY_SEPARATOR;
$docVersion = substr(Kernel::VERSION, 0, 3);
ob_start();

View File

@ -212,8 +212,8 @@ class HttpCacheTest extends HttpCacheTestCase
public function testValidatesPrivateResponsesCachedOnTheClient()
{
$this->setNextResponse(200, [], '', function ($request, $response) {
$etags = preg_split('/\s*,\s*/', $request->headers->get('IF_NONE_MATCH'));
$this->setNextResponse(200, [], '', function (Request $request, $response) {
$etags = preg_split('/\s*,\s*/', $request->headers->get('IF_NONE_MATCH', ''));
if ($request->cookies->has('authenticated')) {
$response->headers->set('Cache-Control', 'private, no-store');
$response->setETag('"private tag"');

View File

@ -266,7 +266,7 @@ class Email extends Message
*/
public function getPriority(): int
{
[$priority] = sscanf($this->getHeaders()->getHeaderBody('X-Priority'), '%[1-5]');
[$priority] = sscanf($this->getHeaders()->getHeaderBody('X-Priority') ?? '', '%[1-5]');
return $priority ?? 3;
}

View File

@ -188,7 +188,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
if (!$optional || $important || !\array_key_exists($varName, $defaults) || (null !== $mergedParams[$varName] && (string) $mergedParams[$varName] !== (string) $defaults[$varName])) {
// check requirement (while ignoring look-around patterns)
if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $token[2]).'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $token[2]).'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]] ?? '')) {
if ($this->strictRequirements) {
throw new InvalidParameterException(strtr($message, ['{parameter}' => $varName, '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$varName]]));
}

View File

@ -72,11 +72,11 @@
</trans-unit>
<trans-unit id="19">
<source>Too many failed login attempts, please try again in %minutes% minute.</source>
<target>Прекалено много неуспешни опити за вход, моля опитайте отново след %minutes% минута.</target>
<target>Твърде много неуспешни опити за вход, моля опитайте отново след %minutes% минута.</target>
</trans-unit>
<trans-unit id="20">
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
<target>Прекалено много неуспешни опити за вход, моля опитайте отново след %minutes% минути.</target>
<target>Твърде много неуспешни опити за вход, моля опитайте отново след %minutes% минути.</target>
</trans-unit>
</body>
</file>

View File

@ -70,6 +70,7 @@ class LdapBindAuthenticationProviderTest extends TestCase
->method('bind')
->willThrowException(new ConnectionException())
;
$ldap->method('escape')->willReturnArgument(0);
$userChecker = $this->createMock(UserCheckerInterface::class);
$provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap);
@ -207,6 +208,7 @@ class LdapBindAuthenticationProviderTest extends TestCase
->method('query')
->willReturn($query)
;
$ldap->method('escape')->willReturnArgument(0);
$userChecker = $this->createMock(UserCheckerInterface::class);
$provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap, '{username}', true, 'elsa', 'test1234A$');

View File

@ -79,8 +79,8 @@ class UsernamePasswordJsonAuthenticationListener extends AbstractListener
public function supports(Request $request): ?bool
{
if (false === strpos($request->getRequestFormat(), 'json')
&& false === strpos($request->getContentType(), 'json')
if (false === strpos($request->getRequestFormat() ?? '', 'json')
&& false === strpos($request->getContentType() ?? '', 'json')
) {
return false;
}

View File

@ -134,7 +134,7 @@ class XliffFileLoader implements LoaderInterface
private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
{
$xml = simplexml_import_dom($dom);
$encoding = strtoupper($dom->encoding);
$encoding = $dom->encoding ? strtoupper($dom->encoding) : null;
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');

View File

@ -446,7 +446,7 @@ EOF
*/
protected function assertValidLocale(string $locale)
{
if (1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
if (null !== $locale && 1 !== preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) {
throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale));
}
}

View File

@ -93,7 +93,7 @@ class IpValidator extends ConstraintValidator
break;
default:
$flag = null;
$flag = 0;
break;
}

View File

@ -624,7 +624,7 @@ class Parser
$data = [];
if ($this->getCurrentLineIndentation() >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
$data[] = substr($this->currentLine, $newIndent ?? 0);
} elseif ($this->isCurrentLineEmpty() || $this->isCurrentLineComment()) {
$data[] = $this->currentLine;
} else {