Merge branch '4.3' into 4.4

* 4.3: (26 commits)
  Fix Twig 1.x compatibility
  [Translator] Improve farsi(persian) translations for Form
  Improve fa translations
  Spell "triggering" properly
  Added tests to cover the possibility of having scalars as services.
  fixed tests on old PHP versions
  [FrameworkBundle] Inform the user when save_path will be ignored
  fixed CS
  [SecurityBundle] Fix profiler dump for non-invokable security listeners
  fixed CS
  [Messenger] Doctrine Transport: Support setting auto_setup from DSN
  [Translator] Load plurals from po files properly
  [Serializer]: AbstractObjectNormalizer ignores the property types of discriminated classes
  [EventDispatcher] Add tag kernel.rest on 'debug.event_dispatcher' service
  [Console] Update to inherit and add licence
  Add missing test for workflow dump description
  [Intl] Remove --dev from intl compile autoloader
  [Messenger] fix publishing headers set on AmqpStamp
  Remove call to deprecated method
  [Intl] Init compile tmp volume
  ...
This commit is contained in:
Nicolas Grekas 2019-07-10 20:47:17 +02:00
commit 2e5a8c80c6
54 changed files with 535 additions and 228 deletions

View File

@ -76,7 +76,7 @@ class Deprecation
// No-op
}
$line = $trace[$i];
$this->trigerringFilePathType = $this->getPathType($file);
$this->triggeringFilePathType = $this->getPathType($file);
if (isset($line['object']) || isset($line['class'])) {
if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) {
$parsedMsg = unserialize($this->message);
@ -88,7 +88,7 @@ class Deprecation
// then we need to use the serialized information to determine
// if the error has been triggered from vendor code.
if (isset($parsedMsg['triggering_file'])) {
$this->trigerringFilePathType = $this->getPathType($parsedMsg['triggering_file']);
$this->triggeringFilePathType = $this->getPathType($parsedMsg['triggering_file']);
}
return;
@ -177,10 +177,10 @@ class Deprecation
*/
public function getType()
{
if (self::PATH_TYPE_SELF === $this->trigerringFilePathType) {
if (self::PATH_TYPE_SELF === $this->triggeringFilePathType) {
return self::TYPE_SELF;
}
if (self::PATH_TYPE_UNDETERMINED === $this->trigerringFilePathType) {
if (self::PATH_TYPE_UNDETERMINED === $this->triggeringFilePathType) {
return self::TYPE_UNDETERMINED;
}
$erroringFile = $erroringPackage = null;

View File

@ -522,6 +522,12 @@ class Configuration implements ConfigurationInterface
$rootNode
->children()
->arrayNode('session')
->validate()
->ifTrue(function ($v) {
return empty($v['handler_id']) && !empty($v['save_path']);
})
->thenInvalid('Session save path is ignored without a handler service')
->end()
->info('session configuration')
->canBeEnabled()
->children()
@ -547,7 +553,7 @@ class Configuration implements ConfigurationInterface
->scalarNode('gc_divisor')->end()
->scalarNode('gc_probability')->defaultValue(1)->end()
->scalarNode('gc_maxlifetime')->end()
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
->scalarNode('save_path')->end()
->integerNode('metadata_update_threshold')
->defaultValue(0)
->info('seconds to wait between 2 session metadata updates')

View File

@ -900,6 +900,11 @@ class FrameworkExtension extends Extension
// session handler (the internal callback registered with PHP session management)
if (null === $config['handler_id']) {
// If the user set a save_path without using a non-default \SessionHandler, it will silently be ignored
if (isset($config['save_path'])) {
throw new LogicException('Session save path is ignored without a handler service');
}
// Set the handler class to be null
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
@ -907,6 +912,10 @@ class FrameworkExtension extends Extension
$container->setAlias('session.handler', $config['handler_id'])->setPrivate(true);
}
if (!isset($config['save_path'])) {
$config['save_path'] = ini_get('session.save_path');
}
$container->setParameter('session.save_path', $config['save_path']);
$container->setParameter('session.metadata.update_threshold', $config['metadata_update_threshold']);

View File

@ -9,6 +9,7 @@
<service id="debug.event_dispatcher" class="Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher" decorates="event_dispatcher">
<tag name="monolog.logger" channel="event" />
<tag name="kernel.reset" method="reset" />
<argument type="service" id="debug.event_dispatcher.inner" />
<argument type="service" id="debug.stopwatch" />
<argument type="service" id="logger" on-invalid="null" />

View File

@ -301,7 +301,6 @@ class ConfigurationTest extends TestCase
'cookie_httponly' => true,
'cookie_samesite' => null,
'gc_probability' => 1,
'save_path' => '%kernel.cache_dir%/sessions',
'metadata_update_threshold' => 0,
],
'request' => [

View File

@ -0,0 +1,8 @@
<?php
$container->loadFromExtension('framework', [
'session' => [
'handler_id' => null,
'save_path' => '/some/path',
],
]);

View File

@ -0,0 +1,12 @@
<?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:session handler-id="null" save-path="/some/path"/>
</framework:config>
</container>

View File

@ -0,0 +1,4 @@
framework:
session:
handler_id: null
save_path: /some/path

View File

@ -563,6 +563,14 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues()));
}
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
*/
public function testNullSessionHandlerWithSavePath()
{
$this->createContainerFromFile('session_savepath');
}
public function testRequest()
{
$container = $this->createContainerFromFile('full');

View File

@ -39,10 +39,6 @@ final class WrappedListener implements ListenerInterface
public function __construct($listener)
{
$this->listener = $listener;
if (null === self::$hasVarDumper) {
self::$hasVarDumper = class_exists(ClassStub::class);
}
}
/**
@ -76,8 +72,25 @@ final class WrappedListener implements ListenerInterface
public function getInfo(): array
{
if (null === $this->stub) {
$this->stub = self::$hasVarDumper ? new ClassStub(\get_class($this->listener)) : \get_class($this->listener);
if (null !== $this->stub) {
// no-op
} elseif (self::$hasVarDumper ?? self::$hasVarDumper = class_exists(ClassStub::class)) {
$this->stub = ClassStub::wrapCallable($this->listener);
} elseif (\is_array($this->listener)) {
$this->stub = (\is_object($this->listener[0]) ? \get_class($this->listener[0]) : $this->listener[0]).'::'.$this->listener[1];
} elseif ($this->listener instanceof \Closure) {
$r = new \ReflectionFunction($this->listener);
if (false !== strpos($r->name, '{closure}')) {
$this->stub = 'closure';
} elseif ($class = $r->getClosureScopeClass()) {
$this->stub = $class->name.'::'.$r->name;
} else {
$this->stub = $r->name;
}
} elseif (\is_string($this->listener)) {
$this->stub = $this->listener;
} else {
$this->stub = \get_class($this->listener).'::__invoke';
}
return [

View File

@ -20,7 +20,6 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
use Symfony\Component\VarDumper\Caster\ClassStub;
/**
* @group time-sensitive
@ -56,9 +55,6 @@ class TraceableFirewallListenerTest extends TestCase
$listeners = $firewall->getWrappedListeners();
$this->assertCount(1, $listeners);
$this->assertSame($response, $listeners[0]['response']);
$this->assertInstanceOf(ClassStub::class, $listeners[0]['stub']);
$this->assertSame(\get_class($listener), (string) $listeners[0]['stub']);
$this->assertSame(1, $listenerCalled);
$this->assertSame($listener, $listeners[0]['stub']);
}
}

View File

@ -64,7 +64,7 @@ class ExceptionController
$exception = $this->profiler->loadProfile($token)->getCollector('exception')->getException();
$template = $this->getTemplate();
if (!$this->twig->getLoader()->exists($template)) {
if (!$this->templateExists($template)) {
return new Response($this->errorRenderer->getBody($exception), 200, ['Content-Type' => 'text/html']);
}

View File

@ -1,5 +1,14 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\CommandLoader;
use Symfony\Component\Console\Command\Command;

View File

@ -1,5 +1,14 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\CommandLoader;
use Psr\Container\ContainerInterface;

View File

@ -77,11 +77,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
}
/**
* Sets style foreground color.
*
* @param string|null $color The color name
*
* @throws InvalidArgumentException When the color name isn't defined
* {@inheritdoc}
*/
public function setForeground($color = null)
{
@ -99,11 +95,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
}
/**
* Sets style background color.
*
* @param string|null $color The color name
*
* @throws InvalidArgumentException When the color name isn't defined
* {@inheritdoc}
*/
public function setBackground($color = null)
{
@ -126,11 +118,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
}
/**
* Sets some specific style option.
*
* @param string $option The option name
*
* @throws InvalidArgumentException When the option name isn't defined
* {@inheritdoc}
*/
public function setOption($option)
{
@ -144,11 +132,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
}
/**
* Unsets some specific style option.
*
* @param string $option The option name
*
* @throws InvalidArgumentException When the option name isn't defined
* {@inheritdoc}
*/
public function unsetOption($option)
{
@ -175,11 +159,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
}
/**
* Applies the style to a given text.
*
* @param string $text The text to style
*
* @return string
* {@inheritdoc}
*/
public function apply($text)
{

View File

@ -21,7 +21,7 @@ interface OutputFormatterStyleInterface
/**
* Sets style foreground color.
*
* @param string $color The color name
* @param string|null $color The color name
*/
public function setForeground($color = null);

View File

@ -272,7 +272,7 @@ final class ProgressBar
*
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), if null it will be inferred from $iterable
*/
public function iterate(iterable $iterable, ?int $max = null): iterable
public function iterate(iterable $iterable, int $max = null): iterable
{
$this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0));

View File

@ -41,6 +41,7 @@ use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
use Symfony\Component\DependencyInjection\Tests\Compiler\Wither;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\SimilarArgumentsDummy;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Component\ExpressionLanguage\Expression;
@ -1601,6 +1602,20 @@ class ContainerBuilderTest extends TestCase
$this->assertSame(['service_container'], array_keys($container->getDefinitions()));
}
public function testScalarService()
{
$c = new ContainerBuilder();
$c->register('foo', 'string')
->setPublic(true)
->setFactory([ScalarFactory::class, 'getSomeValue'])
;
$c->compile();
$this->assertTrue($c->has('foo'));
$this->assertSame('some value', $c->get('foo'));
}
public function testWither()
{
$container = new ContainerBuilder();

View File

@ -295,6 +295,16 @@ class ContainerTest extends TestCase
$this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined');
}
public function testScalarService()
{
$c = new Container();
$c->set('foo', 'some value');
$this->assertTrue($c->has('foo'));
$this->assertSame('some value', $c->get('foo'));
}
public function testInitialized()
{
$sc = new ProjectServiceContainer();

View File

@ -33,6 +33,7 @@ use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
use Symfony\Component\DependencyInjection\Tests\Compiler\Wither;
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber;
use Symfony\Component\DependencyInjection\TypedReference;
@ -1226,6 +1227,25 @@ class PhpDumperTest extends TestCase
$this->assertSame($foo5, $locator->get('foo5'));
}
public function testScalarService()
{
$container = new ContainerBuilder();
$container->register('foo', 'string')
->setPublic(true)
->setFactory([ScalarFactory::class, 'getSomeValue'])
;
$container->compile();
$dumper = new PhpDumper($container);
eval('?>'.$dumper->dump(['class' => 'Symfony_DI_PhpDumper_Test_Scalar_Service']));
$container = new \Symfony_DI_PhpDumper_Test_Scalar_Service();
$this->assertTrue($container->has('foo'));
$this->assertSame('some value', $container->get('foo'));
}
public function testWither()
{
$container = new ContainerBuilder();

View File

@ -0,0 +1,14 @@
<?php
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
final class ScalarFactory
{
/**
* @return string
*/
public static function getSomeValue()
{
return 'some value';
}
}

View File

@ -4,15 +4,15 @@
<body>
<trans-unit id="28">
<source>This form should not contain extra fields.</source>
<target>این فرم نباید فیلد اضافی داشته باشد.</target>
<target>این فرم نباید شامل فیلد اضافه ای باشد.</target>
</trans-unit>
<trans-unit id="29">
<source>The uploaded file was too large. Please try to upload a smaller file.</source>
<target>فایل بارگذاری شده بسیار بزرگ است. لطفا فایل کوچکتری را بارگزاری کنید.</target>
<target>فایل بارگذاری شده بسیار بزرگ می باشد. لطفا فایل کوچکتری را بارگذاری نمایید.</target>
</trans-unit>
<trans-unit id="30">
<source>The CSRF token is invalid. Please try to resubmit the form.</source>
<target>مقدار CSRF نامعتبر است. لطفا فرم را مجددا ارسال فرمایید..</target>
<target>توکن CSRF نامعتبر می باشد. لطفا فرم را مجددا ارسال نمایید.</target>
</trans-unit>
</body>
</file>

View File

@ -917,8 +917,8 @@ class Request
$pos = strrpos($host, ':');
}
if (false !== $pos) {
return (int) substr($host, $pos + 1);
if (false !== $pos && $port = substr($host, $pos + 1)) {
return (int) $port;
}
return 'https' === $this->getScheme() ? 443 : 80;

View File

@ -2319,6 +2319,18 @@ class RequestTest extends TestCase
$this->assertSame(443, $request->getPort());
}
public function testTrustedPortDoesNotDefaultToZero()
{
Request::setTrustedProxies(['1.1.1.1'], Request::HEADER_X_FORWARDED_ALL);
$request = Request::create('/');
$request->server->set('REMOTE_ADDR', '1.1.1.1');
$request->headers->set('X-Forwarded-Host', 'test.example.com');
$request->headers->set('X-Forwarded-Port', '');
$this->assertSame(80, $request->getPort());
}
}
class RequestContentProxy extends Request

View File

@ -23,7 +23,7 @@ final class UrlHelper
private $requestStack;
private $requestContext;
public function __construct(RequestStack $requestStack, ?RequestContext $requestContext = null)
public function __construct(RequestStack $requestStack, RequestContext $requestContext = null)
{
$this->requestStack = $requestStack;
$this->requestContext = $requestContext;

View File

@ -149,7 +149,7 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
return $logs;
}
private function getContainerCompilerLogs(?string $compilerLogsFilepath = null): array
private function getContainerCompilerLogs(string $compilerLogsFilepath = null): array
{
if (!file_exists($compilerLogsFilepath)) {
return [];

View File

@ -63,7 +63,7 @@ class LocaleAwareListener implements EventSubscriberInterface
];
}
private function setLocale(string $locale, ?string $defaultLocale = null): void
private function setLocale(string $locale, string $defaultLocale = null): void
{
foreach ($this->localeAwareServices as $service) {
try {

View File

@ -12,7 +12,7 @@
$autoload = __DIR__.'/../../vendor/autoload.php';
if (!file_exists($autoload)) {
bailout('You should run "composer install --dev" in the component before running this script.');
bailout('You should run "composer install" in the component before running this script.');
}
require_once $autoload;

View File

@ -1,8 +1,7 @@
#!/usr/bin/env bash
if [[ $1 == force ]]; then
docker pull jakzal/php-intl
fi;
[[ $1 == force ]] && docker pull jakzal/php-intl
[[ ! -d /tmp/symfony/icu ]] && mkdir -p /tmp/symfony/icu
docker run \
-it --rm --name symfony-intl \

View File

@ -219,7 +219,7 @@ class MessengerPass implements CompilerPassInterface
throw new RuntimeException(sprintf('Invalid handler service "%s": type-hint of argument "$%s" in method "%s::__invoke()" must be a class , "%s" given.', $serviceId, $parameters[0]->getName(), $handlerClass->getName(), $type));
}
return [(string) $parameters[0]->getType()];
return [$parameters[0]->getType()->getName()];
}
private function registerReceivers(ContainerBuilder $container, array $busIds)

View File

@ -418,6 +418,21 @@ class ConnectionTest extends TestCase
$connection->channel();
}
public function testAmqpStampHeadersAreUsed()
{
$factory = new TestAmqpFactory(
$this->createMock(\AMQPConnection::class),
$this->createMock(\AMQPChannel::class),
$this->createMock(\AMQPQueue::class),
$amqpExchange = $this->createMock(\AMQPExchange::class)
);
$amqpExchange->expects($this->once())->method('publish')->with('body', null, AMQP_NOPARAM, ['headers' => ['Foo' => 'X', 'Bar' => 'Y']]);
$connection = Connection::fromDsn('amqp://localhost', [], $factory);
$connection->publish('body', ['Foo' => 'X'], 0, new AmqpStamp(null, AMQP_NOPARAM, ['headers' => ['Bar' => 'Y']]));
}
public function testItCanPublishWithTheDefaultRoutingKey()
{
$factory = new TestAmqpFactory(

View File

@ -157,61 +157,86 @@ class ConnectionTest extends TestCase
/**
* @dataProvider buildConfigurationProvider
*/
public function testBuildConfiguration($dsn, $options, $expectedManager, $expectedTableName, $expectedRedeliverTimeout, $expectedQueue)
public function testBuildConfiguration($dsn, $options, $expectedConnection, $expectedTableName, $expectedRedeliverTimeout, $expectedQueue, $expectedAutoSetup)
{
$config = Connection::buildConfiguration($dsn, $options);
$this->assertEquals($expectedManager, $config['connection']);
$this->assertEquals($expectedConnection, $config['connection']);
$this->assertEquals($expectedTableName, $config['table_name']);
$this->assertEquals($expectedRedeliverTimeout, $config['redeliver_timeout']);
$this->assertEquals($expectedQueue, $config['queue_name']);
$this->assertEquals($expectedAutoSetup, $config['auto_setup']);
}
public function buildConfigurationProvider()
{
return [
[
'dsn' => 'doctrine://default',
'options' => [],
'expectedManager' => 'default',
'expectedTableName' => 'messenger_messages',
'expectedRedeliverTimeout' => 3600,
'expectedQueue' => 'default',
yield 'no options' => [
'dsn' => 'doctrine://default',
'options' => [],
'expectedConnection' => 'default',
'expectedTableName' => 'messenger_messages',
'expectedRedeliverTimeout' => 3600,
'expectedQueue' => 'default',
'expectedAutoSetup' => true,
];
yield 'test options array' => [
'dsn' => 'doctrine://default',
'options' => [
'table_name' => 'name_from_options',
'redeliver_timeout' => 1800,
'queue_name' => 'important',
'auto_setup' => false,
],
// test options from options array
[
'dsn' => 'doctrine://default',
'options' => [
'table_name' => 'name_from_options',
'redeliver_timeout' => 1800,
'queue_name' => 'important',
],
'expectedManager' => 'default',
'expectedTableName' => 'name_from_options',
'expectedRedeliverTimeout' => 1800,
'expectedQueue' => 'important',
],
// tests options from dsn
[
'dsn' => 'doctrine://default?table_name=name_from_dsn&redeliver_timeout=1200&queue_name=normal',
'options' => [],
'expectedManager' => 'default',
'expectedTableName' => 'name_from_dsn',
'expectedRedeliverTimeout' => 1200,
'expectedQueue' => 'normal',
],
// test options from options array wins over options from dsn
[
'dsn' => 'doctrine://default?table_name=name_from_dsn&redeliver_timeout=1200&queue_name=normal',
'options' => [
'table_name' => 'name_from_options',
'redeliver_timeout' => 1800,
'queue_name' => 'important',
],
'expectedManager' => 'default',
'expectedTableName' => 'name_from_options',
'expectedRedeliverTimeout' => 1800,
'expectedQueue' => 'important',
'expectedConnection' => 'default',
'expectedTableName' => 'name_from_options',
'expectedRedeliverTimeout' => 1800,
'expectedQueue' => 'important',
'expectedAutoSetup' => false,
];
yield 'options from dsn' => [
'dsn' => 'doctrine://default?table_name=name_from_dsn&redeliver_timeout=1200&queue_name=normal&auto_setup=false',
'options' => [],
'expectedConnection' => 'default',
'expectedTableName' => 'name_from_dsn',
'expectedRedeliverTimeout' => 1200,
'expectedQueue' => 'normal',
'expectedAutoSetup' => false,
];
yield 'options from options array wins over options from dsn' => [
'dsn' => 'doctrine://default?table_name=name_from_dsn&redeliver_timeout=1200&queue_name=normal&auto_setup=true',
'options' => [
'table_name' => 'name_from_options',
'redeliver_timeout' => 1800,
'queue_name' => 'important',
'auto_setup' => false,
],
'expectedConnection' => 'default',
'expectedTableName' => 'name_from_options',
'expectedRedeliverTimeout' => 1800,
'expectedQueue' => 'important',
'expectedAutoSetup' => false,
];
yield 'options from dsn with falsey boolean' => [
'dsn' => 'doctrine://default?auto_setup=0',
'options' => [],
'expectedConnection' => 'default',
'expectedTableName' => 'messenger_messages',
'expectedRedeliverTimeout' => 3600,
'expectedQueue' => 'default',
'expectedAutoSetup' => false,
];
yield 'options from dsn with thruthy boolean' => [
'dsn' => 'doctrine://default?auto_setup=1',
'options' => [],
'expectedConnection' => 'default',
'expectedTableName' => 'messenger_messages',
'expectedRedeliverTimeout' => 3600,
'expectedQueue' => 'default',
'expectedAutoSetup' => true,
];
}

View File

@ -11,13 +11,13 @@
namespace Symfony\Component\Messenger\Transport\AmqpExt;
use Symfony\Component\Messenger\Stamp\StampInterface;
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
/**
* @author Guillaume Gammelin <ggammelin@gmail.com>
* @author Samuel Roze <samuel.roze@gmail.com>
*/
final class AmqpStamp implements StampInterface
final class AmqpStamp implements NonSendableStampInterface
{
private $routingKey;
private $flags;

View File

@ -189,9 +189,7 @@ class Connection
$this->exchange(),
$body,
$this->getRoutingKeyForMessage($amqpStamp),
[
'headers' => $headers,
],
$headers,
$amqpStamp
);
}
@ -221,20 +219,21 @@ class Connection
$this->getDelayExchange(),
$body,
$this->getRoutingKeyForDelay($delay, $routingKey),
[
'headers' => $headers,
],
$headers,
$amqpStamp
);
}
private function publishOnExchange(\AMQPExchange $exchange, string $body, string $routingKey = null, array $attributes = [], AmqpStamp $amqpStamp = null)
private function publishOnExchange(\AMQPExchange $exchange, string $body, string $routingKey = null, array $headers = [], AmqpStamp $amqpStamp = null)
{
$attributes = $amqpStamp ? $amqpStamp->getAttributes() : [];
$attributes['headers'] = array_merge($headers, $attributes['headers'] ?? []);
$exchange->publish(
$body,
$routingKey,
$amqpStamp ? $amqpStamp->getFlags() : AMQP_NOPARAM,
array_merge($amqpStamp ? $amqpStamp->getAttributes() : [], $attributes)
$attributes
);
}

View File

@ -74,22 +74,19 @@ class Connection
parse_str($components['query'], $query);
}
$configuration = [
'connection' => $components['host'],
'table_name' => $options['table_name'] ?? ($query['table_name'] ?? self::DEFAULT_OPTIONS['table_name']),
'queue_name' => $options['queue_name'] ?? ($query['queue_name'] ?? self::DEFAULT_OPTIONS['queue_name']),
'redeliver_timeout' => $options['redeliver_timeout'] ?? ($query['redeliver_timeout'] ?? self::DEFAULT_OPTIONS['redeliver_timeout']),
'auto_setup' => $options['auto_setup'] ?? ($query['auto_setup'] ?? self::DEFAULT_OPTIONS['auto_setup']),
];
$configuration = ['connection' => $components['host']];
$configuration += $options + $query + self::DEFAULT_OPTIONS;
$configuration['auto_setup'] = filter_var($configuration['auto_setup'], FILTER_VALIDATE_BOOLEAN);
// check for extra keys in options
$optionsExtraKeys = array_diff(array_keys($options), array_keys($configuration));
$optionsExtraKeys = array_diff(array_keys($options), array_keys(self::DEFAULT_OPTIONS));
if (0 < \count($optionsExtraKeys)) {
throw new InvalidArgumentException(sprintf('Unknown option found : [%s]. Allowed options are [%s]', implode(', ', $optionsExtraKeys), implode(', ', self::DEFAULT_OPTIONS)));
}
// check for extra keys in options
$queryExtraKeys = array_diff(array_keys($query), array_keys($configuration));
$queryExtraKeys = array_diff(array_keys($query), array_keys(self::DEFAULT_OPTIONS));
if (0 < \count($queryExtraKeys)) {
throw new InvalidArgumentException(sprintf('Unknown option found in DSN: [%s]. Allowed options are [%s]', implode(', ', $queryExtraKeys), implode(', ', self::DEFAULT_OPTIONS)));
}

View File

@ -25,7 +25,7 @@ class AuthenticationTrustResolver implements AuthenticationTrustResolverInterfac
private $anonymousClass;
private $rememberMeClass;
public function __construct(?string $anonymousClass = null, ?string $rememberMeClass = null)
public function __construct(string $anonymousClass = null, string $rememberMeClass = null)
{
$this->anonymousClass = $anonymousClass;
$this->rememberMeClass = $rememberMeClass;

View File

@ -21,7 +21,7 @@ final class ObjectPropertyListExtractor implements ObjectPropertyListExtractorIn
private $propertyListExtractor;
private $objectClassResolver;
public function __construct(PropertyListExtractorInterface $propertyListExtractor, ?callable $objectClassResolver = null)
public function __construct(PropertyListExtractorInterface $propertyListExtractor, callable $objectClassResolver = null)
{
$this->propertyListExtractor = $propertyListExtractor;
$this->objectClassResolver = $objectClassResolver;

View File

@ -330,13 +330,14 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
$reflectionClass = new \ReflectionClass($class);
$object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format);
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
foreach ($normalizedData as $attribute => $value) {
if ($this->nameConverter) {
$attribute = $this->nameConverter->denormalize($attribute, $class, $format, $context);
$attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context);
}
if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($class, $attribute, $format, $context)) {
if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($resolvedClass, $attribute, $format, $context)) {
if (!($context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES])) {
$extraAttributes[] = $attribute;
}
@ -351,7 +352,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
}
}
$value = $this->validateAndDenormalize($class, $attribute, $value, $format, $context);
$value = $this->validateAndDenormalize($resolvedClass, $attribute, $value, $format, $context);
try {
$this->setAttributeValue($object, $attribute, $value, $format, $context);
} catch (InvalidArgumentException $e) {

View File

@ -15,10 +15,23 @@ class AbstractDummyFirstChild extends AbstractDummy
{
public $bar;
/** @var DummyFirstChildQuux|null */
public $quux;
public function __construct($foo = null, $bar = null)
{
parent::__construct($foo);
$this->bar = $bar;
}
public function getQuux(): ?DummyFirstChildQuux
{
return $this->quux;
}
public function setQuux(DummyFirstChildQuux $quux): void
{
$this->quux = $quux;
}
}

View File

@ -15,10 +15,23 @@ class AbstractDummySecondChild extends AbstractDummy
{
public $baz;
/** @var DummySecondChildQuux|null */
public $quux;
public function __construct($foo = null, $baz = null)
{
parent::__construct($foo);
$this->baz = $baz;
}
public function getQuux(): ?DummySecondChildQuux
{
return $this->quux;
}
public function setQuux(DummySecondChildQuux $quux): void
{
$this->quux = $quux;
}
}

View File

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\Tests\Fixtures;
class DummyFirstChildQuux
{
/**
* @var string
*/
private $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function getValue(): string
{
return $this->value;
}
}

View File

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\Tests\Fixtures;
class DummySecondChildQuux
{
/**
* @var string
*/
private $value;
public function __construct(string $value)
{
$this->value = $value;
}
public function getValue(): string
{
return $this->value;
}
}

View File

@ -16,13 +16,22 @@ use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
use Symfony\Component\Serializer\Mapping\ClassMetadata;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux;
class AbstractObjectNormalizerTest extends TestCase
{
@ -147,6 +156,39 @@ class AbstractObjectNormalizerTest extends TestCase
return $denormalizer;
}
public function testDenormalizeWithDiscriminatorMapUsesCorrectClassname()
{
$factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$loaderMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
$loaderMock->method('hasMetadataFor')->willReturnMap([
[
AbstractDummy::class,
true,
],
]);
$loaderMock->method('getMetadataFor')->willReturnMap([
[
AbstractDummy::class,
new ClassMetadata(
AbstractDummy::class,
new ClassDiscriminatorMapping('type', [
'first' => AbstractDummyFirstChild::class,
'second' => AbstractDummySecondChild::class,
])
),
],
]);
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
$normalizer = new AbstractObjectNormalizerDummy($factory, null, new PhpDocExtractor(), $discriminatorResolver);
$serializer = new Serializer([$normalizer]);
$normalizer->setSerializer($serializer);
$normalizedData = $normalizer->denormalize(['foo' => 'foo', 'baz' => 'baz', 'quux' => ['value' => 'quux'], 'type' => 'second'], AbstractDummy::class);
$this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData->quux);
}
/**
* Test that additional attributes throw an exception if no metadata factory is specified.
*
@ -180,7 +222,7 @@ class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = [])
{
return \in_array($attribute, ['foo', 'baz']);
return \in_array($attribute, ['foo', 'baz', 'quux', 'value']);
}
public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null)

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\Serializer\Tests;
use Doctrine\Common\Annotations\AnnotationReader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
@ -34,6 +35,7 @@ use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild;
use Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild;
use Symfony\Component\Serializer\Tests\Fixtures\DummyFirstChildQuux;
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageInterface;
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberOne;
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo;
@ -382,6 +384,7 @@ class SerializerTest extends TestCase
public function testDeserializeAndSerializeAbstractObjectsWithTheClassMetadataDiscriminatorResolver()
{
$example = new AbstractDummyFirstChild('foo-value', 'bar-value');
$example->setQuux(new DummyFirstChildQuux('quux'));
$loaderMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
$loaderMock->method('hasMetadataFor')->willReturnMap([
@ -405,9 +408,9 @@ class SerializerTest extends TestCase
]);
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
$serializer = new Serializer([new ObjectNormalizer(null, null, null, null, $discriminatorResolver)], ['json' => new JsonEncoder()]);
$serializer = new Serializer([new ObjectNormalizer(null, null, null, new PhpDocExtractor(), $discriminatorResolver)], ['json' => new JsonEncoder()]);
$jsonData = '{"type":"first","bar":"bar-value","foo":"foo-value"}';
$jsonData = '{"type":"first","quux":{"value":"quux"},"bar":"bar-value","foo":"foo-value"}';
$deserialized = $serializer->deserialize($jsonData, AbstractDummy::class, 'json');
$this->assertEquals($example, $deserialized);

View File

@ -111,17 +111,12 @@ class MoFileLoader extends FileLoader
$ids = ['singular' => $singularId, 'plural' => $pluralId];
$item = compact('ids', 'translated');
if (\is_array($item['translated'])) {
$messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]);
if (!empty($item['ids']['singular'])) {
$id = $item['ids']['singular'];
if (isset($item['ids']['plural'])) {
$plurals = [];
foreach ($item['translated'] as $plural => $translated) {
$plurals[] = sprintf('{%d} %s', $plural, $translated);
}
$messages[$item['ids']['plural']] = stripcslashes(implode('|', $plurals));
$id .= '|'.$item['ids']['plural'];
}
} elseif (!empty($item['ids']['singular'])) {
$messages[$item['ids']['singular']] = stripcslashes($item['translated']);
$messages[$id] = stripcslashes(implode('|', (array) $item['translated']));
}
}

View File

@ -126,23 +126,24 @@ class PoFileLoader extends FileLoader
*/
private function addMessage(array &$messages, array $item)
{
if (\is_array($item['translated'])) {
$messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated'][0]);
if (!empty($item['ids']['singular'])) {
$id = stripcslashes($item['ids']['singular']);
if (isset($item['ids']['plural'])) {
$plurals = $item['translated'];
// PO are by definition indexed so sort by index.
ksort($plurals);
// Make sure every index is filled.
end($plurals);
$count = key($plurals);
// Fill missing spots with '-'.
$empties = array_fill(0, $count + 1, '-');
$plurals += $empties;
ksort($plurals);
$messages[stripcslashes($item['ids']['plural'])] = stripcslashes(implode('|', $plurals));
$id .= '|'.stripcslashes($item['ids']['plural']);
}
} elseif (!empty($item['ids']['singular'])) {
$messages[stripcslashes($item['ids']['singular'])] = stripcslashes($item['translated']);
$translated = (array) $item['translated'];
// PO are by definition indexed so sort by index.
ksort($translated);
// Make sure every index is filled.
end($translated);
$count = key($translated);
// Fill missing spots with '-'.
$empties = array_fill(0, $count + 1, '-');
$translated += $empties;
ksort($translated);
$messages[$id] = stripcslashes(implode('|', $translated));
}
}
}

View File

@ -34,7 +34,10 @@ class MoFileLoaderTest extends TestCase
$resource = __DIR__.'/../fixtures/plurals.mo';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals(['foo' => 'bar', 'foos' => '{0} bar|{1} bars'], $catalogue->all('domain1'));
$this->assertEquals([
'foo|foos' => 'bar|bars',
'{0} no foos|one foo|%count% foos' => '{0} no bars|one bar|%count% bars',
], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
$this->assertEquals([new FileResource($resource)], $catalogue->getResources());
}

View File

@ -35,8 +35,7 @@ class PoFileLoaderTest extends TestCase
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([
'foo' => 'bar',
'foos' => 'bar|bars',
'foo|foos' => 'bar|bars',
'{0} no foos|one foo|%count% foos' => '{0} no bars|one bar|%count% bars',
], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
@ -93,10 +92,8 @@ class PoFileLoaderTest extends TestCase
$catalogue = $loader->load($resource, 'en', 'domain1');
$messages = $catalogue->all('domain1');
$this->assertArrayHasKey('escaped "foo"', $messages);
$this->assertArrayHasKey('escaped "foos"', $messages);
$this->assertEquals('escaped "bar"', $messages['escaped "foo"']);
$this->assertEquals('escaped "bar"|escaped "bars"', $messages['escaped "foos"']);
$this->assertArrayHasKey('escaped "foo"|escaped "foos"', $messages);
$this->assertEquals('escaped "bar"|escaped "bars"', $messages['escaped "foo"|escaped "foos"']);
}
public function testSkipFuzzyTranslations()
@ -110,4 +107,16 @@ class PoFileLoaderTest extends TestCase
$this->assertArrayNotHasKey('foo2', $messages);
$this->assertArrayHasKey('foo3', $messages);
}
public function testMissingPlurals()
{
$loader = new PoFileLoader();
$resource = __DIR__.'/../fixtures/missing-plurals.po';
$catalogue = $loader->load($resource, 'en', 'domain1');
$this->assertEquals([
'foo|foos' => '-|bar|-|bars',
], $catalogue->all('domain1'));
$this->assertEquals('en', $catalogue->getLocale());
}
}

View File

@ -0,0 +1,4 @@
msgid "foo"
msgid_plural "foos"
msgstr[3] "bars"
msgstr[1] "bar"

View File

@ -16,19 +16,19 @@
</trans-unit>
<trans-unit id="4">
<source>This value should be blank.</source>
<target>این فیلد باید خالی باشد.</target>
<target>این مقدار باید خالی باشد.</target>
</trans-unit>
<trans-unit id="5">
<source>The value you selected is not a valid choice.</source>
<target>گزینه انتخابی معتبر نیست.</target>
<target>مقدار انتخاب شده شامل گزینه های معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="6">
<source>You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.</source>
<target>باید حداقل {{ limit }} گزینه انتخاب کنید.|باید حداقل {{ limit }} گزینه انتخاب کنید.</target>
<target>باید حداقل {{ limit }} گزینه انتخاب نمایید.|باید حداقل {{ limit }} گزینه انتخاب نمایید.</target>
</trans-unit>
<trans-unit id="7">
<source>You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.</source>
<target>حداکثر {{ limit }} گزینه می توانید انتخاب کنید.|حداکثر {{ limit }} گزینه می توانید انتخاب کنید.</target>
<target>حداکثر {{ limit }} گزینه می توانید انتخاب نمایید.|حداکثر {{ limit }} گزینه می توانید انتخاب نمایید.</target>
</trans-unit>
<trans-unit id="8">
<source>One or more of the given values is invalid.</source>
@ -44,87 +44,87 @@
</trans-unit>
<trans-unit id="11">
<source>This value is not a valid date.</source>
<target>این مقدار یک تاریخ معتبر نیست.</target>
<target>این مقدار یک تاریخ معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="12">
<source>This value is not a valid datetime.</source>
<target>این مقدار یک تاریخ و زمان معتبر نیست.</target>
<target>این مقدار یک تاریخ و زمان معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="13">
<source>This value is not a valid email address.</source>
<target>این یک رایانامه معتبر نیست.</target>
<target>این یک رایانامه معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="14">
<source>The file could not be found.</source>
<target>فایل پیدا نشد.</target>
<target>فایل یافت نشد.</target>
</trans-unit>
<trans-unit id="15">
<source>The file is not readable.</source>
<target>فایل قابلیت خواندن ندارد.</target>
<target>فایل قابلیت خوانده شدن ندارد.</target>
</trans-unit>
<trans-unit id="16">
<source>The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.</source>
<target>فایل بیش از اندازه بزرگ است({{ size }} {{ suffix }}). حداکثر اندازه مجاز برابر {{ limit }} {{ suffix }} است.</target>
<target>فایل بیش از اندازه بزرگ است({{ size }} {{ suffix }}). حداکثر اندازه مجاز برابر با {{ limit }} {{ suffix }} می باشد.</target>
</trans-unit>
<trans-unit id="17">
<source>The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.</source>
<target>این نوع فایل مجاز نیست({{ type }}). نوع های مجاز {{ types }} هستند.</target>
<target>این نوع فایل مجاز نمی باشد({{ type }}). نوع های مجاز شامل {{ types }} می باشند.</target>
</trans-unit>
<trans-unit id="18">
<source>This value should be {{ limit }} or less.</source>
<target>این مقدار باید کوچکتر یا مساوی {{ limit }} باشد.</target>
<target>این مقدار باید کوچکتر و یا مساوی {{ limit }} باشد.</target>
</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>بسیار طولانی است.حداکثر تعداد حروف مجاز برابر {{ limit }} است.|بسیار طولانی است.حداکثر تعداد حروف مجاز برابر {{ limit }} است.</target>
<target>بسیار طولانی است.حداکثر تعداد حروف مجاز برابر {{ limit }} می باشد.|بسیار طولانی است.حداکثر تعداد حروف مجاز برابر {{ limit }} می باشد.</target>
</trans-unit>
<trans-unit id="20">
<source>This value should be {{ limit }} or more.</source>
<target>این مقدار باید برابر و یا بیشتر از {{ limit }} باشد.</target>
<target>این مقدار باید بزرگتر و یا مساوی {{ limit }} باشد.</target>
</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>بسیار کوتاه است.تعداد حروف باید حداقل {{ limit }} باشد.|بسیار کوتاه است.تعداد حروف باید حداقل {{ limit }} باشد.</target>
<target>مقدار وارد شده بسیار کوتاه است.تعداد حروف وارد شده، باید حداقل شامل {{ limit }} کاراکتر باشد.|مقدار وارد شده بسیار کوتاه است.تعداد حروف وارد شده، باید حداقل شامل {{ limit }} کاراکتر باشد.</target>
</trans-unit>
<trans-unit id="22">
<source>This value should not be blank.</source>
<target>این مقدار نباید تهی باشد.</target>
<target>این مقدار نباید خالی باشد.</target>
</trans-unit>
<trans-unit id="23">
<source>This value should not be null.</source>
<target>باید مقداری داشته باشد..</target>
<target>این مقدار باید شامل چیزی باشد.</target>
</trans-unit>
<trans-unit id="24">
<source>This value should be null.</source>
<target>نباید مقداری داشته باشد.</target>
<target>این مقدار باید شامل چیزی نباشد.</target>
</trans-unit>
<trans-unit id="25">
<source>This value is not valid.</source>
<target>این مقدار معتبر نیست.</target>
<target>این مقدار معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="26">
<source>This value is not a valid time.</source>
<target>این مقدار یک زمان صحیح نیست.</target>
<target>این مقدار یک زمان صحیح نمی باشد.</target>
</trans-unit>
<trans-unit id="27">
<source>This value is not a valid URL.</source>
<target>این یک URL معتبر نیست.</target>
<target>این مقدار شامل یک URL معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="31">
<source>The two values should be equal.</source>
<target>دو مقدار باید برابر باشند.</target>
<target>دو مقدار باید با یکدیگر برابر باشند.</target>
</trans-unit>
<trans-unit id="32">
<source>The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.</source>
<target>فایل بیش از اندازه بزرگ است. حداکثر اندازه مجاز برابر {{ limit }} {{ suffix }} است.</target>
<target>فایل بیش از اندازه بزرگ است. حداکثر اندازه مجاز برابر با {{ limit }} {{ suffix }} می باشد.</target>
</trans-unit>
<trans-unit id="33">
<source>The file is too large.</source>
<target>فایل بیش از اندازه بزرگ است.</target>
<target>فایل بیش از اندازه بزرگ می باشد.</target>
</trans-unit>
<trans-unit id="34">
<source>The file could not be uploaded.</source>
<target>بارگذاری فایل با شکست مواجه شد.</target>
<target>بارگذاری فایل با شکست مواجه گردید.</target>
</trans-unit>
<trans-unit id="35">
<source>This value should be a valid number.</source>
@ -132,23 +132,23 @@
</trans-unit>
<trans-unit id="36">
<source>This file is not a valid image.</source>
<target>این فایل یک تصویر نیست.</target>
<target>این فایل یک تصویر نمی باشد.</target>
</trans-unit>
<trans-unit id="37">
<source>This is not a valid IP address.</source>
<target>این مقدار یک IP معتبر نیست.</target>
<target>این مقدار یک IP معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="38">
<source>This value is not a valid language.</source>
<target>این مقدار یک زبان صحیح نیست.</target>
<target>این مقدار یک زبان صحیح نمی باشد.</target>
</trans-unit>
<trans-unit id="39">
<source>This value is not a valid locale.</source>
<target>این مقدار یک محل صحیح نیست.</target>
<target>این مقدار یک محل صحیح نمی باشد.</target>
</trans-unit>
<trans-unit id="40">
<source>This value is not a valid country.</source>
<target>این مقدار یک کشور صحیح نیست.</target>
<target>این مقدار یک کشور صحیح نمی باشد.</target>
</trans-unit>
<trans-unit id="41">
<source>This value is already used.</source>
@ -156,23 +156,23 @@
</trans-unit>
<trans-unit id="42">
<source>The size of the image could not be detected.</source>
<target>اندازه تصویر قابل شناسایی نیست.</target>
<target>اندازه تصویر قابل شناسایی نمی باشد.</target>
</trans-unit>
<trans-unit id="43">
<source>The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.</source>
<target>طول تصویر بسیار بزرگ است ({{ width }}px). بشینه طول مجاز {{ max_width }}px است.</target>
<target>طول تصویر بسیار بزرگ است ({{ width }}px). بشینه طول مجاز {{ max_width }}px می باشد.</target>
</trans-unit>
<trans-unit id="44">
<source>The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.</source>
<target>طول تصویر بسیار کوچک است ({{ width }}px). کمینه طول موردنظر {{ min_width }}px است.</target>
<target>طول تصویر بسیار کوچک است ({{ width }}px). کمینه طول موردنظر {{ min_width }}px می باشد.</target>
</trans-unit>
<trans-unit id="45">
<source>The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.</source>
<target>ارتفاع تصویر بسیار بزرگ است ({{ height }}px). بشینه ارتفاع مجاز {{ max_height }}px است.</target>
<target>ارتفاع تصویر بسیار بزرگ است ({{ height }}px). بشینه ارتفاع مجاز {{ max_height }}px می باشد.</target>
</trans-unit>
<trans-unit id="46">
<source>The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.</source>
<target>ارتفاع تصویر بسیار کوچک است ({{ height }}px). کمینه ارتفاع موردنظر {{ min_height }}px است.</target>
<target>ارتفاع تصویر بسیار کوچک است ({{ height }}px). کمینه ارتفاع موردنظر {{ min_height }}px می باشد.</target>
</trans-unit>
<trans-unit id="47">
<source>This value should be the user's current password.</source>
@ -184,67 +184,67 @@
</trans-unit>
<trans-unit id="49">
<source>The file was only partially uploaded.</source>
<target>فایل به صورت جزیی بارگذاری شده است.</target>
<target>پرونده به صورت جزیی بارگذاری گردیده است.</target>
</trans-unit>
<trans-unit id="50">
<source>No file was uploaded.</source>
<target>هیچ فایلی بارگذاری نشد.</target>
<target>هیچ پرونده ای بارگذاری نگردیده است.</target>
</trans-unit>
<trans-unit id="51">
<source>No temporary folder was configured in php.ini.</source>
<target>فولدر موقت در php.ini پیکربندی نشده است.</target>
<target>فولدر موقت در php.ini پیکربندی نگردیده است.</target>
</trans-unit>
<trans-unit id="52">
<source>Cannot write temporary file to disk.</source>
<target>فایل موقت را نمی توان در دیسک نوشت.</target>
<target>فایل موقتی را نمی توان در دیسک نوشت.</target>
</trans-unit>
<trans-unit id="53">
<source>A PHP extension caused the upload to fail.</source>
<target>اکستنشن PHP موجب شد که بارگذاری فایل با شکست مواجه شود.</target>
<target>یک اکستنشن PHP موجب شد که بارگذاری فایل با شکست مواجه گردد.</target>
</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>این مجموعه می بایست دارای {{ limit }} عنصر یا بیشتر باشد.|این مجموعه می بایست دارای {{ limit }} عنصر یا بیشتر باشد.</target>
<target>این مجموعه می بایست دارای حداقل {{ limit }} عنصر یا بیشتر باشد.|این مجموعه می بایست دارای حداقل {{ limit }} عنصر یا بیشتر باشد.</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>این مجموعه می بایست دارای حداقل {{ limit }} عنصر یا کمتر باشد.|این مجموعه می بایست دارای {{ limit }} عنصر یا کمتر باشد.</target>
<target>این مجموعه می بایست دارای حداکثر {{ limit }} عنصر یا کمتر باشد.|این مجموعه می بایست دارای حداکثر {{ limit }} عنصر یا کمتر باشد.</target>
</trans-unit>
<trans-unit id="56">
<source>This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.</source>
<target>این مجموعه می بایست به طور دقیق دارا {{ limit }} عنصر باشد.|این مجموعه می بایست به طور دقیق دارای {{ limit }} قلم باشد.</target>
<target>این مجموعه می بایست به طور دقیق دارای {{ limit }} عنصر باشد.|این مجموعه می بایست به طور دقیق دارای {{ limit }} عنصر باشد.</target>
</trans-unit>
<trans-unit id="57">
<source>Invalid card number.</source>
<target>شماره کارت نامعتبر است.</target>
<target>شماره کارت نامعتبر می باشد.</target>
</trans-unit>
<trans-unit id="58">
<source>Unsupported card type or invalid card number.</source>
<target>نوع کارت پشتیبانی نمی شود یا شماره کارت نامعتبر است.</target>
<target>نوع کارت پشتیبانی نمی شود و یا شماره کارت نامعتبر می باشد.</target>
</trans-unit>
<trans-unit id="59">
<source>This is not a valid International Bank Account Number (IBAN).</source>
<target>این یک شماره حساب بین المللی بانک (IBAN) درست نیست.</target>
<target>این یک شماره حساب بانک بین المللی معتبر نمی باشد (IBAN).</target>
</trans-unit>
<trans-unit id="60">
<source>This value is not a valid ISBN-10.</source>
<target>این مقدار یک ISBN-10 درست نیست.</target>
<target>این مقدار یک ISBN-10 معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="61">
<source>This value is not a valid ISBN-13.</source>
<target>این مقدار یک ISBN-13 درست نیست.</target>
<target>این مقدار یک ISBN-13 معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="62">
<source>This value is neither a valid ISBN-10 nor a valid ISBN-13.</source>
<target>این مقدار یک ISBN-10 درست یا ISBN-13 درست نیست.</target>
<target>این مقدار یک ISBN-10 صحیح و یا ISBN-13 معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="63">
<source>This value is not a valid ISSN.</source>
<target>این مقدار یک ISSN درست نیست.</target>
<target>این مقدار یک ISSN معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="64">
<source>This value is not a valid currency.</source>
<target>این مقدار یک یکای پول درست نیست.</target>
<target>این مقدار یک واحد پول معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="65">
<source>This value should be equal to {{ compared_value }}.</source>
@ -256,11 +256,11 @@
</trans-unit>
<trans-unit id="67">
<source>This value should be greater than or equal to {{ compared_value }}.</source>
<target>این مقدار باید بزرگتر یا مساوی با {{ compared_value }} باشد.</target>
<target>این مقدار باید بزرگتر و یا مساوی با {{ compared_value }} باشد.</target>
</trans-unit>
<trans-unit id="68">
<source>This value should be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>این مقدار باید با {{ compared_value_type }} {{ compared_value }} یکی باشد.</target>
<target>این مقدار باید با {{ compared_value_type }} {{ compared_value }} یکسان باشد.</target>
</trans-unit>
<trans-unit id="69">
<source>This value should be less than {{ compared_value }}.</source>
@ -268,7 +268,7 @@
</trans-unit>
<trans-unit id="70">
<source>This value should be less than or equal to {{ compared_value }}.</source>
<target>این مقدار باید کمتر یا مساوی با {{ compared_value }} باشد.</target>
<target>این مقدار باید کمتر و یا مساوی با {{ compared_value }} باشد.</target>
</trans-unit>
<trans-unit id="71">
<source>This value should not be equal to {{ compared_value }}.</source>
@ -276,43 +276,43 @@
</trans-unit>
<trans-unit id="72">
<source>This value should not be identical to {{ compared_value_type }} {{ compared_value }}.</source>
<target>این مقدار نباید {{ compared_value_type }} {{ compared_value }} یکی باشد.</target>
<target>این مقدار نباید با {{ compared_value_type }} {{ compared_value }} یکسان باشد.</target>
</trans-unit>
<trans-unit id="73">
<source>The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.</source>
<target>ابعاد {{ ratio }} عکس بیش از حد بزرگ است.حداکثر ابعاد مجاز {{ max_ratio }} است.</target>
<target>ابعاد {{ ratio }} عکس بیش از حد بزرگ است.حداکثر ابعاد مجاز {{ max_ratio }} می باشد.</target>
</trans-unit>
<trans-unit id="74">
<source>The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.</source>
<target>ابعاد {{ ratio }} عکس بیش از حد کوچک است.حداقل ابعاد مجاز {{ min_ratio }} است.</target>
<target>ابعاد {{ ratio }} عکس بیش از حد کوچک است.حداقل ابعاد مجاز {{ min_ratio }} می باشد.</target>
</trans-unit>
<trans-unit id="75">
<source>The image is square ({{ width }}x{{ height }}px). Square images are not allowed.</source>
<target>این عکس مربع width }}x{{ height }}px}} می باشد.عکس مربع مجاز نمی باشد.</target>
<target>این تصویر یک مربع width }}x{{ height }}px}} می باشد.تصویر مربع مجاز نمی باشد.</target>
</trans-unit>
<trans-unit id="76">
<source>The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.</source>
<target>این عکس افقی width }}x{{ height }}px}} می باشد.عکس افقی مجاز نمی باشد.</target>
<target>این تصویر افقی width }}x{{ height }}px}} می باشد.تصویر افقی مجاز نمی باشد.</target>
</trans-unit>
<trans-unit id="77">
<source>The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.</source>
<target>این عکس عمودی width }}x{{ height }}px}} می باشد.عکس عمودی مجاز نمی باشد.</target>
<target>این تصویر عمودی width }}x{{ height }}px}} می باشد.تصویر عمودی مجاز نمی باشد.</target>
</trans-unit>
<trans-unit id="78">
<source>An empty file is not allowed.</source>
<target>فایل خالی مجاز نمی باشد.</target>
<target>پرونده خالی مجاز نمی باشد.</target>
</trans-unit>
<trans-unit id="79">
<source>The host could not be resolved.</source>
<target>هاست قابل حل نیست.</target>
<target>میزبان قابل حل نمی باشد.</target>
</trans-unit>
<trans-unit id="80">
<source>This value does not match the expected {{ charset }} charset.</source>
<target>این مقدار مورد نظر نمی باشد. مقدار مورد نظر {{ charset }} می باشد.</target>
<target>این مقدار مطابق با مقدار مورد انتظار {{ charset }} نمی باشد.</target>
</trans-unit>
<trans-unit id="81">
<source>This is not a valid Business Identifier Code (BIC).</source>
<target>این مقدار یک BIC درست نیست.</target>
<target>این مقدار یک BIC معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="82">
<source>Error</source>
@ -320,7 +320,7 @@
</trans-unit>
<trans-unit id="83">
<source>This is not a valid UUID.</source>
<target>این مقدار یک UUID درست نیست.</target>
<target>این مقدار یک UUID معتبر نمی باشد.</target>
</trans-unit>
<trans-unit id="84">
<source>This value should be a multiple of {{ compared_value }}.</source>
@ -328,7 +328,7 @@
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>این BIC با IBAN ارتباط ندارد.</target>
<target>این BIC با IBAN ارتباطی ندارد.</target>
</trans-unit>
</body>
</file>

View File

@ -56,6 +56,7 @@ trait WorkflowBuilderTrait
$placesMetadata = [];
$placesMetadata['c'] = [
'bg_color' => 'DeepSkyBlue',
'description' => 'My custom place description',
];
$transitionsMetadata = new \SplObjectStorage();

View File

@ -17,7 +17,8 @@ skinparam agent {
}
state "a" <<initial>>
state "b" <<marked>>
state "c" <<DeepSkyBlue>>
state "c" <<DeepSkyBlue>> as c
c : My custom place description
agent "t1"
agent "t2"
"a" -[#Purple]-> "t1": "<font color=Grey>My custom transition label 2</font>"

View File

@ -17,7 +17,8 @@ skinparam agent {
}
state "a" <<initial>>
state "b"
state "c" <<DeepSkyBlue>>
state "c" <<DeepSkyBlue>> as c
c : My custom place description
agent "t1"
agent "t2"
"a" -[#Purple]-> "t1": "<font color=Grey>My custom transition label 2</font>"