8000 Merge branch '3.1' · symfony/symfony@77e0161 · GitHub
[go: up one dir, main page]

Skip to content

Commit 77e0161

Browse files
Merge branch '3.1'
* 3.1: [Routing] Add missing options in docblock [VarDumper] Fix dumping continuations [PropertyInfo] Fix an error in PropertyInfoCacheExtractor [HttpFoundation] fixed Request::getContent() reusage bug [Form] Skip CSRF validation on form when POST max size is exceeded Use try-finally where it possible [DependencyInjection] ContainerBuilder: Remove obsolete definitions Enhance the phpDoc return types so IDEs can handle the configuration tree. fixes Remove 3.0 from branch suggestions for fixes in PR template [Process] Strengthen Windows pipe files opening (again...) [Cache] Handle unserialize() failures gracefully Fix #19531 [Form] DateType fails parsing when midnight is not a valid time
2 parents 5ace4fd + d7f8ca7 commit 77e0161

36 files changed

+429
-156
lines changed

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
| Q | A
22
| ------------- | ---
3-
| Branch? | "master" for new features / 2.7, 2.8, 3.0 or 3.1 for fixes
3+
| Branch? | "master" for new features / 2.7, 2.8 or 3.1 for fixes
44
| Bug fix? | yes/no
55
| New feature? | yes/no
66
| BC breaks? | yes/no

src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<argument>%form.type_extension.csrf.field_name%</argument>
1313
<argument type="service" id="translator.default" />
1414
<argument>%validator.translation_domain%</argument>
15+
<argument type="service" id="form.server_params" />
1516
</service>
1617
</services>
1718
</container>

src/Symfony/Component/Cache/Adapter/AbstractAdapter.php

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,33 @@ public function __destruct()
370370
}
371371
}
372372

373+
/**
374+
* Like the native unserialize() function but throws an exception if anything goes wrong.
375+
*
376+
* @param string $value
377+
*
378+
* @return mixed
379+
*
380+
* @throws \Exception
381+
*/
382+
protected static function unserialize($value)
383+
{
384+
if ('b:0;' === $value) {
385+
return false;
386+
}
387+
$unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
388+
try {
389+
if (false !== $value = unserialize($value)) {
390+
return $value;
391+
}
392+
throw new \DomainException('Failed to unserialize cached value');
393+
} catch (\Error $e) {
394+
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
395+
} finally {
396+
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
397+
}
398+
}
399+
373400
private function getId($key)
374401
{
375402
CacheItem::validateKey($key);
@@ -381,13 +408,26 @@ private function generateItems($items, &$keys)
381408
{
382409
$f = $this->createCacheItem;
383410

384-
foreach ($items as $id => $value) {
385-
yield $keys[$id] => $f($keys[$id], $value, true);
386-
unset($keys[$id]);
411+
try {
412+
foreach ($items as $id => $value) {
413+
$key = $keys[$id];
414+
unset($keys[$id]);
415+
yield $key => $f($key, $value, true);
416+
}
417+
} catch (\Exception $e) {
418+
CacheItem::log($this->logger, 'Failed to fetch requested items', array('keys' => array_values($keys), 'exception' => $e));
387419
}
388420

389421
foreach ($keys as $key) {
390422
yield $key => $f($key, null, false);
391423
}
392424
}
425+
426+
/**
427+
* @internal
428+
*/
429+
public static function handleUnserializeCallback($class)
430+
{
431+
throw new \DomainException('Class not found: '.$class);
432+
}
393433
}

src/Symfony/Component/Cache/Adapter/ApcuAdapter.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ public function __construct($namespace = '', $defaultLifetime = 0, $version = nu
4949
*/
5050
protected function doFetch(array $ids)
5151
{
52-
return apcu_fetch($ids);
52+
try {
F438 53+
return apcu_fetch($ids);
54+
} catch (\Error $e) {
55+
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
56+
}
5357
}
5458

5559
/**

src/Symfony/Component/Cache/Adapter/ArrayAdapter.php

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,22 @@ function ($key, $value, $isHit) use ($defaultLifetime) {
5555
*/
5656
public function getItem($key)
5757
{
58-
if (!$isHit = $this->hasItem($key)) {
58+
$isHit = $this->hasItem($key);
59+
try {
60+
if (!$isHit) {
61+
$this->values[$key] = $value = null;
62+
} elseif (!$this->storeSerialized) {
63+
$value = $this->values[$key];
64+
} elseif ('b:0;' === $value = $this->values[$key]) {
65+
$value = false;
66+
} elseif (false === $value = unserialize($value)) {
67+
$value = null;
68+
$isHit = false;
69+
}
70+
} catch (\Exception $e) {
71+
CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e));
5972
$this->values[$key] = $value = null;
60-
} elseif ($this->storeSerialized) {
61-
$value = unserialize($this->values[$key]);
62-
} else {
63-
$value = $this->values[$key];
73+
$isHit = false;
6474
}
6575
$f = $this->createCacheItem;
6676

@@ -191,16 +201,30 @@ private function generateItems(array $keys, $now)
191201
{
192202
$f = $this->createCacheItem;
193203

194-
foreach ($keys as $key) {
195-
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] >= $now || !$this->deleteItem($key))) {
204+
foreach ($keys as $i => $key) {
205+
try {
206+
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] >= $now || !$this->deleteItem($key))) {
207+
$this->values[$key] = $value = null;
208+
} elseif (!$this->storeSerialized) {
209+
$value = $this->values[$key];
210+
} elseif ('b:0;' === $value = $this->values[$key]) {
211+
$value = false;
212+
} elseif (false === $value = unserialize($value)) {
213+
$value = null;
214+
$isHit = false;
215+
}
216+
} catch (\Exception $e) {
217+
CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e));
196218
$this->values[$key] = $value = null;
197-
} elseif ($this->storeSerialized) {
198-
$value = unserialize($this->values[$key]);
199-
} else {
200-
$value = $this->values[$key];
219+
$isHit = false;
201220
}
221+
unset($keys[$i]);
202222

203223
yield $key => $f($key, $value, $isHit);
204224
}
225+
226+
foreach ($keys as $key) {
227+
yield $key => $f($key, null, false);
228+
}
205229
}
206230
}

src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,25 @@ public function __construct(CacheProvider $provider, $namespace = '', $defaultLi
3232
*/
3333
protected function doFetch(array $ids)
3434
{
35-
return $this->provider->fetchMultiple($ids);
35+
$unserializeCallbackHandler = ini_set('unserialize_callback_func', parent::class.'::handleUnserializeCallback');
36+
try {
37+
return $this->provider->fetchMultiple($ids);
38+
} catch (\Error $e) {
39+
$trace = $e->getTrace();
40+
41+
if (isset($trace[0]['function']) && !isset($trace[0]['class'])) {
42+
switch ($trace[0]['function']) {
43+
case 'unserialize':
44+
case 'apcu_fetch':
45+
case 'apc_fetch':
46+
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
47+
}
48+
}
49+
50+
throw $e;
51+
} finally {
52+
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
53+
}
3654
}
3755

3856
/**

src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ protected function doFetch(array $ids)
3434

3535
foreach ($ids as $id) {
3636
$file = $this->getFile($id);
37-
if (!$h = @fopen($file, 'rb')) {
37+
if (!file_exists($file) || !$h = @fopen($file, 'rb')) {
3838
continue;
3939
}
4040
if ($now >= (int) $expiresAt = fgets($h)) {
@@ -47,7 +47,7 @@ protected function doFetch(array $ids)
4747
$value = stream_get_contents($h);
4848
fclose($h);
4949
if ($i === $id) {
50-
$values[$id] = unserialize($value);
50+
$values[$id] = parent::unserialize($value);
5151
}
5252
}
5353
}

src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ public function __construct($file, AdapterInterface $fallbackPool)
3939
$this->file = $file;
4040
$this->fallbackPool = $fallbackPool;
4141
$this->createCacheItem = \Closure::bind(
42-
function ($key, $value) {
42+
function ($key, $value, $isHit) {
4343
$item = new CacheItem();
4444
$item->key = $key;
4545
$item->value = $value;
46-
$item->isHit = true;
46+
$item->isHit = $isHit;
4747

4848
return $item;
4949
},
@@ -176,16 +176,26 @@ public function getItem($key)
176176
}
177177

178178
$value = $this->values[$key];
179+
$isHit = true;
179180

180181
if ('N;' === $value) {
181182
$value = null;
182183
} elseif (is_string($value) && isset($value[2]) && ':' === $value[1]) {
183-
$value = unserialize($value);
184+
try {
185+
$e = null;
186+
$value = unserialize($value);
187+
} catch (\Error $e) {
188+
} catch (\Exception $e) {
189+
}
190+
if (null !== $e) {
191+
$value = null;
192+
$isHit = false;
193+
}
184194
}
185195

186196
$f = $this->createCacheItem;
187197

188-
return $f($key, $value);
198+
return $f($key, $value, $isHit);
189199
}
190200

191201
/**
@@ -338,12 +348,18 @@ private function generateItems(array $keys)
338348
$value = $this->values[$key];
339349

340350
if ('N;' === $value) {
341-
$value = null;
351+
yield $key => $f($key, null, true);
342352
} elseif (is_string($value) && isset($value[2]) && ':' === $value[1]) {
343-
$value = unserialize($value);
353+
try {
354+
yield $key => $f($key, unserialize($value), true);
355+
} catch (\Error $e) {
356+
yield $key => $f($key, null, false);
357+
} catch (\Exception $e) {
358+
yield $key => $f($key, null, false);
359+
}
360+
} else {
361+
yield $key => $f($key, $value, true);
344362
}
345-
346-
yield $key => $f($key, $value);
347363
} else {
348364
$fallbackKeys[] = $key;
349365
}

src/Symfony/Component/Cache/Adapter/RedisAdapter.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,19 +134,15 @@ public static function createConnection($dsn, array $options = array())
134134
*/
135135
protected function doFetch(array $ids)
136136
{
137-
$result = array();
138-
139137
if ($ids) {
140138
$values = $this->redis->mGet($ids);
141139
$index = 0;
142140
foreach ($ids as $id) {
143141
if ($value = $values[$index++]) {
144-
$result[$id] = unserialize($value);
142+
yield $id => parent::unserialize($value);
145143
}
146144
}
147145
}
148-
149-
return $result;
150146
}
151147

152148
/**

src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,42 @@ public function testDefaultLifeTime()
4646
$item = $cache->getItem('key.dlt');
4747
$this->assertFalse($item->isHit());
4848
}
49+
50+
public function testNotUnserializable()
51+
{
52+
if (isset($this->skippedTests[__FUNCTION__])) {
53+
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
54+
55+
return;
56+
}
57+
58+
$cache = $this->createCachePool();
59+
60+
$item = $cache->getItem('foo');
61+
$cache->save($item->set(new NotUnserializable()));
62+
63+
$item = $cache->getItem('foo');
64+
$this->assertFalse($item->isHit());
65+
66+
foreach ($cache->getItems(array('foo')) as $item) {
67+
}
68+
$cache->save($item->set(new NotUnserializable()));
69+
70+
foreach ($cache->getItems(array('foo')) as $item) {
71+
}
72+
$this->assertFalse($item->isHit());
73+
}
74+
}
75+
76+
class NotUnserializable implements \Serializable
77+
{
78+
public function serialize()
79+
{
80+
return serialize(123);
81+
}
82+
83+
public function unserialize($ser)
84+
{
85+
throw new \Exception(__CLASS__);
86+
}
4987
}

src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class DoctrineAdapterTest extends AdapterTestCase
2222
protected $skippedTests = array(
2323
'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayCache is not.',
2424
'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayCache is not.',
25+
'testNotUnserializable' => 'ArrayCache does not use serialize/unserialize',
2526
);
2627

2728
public function createCachePool($defaultLifetime = 0)

src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public function variableNode($name)
138138
/**
139139
* Returns the parent node.
140140
*
141-
* @return ParentNodeDefinitionInterface The parent node
141+
* @return ParentNodeDefinitionInterface|NodeDefinition The parent node
142142
*/
143143
public function end()
144144
{

src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public function attribute($key, $value)
107107
/**
108108
* Returns the parent node.
109109
*
110-
* @return NodeParentInterface|null The builder of the parent node
110+
* @return NodeParentInterface|NodeBuilder|NodeDefinition|null The builder of the parent node
111111
*/
112112
public function end()
113113
{

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

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,15 @@ public function process(ContainerBuilder $container)
4545
$this->completeDefinition($id, $definition);
4646
}
4747
}
48-
} catch (\Exception $e) {
49-
} catch (\Throwable $e) {
50-
}
51-
52-
spl_autoload_unregister($throwingAutoloader);
53-
54-
// Free memory and remove circular reference to container
55-
$this->container = null;
56-
$this->reflectionClasses = array();
57-
$this->definedTypes = array();
58-
$this->types = null;
59-
$this->ambiguousServiceTypes = array();
60-
61-
if (isset($e)) {
62-
throw $e;
48+
} finally {
49+
spl_autoload_unregister($throwingAutoloader);
50+
51+
// Free memory and remove circular reference to container
52+
$this->container = null;
53+
$this->reflectionClasses = array();
54+
$this->definedTypes = array();
55+
$this->types = null;
56+
$this->ambiguousServiceTypes = array();
6357
}
6458
}
6559

0 commit comments

Comments
 (0)
0