Merge branch '5.0'
* 5.0: [Filesystem] chown and chgrp should also accept int as owner and group [DI] Fix EnvVar not loaded when Loader requires an env var Fixed #34713 Move new messages to intl domain when possible [FrameworkBundle] Fix small typo in output comment chown and chgrp should also accept int as owner and group Revert "Fixed translations file dumper behavior" Fix RememberMe with null password [Validator] Fix plurals for sr_Latn (Serbian language written in latin script) validation messages Set booted flag to false when test kernel is unset [FrameworkBundle] remove messenger cache if not enabled [PhpUnitBridge][SymfonyTestsListenerTrait] Remove some unneeded code [HttpClient] Fix strict parsing of response status codes fix PHP const mapping keys using the inline notation [SecurityBundle] Drop duplicated code [FrameworkBundle] Make sure one can use fragments.hinclude_default_template Fix that no-cache requires positive validation with the origin, even for fresh responses Improve upgrading instructions for deprecated router options [DI] Suggest typed argument when binding fails with untyped argument
This commit is contained in:
commit
29791d4dfa
@ -393,7 +393,7 @@ Routing
|
||||
-------
|
||||
|
||||
* The `generator_base_class`, `generator_cache_class`, `matcher_base_class`, and `matcher_cache_class` router
|
||||
options have been removed.
|
||||
options have been removed. If you are using multiple Router instances and need separate caches for them, set a unique `cache_dir` per Router instance instead.
|
||||
* `Serializable` implementing methods for `Route` and `CompiledRoute` are final.
|
||||
Instead of overwriting them, use `__serialize` and `__unserialize` as extension points which are forward compatible
|
||||
with the new serialization methods in PHP 7.4.
|
||||
|
@ -40,7 +40,6 @@ class SymfonyTestsListenerTrait
|
||||
private $expectedDeprecations = array();
|
||||
private $gatheredDeprecations = array();
|
||||
private $previousErrorHandler;
|
||||
private $reportUselessTests;
|
||||
private $error;
|
||||
private $runsInSeparateProcess = false;
|
||||
|
||||
@ -194,10 +193,6 @@ class SymfonyTestsListenerTrait
|
||||
public function startTest($test)
|
||||
{
|
||||
if (-2 < $this->state && ($test instanceof \PHPUnit\Framework\TestCase || $test instanceof TestCase)) {
|
||||
if (null !== $test->getTestResultObject()) {
|
||||
$this->reportUselessTests = $test->getTestResultObject()->isStrictAboutTestsThatDoNotTestAnything();
|
||||
}
|
||||
|
||||
// This event is triggered before the test is re-run in isolation
|
||||
if ($this->willBeIsolated($test)) {
|
||||
$this->runsInSeparateProcess = tempnam(sys_get_temp_dir(), 'deprec');
|
||||
@ -243,11 +238,6 @@ class SymfonyTestsListenerTrait
|
||||
$className = \get_class($test);
|
||||
$groups = Test::getGroups($className, $test->getName(false));
|
||||
|
||||
if (null !== $this->reportUselessTests) {
|
||||
$test->getTestResultObject()->beStrictAboutTestsThatDoNotTestAnything($this->reportUselessTests);
|
||||
$this->reportUselessTests = null;
|
||||
}
|
||||
|
||||
if ($errored = null !== $this->error) {
|
||||
$test->getTestResultObject()->addError($test, $this->error, 0);
|
||||
$this->error = null;
|
||||
|
@ -101,7 +101,7 @@ EOF
|
||||
(new SymfonyStyle($input, $output))
|
||||
->table(['Secret', 'Value'] + (null !== $localSecrets ? [2 => 'Local Value'] : []), $rows);
|
||||
|
||||
$io->comment("Local values override secret values.\nUse <info>secrets:set --local</info> to defined them.");
|
||||
$io->comment("Local values override secret values.\nUse <info>secrets:set --local</info> to define them.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ use Symfony\Component\Translation\Catalogue\MergeOperation;
|
||||
use Symfony\Component\Translation\Catalogue\TargetOperation;
|
||||
use Symfony\Component\Translation\Extractor\ExtractorInterface;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\MessageCatalogueInterface;
|
||||
use Symfony\Component\Translation\Reader\TranslationReaderInterface;
|
||||
use Symfony\Component\Translation\Writer\TranslationWriterInterface;
|
||||
|
||||
@ -216,6 +217,24 @@ EOF
|
||||
|
||||
$resultMessage = 'Translation files were successfully updated';
|
||||
|
||||
// move new messages to intl domain when possible
|
||||
if (class_exists(\MessageFormatter::class)) {
|
||||
foreach ($operation->getDomains() as $domain) {
|
||||
$intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
|
||||
$newMessages = $operation->getNewMessages($domain);
|
||||
|
||||
if ([] === $newMessages || ([] === $currentCatalogue->all($intlDomain) && [] !== $currentCatalogue->all($domain))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $operation->getResult();
|
||||
$allIntlMessages = $result->all($intlDomain);
|
||||
$currentMessages = array_diff_key($newMessages, $result->all($domain));
|
||||
$result->replace($currentMessages, $domain);
|
||||
$result->replace($allIntlMessages + $newMessages, $intlDomain);
|
||||
}
|
||||
}
|
||||
|
||||
// show compiled list of messages
|
||||
if (true === $input->getOption('dump-messages')) {
|
||||
$extractedMessagesCount = 0;
|
||||
|
@ -311,6 +311,7 @@ class FrameworkExtension extends Extension
|
||||
$container->removeDefinition('console.command.messenger_failed_messages_retry');
|
||||
$container->removeDefinition('console.command.messenger_failed_messages_show');
|
||||
$container->removeDefinition('console.command.messenger_failed_messages_remove');
|
||||
$container->removeDefinition('cache.messenger.restart_workers_signal');
|
||||
}
|
||||
|
||||
if ($this->httpClientConfigEnabled = $this->isConfigEnabled($container, $config['http_client'])) {
|
||||
|
@ -412,6 +412,7 @@
|
||||
<xsd:element name="bus" type="messenger_bus" minOccurs="0" maxOccurs="unbounded" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="default-bus" type="xsd:string" />
|
||||
<xsd:attribute name="enabled" type="xsd:boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="messenger_serializer">
|
||||
|
@ -43,6 +43,7 @@ abstract class KernelTestCase extends TestCase
|
||||
{
|
||||
static::ensureKernelShutdown();
|
||||
static::$kernel = null;
|
||||
static::$booted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
$container->loadFromExtension('framework', [
|
||||
'fragments' => [
|
||||
'enabled' => true,
|
||||
'hinclude_default_template' => 'global_hinclude_template',
|
||||
],
|
||||
]);
|
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
$container->loadFromExtension('framework', [
|
||||
'messenger' => false,
|
||||
]);
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
|
||||
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||
|
||||
<framework:config>
|
||||
<framework:fragments enabled="true" hinclude-default-template="global_hinclude_template"/>
|
||||
</framework:config>
|
||||
</container>
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
|
||||
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
||||
|
||||
<framework:config>
|
||||
<framework:messenger enabled="false" />
|
||||
</framework:config>
|
||||
</container>
|
@ -0,0 +1,4 @@
|
||||
framework:
|
||||
fragments:
|
||||
enabled: true
|
||||
hinclude_default_template: global_hinclude_template
|
@ -0,0 +1,2 @@
|
||||
framework:
|
||||
messenger: false
|
@ -159,6 +159,13 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
$this->assertFalse($container->hasDefinition('esi'));
|
||||
}
|
||||
|
||||
public function testFragmentsAndHinclude()
|
||||
{
|
||||
$container = $this->createContainerFromFile('fragments_and_hinclude');
|
||||
$this->assertTrue($container->hasParameter('fragment.renderer.hinclude.global_template'));
|
||||
$this->assertEquals('global_hinclude_template', $container->getParameter('fragment.renderer.hinclude.global_template'));
|
||||
}
|
||||
|
||||
public function testSsi()
|
||||
{
|
||||
$container = $this->createContainerFromFile('full');
|
||||
@ -565,9 +572,23 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
$this->assertTrue($container->hasDefinition('web_link.add_link_header_listener'));
|
||||
}
|
||||
|
||||
public function testMessengerServicesRemovedWhenDisabled()
|
||||
{
|
||||
$container = $this->createContainerFromFile('messenger_disabled');
|
||||
$this->assertFalse($container->hasDefinition('console.command.messenger_consume_messages'));
|
||||
$this->assertFalse($container->hasDefinition('console.command.messenger_debug'));
|
||||
$this->assertFalse($container->hasDefinition('console.command.messenger_stop_workers'));
|
||||
$this->assertFalse($container->hasDefinition('console.command.messenger_setup_transports'));
|
||||
$this->assertFalse($container->hasDefinition('console.command.messenger_failed_messages_retry'));
|
||||
$this->assertFalse($container->hasDefinition('console.command.messenger_failed_messages_show'));
|
||||
$this->assertFalse($container->hasDefinition('console.command.messenger_failed_messages_remove'));
|
||||
$this->assertFalse($container->hasDefinition('cache.messenger.restart_workers_signal'));
|
||||
}
|
||||
|
||||
public function testMessenger()
|
||||
{
|
||||
$container = $this->createContainerFromFile('messenger');
|
||||
$this->assertTrue($container->hasDefinition('console.command.messenger_consume_messages'));
|
||||
$this->assertTrue($container->hasAlias('messenger.default_bus'));
|
||||
$this->assertTrue($container->getAlias('messenger.default_bus')->isPublic());
|
||||
$this->assertTrue($container->hasDefinition('messenger.transport.amqp.factory'));
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Bundle\SecurityBundle\Debug;
|
||||
|
||||
use Symfony\Component\VarDumper\Caster\ClassStub;
|
||||
|
||||
/**
|
||||
* @author Robin Chalas <robin.chalas@gmail.com>
|
||||
*
|
||||
@ -35,4 +37,13 @@ trait TraceableListenerTrait
|
||||
{
|
||||
return $this->listener;
|
||||
}
|
||||
|
||||
public function getInfo(): array
|
||||
{
|
||||
return [
|
||||
'response' => $this->response,
|
||||
'time' => $this->time,
|
||||
'stub' => $this->stub ?? $this->stub = ClassStub::wrapCallable($this->listener),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -59,13 +59,4 @@ final class WrappedLazyListener extends AbstractListener
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getInfo(): array
|
||||
{
|
||||
return [
|
||||
'response' => $this->response,
|
||||
'time' => $this->time,
|
||||
'stub' => $this->stub ?? $this->stub = ClassStub::wrapCallable($this->listener),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -37,13 +37,4 @@ final class WrappedListener
|
||||
$this->time = microtime(true) - $startTime;
|
||||
$this->response = $event->getResponse();
|
||||
}
|
||||
|
||||
public function getInfo(): array
|
||||
{
|
||||
return [
|
||||
'response' => $this->response,
|
||||
'time' => $this->time,
|
||||
'stub' => $this->stub ?? $this->stub = ClassStub::wrapCallable($this->listener),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +114,8 @@ class ResolveBindingsPass extends AbstractRecursivePass
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
|
||||
$bindingNames = [];
|
||||
|
||||
foreach ($bindings as $key => $binding) {
|
||||
list($bindingValue, $bindingId, $used, $bindingType, $file) = $binding->getValues();
|
||||
if ($used) {
|
||||
@ -123,7 +125,11 @@ class ResolveBindingsPass extends AbstractRecursivePass
|
||||
$this->unusedBindings[$bindingId] = [$key, $this->currentId, $bindingType, $file];
|
||||
}
|
||||
|
||||
if (preg_match('/^(?:(?:array|bool|float|int|string) )?\$/', $key)) {
|
||||
if (preg_match('/^(?:(?:array|bool|float|int|string|([^ $]++)) )\$/', $key, $m)) {
|
||||
$bindingNames[substr($key, \strlen($m[0]))] = $binding;
|
||||
}
|
||||
|
||||
if (!isset($m[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -184,11 +190,17 @@ class ResolveBindingsPass extends AbstractRecursivePass
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$typeHint || '\\' !== $typeHint[0] || !isset($bindings[$typeHint = substr($typeHint, 1)])) {
|
||||
if ($typeHint && '\\' === $typeHint[0] && isset($bindings[$typeHint = substr($typeHint, 1)])) {
|
||||
$arguments[$key] = $this->getBindingValue($bindings[$typeHint]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$arguments[$key] = $this->getBindingValue($bindings[$typeHint]);
|
||||
if (isset($bindingNames[$parameter->name])) {
|
||||
$bindingKey = array_search($binding, $bindings, true);
|
||||
$argumentType = substr($bindingKey, 0, strpos($bindingKey, ' '));
|
||||
$this->errorMessages[] = sprintf('Did you forget to add the type "%s" to argument "$%s" of method "%s::%s()"?', $argumentType, $parameter->name, $reflectionMethod->class, $reflectionMethod->name);
|
||||
}
|
||||
}
|
||||
|
||||
if ($arguments !== $call[1]) {
|
||||
|
@ -30,8 +30,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
|
||||
public function __construct(ContainerInterface $container, \Traversable $loaders = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->loaders = new \IteratorIterator($loaders ?? new \ArrayIterator());
|
||||
$this->loaders = $this->loaders->getInnerIterator();
|
||||
$this->loaders = $loaders ?? new \ArrayIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,21 +140,33 @@ class EnvVarProcessor implements EnvVarProcessorInterface
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $env || null === $env) {
|
||||
$loaders = $this->loaders;
|
||||
$this->loaders = new \ArrayIterator();
|
||||
|
||||
try {
|
||||
while ((false === $env || null === $env) && $loaders->valid()) {
|
||||
$loader = $loaders->current();
|
||||
$loaders->next();
|
||||
$i = 0;
|
||||
$ended = true;
|
||||
$count = $loaders instanceof \Countable ? $loaders->count() : 0;
|
||||
foreach ($loaders as $loader) {
|
||||
if (\count($this->loadedVars) > $i++) {
|
||||
continue;
|
||||
}
|
||||
$this->loadedVars[] = $vars = $loader->loadEnvVars();
|
||||
$env = $vars[$name] ?? false;
|
||||
if (false !== $env = $vars[$name] ?? false) {
|
||||
$ended = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($ended || $count === $i) {
|
||||
$loaders = $this->loaders;
|
||||
}
|
||||
} catch (ParameterCircularReferenceException $e) {
|
||||
// skip loaders that need an env var that is not defined
|
||||
} finally {
|
||||
$this->loaders = $loaders;
|
||||
}
|
||||
}
|
||||
|
||||
if (false === $env || null === $env) {
|
||||
if (!$this->container->hasParameter("env($name)")) {
|
||||
|
@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Compiler\DefinitionErrorExceptionPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy;
|
||||
@ -169,4 +170,19 @@ class ResolveBindingsPassTest extends TestCase
|
||||
|
||||
$this->assertSame([1 => 'bar'], $container->getDefinition(NamedArgumentsDummy::class)->getArguments());
|
||||
}
|
||||
|
||||
public function testEmptyBindingTypehint()
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Did you forget to add the type "string" to argument "$apiKey" of method "Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy::__construct()"?');
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$bindings = [
|
||||
'string $apiKey' => new BoundArgument('foo'),
|
||||
];
|
||||
$definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class);
|
||||
$definition->setBindings($bindings);
|
||||
$pass = new ResolveBindingsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,12 @@
|
||||
namespace Symfony\Component\DependencyInjection\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\EnvVarProcessor;
|
||||
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
|
||||
|
||||
class EnvVarProcessorTest extends TestCase
|
||||
{
|
||||
@ -553,4 +555,44 @@ CSV;
|
||||
$result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {});
|
||||
$this->assertSame('123', $result); // check twice
|
||||
}
|
||||
|
||||
public function testCircularEnvLoader()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('env(FOO_CONTAINER)', 'foo');
|
||||
$container->compile();
|
||||
|
||||
$index = 0;
|
||||
$loaders = function () use (&$index) {
|
||||
if (0 === $index++) {
|
||||
throw new ParameterCircularReferenceException(['FOO_CONTAINER']);
|
||||
}
|
||||
|
||||
yield new class() implements EnvVarLoaderInterface {
|
||||
public function loadEnvVars(): array
|
||||
{
|
||||
return [
|
||||
'FOO_ENV_LOADER' => '123',
|
||||
];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
$processor = new EnvVarProcessor($container, new RewindableGenerator($loaders, 1));
|
||||
|
||||
$result = $processor->getEnv('string', 'FOO_CONTAINER', function () {});
|
||||
$this->assertSame('foo', $result);
|
||||
|
||||
$result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {});
|
||||
$this->assertSame('123', $result);
|
||||
|
||||
$result = $processor->getEnv('default', ':BAR_CONTAINER', function ($name) use ($processor) {
|
||||
$this->assertSame('BAR_CONTAINER', $name);
|
||||
|
||||
return $processor->getEnv('string', $name, function () {});
|
||||
});
|
||||
$this->assertNull($result);
|
||||
|
||||
$this->assertSame(2, $index);
|
||||
}
|
||||
}
|
||||
|
@ -207,10 +207,12 @@ class Filesystem
|
||||
* Change the owner of an array of files or directories.
|
||||
*
|
||||
* @param string|iterable $files A filename, an array of files, or a \Traversable instance to change owner
|
||||
* @param string|int $user A user name or number
|
||||
* @param bool $recursive Whether change the owner recursively or not
|
||||
*
|
||||
* @throws IOException When the change fails
|
||||
*/
|
||||
public function chown($files, string $user, bool $recursive = false)
|
||||
public function chown($files, $user, bool $recursive = false)
|
||||
{
|
||||
foreach ($this->toIterable($files) as $file) {
|
||||
if ($recursive && is_dir($file) && !is_link($file)) {
|
||||
@ -232,10 +234,12 @@ class Filesystem
|
||||
* Change the group of an array of files or directories.
|
||||
*
|
||||
* @param string|iterable $files A filename, an array of files, or a \Traversable instance to change group
|
||||
* @param string|int $group A group name or number
|
||||
* @param bool $recursive Whether change the group recursively or not
|
||||
*
|
||||
* @throws IOException When the change fails
|
||||
*/
|
||||
public function chgrp($files, string $group, bool $recursive = false)
|
||||
public function chgrp($files, $group, bool $recursive = false)
|
||||
{
|
||||
foreach ($this->toIterable($files) as $file) {
|
||||
if ($recursive && is_dir($file) && !is_link($file)) {
|
||||
|
@ -521,7 +521,7 @@ class FilesystemTest extends FilesystemTestCase
|
||||
$this->assertFilePermissions(753, $subdirectory);
|
||||
}
|
||||
|
||||
public function testChown()
|
||||
public function testChownByName()
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
@ -534,7 +534,20 @@ class FilesystemTest extends FilesystemTestCase
|
||||
$this->assertSame($owner, $this->getFileOwner($dir));
|
||||
}
|
||||
|
||||
public function testChownRecursive()
|
||||
public function testChownById()
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
$dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
|
||||
mkdir($dir);
|
||||
|
||||
$ownerId = $this->getFileOwnerId($dir);
|
||||
$this->filesystem->chown($dir, $ownerId);
|
||||
|
||||
$this->assertSame($ownerId, $this->getFileOwnerId($dir));
|
||||
}
|
||||
|
||||
public function testChownRecursiveByName()
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
@ -549,6 +562,21 @@ class FilesystemTest extends FilesystemTestCase
|
||||
$this->assertSame($owner, $this->getFileOwner($file));
|
||||
}
|
||||
|
||||
public function testChownRecursiveById()
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
$dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
|
||||
mkdir($dir);
|
||||
$file = $dir.\DIRECTORY_SEPARATOR.'file';
|
||||
touch($file);
|
||||
|
||||
$ownerId = $this->getFileOwnerId($dir);
|
||||
$this->filesystem->chown($dir, $ownerId, true);
|
||||
|
||||
$this->assertSame($ownerId, $this->getFileOwnerId($file));
|
||||
}
|
||||
|
||||
public function testChownSymlink()
|
||||
{
|
||||
$this->markAsSkippedIfSymlinkIsMissing();
|
||||
@ -624,7 +652,7 @@ class FilesystemTest extends FilesystemTestCase
|
||||
$this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999));
|
||||
}
|
||||
|
||||
public function testChgrp()
|
||||
public function testChgrpByName()
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
@ -637,6 +665,19 @@ class FilesystemTest extends FilesystemTestCase
|
||||
$this->assertSame($group, $this->getFileGroup($dir));
|
||||
}
|
||||
|
||||
public function testChgrpById()
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
$dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir';
|
||||
mkdir($dir);
|
||||
|
||||
$groupId = $this->getFileGroupId($dir);
|
||||
$this->filesystem->chgrp($dir, $groupId);
|
||||
|
||||
$this->assertSame($groupId, $this->getFileGroupId($dir));
|
||||
}
|
||||
|
||||
public function testChgrpRecursive()
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
@ -652,7 +693,7 @@ class FilesystemTest extends FilesystemTestCase
|
||||
$this->assertSame($group, $this->getFileGroup($file));
|
||||
}
|
||||
|
||||
public function testChgrpSymlink()
|
||||
public function testChgrpSymlinkByName()
|
||||
{
|
||||
$this->markAsSkippedIfSymlinkIsMissing();
|
||||
|
||||
@ -669,6 +710,23 @@ class FilesystemTest extends FilesystemTestCase
|
||||
$this->assertSame($group, $this->getFileGroup($link));
|
||||
}
|
||||
|
||||
public function testChgrpSymlinkById()
|
||||
{
|
||||
$this->markAsSkippedIfSymlinkIsMissing();
|
||||
|
||||
$file = $this->workspace.\DIRECTORY_SEPARATOR.'file';
|
||||
$link = $this->workspace.\DIRECTORY_SEPARATOR.'link';
|
||||
|
||||
touch($file);
|
||||
|
||||
$this->filesystem->symlink($file, $link);
|
||||
|
||||
$groupId = $this->getFileGroupId($link);
|
||||
$this->filesystem->chgrp($link, $groupId);
|
||||
|
||||
$this->assertSame($groupId, $this->getFileGroupId($link));
|
||||
}
|
||||
|
||||
public function testChgrpLink()
|
||||
{
|
||||
$this->markAsSkippedIfLinkIsMissing();
|
||||
|
@ -105,21 +105,36 @@ class FilesystemTestCase extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
protected function getFileOwner($filepath)
|
||||
protected function getFileOwnerId($filepath)
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
$infos = stat($filepath);
|
||||
|
||||
return ($datas = posix_getpwuid($infos['uid'])) ? $datas['name'] : null;
|
||||
return $infos['uid'];
|
||||
}
|
||||
|
||||
protected function getFileOwner($filepath)
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
return ($datas = posix_getpwuid($this->getFileOwnerId($filepath))) ? $datas['name'] : null;
|
||||
}
|
||||
|
||||
protected function getFileGroupId($filepath)
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
$infos = stat($filepath);
|
||||
|
||||
return $infos['gid'];
|
||||
}
|
||||
|
||||
protected function getFileGroup($filepath)
|
||||
{
|
||||
$this->markAsSkippedIfPosixIsMissing();
|
||||
|
||||
$infos = stat($filepath);
|
||||
if ($datas = posix_getgrgid($infos['gid'])) {
|
||||
if ($datas = posix_getgrgid($this->getFileGroupId($filepath))) {
|
||||
return $datas['name'];
|
||||
}
|
||||
|
||||
|
@ -253,7 +253,7 @@ trait ResponseTrait
|
||||
private static function addResponseHeaders(array $responseHeaders, array &$info, array &$headers, string &$debug = ''): void
|
||||
{
|
||||
foreach ($responseHeaders as $h) {
|
||||
if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([12345]\d\d) .*#', $h, $m)) {
|
||||
if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([12345]\d\d)(?: |$)#', $h, $m)) {
|
||||
if ($headers) {
|
||||
$debug .= "< \r\n";
|
||||
$headers = [];
|
||||
|
@ -350,6 +350,10 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
return $this->validate($request, $entry, $catch);
|
||||
}
|
||||
|
||||
if ($entry->headers->hasCacheControlDirective('no-cache')) {
|
||||
return $this->validate($request, $entry, $catch);
|
||||
}
|
||||
|
||||
$this->record($request, 'fresh');
|
||||
|
||||
$entry->headers->set('Age', $entry->getAge());
|
||||
|
@ -443,6 +443,22 @@ class HttpCacheTest extends HttpCacheTestCase
|
||||
$this->assertTrue($this->response->headers->has('Age'));
|
||||
}
|
||||
|
||||
public function testRevalidatesResponsesWithNoCacheDirectiveEvenIfFresh()
|
||||
{
|
||||
$this->setNextResponse(200, ['Cache-Control' => 'public, no-cache, max-age=10', 'ETag' => 'some-etag'], 'OK');
|
||||
$this->request('GET', '/'); // warm the cache
|
||||
|
||||
sleep(5);
|
||||
|
||||
$this->setNextResponse(304, ['Cache-Control' => 'public, no-cache, max-age=10', 'ETag' => 'some-etag']);
|
||||
$this->request('GET', '/');
|
||||
|
||||
$this->assertHttpKernelIsCalled(); // no-cache -> MUST have revalidated at origin
|
||||
$this->assertTraceContains('valid');
|
||||
$this->assertEquals('OK', $this->response->getContent());
|
||||
$this->assertEquals(0, $this->response->getAge());
|
||||
}
|
||||
|
||||
public function testCachesResponsesWithAnExpirationHeader()
|
||||
{
|
||||
$time = \DateTime::createFromFormat('U', time() + 5);
|
||||
|
@ -51,37 +51,36 @@ abstract class FileDumper implements DumperInterface
|
||||
throw new InvalidArgumentException('The file dumper needs a path option.');
|
||||
}
|
||||
|
||||
$hasMessageFormatter = class_exists(\MessageFormatter::class);
|
||||
|
||||
// save a file for each domain
|
||||
foreach ($messages->getDomains() as $domain) {
|
||||
if ($hasMessageFormatter) {
|
||||
$defaultDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX;
|
||||
$altDomain = $domain;
|
||||
} else {
|
||||
$defaultDomain = $domain;
|
||||
$altDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX;
|
||||
}
|
||||
$defaultPath = $options['path'].'/'.$this->getRelativePath($defaultDomain, $messages->getLocale());
|
||||
$altPath = $options['path'].'/'.$this->getRelativePath($altDomain, $messages->getLocale());
|
||||
|
||||
if (!file_exists($defaultPath) && file_exists($altPath)) {
|
||||
[$defaultPath, $altPath] = [$altPath, $defaultPath];
|
||||
}
|
||||
|
||||
if (!file_exists($defaultPath)) {
|
||||
$directory = \dirname($defaultPath);
|
||||
$fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale());
|
||||
if (!file_exists($fullpath)) {
|
||||
$directory = \dirname($fullpath);
|
||||
if (!file_exists($directory) && !@mkdir($directory, 0777, true)) {
|
||||
throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory));
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($altPath)) {
|
||||
// clear alternative translation file
|
||||
file_put_contents($altPath, $this->formatCatalogue(new MessageCatalogue($messages->getLocale()), $altDomain, $options));
|
||||
$intlDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX;
|
||||
$intlMessages = $messages->all($intlDomain);
|
||||
|
||||
if ($intlMessages) {
|
||||
$intlPath = $options['path'].'/'.$this->getRelativePath($intlDomain, $messages->getLocale());
|
||||
file_put_contents($intlPath, $this->formatCatalogue($messages, $intlDomain, $options));
|
||||
|
||||
$messages->replace([], $intlDomain);
|
||||
|
||||
try {
|
||||
if ($messages->all($domain)) {
|
||||
file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
|
||||
}
|
||||
continue;
|
||||
} finally {
|
||||
$messages->replace($intlMessages, $intlDomain);
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($defaultPath, $this->formatCatalogue($messages, $domain, $options));
|
||||
file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,15 +27,11 @@ class FileDumperTest extends TestCase
|
||||
$dumper = new ConcreteFileDumper();
|
||||
$dumper->dump($catalogue, ['path' => $tempDir]);
|
||||
|
||||
$suffix = class_exists(\MessageFormatter::class) ? '+intl-icu' : '';
|
||||
$this->assertFileExists($tempDir."/messages$suffix.en.concrete");
|
||||
$this->assertFileExists($tempDir.'/messages.en.concrete');
|
||||
|
||||
@unlink($tempDir."/messages$suffix.en.concrete");
|
||||
@unlink($tempDir.'/messages.en.concrete');
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension intl
|
||||
*/
|
||||
public function testDumpIntl()
|
||||
{
|
||||
$tempDir = sys_get_temp_dir();
|
||||
@ -46,11 +42,13 @@ class FileDumperTest extends TestCase
|
||||
$catalogue->add(['bar' => 'foo'], 'd2+intl-icu');
|
||||
|
||||
$dumper = new ConcreteFileDumper();
|
||||
@unlink($tempDir.'/d2.en.concrete');
|
||||
$dumper->dump($catalogue, ['path' => $tempDir]);
|
||||
|
||||
$this->assertFileNotExists($tempDir.'/d1.en.concrete');
|
||||
$this->assertStringEqualsFile($tempDir.'/d1.en.concrete', 'foo=bar');
|
||||
@unlink($tempDir.'/d1.en.concrete');
|
||||
|
||||
$this->assertStringEqualsFile($tempDir.'/d1+intl-icu.en.concrete', 'bar=foo&foo=bar');
|
||||
$this->assertStringEqualsFile($tempDir.'/d1+intl-icu.en.concrete', 'bar=foo');
|
||||
@unlink($tempDir.'/d1+intl-icu.en.concrete');
|
||||
|
||||
$this->assertFileNotExists($tempDir.'/d2.en.concrete');
|
||||
@ -62,8 +60,7 @@ class FileDumperTest extends TestCase
|
||||
{
|
||||
$tempDir = sys_get_temp_dir();
|
||||
$translationsDir = $tempDir.'/test/translations';
|
||||
$suffix = class_exists(\MessageFormatter::class) ? '+intl-icu' : '';
|
||||
$file = $translationsDir."/messages$suffix.en.concrete";
|
||||
$file = $translationsDir.'/messages.en.concrete';
|
||||
|
||||
$catalogue = new MessageCatalogue('en');
|
||||
$catalogue->add(['foo' => 'bar']);
|
||||
|
@ -24,11 +24,11 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="6">
|
||||
<source>You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.</source>
|
||||
<target>Morate odabrati bar {{ limit }} mogućnost.|Morate odabrati bar {{ limit }} mogućnosti.</target>
|
||||
<target>Morate odabrati bar {{ limit }} mogućnost.|Morate odabrati bar {{ limit }} mogućnosti.|Morate odabrati bar {{ limit }} mogućnosti.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="7">
|
||||
<source>You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.</source>
|
||||
<target>Morate odabrati najviše {{ limit }} mogućnost.|Morate odabrati najviše {{ limit }} mogućnosti.</target>
|
||||
<target>Morate odabrati najviše {{ limit }} mogućnost.|Morate odabrati najviše {{ limit }} mogućnosti.|Morate odabrati najviše {{ limit }} mogućnosti.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="8">
|
||||
<source>One or more of the given values is invalid.</source>
|
||||
@ -76,7 +76,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="19">
|
||||
<source>This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.</source>
|
||||
<target>Vrednost je predugačka. Trebalo bi da ima {{ limit }} karakter ili manje.|Vrednost je predugačka. Trebalo bi da ima {{ limit }} karaktera ili manje.</target>
|
||||
<target>Vrednost je predugačka. Trebalo bi da ima {{ limit }} karakter ili manje.|Vrednost je predugačka. Trebalo bi da ima {{ limit }} karaktera ili manje.|Vrednost je predugačka. Trebalo bi da ima {{ limit }} karaktera ili manje.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="20">
|
||||
<source>This value should be {{ limit }} or more.</source>
|
||||
@ -84,7 +84,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="21">
|
||||
<source>This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.</source>
|
||||
<target>Vrednost je prekratka. Trebalo bi da ima {{ limit }} karakter ili više.|Vrednost je prekratka. Trebalo bi da ima {{ limit }} karaktera ili više.</target>
|
||||
<target>Vrednost je prekratka. Trebalo bi da ima {{ limit }} karakter ili više.|Vrednost je prekratka. Trebalo bi da ima {{ limit }} karaktera ili više.|Vrednost je prekratka. Trebalo bi da ima {{ limit }} karaktera ili više.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="22">
|
||||
<source>This value should not be blank.</source>
|
||||
@ -180,7 +180,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="48">
|
||||
<source>This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.</source>
|
||||
<target>Vrednost bi trebalo da ima tačno {{ limit }} karakter.|Vrednost bi trebalo da ima tačno {{ limit }} karaktera.</target>
|
||||
<target>Vrednost bi trebalo da ima tačno {{ limit }} karakter.|Vrednost bi trebalo da ima tačno {{ limit }} karaktera.|Vrednost bi trebalo da ima tačno {{ limit }} karaktera.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="49">
|
||||
<source>The file was only partially uploaded.</source>
|
||||
@ -204,15 +204,15 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="54">
|
||||
<source>This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.</source>
|
||||
<target>Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.</target>
|
||||
<target>Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili više elemenata.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="55">
|
||||
<source>This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.</source>
|
||||
<target>Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.</target>
|
||||
<target>Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.|Ova kolekcija bi trebalo da sadrži {{ limit }} ili manje elemenata.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="56">
|
||||
<source>This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.</source>
|
||||
<target>Ova kolekcija bi trebalo da sadrži tačno {{ limit }} element.|Ova kolekcija bi trebalo da sadrži tačno {{ limit }} elemenata.</target>
|
||||
<target>Ova kolekcija bi trebalo da sadrži tačno {{ limit }} element.|Ova kolekcija bi trebalo da sadrži tačno {{ limit }} elementa.|Ova kolekcija bi trebalo da sadrži tačno {{ limit }} elemenata.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="57">
|
||||
<source>Invalid card number.</source>
|
||||
|
@ -439,6 +439,11 @@ class Inline
|
||||
throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping);
|
||||
}
|
||||
|
||||
if ('!php/const' === $key) {
|
||||
$key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false, []);
|
||||
$key = self::evaluateScalar($key, $flags);
|
||||
}
|
||||
|
||||
if (false === $i = strpos($mapping, ':', $i)) {
|
||||
break;
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ class InlineTest extends TestCase
|
||||
['!php/const PHP_INT_MAX', PHP_INT_MAX],
|
||||
['[!php/const PHP_INT_MAX]', [PHP_INT_MAX]],
|
||||
['{ foo: !php/const PHP_INT_MAX }', ['foo' => PHP_INT_MAX]],
|
||||
['{ !php/const PHP_INT_MAX: foo }', [PHP_INT_MAX => 'foo']],
|
||||
['!php/const NULL', null],
|
||||
];
|
||||
}
|
||||
|
Reference in New Issue
Block a user