Skip to content

Commit a306d99

Browse files
committed
Merge branch '4.4'
2 parents 90ca602 + 38b9a27 commit a306d99

File tree

51 files changed

+881
-643
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+881
-643
lines changed

UPGRADE-4.4.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,33 @@ HttpKernel
155155
current directory or with a glob pattern. The fallback directories have never been advocated
156156
so you likely do not use those in any app based on the SF Standard or Flex edition.
157157
* Getting the container from a non-booted kernel is deprecated
158+
* Deprecated passing the `exception` attribute (instance of `Symfony\Component\Debug\Exception\FlattenException`)
159+
to the configured controller of the `ExceptionListener`, use the `e` attribute
160+
(instance of `Symfony\Component\ErrorRenderer\Exception\FlattenException`) instead
161+
162+
before:
163+
```php
164+
use Symfony\Component\Debug\Exception\FlattenException;
165+
166+
class ExceptionController
167+
{
168+
public function __invoke(FlattenException $exception)
169+
{
170+
}
171+
}
172+
```
173+
174+
after:
175+
```php
176+
use Symfony\Component\ErrorRenderer\Exception\FlattenException;
177+
178+
class ExceptionController
179+
{
180+
public function __invoke(FlattenException $e)
181+
{
182+
}
183+
}
184+
```
158185

159186
Lock
160187
----

UPGRADE-5.0.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,33 @@ HttpKernel
341341
* Removed the second and third argument of `FileLocator::__construct`
342342
* Removed loading resources from `%kernel.root_dir%/Resources` and `%kernel.root_dir%` as
343343
fallback directories.
344+
* Removed passing the `exception` attribute (instance of `Symfony\Component\Debug\Exception\FlattenException`)
345+
to the configured controller of the `ExceptionListener`, use the `e` attribute
346+
(instance of `Symfony\Component\ErrorRenderer\Exception\FlattenException`) instead
347+
348+
before:
349+
```php
350+
use Symfony\Component\Debug\Exception\FlattenException;
351+
352+
class ExceptionController
353+
{
354+
public function __invoke(FlattenException $exception)
355+
{
356+
}
357+
}
358+
```
359+
360+
after:
361+
```php
362+
use Symfony\Component\ErrorRenderer\Exception\FlattenException;
363+
364+
class ExceptionController
365+
{
366+
public function __invoke(FlattenException $e)
367+
{
368+
}
369+
}
370+
```
344371

345372
Intl
346373
----

src/Symfony/Bundle/FrameworkBundle/Console/Application.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
use Symfony\Component\Console\Output\OutputInterface;
2121
use Symfony\Component\Console\Style\SymfonyStyle;
2222
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
23-
use Symfony\Component\ErrorHandler\Exception\FatalThrowableError;
23+
use Symfony\Component\ErrorHandler\Exception\ErrorException;
2424
use Symfony\Component\HttpKernel\Bundle\Bundle;
2525
use Symfony\Component\HttpKernel\Kernel;
2626
use Symfony\Component\HttpKernel\KernelInterface;
@@ -208,7 +208,7 @@ private function renderRegistrationErrors(InputInterface $input, OutputInterface
208208

209209
foreach ($this->registrationErrors as $error) {
210210
if (!$error instanceof \Exception) {
211-
$error = new FatalThrowableError($error);
211+
$error = new ErrorException($error);
212212
}
213213

214214
$this->doRenderException($error, $output);

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,16 @@ abstract protected function doDelete(array $ids, array $tagData = []): bool;
149149
*/
150150
abstract protected function doInvalidate(array $tagIds): bool;
151151

152+
/**
153+
* Returns the tags bound to the provided ids.
154+
*/
155+
protected function doFetchTags(array $ids): iterable
156+
{
157+
foreach ($this->doFetch($ids) as $id => $value) {
158+
yield $id => \is_array($value) && \is_array($value['tags'] ?? null) ? $value['tags'] : [];
159+
}
160+
}
161+
152162
/**
153163
* {@inheritdoc}
154164
*
@@ -232,10 +242,14 @@ public function deleteItems(array $keys)
232242
unset($this->deferred[$key]);
233243
}
234244

235-
foreach ($this->doFetch($ids) as $id => $value) {
236-
foreach ($value['tags'] ?? [] as $tag) {
237-
$tagData[$this->getId(self::TAGS_PREFIX.$tag)][] = $id;
245+
try {
246+
foreach ($this->doFetchTags($ids) as $id => $tags) {
247+
foreach ($tags as $tag) {
248+
$tagData[$this->getId(self::TAGS_PREFIX.$tag)][] = $id;
249+
}
238250
}
251+
} catch (\Exception $e) {
252+
// ignore unserialization failures
239253
}
240254

241255
try {

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212
namespace Symfony\Component\Cache\Adapter;
1313

14-
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
1514
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
15+
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
1616
use Symfony\Component\Cache\PruneableInterface;
1717
use Symfony\Component\Cache\Traits\FilesystemTrait;
1818

@@ -37,7 +37,7 @@ class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements Prune
3737

3838
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null)
3939
{
40-
$this->marshaller = $marshaller ?? new DefaultMarshaller();
40+
$this->marshaller = new TagAwareMarshaller($marshaller);
4141
parent::__construct('', $defaultLifetime);
4242
$this->init($namespace, $directory);
4343
}
@@ -130,6 +130,40 @@ protected function doSave(array $values, int $lifetime, array $addTagData = [],
130130
return $failed;
131131
}
132132

133+
/**
134+
* {@inheritdoc}
135+
*/
136+
protected function doFetchTags(array $ids): iterable
137+
{
138+
foreach ($ids as $id) {
139+
$file = $this->getFile($id);
140+
if (!file_exists($file) || !$h = @fopen($file, 'rb')) {
141+
continue;
142+
}
143+
144+
$meta = explode("\n", fread($h, 4096), 3)[2] ?? '';
145+
146+
// detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
147+
if (13 < \strlen($meta) && "\x9D" === $meta[0] && "\0" === $meta[5] && "\x5F" === $meta[9]) {
148+
$meta[9] = "\0";
149+
$tagLen = unpack('Nlen', $meta, 9)['len'];
150+
$meta = substr($meta, 13, $tagLen);
151+
152+
if (0 < $tagLen -= \strlen($meta)) {
153+
$meta .= fread($h, $tagLen);
154+
}
155+
156+
try {
157+
yield $id => '' === $meta ? [] : $this->marshaller->unmarshall($meta);
158+
} catch (\Exception $e) {
159+
yield $id => [];
160+
}
161+
}
162+
163+
fclose($h);
164+
}
165+
}
166+
133167
/**
134168
* {@inheritdoc}
135169
*/

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

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Predis\Response\Status;
1717
use Symfony\Component\Cache\Exception\InvalidArgumentException;
1818
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
19+
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
1920
use Symfony\Component\Cache\Traits\RedisTrait;
2021

2122
/**
@@ -67,7 +68,7 @@ public function __construct($redisClient, string $namespace = '', int $defaultLi
6768
throw new InvalidArgumentException(sprintf('Unsupported Predis cluster connection: only "%s" is, "%s" given.', PredisCluster::class, \get_class($redisClient->getConnection())));
6869
}
6970

70-
$this->init($redisClient, $namespace, $defaultLifetime, $marshaller);
71+
$this->init($redisClient, $namespace, $defaultLifetime, new TagAwareMarshaller($marshaller));
7172
}
7273

7374
/**
@@ -81,7 +82,7 @@ protected function doSave(array $values, int $lifetime, array $addTagData = [],
8182
}
8283

8384
// While pipeline isn't supported on RedisCluster, other setups will at least benefit from doing this in one op
84-
$results = $this->pipeline(static function () use ($serialized, $lifetime, $addTagData, $delTagData) {
85+
$results = $this->pipeline(static function () use ($serialized, $lifetime, $addTagData, $delTagData, $failed) {
8586
// Store cache items, force a ttl if none is set, as there is no MSETEX we need to set each one
8687
foreach ($serialized as $id => $value) {
8788
yield 'setEx' => [
@@ -93,11 +94,15 @@ protected function doSave(array $values, int $lifetime, array $addTagData = [],
9394

9495
// Add and Remove Tags
9596
foreach ($addTagData as $tagId => $ids) {
96-
yield 'sAdd' => array_merge([$tagId], $ids);
97+
if (!$failed || $ids = array_diff($ids, $failed)) {
98+
yield 'sAdd' => array_merge([$tagId], $ids);
99+
}
97100
}
98101

99102
foreach ($delTagData as $tagId => $ids) {
100-
yield 'sRem' => array_merge([$tagId], $ids);
103+
if (!$failed || $ids = array_diff($ids, $failed)) {
104+
yield 'sRem' => array_merge([$tagId], $ids);
105+
}
101106
}
102107
});
103108

@@ -115,6 +120,42 @@ protected function doSave(array $values, int $lifetime, array $addTagData = [],
115120
return $failed;
116121
}
117122

123+
/**
124+
* {@inheritdoc}
125+
*/
126+
protected function doFetchTags(array $ids): iterable
127+
{
128+
$lua = <<<'EOLUA'
129+
local v = redis.call('GET', KEYS[1])
130+
131+
if not v or v:len() <= 13 or v:byte(1) ~= 0x9D or v:byte(6) ~= 0 or v:byte(10) ~= 0x5F then
132+
return ''
133+
end
134+
135+
return v:sub(14, 13 + v:byte(13) + v:byte(12) * 256 + v:byte(11) * 65536)
136+
EOLUA;
137+
138+
if ($this->redis instanceof \Predis\ClientInterface) {
139+
$evalArgs = [$lua, 1, &$id];
140+
} else {
141+
$evalArgs = [$lua, [&$id], 1];
142+
}
143+
144+
$results = $this->pipeline(function () use ($ids, &$id, $evalArgs) {
145+
foreach ($ids as $id) {
146+
yield 'eval' => $evalArgs;
147+
}
148+
});
149+
150+
foreach ($results as $id => $result) {
151+
try {
152+
yield $id => !\is_string($result) || '' === $result ? [] : $this->marshaller->unmarshall($result);
153+
} catch (\Exception $e) {
154+
yield $id => [];
155+
}
156+
}
157+
}
158+
118159
/**
119160
* {@inheritdoc}
120161
*/

src/Symfony/Component/Cache/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ CHANGELOG
1515
* added support for connecting to Redis Sentinel clusters
1616
* added argument `$prefix` to `AdapterInterface::clear()`
1717
* improved `RedisTagAwareAdapter` to support Redis server >= 2.8 and up to 4B items per tag
18+
* added `TagAwareMarshaller` for optimized data storage when using `AbstractTagAwareAdapter`
1819
* [BC BREAK] `RedisTagAwareAdapter` is not compatible with `RedisCluster` from `Predis` anymore, use `phpredis` instead
1920

2021
4.3.0
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Component\Cache\Marshaller;
13+
14+
/**
15+
* A marshaller optimized for data structures generated by AbstractTagAwareAdapter.
16+
*
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*/
19+
class TagAwareMarshaller implements MarshallerInterface
20+
{
21+
private $marshaller;
22+
23+
public function __construct(MarshallerInterface $marshaller = null)
24+
{
25+
$this->marshaller = $marshaller ?? new DefaultMarshaller();
26+
}
27+
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
public function marshall(array $values, ?array &$failed): array
32+
{
33+
$failed = $notSerialized = $serialized = [];
34+
35+
foreach ($values as $id => $value) {
36+
if (\is_array($value) && \is_array($value['tags'] ?? null) && \array_key_exists('value', $value) && \count($value) === 2 + (\is_string($value['meta'] ?? null) && 8 === \strlen($value['meta']))) {
37+
// if the value is an array with keys "tags", "value" and "meta", use a compact serialization format
38+
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F allow detecting this format quickly in unmarshall()
39+
40+
$v = $this->marshaller->marshall($value, $f);
41+
42+
if ($f) {
43+
$f = [];
44+
$failed[] = $id;
45+
} else {
46+
if ([] === $value['tags']) {
47+
$v['tags'] = '';
48+
}
49+
50+
$serialized[$id] = "\x9D".($value['meta'] ?? "\0\0\0\0\0\0\0\0").pack('N', \strlen($v['tags'])).$v['tags'].$v['value'];
51+
$serialized[$id][9] = "\x5F";
52+
}
53+
} else {
54+
// other arbitratry values are serialized using the decorated marshaller below
55+
$notSerialized[$id] = $value;
56+
}
57+
}
58+
59+
if ($notSerialized) {
60+
$serialized += $this->marshaller->marshall($notSerialized, $f);
61+
$failed = array_merge($failed, $f);
62+
}
63+
64+
return $serialized;
65+
}
66+
67+
/**
68+
* {@inheritdoc}
69+
*/
70+
public function unmarshall(string $value)
71+
{
72+
// detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
73+
if (13 >= \strlen($value) || "\x9D" !== $value[0] || "\0" !== $value[5] || "\x5F" !== $value[9]) {
74+
return $this->marshaller->unmarshall($value);
75+
}
76+
77+
// data consists of value, tags and metadata which we need to unpack
78+
$meta = substr($value, 1, 12);
79+
$meta[8] = "\0";
80+
$tagLen = unpack('Nlen', $meta, 8)['len'];
81+
$meta = substr($meta, 0, 8);
82+
83+
return [
84+
'value' => $this->marshaller->unmarshall(substr($value, 13 + $tagLen)),
85+
'tags' => $tagLen ? $this->marshaller->unmarshall(substr($value, 13, $tagLen)) : [],
86+
'meta' => "\0\0\0\0\0\0\0\0" === $meta ? null : $meta,
87+
];
88+
}
89+
}

src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,13 @@ public function getItem($key)
211211
foreach ($this->doFetch([$id]) as $value) {
212212
$isHit = true;
213213
}
214+
215+
return $f($key, $value, $isHit);
214216
} catch (\Exception $e) {
215217
CacheItem::log($this->logger, 'Failed to fetch key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
216218
}
217219

218-
return $f($key, $value, $isHit);
220+
return $f($key, null, false);
219221
}
220222

221223
/**

0 commit comments

Comments
 (0)