Merge branch '3.2'
* 3.2: [WebProfilerBundle] Fix AJAX panel with fetch requests Don’t compile when Opcache is not enabled on CLI DateIntervalType: 'invert' should not inherit the 'required' option [Form] DateIntervalType: Do not try to translate choices [TwigBridge] fix constructor args check Allow simple-phpunit to be used with an HTTP proxy Minor fixes for 3.2 Fix a web profiler form issue with fields added to the form after the form was built do not trigger deprecations for valid YAML Write an exception message in a one heading line [Workflow] Added missing docblock [Finder] Refine phpdoc about argument for NumberComparator Fixed max width from ajax request url element (td) Fix unresolved parameters from default bundle configs in debug:config [github] Tweak PR template [Serializer] Optimize max depth checking
This commit is contained in:
commit
40280f203c
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,11 +1,19 @@
|
||||
| Q | A
|
||||
| ------------- | ---
|
||||
| Branch? | "master" for new features / 2.7, 2.8 or 3.1 for fixes
|
||||
| Branch? | master / 2.7, 2.8, 3.1 or 3.2 <!--see comment below-->
|
||||
| Bug fix? | yes/no
|
||||
| New feature? | yes/no
|
||||
| BC breaks? | yes/no
|
||||
| Deprecations? | yes/no
|
||||
| Tests pass? | yes/no
|
||||
| Fixed tickets | comma-separated list of tickets fixed by the PR, if any
|
||||
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
|
||||
| License | MIT
|
||||
| Doc PR | reference to the documentation PR, if any
|
||||
| Doc PR | symfony/symfony-docs#... <!--highly recommended for new features-->
|
||||
|
||||
<!--
|
||||
- Bug fixes must be submitted against the lowest branch where they apply
|
||||
(lowest branches are regularly merged to upper ones so they get the fixes too).
|
||||
- Features and deprecations must be submitted against the master branch.
|
||||
- Please fill in this template according to the PR you're about to submit.
|
||||
- Replace this comment by a description of what your PR is solving.
|
||||
-->
|
||||
|
@ -37,7 +37,7 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__
|
||||
if (file_exists("phpunit-$PHPUNIT_VERSION")) {
|
||||
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? '(del /S /F /Q %s & rmdir %1$s) >nul': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION"));
|
||||
}
|
||||
if (extension_loaded('openssl') && ini_get('allow_url_fopen')) {
|
||||
if (extension_loaded('openssl') && ini_get('allow_url_fopen') && !isset($_SERVER['http_proxy']) && !isset($_SERVER['https_proxy'])) {
|
||||
stream_copy_to_stream(fopen("https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip", 'rb'), fopen("$PHPUNIT_VERSION.zip", 'wb'));
|
||||
} else {
|
||||
@unlink("$PHPUNIT_VERSION.zip");
|
||||
|
@ -31,7 +31,7 @@ class FormExtension extends \Twig_Extension implements \Twig_Extension_InitRunti
|
||||
|
||||
public function __construct($renderer = null)
|
||||
{
|
||||
if ($this->renderer instanceof TwigRendererInterface) {
|
||||
if ($renderer instanceof TwigRendererInterface) {
|
||||
@trigger_error(sprintf('Passing a Twig Form Renderer to the "%s" constructor is deprecated since version 3.2 and won\'t be possible in 4.0. Pass the Twig_Environment to the TwigRendererEngine constructor instead.', static::class), E_USER_DEPRECATED);
|
||||
} elseif (null !== $renderer && !(is_array($renderer) && isset($renderer[0], $renderer[1]) && $renderer[0] instanceof ContainerInterface)) {
|
||||
throw new \InvalidArgumentException(sprintf('Passing any arguments the constructor of %s is reserved for internal use.', __CLASS__));
|
||||
|
@ -105,7 +105,7 @@ EOF
|
||||
|
||||
$io->title(sprintf('Current configuration for "%s.%s"', $extensionAlias, $path));
|
||||
|
||||
$io->writeln(Yaml::dump($config, 10));
|
||||
$io->writeln(Yaml::dump($container->getParameterBag()->resolveValue($config), 10));
|
||||
}
|
||||
|
||||
private function compileContainer()
|
||||
|
@ -17,7 +17,6 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
|
||||
use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper;
|
||||
use Symfony\Component\Workflow\Marking;
|
||||
use Symfony\Component\Workflow\Workflow;
|
||||
|
||||
/**
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
|
@ -137,7 +137,7 @@ abstract class Controller implements ContainerAwareInterface
|
||||
protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
|
||||
{
|
||||
$response = new BinaryFileResponse($file);
|
||||
$response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFileName() : $fileName);
|
||||
$response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFilename() : $fileName);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ProxyAdapter;
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
|
@ -9,9 +9,7 @@
|
||||
<div class="text-exception">
|
||||
<div class="open-quote">“</div>
|
||||
|
||||
<h1>
|
||||
{{ exception.message|nl2br|format_file_from_text }}
|
||||
</h1>
|
||||
<h1>{{ exception.message|nl2br|format_file_from_text }}</h1>
|
||||
|
||||
<div>
|
||||
<strong>{{ status_code }}</strong> {{ status_text }} - {{ exception.class|abbr_class }}
|
||||
|
@ -457,7 +457,7 @@
|
||||
{% import _self as tree %}
|
||||
<div class="tree-details{% if not show|default(false) %} hidden{% endif %}" {% if data.id is defined %}id="{{ data.id }}-details"{% endif %}>
|
||||
<h2 class="dump-inline">
|
||||
{{ name|default('(no name)') }} ({{ profiler_dump(data.type_class) }})
|
||||
{{ name|default('(no name)') }} {% if data.type_class is defined %}({{ profiler_dump(data.type_class) }}){% endif %}
|
||||
</h2>
|
||||
|
||||
{% if data.errors is defined and data.errors|length > 0 %}
|
||||
|
@ -234,15 +234,28 @@
|
||||
var oldFetch = window.fetch;
|
||||
window.fetch = function () {
|
||||
var promise = oldFetch.apply(this, arguments);
|
||||
if (!arguments[0].match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) {
|
||||
var url = arguments[0];
|
||||
var params = arguments[1];
|
||||
var paramType = Object.prototype.toString.call(arguments[0]);
|
||||
if (paramType === '[object Request]') {
|
||||
url = arguments[0].url;
|
||||
params = {
|
||||
method: arguments[0].method,
|
||||
credentials: arguments[0].credentials,
|
||||
headers: arguments[0].headers,
|
||||
mode: arguments[0].mode,
|
||||
redirect: arguments[0].redirect
|
||||
};
|
||||
}
|
||||
if (!url.match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) {
|
||||
var method = 'GET';
|
||||
if (arguments[1] && arguments[1].method !== undefined) {
|
||||
method = arguments[1].method;
|
||||
if (params && params.method !== undefined) {
|
||||
method = params.method;
|
||||
}
|
||||
|
||||
var stackElement = {
|
||||
error: false,
|
||||
url: arguments[0],
|
||||
url: url,
|
||||
method: method,
|
||||
type: 'fetch',
|
||||
start: new Date()
|
||||
|
@ -93,7 +93,7 @@ class PhpFilesAdapter extends AbstractAdapter
|
||||
$ok = true;
|
||||
$data = array($lifetime ? time() + $lifetime : PHP_INT_MAX, '');
|
||||
|
||||
foreach ($values as $id => $value) {
|
||||
foreach ($values as $key => $value) {
|
||||
if (null === $value || is_object($value)) {
|
||||
$value = serialize($value);
|
||||
} elseif (is_array($value)) {
|
||||
@ -109,14 +109,17 @@ class PhpFilesAdapter extends AbstractAdapter
|
||||
$value = serialize($value);
|
||||
}
|
||||
} elseif (!is_scalar($value)) {
|
||||
throw new InvalidArgumentException(sprintf('Value of type "%s" is not serializable', $key, gettype($value)));
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value)));
|
||||
}
|
||||
|
||||
$data[1] = $value;
|
||||
$file = $this->getFile($id, true);
|
||||
$file = $this->getFile($key, true);
|
||||
$ok = $this->write($file, '<?php return '.var_export($data, true).';') && $ok;
|
||||
|
||||
if ('cli' !== PHP_SAPI || ini_get('opcache.enable_cli')) {
|
||||
@opcache_compile_file($file);
|
||||
}
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class TagAwareAdapterTest extends AdapterTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Psr\Cache\InvalidArgumentException
|
||||
* @expectedException \Psr\Cache\InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidTag()
|
||||
{
|
||||
|
@ -65,7 +65,7 @@ class CacheItemTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidKey
|
||||
* @expectedException Symfony\Component\Cache\Exception\InvalidArgumentException
|
||||
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Cache tag
|
||||
*/
|
||||
public function testInvalidTag($tag)
|
||||
|
@ -13,7 +13,6 @@ namespace Symfony\Component\Console\Tests\Style;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class SymfonyStyleTest extends PHPUnit_Framework_TestCase
|
||||
|
@ -37,7 +37,7 @@ class NumberComparator extends Comparator
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $test A comparison string
|
||||
* @param string|int $test A comparison string or an integer
|
||||
*
|
||||
* @throws \InvalidArgumentException If the test is not understood
|
||||
*/
|
||||
|
@ -111,7 +111,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
* $finder->depth('> 1') // the Finder will start matching at level 1.
|
||||
* $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
|
||||
*
|
||||
* @param int $level The depth level expression
|
||||
* @param string|int $level The depth level expression
|
||||
*
|
||||
* @return Finder|SplFileInfo[] The current Finder instance
|
||||
*
|
||||
@ -283,7 +283,7 @@ class Finder implements \IteratorAggregate, \Countable
|
||||
* $finder->size('<= 1Ki');
|
||||
* $finder->size(4);
|
||||
*
|
||||
* @param string $size A size range string
|
||||
* @param string|int $size A size range string or an integer
|
||||
*
|
||||
* @return Finder|SplFileInfo[] The current Finder instance
|
||||
*
|
||||
|
@ -102,14 +102,12 @@ class DateIntervalType extends AbstractType
|
||||
$childOptions[$part] = array();
|
||||
$childOptions[$part]['error_bubbling'] = true;
|
||||
if ('choice' === $options['widget']) {
|
||||
$childOptions[$part]['choice_translation_domain'] = false;
|
||||
$childOptions[$part]['choices'] = $options[$part];
|
||||
$childOptions[$part]['placeholder'] = $options['placeholder'][$part];
|
||||
}
|
||||
}
|
||||
}
|
||||
$invertOptions = array(
|
||||
'error_bubbling' => true,
|
||||
);
|
||||
// Append generic carry-along options
|
||||
foreach (array('required', 'translation_domain') as $passOpt) {
|
||||
foreach ($this->timeParts as $part) {
|
||||
@ -117,9 +115,6 @@ class DateIntervalType extends AbstractType
|
||||
$childOptions[$part][$passOpt] = $options[$passOpt];
|
||||
}
|
||||
}
|
||||
if ($options['with_invert']) {
|
||||
$invertOptions[$passOpt] = $options[$passOpt];
|
||||
}
|
||||
}
|
||||
foreach ($this->timeParts as $part) {
|
||||
if ($options['with_'.$part]) {
|
||||
@ -135,7 +130,11 @@ class DateIntervalType extends AbstractType
|
||||
}
|
||||
}
|
||||
if ($options['with_invert']) {
|
||||
$builder->add('invert', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', $invertOptions);
|
||||
$builder->add('invert', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', array(
|
||||
'error_bubbling' => true,
|
||||
'required' => false,
|
||||
'translation_domain' => $options['translation_domain'],
|
||||
));
|
||||
}
|
||||
$builder->addViewTransformer(new DateIntervalToArrayTransformer($parts, 'text' === $options['widget']));
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateIntervalType;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\Test\TypeTestCase as TestCase;
|
||||
|
||||
@ -364,4 +365,38 @@ class DateIntervalTypeTest extends TestCase
|
||||
$this->assertSame(array(), iterator_to_array($form['years']->getErrors()));
|
||||
$this->assertSame(array($error), iterator_to_array($form->getErrors()));
|
||||
}
|
||||
|
||||
public function testTranslationsAreDisabledForChoiceWidget()
|
||||
{
|
||||
$form = $this->factory->create(
|
||||
DateIntervalType::class,
|
||||
null,
|
||||
array(
|
||||
'widget' => 'choice',
|
||||
'with_hours' => true,
|
||||
'with_minutes' => true,
|
||||
'with_seconds' => true,
|
||||
)
|
||||
);
|
||||
$this->assertFalse($form->get('years')->getConfig()->getOption('choice_translation_domain'));
|
||||
$this->assertFalse($form->get('months')->getConfig()->getOption('choice_translation_domain'));
|
||||
$this->assertFalse($form->get('days')->getConfig()->getOption('choice_translation_domain'));
|
||||
$this->assertFalse($form->get('hours')->getConfig()->getOption('choice_translation_domain'));
|
||||
$this->assertFalse($form->get('minutes')->getConfig()->getOption('choice_translation_domain'));
|
||||
$this->assertFalse($form->get('seconds')->getConfig()->getOption('choice_translation_domain'));
|
||||
}
|
||||
|
||||
public function testInvertDoesNotInheritRequiredOption()
|
||||
{
|
||||
$form = $this->factory->create(
|
||||
'Symfony\Component\Form\Extension\Core\Type\DateIntervalType',
|
||||
null,
|
||||
array(
|
||||
'input' => 'dateinterval',
|
||||
'with_invert' => true,
|
||||
'required' => true,
|
||||
)
|
||||
);
|
||||
$this->assertFalse($form->get('invert')->getConfig()->getOption('required'));
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ namespace Symfony\Component\Serializer\Encoder;
|
||||
|
||||
use Symfony\Component\Yaml\Dumper;
|
||||
use Symfony\Component\Yaml\Parser;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Encodes YAML data.
|
||||
|
@ -18,6 +18,7 @@ use Symfony\Component\Serializer\Exception\LogicException;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
|
||||
|
||||
@ -68,9 +69,10 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
$stack = array();
|
||||
$attributes = $this->getAttributes($object, $format, $context);
|
||||
$class = get_class($object);
|
||||
$attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null;
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($this->isMaxDepthReached($class, $attribute, $context)) {
|
||||
if (null !== $attributesMetadata && $this->isMaxDepthReached($attributesMetadata, $class, $attribute, $context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -300,42 +302,35 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
/**
|
||||
* Is the max depth reached for the given attribute?
|
||||
*
|
||||
* @param AttributeMetadataInterface[] $attributesMetadata
|
||||
* @param string $class
|
||||
* @param string $attribute
|
||||
* @param array $context
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isMaxDepthReached($class, $attribute, array &$context)
|
||||
private function isMaxDepthReached(array $attributesMetadata, $class, $attribute, array &$context)
|
||||
{
|
||||
if (!$this->classMetadataFactory || !isset($context[static::ENABLE_MAX_DEPTH])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classMetadata = $this->classMetadataFactory->getMetadataFor($class);
|
||||
$attributesMetadata = $classMetadata->getAttributesMetadata();
|
||||
|
||||
if (!isset($attributesMetadata[$attribute])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$maxDepth = $attributesMetadata[$attribute]->getMaxDepth();
|
||||
if (null === $maxDepth) {
|
||||
if (
|
||||
!isset($context[static::ENABLE_MAX_DEPTH]) ||
|
||||
!isset($attributesMetadata[$attribute]) ||
|
||||
null === $maxDepth = $attributesMetadata[$attribute]->getMaxDepth()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$key = sprintf(static::DEPTH_KEY_PATTERN, $class, $attribute);
|
||||
$keyExist = isset($context[$key]);
|
||||
if (!isset($context[$key])) {
|
||||
$context[$key] = 1;
|
||||
|
||||
if ($keyExist && $context[$key] === $maxDepth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($context[$key] === $maxDepth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($keyExist) {
|
||||
++$context[$key];
|
||||
} else {
|
||||
$context[$key] = 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ class StubCaster
|
||||
$stub->attr = $c->attr;
|
||||
|
||||
if (Stub::TYPE_REF === $c->type && !$c->class && is_string($c->value) && !preg_match('//u', $c->value)) {
|
||||
$stub->type = self::TYPE_STRING;
|
||||
$stub->class = self::STRING_BINARY;
|
||||
$stub->type = Stub::TYPE_STRING;
|
||||
$stub->class = Stub::STRING_BINARY;
|
||||
}
|
||||
|
||||
return array();
|
||||
|
@ -79,6 +79,9 @@ class DefinitionBuilder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transition[] $transitions
|
||||
*/
|
||||
public function addTransitions(array $transitions)
|
||||
{
|
||||
foreach ($transitions as $transition) {
|
||||
|
@ -30,6 +30,12 @@ class Registry
|
||||
$this->workflows[] = array($workflow, $className);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $subject
|
||||
* @param string|null $workflowName
|
||||
*
|
||||
* @return Workflow
|
||||
*/
|
||||
public function get($subject, $workflowName = null)
|
||||
{
|
||||
$matched = null;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace Symfony\Component\Workflow\Tests;
|
||||
|
||||
use Symfony\Component\Workflow\Marking;
|
||||
use Symfony\Component\Workflow\StateMachine;
|
||||
|
||||
class StateMachineTest extends \PHPUnit_Framework_TestCase
|
||||
|
@ -463,8 +463,8 @@ class Inline
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($mapping[$i + 1]) || ' ' !== $mapping[$i + 1]) {
|
||||
@trigger_error('Omitting the space after the colon that follows a mapping key definition is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
|
||||
if (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', '[', ']', '{', '}'), true)) {
|
||||
@trigger_error('Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
// value
|
||||
|
@ -166,7 +166,7 @@ class InlineTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
* @expectedDeprecation Omitting the space after the colon that follows a mapping key definition is deprecated since version 3.2 and will throw a ParseException in 4.0.
|
||||
* @expectedDeprecation Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.
|
||||
* throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
|
||||
*/
|
||||
public function testParseMappingKeyWithColonNotFollowedBySpace()
|
||||
@ -395,6 +395,8 @@ class InlineTest extends \PHPUnit_Framework_TestCase
|
||||
array('[foo, {bar: foo}]', array('foo', array('bar' => 'foo'))),
|
||||
array('{ foo: {bar: foo} }', array('foo' => array('bar' => 'foo'))),
|
||||
array('{ foo: [bar, foo] }', array('foo' => array('bar', 'foo'))),
|
||||
array('{ foo:{bar: foo} }', array('foo' => array('bar' => 'foo'))),
|
||||
array('{ foo:[bar, foo] }', array('foo' => array('bar', 'foo'))),
|
||||
|
||||
array('[ foo, [ bar, foo ] ]', array('foo', array('bar', 'foo'))),
|
||||
|
||||
|
Reference in New Issue
Block a user