Merge branch '3.4'

* 3.4:
  [Console] Fix descriptor tests
  Change wording from object to subject
  add changelog entry for Stopwatch::reset()
  Add DateCaster
  [Dotenv] parse concatenated variable values
  [Yaml] deprecate the !str tag
  Add filter in VarDumperTestTrait
  Support for parsing PHP constants in yaml loader
This commit is contained in:
Fabien Potencier 2017-07-03 16:32:35 +03:00
commit 1437cafa1b
23 changed files with 351 additions and 89 deletions

View File

@ -61,5 +61,7 @@ Validator
Yaml
----
* Support for the `!str` tag is deprecated, use the `!!str` tag instead.
* Using the non-specific tag `!` is deprecated and will have a different
behavior in 4.0. Use a plain integer or `!!float` instead.

View File

@ -569,6 +569,39 @@ Validator
changed to `true` as of 4.0. If you need the previous behaviour ensure to
set the option to `false`.
VarDumper
---------
* The `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$context = null`
argument and moves `$message = ''` argument at 4th position.
Before:
```php
VarDumperTestTrait::assertDumpEquals($dump, $data, $message = '');
```
After:
```php
VarDumperTestTrait::assertDumpEquals($dump, $data, $filter = 0, $message = '');
```
* The `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$context = null`
argument and moves `$message = ''` argument at 4th position.
Before:
```php
VarDumperTestTrait::assertDumpMatchesFormat($dump, $data, $message = '');
```
After:
```php
VarDumperTestTrait::assertDumpMatchesFormat($dump, $data, $filter = 0, $message = '');
```
Workflow
--------
@ -577,6 +610,8 @@ Workflow
Yaml
----
* Support for the `!str` tag was removed, use the `!!str` tag instead.
* Starting an unquoted string with a question mark followed by a space
throws a `ParseException`.

View File

@ -159,40 +159,40 @@ trait ControllerTrait
}
/**
* Checks if the attributes are granted against the current authentication token and optionally supplied object.
* Checks if the attributes are granted against the current authentication token and optionally supplied subject.
*
* @param mixed $attributes The attributes
* @param mixed $object The object
* @param mixed $subject The subject
*
* @return bool
*
* @throws \LogicException
*/
protected function isGranted($attributes, $object = null)
protected function isGranted($attributes, $subject = null)
{
if (!$this->container->has('security.authorization_checker')) {
throw new \LogicException('The SecurityBundle is not registered in your application.');
}
return $this->container->get('security.authorization_checker')->isGranted($attributes, $object);
return $this->container->get('security.authorization_checker')->isGranted($attributes, $subject);
}
/**
* Throws an exception unless the attributes are granted against the current authentication token and optionally
* supplied object.
* supplied subject.
*
* @param mixed $attributes The attributes
* @param mixed $object The object
* @param mixed $subject The subject
* @param string $message The message passed to the exception
*
* @throws AccessDeniedException
*/
protected function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.')
protected function denyAccessUnlessGranted($attributes, $subject = null, $message = 'Access Denied.')
{
if (!$this->isGranted($attributes, $object)) {
if (!$this->isGranted($attributes, $subject)) {
$exception = $this->createAccessDeniedException($message);
$exception->setAttributes($attributes);
$exception->setSubject($object);
$exception->setSubject($subject);
throw $exception;
}

View File

@ -1,7 +1,7 @@
**argument_name:**
#### `argument_name`
argument description
* Name: argument_name
* Is required: no
* Is array: no
* Description: argument description
* Default: `INF`

View File

@ -1,9 +1,8 @@
**option_name:**
#### `--option_name|-o`
option description
* Name: `--option_name`
* Shortcut: `-o`
* Accept value: yes
* Is value required: no
* Is multiple: no
* Description: option description
* Default: `INF`

View File

@ -169,73 +169,110 @@ final class Dotenv
throw $this->createFormatException('Whitespace are not supported before the value');
}
$value = '';
$singleQuoted = false;
$notQuoted = false;
if ("'" === $this->data[$this->cursor]) {
$singleQuoted = true;
++$this->cursor;
while ("\n" !== $this->data[$this->cursor]) {
if ("'" === $this->data[$this->cursor]) {
if ($this->cursor + 1 === $this->end) {
$v = '';
do {
if ("'" === $this->data[$this->cursor]) {
$value = '';
++$this->cursor;
while ("\n" !== $this->data[$this->cursor]) {
if ("'" === $this->data[$this->cursor]) {
break;
}
if ("'" !== $this->data[$this->cursor + 1]) {
break;
$value .= $this->data[$this->cursor];
++$this->cursor;
if ($this->cursor === $this->end) {
throw $this->createFormatException('Missing quote to end the value');
}
}
if ("\n" === $this->data[$this->cursor]) {
throw $this->createFormatException('Missing quote to end the value');
}
++$this->cursor;
$v .= $value;
} elseif ('"' === $this->data[$this->cursor]) {
$value = '';
++$this->cursor;
while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
$value .= $this->data[$this->cursor];
++$this->cursor;
if ($this->cursor === $this->end) {
throw $this->createFormatException('Missing quote to end the value');
}
}
if ("\n" === $this->data[$this->cursor]) {
throw $this->createFormatException('Missing quote to end the value');
}
++$this->cursor;
$value = str_replace(array('\\\\', '\\"', '\r', '\n'), array('\\', '"', "\r", "\n"), $value);
$resolvedValue = $value;
$resolvedValue = $this->resolveVariables($resolvedValue);
$resolvedValue = $this->resolveCommands($resolvedValue);
$v .= $resolvedValue;
} else {
$value = '';
$prevChr = $this->data[$this->cursor - 1];
while ($this->cursor < $this->end && !in_array($this->data[$this->cursor], array("\n", '"', "'"), true) && !((' ' === $prevChr || "\t" === $prevChr) && '#' === $this->data[$this->cursor])) {
if ('\\' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && ('"' === $this->data[$this->cursor + 1] || "'" === $this->data[$this->cursor + 1])) {
++$this->cursor;
}
$value .= $prevChr = $this->data[$this->cursor];
if ('$' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && '(' === $this->data[$this->cursor + 1]) {
++$this->cursor;
$value .= '('.$this->lexNestedExpression().')';
}
++$this->cursor;
}
$value .= $this->data[$this->cursor];
++$this->cursor;
$value = rtrim($value);
$resolvedValue = $value;
$resolvedValue = $this->resolveVariables($resolvedValue);
$resolvedValue = $this->resolveCommands($resolvedValue);
if ($this->cursor === $this->end) {
throw $this->createFormatException('Missing quote to end the value');
}
}
if ("\n" === $this->data[$this->cursor]) {
throw $this->createFormatException('Missing quote to end the value');
}
++$this->cursor;
} elseif ('"' === $this->data[$this->cursor]) {
++$this->cursor;
while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
$value .= $this->data[$this->cursor];
++$this->cursor;
if ($this->cursor === $this->end) {
throw $this->createFormatException('Missing quote to end the value');
}
}
if ("\n" === $this->data[$this->cursor]) {
throw $this->createFormatException('Missing quote to end the value');
}
++$this->cursor;
$value = str_replace(array('\\\\', '\\"', '\r', '\n'), array('\\', '"', "\r", "\n"), $value);
} else {
$notQuoted = true;
$prevChr = $this->data[$this->cursor - 1];
while ($this->cursor < $this->end && "\n" !== $this->data[$this->cursor] && !((' ' === $prevChr || "\t" === $prevChr) && '#' === $this->data[$this->cursor])) {
if ('\\' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && ('"' === $this->data[$this->cursor + 1] || "'" === $this->data[$this->cursor + 1])) {
++$this->cursor;
if ($resolvedValue === $value && preg_match('/\s+/', $value)) {
throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
}
$value .= $prevChr = $this->data[$this->cursor];
++$this->cursor;
$v .= $resolvedValue;
if ($this->cursor < $this->end && '#' === $this->data[$this->cursor]) {
break;
}
}
$value = rtrim($value);
}
} while ($this->cursor < $this->end && "\n" !== $this->data[$this->cursor]);
$this->skipEmptyLines();
$currentValue = $value;
if (!$singleQuoted) {
$value = $this->resolveVariables($value);
$value = $this->resolveCommands($value);
return $v;
}
private function lexNestedExpression()
{
++$this->cursor;
$value = '';
while ("\n" !== $this->data[$this->cursor] && ')' !== $this->data[$this->cursor]) {
$value .= $this->data[$this->cursor];
if ('(' === $this->data[$this->cursor]) {
$value .= $this->lexNestedExpression().')';
}
++$this->cursor;
if ($this->cursor === $this->end) {
throw $this->createFormatException('Missing closing parenthesis.');
}
}
if ($notQuoted && $currentValue == $value && preg_match('/\s+/', $value)) {
throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
if ("\n" === $this->data[$this->cursor]) {
throw $this->createFormatException('Missing closing parenthesis.');
}
return $value;

View File

@ -85,7 +85,6 @@ class DotenvTest extends TestCase
array("FOO='bar'\n", array('FOO' => 'bar')),
array("FOO='bar\"foo'\n", array('FOO' => 'bar"foo')),
array("FOO=\"bar\\\"foo\"\n", array('FOO' => 'bar"foo')),
array("FOO='bar''foo'\n", array('FOO' => 'bar\'foo')),
array('FOO="bar\nfoo"', array('FOO' => "bar\nfoo")),
array('FOO="bar\rfoo"', array('FOO' => "bar\rfoo")),
array('FOO=\'bar\nfoo\'', array('FOO' => 'bar\nfoo')),
@ -99,6 +98,10 @@ class DotenvTest extends TestCase
array('FOO=\\"BAR', array('FOO' => '"BAR')),
// concatenated values
array("FOO='bar''foo'\n", array('FOO' => 'barfoo')),
array("FOO='bar '' baz'", array('FOO' => 'bar baz')),
array("FOO=bar\nBAR='baz'\"\$FOO\"", array('FOO' => 'bar', 'BAR' => 'bazbar')),
array("FOO='bar '\\'' baz'", array('FOO' => "bar ' baz")),
// comments
array("#FOO=bar\nBAR=foo", array('BAR' => 'foo')),

View File

@ -51,7 +51,7 @@ class AuthorizationChecker implements AuthorizationCheckerInterface
*
* @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token.
*/
final public function isGranted($attributes, $object = null)
final public function isGranted($attributes, $subject = null)
{
if (null === ($token = $this->tokenStorage->getToken())) {
throw new AuthenticationCredentialsNotFoundException('The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL.');
@ -65,6 +65,6 @@ class AuthorizationChecker implements AuthorizationCheckerInterface
$attributes = array($attributes);
}
return $this->accessDecisionManager->decide($token, $attributes, $object);
return $this->accessDecisionManager->decide($token, $attributes, $subject);
}
}

View File

@ -19,12 +19,12 @@ namespace Symfony\Component\Security\Core\Authorization;
interface AuthorizationCheckerInterface
{
/**
* Checks if the attributes are granted against the current authentication token and optionally supplied object.
* Checks if the attributes are granted against the current authentication token and optionally supplied subject.
*
* @param mixed $attributes
* @param mixed $object
* @param mixed $subject
*
* @return bool
*/
public function isGranted($attributes, $object = null);
public function isGranted($attributes, $subject = null);
}

View File

@ -0,0 +1,7 @@
CHANGELOG
=========
3.4.0
-----
* added the `Stopwatch::reset()` method

View File

@ -27,6 +27,7 @@ CHANGELOG
-----
* deprecated `Tests\Constraints\AbstractContraintValidatorTest` in favor of `Test\ConstraintValidatorTestCase`
* added support for PHP constants in YAML configuration files
3.1.0
-----

View File

@ -116,7 +116,7 @@ class YamlFileLoader extends FileLoader
private function parseFile($path)
{
try {
$classes = $this->yamlParser->parse(file_get_contents($path), Yaml::PARSE_KEYS_AS_STRINGS);
$classes = $this->yamlParser->parse(file_get_contents($path), Yaml::PARSE_KEYS_AS_STRINGS | Yaml::PARSE_CONSTANT);
} catch (ParseException $e) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e);
}

View File

@ -124,6 +124,19 @@ class YamlFileLoaderTest extends TestCase
$this->assertEquals($expected, $metadata);
}
public function testLoadClassMetadataWithConstants()
{
$loader = new YamlFileLoader(__DIR__.'/mapping-with-constants.yml');
$metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\Entity');
$loader->loadClassMetadata($metadata);
$expected = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\Entity');
$expected->addPropertyConstraint('firstName', new Range(array('max' => PHP_INT_MAX)));
$this->assertEquals($expected, $metadata);
}
public function testLoadGroupSequenceProvider()
{
$loader = new YamlFileLoader(__DIR__.'/constraint-mapping.yml');

View File

@ -0,0 +1,8 @@
namespaces:
custom: Symfony\Component\Validator\Tests\Fixtures\
Symfony\Component\Validator\Tests\Fixtures\Entity:
properties:
firstName:
- Range:
max: !php/const:PHP_INT_MAX

View File

@ -0,0 +1,41 @@
<?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\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
/**
* Casts DateTimeInterface related classes to array representation.
*
* @author Dany Maillard <danymaillard93b@gmail.com>
*/
class DateCaster
{
public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, $isNested, $filter)
{
$prefix = Caster::PREFIX_VIRTUAL;
$location = $d->getTimezone()->getLocation();
$fromNow = (new \DateTime())->diff($d);
$title = $d->format('l, F j, Y')
."\n".$fromNow->format('%R').(ltrim($fromNow->format('%yy %mm %dd %H:%I:%Ss'), ' 0ymd:s') ?: '0s').' from now'
.($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '')
;
$a = array();
$a[$prefix.'date'] = new ConstStub($d->format('Y-m-d H:i:s.u '.($location ? 'e (P)' : 'P')), $title);
$stub->class .= $d->format(' @U');
return $a;
}
}

View File

@ -109,6 +109,8 @@ abstract class AbstractCloner implements ClonerInterface
'Redis' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'),
'RedisArray' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'),
'DateTimeInterface' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'),
':curl' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'),
':dba' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'),
':dba persistent' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'),

View File

@ -19,17 +19,29 @@ use Symfony\Component\VarDumper\Dumper\CliDumper;
*/
trait VarDumperTestTrait
{
public function assertDumpEquals($dump, $data, $message = '')
public function assertDumpEquals($dump, $data, $filter = 0, $message = '')
{
$this->assertSame(rtrim($dump), $this->getDump($data), $message);
if (is_string($filter)) {
@trigger_error(sprintf('The $message argument of the "%s()" method at 3rd position is deprecated since version 3.4 and will be moved at 4th position in 4.0.', __METHOD__), E_USER_DEPRECATED);
$message = $filter;
$filter = 0;
}
$this->assertSame(rtrim($dump), $this->getDump($data, null, $filter), $message);
}
public function assertDumpMatchesFormat($dump, $data, $message = '')
public function assertDumpMatchesFormat($dump, $data, $filter = 0, $message = '')
{
$this->assertStringMatchesFormat(rtrim($dump), $this->getDump($data), $message);
if (is_string($filter)) {
@trigger_error(sprintf('The $message argument of the "%s()" method at 3rd position is deprecated since version 3.4 and will be moved at 4th position in 4.0.', __METHOD__), E_USER_DEPRECATED);
$message = $filter;
$filter = 0;
}
$this->assertStringMatchesFormat(rtrim($dump), $this->getDump($data, null, $filter), $message);
}
protected function getDump($data, $key = null)
protected function getDump($data, $key = null, $filter = 0)
{
$flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0;
$flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0;
@ -38,7 +50,7 @@ trait VarDumperTestTrait
$cloner->setMaxItems(-1);
$dumper = new CliDumper(null, null, $flags);
$dumper->setColors(false);
$data = $cloner->cloneVar($data)->withRefHandles(false);
$data = $cloner->cloneVar($data, $filter)->withRefHandles(false);
if (null !== $key && null === $data = $data->seek($key)) {
return;
}

View File

@ -0,0 +1,87 @@
<?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\VarDumper\Tests\Caster;
use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\Caster\DateCaster;
use Symfony\Component\VarDumper\Cloner\Stub;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;
/**
* @author Dany Maillard <danymaillard93b@gmail.com>
*/
class DateCasterTest extends TestCase
{
use VarDumperTestTrait;
/**
* @dataProvider provideDateTimes
*/
public function testDumpDateTime($time, $timezone, $expected)
{
if ((defined('HHVM_VERSION_ID') || PHP_VERSION_ID <= 50509) && preg_match('/[-+]\d{2}:\d{2}/', $timezone)) {
$this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.');
}
$date = new \DateTime($time, new \DateTimeZone($timezone));
$xDump = <<<EODUMP
DateTime @1493503200 {
date: $expected
}
EODUMP;
$this->assertDumpMatchesFormat($xDump, $date);
}
public function testCastDateTime()
{
$stub = new Stub();
$date = new \DateTime('2017-08-30 00:00:00.000000', new \DateTimeZone('Europe/Zurich'));
$cast = DateCaster::castDateTime($date, array('foo' => 'bar'), $stub, false, 0);
$xDump = <<<'EODUMP'
array:1 [
"\x00~\x00date" => 2017-08-30 00:00:00.000000 Europe/Zurich (+02:00)
]
EODUMP;
$this->assertDumpMatchesFormat($xDump, $cast);
$xDump = <<<'EODUMP'
Symfony\Component\VarDumper\Caster\ConstStub {
+type: "ref"
+class: "2017-08-30 00:00:00.000000 Europe/Zurich (+02:00)"
+value: """
Wednesday, August 30, 2017\n
+%a from now\n
DST On
"""
+cut: 0
+handle: 0
+refCount: 0
+position: 0
+attr: []
}
EODUMP;
$this->assertDumpMatchesFormat($xDump, $cast["\0~\0date"]);
}
public function provideDateTimes()
{
return array(
array('2017-04-30 00:00:00.000000', 'Europe/Zurich', '2017-04-30 00:00:00.000000 Europe/Zurich (+02:00)'),
array('2017-04-30 00:00:00.000000', '+02:00', '2017-04-30 00:00:00.000000 +02:00'),
);
}
}

View File

@ -27,6 +27,8 @@ CHANGELOG
3.4.0
-----
* Support for the `!str` tag is deprecated, use the `!!str` tag instead.
* Deprecated using the non-specific tag `!` as its behavior will change in 4.0.
It will force non-evaluating your values in 4.0. Use plain integers or `!!float` instead.

View File

@ -551,7 +551,11 @@ class Inline
case $scalar[0] === '!':
switch (true) {
case 0 === strpos($scalar, '!str'):
@trigger_error('Support for the !str tag is deprecated since version 3.4. Use the !!str tag instead.', E_USER_DEPRECATED);
return (string) substr($scalar, 5);
case 0 === strpos($scalar, '!!str '):
return (string) substr($scalar, 6);
case 0 === strpos($scalar, '! '):
return substr($scalar, 2);
case 0 === strpos($scalar, '!php/object:'):

View File

@ -572,7 +572,7 @@ test: Various explicit families
todo: true
spec: 2.23
yaml: |
not-date: !str 2002-04-28
not-date: !!str 2002-04-28
picture: !binary |
R0lGODlhDAAMAIQAAP//9/X
17unp5WZmZgAAAOfn515eXv
@ -911,7 +911,7 @@ test: Explicit typing
yaml: |
integer: 12
no int: ! 12
string: !str 12
string: !!str 12
php: |
array( 'integer' => 12, 'no int' => '12', 'string' => '12' )
---
@ -943,7 +943,7 @@ documents: 2
test: Type family under yaml.org
yaml: |
# The URI is 'tag:yaml.org,2002:str'
- !str a Unicode string
- !!str a Unicode string
php: |
array( 'a Unicode string' )
---
@ -1330,7 +1330,7 @@ yaml: |
second: 12 ## This is an integer.
third: !str 12 ## This is a string.
third: !!str 12 ## This is a string.
span: this contains
six spaces
@ -1399,7 +1399,7 @@ yaml: |
# The following scalars
# are loaded to the
# string value '1' '2'.
- !str 12
- !!str 12
- '12'
- "12"
- "\

View File

@ -52,10 +52,10 @@ php: |
test: Forcing Strings
brief: >
Any YAML type can be forced into a string using the
explicit !str method.
explicit !!str method.
yaml: |
date string: !str 2001-08-01
number string: !str 192
date string: !!str 2001-08-01
number string: !!str 192
php: |
array(
'date string' => '2001-08-01',

View File

@ -703,4 +703,13 @@ class InlineTest extends TestCase
'float' => array('{0.25: "foo"}', array('0.25' => 'foo')),
);
}
/**
* @group legacy
* @expectedDeprecation Support for the !str tag is deprecated since version 3.4. Use the !!str tag instead.
*/
public function testDeprecatedStrTag()
{
$this->assertSame(array('foo' => 'bar'), Inline::parse('{ foo: !str bar }'));
}
}