Skip to content

Commit e8895ad

Browse files
committed
Merge branch '2.8' into 3.1
* 2.8: always check for all fields to be mapped clarify exception when no args are configured [PropertyAccess] Handle interfaces in the invalid argument exception [DI] Fix defaults overriding empty strings in AutowirePass [Debug] Workaround "null" $context [Debug] Remove $context arg from handleError(), preparing for PHP 7.2 [Routing] Fix BC break in AnnotationClassLoader defaults attributes handling Fix tests with ICU 57.1 Fix the condition checking the minimum ICU version
2 parents 7fa7aeb + 791b143 commit e8895ad

File tree

20 files changed

+253
-30
lines changed

20 files changed

+253
-30
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@
9393
},
9494
"conflict": {
9595
"phpdocumentor/reflection-docblock": "<3.0",
96-
"phpdocumentor/type-resolver": "<0.2.0"
96+
"phpdocumentor/type-resolver": "<0.2.0",
97+
"sensio/framework-extra-bundle": "^3.0.2"
9798
},
9899
"provide": {
99100
"psr/cache-implementation": "1.0"

src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,23 @@ public function testValidateUniquenessWithIgnoreNull()
258258
->assertRaised();
259259
}
260260

261+
/**
262+
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
263+
*/
264+
public function testAllConfiguredFieldsAreCheckedOfBeingMappedByDoctrineWithIgnoreNullEnabled()
265+
{
266+
$constraint = new UniqueEntity(array(
267+
'message' => 'myMessage',
268+
'fields' => array('name', 'name2'),
269+
'em' => self::EM_NAME,
270+
'ignoreNull' => true,
271+
));
272+
273+
$entity1 = new SingleIntIdEntity(1, null);
274+
275+
$this->validator->validate($entity1, $constraint);
276+
}
277+
261278
public function testValidateUniquenessWithValidCustomErrorPath()
262279
{
263280
$constraint = new UniqueEntity(array(

src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,14 @@ public function validate($entity, Constraint $constraint)
8686
throw new ConstraintDefinitionException(sprintf('The field "%s" is not mapped by Doctrine, so it cannot be validated for uniqueness.', $fieldName));
8787
}
8888

89-
$criteria[$fieldName] = $class->reflFields[$fieldName]->getValue($entity);
89+
$fieldValue = $class->reflFields[$fieldName]->getValue($entity);
9090

91-
if ($constraint->ignoreNull && null === $criteria[$fieldName]) {
92-
return;
91+
if ($constraint->ignoreNull && null === $fieldValue) {
92+
continue;
9393
}
9494

95+
$criteria[$fieldName] = $fieldValue;
96+
9597
if (null !== $criteria[$fieldName] && $class->hasAssociation($fieldName)) {
9698
/* Ensure the Proxy is initialized before using reflection to
9799
* read its identifiers. This is necessary because the wrapped
@@ -101,6 +103,12 @@ public function validate($entity, Constraint $constraint)
101103
}
102104
}
103105

106+
// skip validation if there are no criteria (this can happen when the
107+
// "ignoreNull" option is enabled and fields to be checked are null
108+
if (empty($criteria)) {
109+
return;
110+
}
111+
104112
$repository = $em->getRepository(get_class($entity));
105113
$result = $repository->{$constraint->repositoryMethod}($criteria);
106114

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
13+
14+
class AnnotatedControllerTest extends WebTestCase
15+
{
16+
/**
17+
* @dataProvider getRoutes
18+
*/
19+
public function testAnnotatedController($path, $expectedValue)
20+
{
21+
$client = $this->createClient(array('test_case' => 'AnnotatedController', 'root_config' => 'config.yml'));
22+
$client->request('GET', '/annotated'.$path);
23+
24+
$this->assertSame(200, $client->getResponse()->getStatusCode());
25+
$this->assertSame($expectedValue, $client->getResponse()->getContent());
26+
}
27+
28+
public function getRoutes()
29+
{
30+
return array(
31+
array('/null_request', 'Symfony\Component\HttpFoundation\Request'),
32+
array('/null_argument', ''),
33+
array('/null_argument_with_route_param', ''),
34+
array('/null_argument_with_route_param/value', 'value'),
35+
array('/argument_with_route_param_and_default', 'value'),
36+
array('/argument_with_route_param_and_default/custom', 'custom'),
37+
);
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Response;
16+
use Symfony\Component\Routing\Annotation\Route;
17+
18+
class AnnotatedController
19+
{
20+
/**
21+
* @Route("/null_request", name="null_request")
22+
*/
23+
public function requestDefaultNullAction(Request $request = null)
24+
{
25+
return new Response($request ? get_class($request) : null);
26+
}
27+
28+
/**
29+
* @Route("/null_argument", name="null_argument")
30+
*/
31+
public function argumentDefaultNullWithoutRouteParamAction($value = null)
32+
{
33+
return new Response($value);
34+
}
35+
36+
/**
37+
* @Route("/null_argument_with_route_param/{value}", name="null_argument_with_route_param")
38+
*/
39+
public function argumentDefaultNullWithRouteParamAction($value = null)
40+
{
41+
return new Response($value);
42+
}
43+
44+
/**
45+
* @Route("/argument_with_route_param_and_default/{value}", defaults={"value": "value"}, name="argument_with_route_param_and_default")
46+
*/
47+
public function argumentWithoutDefaultWithRouteParamAndDefaultAction($value)
48+
{
49+
return new Response($value);
50+
}
51+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle;
13+
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
14+
use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle;
15+
16+
return array(
17+
new FrameworkBundle(),
18+
new TestBundle(),
19+
new SensioFrameworkExtraBundle(),
20+
);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
imports:
2+
- { resource: ../config/default.yml }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
annotated_controller:
2+
prefix: /annotated
3+
resource: "@TestBundle/Controller/AnnotatedController.php"
4+
type: annotation

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"symfony/polyfill-mbstring": "~1.0",
2929
"symfony/filesystem": "~2.8|~3.0",
3030
"symfony/finder": "~2.8|~3.0",
31-
"symfony/routing": "~3.0",
31+
"symfony/routing": "~3.0.11|~3.1",
3232
"symfony/security-core": "~2.8|~3.0",
3333
"symfony/security-csrf": "~2.8|~3.0",
3434
"symfony/stopwatch": "~2.8|~3.0",
@@ -52,7 +52,8 @@
5252
"symfony/yaml": "~2.8|~3.0",
5353
"symfony/property-info": "~2.8|~3.0",
5454
"phpdocumentor/reflection-docblock": "^3.0",
55-
"twig/twig": "~1.23|~2.0"
55+
"twig/twig": "~1.23|~2.0",
56+
"sensio/framework-extra-bundle": "^3.0.2"
5657
},
5758
"conflict": {
5859
"phpdocumentor/reflection-docblock": "<3.0",

src/Symfony/Component/Debug/ErrorHandler.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -348,20 +348,18 @@ private function reRegister($prev)
348348
/**
349349
* Handles errors by filtering then logging them according to the configured bit fields.
350350
*
351-
* @param int $type One of the E_* constants
351+
* @param int $type One of the E_* constants
352352
* @param string $message
353353
* @param string $file
354354
* @param int $line
355-
* @param array $context
356-
* @param array $backtrace
357355
*
358356
* @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
359357
*
360358
* @throws \ErrorException When $this->thrownErrors requests so
361359
*
362360
* @internal
363361
*/
364-
public function handleError($type, $message, $file, $line, array $context, array $backtrace = null)
362+
public function handleError($type, $message, $file, $line)
365363
{
366364
$level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
367365
$log = $this->loggedErrors & $type;
@@ -371,9 +369,20 @@ public function handleError($type, $message, $file, $line, array $context, array
371369
if (!$type || (!$log && !$throw)) {
372370
return $type && $log;
373371
}
372+
$scope = $this->scopedErrors & $type;
374373

375-
if (isset($context['GLOBALS']) && ($this->scopedErrors & $type)) {
376-
unset($context['GLOBALS']);
374+
if (4 < $numArgs = func_num_args()) {
375+
$context = $scope ? (func_get_arg(4) ?: array()) : array();
376+
$backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM
377+
} else {
378+
$context = array();
379+
$backtrace = null;
380+
}
381+
382+
if (isset($context['GLOBALS']) && $scope) {
383+
$e = $context; // Whatever the signature of the method,
384+
unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
385+
$context = $e;
377386
}
378387

379388
if (null !== $backtrace && $type & E_ERROR) {
@@ -389,7 +398,8 @@ public function handleError($type, $message, $file, $line, array $context, array
389398
if (null !== self::$toStringException) {
390399
$throw = self::$toStringException;
391400
self::$toStringException = null;
392-
} elseif (($this->scopedErrors & $type) && class_exists(ContextErrorException::class)) {
401+
} elseif ($scope && class_exists(ContextErrorException::class)) {
402+
// Checking for class existence is a work around for https://bugs.php.net/42098
393403
$throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
394404
} else {
395405
$throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
@@ -452,7 +462,7 @@ public function handleError($type, $message, $file, $line, array $context, array
452462
$e = compact('type', 'file', 'line', 'level');
453463

454464
if ($type & $level) {
455-
if ($this->scopedErrors & $type) {
465+
if ($scope) {
456466
$e['scope_vars'] = $context;
457467
if ($trace) {
458468
$e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);

src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,10 @@ private function completeDefinition($id, Definition $definition)
121121
throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id));
122122
}
123123

124-
// specifically pass the default value
125-
$arguments[$index] = $parameter->getDefaultValue();
124+
if (!array_key_exists($index, $arguments)) {
125+
// specifically pass the default value
126+
$arguments[$index] = $parameter->getDefaultValue();
127+
}
126128

127129
continue;
128130
}

src/Symfony/Component/DependencyInjection/Definition.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ public function addArgument($argument)
198198
*/
199199
public function replaceArgument($index, $argument)
200200
{
201+
if (0 === count($this->arguments)) {
202+
throw new OutOfBoundsException('Cannot replace arguments if none have been configured yet.');
203+
}
204+
201205
if ($index < 0 || $index > count($this->arguments) - 1) {
202206
throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, count($this->arguments) - 1));
203207
}

src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,22 @@ public function testIgnoreServiceWithClassNotExisting()
494494

495495
$this->assertTrue($container->hasDefinition('bar'));
496496
}
497+
498+
public function testEmptyStringIsKept()
499+
{
500+
$container = new ContainerBuilder();
501+
502+
$container->register('a', __NAMESPACE__.'\A');
503+
$container->register('lille', __NAMESPACE__.'\Lille');
504+
$container->register('foo', __NAMESPACE__.'\MultipleArgumentsOptionalScalar')
505+
->setAutowired(true)
506+
->setArguments(array('', ''));
507+
508+
$pass = new AutowirePass();
509+
$pass->process($container);
510+
511+
$this->assertEquals(array(new Reference('a'), '', new Reference('lille')), $container->getDefinition('foo')->getArguments());
512+
}
497513
}
498514

499515
class Foo

src/Symfony/Component/DependencyInjection/Tests/DefinitionTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ public function testGetArgumentShouldCheckBounds()
257257

258258
/**
259259
* @expectedException \OutOfBoundsException
260+
* @expectedExceptionMessage The index "1" is not in the range [0, 0].
260261
*/
261262
public function testReplaceArgumentShouldCheckBounds()
262263
{
@@ -266,6 +267,16 @@ public function testReplaceArgumentShouldCheckBounds()
266267
$def->replaceArgument(1, 'bar');
267268
}
268269

270+
/**
271+
* @expectedException \OutOfBoundsException
272+
* @expectedExceptionMessage Cannot replace arguments if none have been configured yet.
273+
*/
274+
public function testReplaceArgumentWithoutExistingArgumentsShouldCheckBounds()
275+
{
276+
$def = new Definition('stdClass');
277+
$def->replaceArgument(0, 'bar');
278+
}
279+
269280
public function testSetGetProperties()
270281
{
271282
$def = new Definition('stdClass');

0 commit comments

Comments
 (0)