dc291e103f3838f3e7158169e885a4b208ff4c4e
[friendica-addons.git/.git] / advancedcontentfilter / vendor / symfony / cache / Traits / AbstractTrait.php
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\Traits;
13
14 use Psr\Log\LoggerAwareTrait;
15 use Symfony\Component\Cache\CacheItem;
16
17 /**
18  * @author Nicolas Grekas <p@tchwork.com>
19  *
20  * @internal
21  */
22 trait AbstractTrait
23 {
24     use LoggerAwareTrait;
25
26     private $namespace;
27     private $namespaceVersion = '';
28     private $versioningIsEnabled = false;
29     private $deferred = [];
30
31     /**
32      * @var int|null The maximum length to enforce for identifiers or null when no limit applies
33      */
34     protected $maxIdLength;
35
36     /**
37      * Fetches several cache items.
38      *
39      * @param array $ids The cache identifiers to fetch
40      *
41      * @return array|\Traversable The corresponding values found in the cache
42      */
43     abstract protected function doFetch(array $ids);
44
45     /**
46      * Confirms if the cache contains specified cache item.
47      *
48      * @param string $id The identifier for which to check existence
49      *
50      * @return bool True if item exists in the cache, false otherwise
51      */
52     abstract protected function doHave($id);
53
54     /**
55      * Deletes all items in the pool.
56      *
57      * @param string $namespace The prefix used for all identifiers managed by this pool
58      *
59      * @return bool True if the pool was successfully cleared, false otherwise
60      */
61     abstract protected function doClear($namespace);
62
63     /**
64      * Removes multiple items from the pool.
65      *
66      * @param array $ids An array of identifiers that should be removed from the pool
67      *
68      * @return bool True if the items were successfully removed, false otherwise
69      */
70     abstract protected function doDelete(array $ids);
71
72     /**
73      * Persists several cache items immediately.
74      *
75      * @param array $values   The values to cache, indexed by their cache identifier
76      * @param int   $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning
77      *
78      * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not
79      */
80     abstract protected function doSave(array $values, $lifetime);
81
82     /**
83      * {@inheritdoc}
84      */
85     public function hasItem($key)
86     {
87         $id = $this->getId($key);
88
89         if (isset($this->deferred[$key])) {
90             $this->commit();
91         }
92
93         try {
94             return $this->doHave($id);
95         } catch (\Exception $e) {
96             CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached', ['key' => $key, 'exception' => $e]);
97
98             return false;
99         }
100     }
101
102     /**
103      * {@inheritdoc}
104      */
105     public function clear()
106     {
107         $this->deferred = [];
108         if ($cleared = $this->versioningIsEnabled) {
109             $namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::NS_SEPARATOR, 5);
110             try {
111                 $cleared = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0);
112             } catch (\Exception $e) {
113                 $cleared = false;
114             }
115             if ($cleared = true === $cleared || [] === $cleared) {
116                 $this->namespaceVersion = $namespaceVersion;
117             }
118         }
119
120         try {
121             return $this->doClear($this->namespace) || $cleared;
122         } catch (\Exception $e) {
123             CacheItem::log($this->logger, 'Failed to clear the cache', ['exception' => $e]);
124
125             return false;
126         }
127     }
128
129     /**
130      * {@inheritdoc}
131      */
132     public function deleteItem($key)
133     {
134         return $this->deleteItems([$key]);
135     }
136
137     /**
138      * {@inheritdoc}
139      */
140     public function deleteItems(array $keys)
141     {
142         $ids = [];
143
144         foreach ($keys as $key) {
145             $ids[$key] = $this->getId($key);
146             unset($this->deferred[$key]);
147         }
148
149         try {
150             if ($this->doDelete($ids)) {
151                 return true;
152             }
153         } catch (\Exception $e) {
154         }
155
156         $ok = true;
157
158         // When bulk-delete failed, retry each item individually
159         foreach ($ids as $key => $id) {
160             try {
161                 $e = null;
162                 if ($this->doDelete([$id])) {
163                     continue;
164                 }
165             } catch (\Exception $e) {
166             }
167             CacheItem::log($this->logger, 'Failed to delete key "{key}"', ['key' => $key, 'exception' => $e]);
168             $ok = false;
169         }
170
171         return $ok;
172     }
173
174     /**
175      * Enables/disables versioning of items.
176      *
177      * When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed,
178      * but old keys may need garbage collection and extra round-trips to the back-end are required.
179      *
180      * Calling this method also clears the memoized namespace version and thus forces a resynchonization of it.
181      *
182      * @param bool $enable
183      *
184      * @return bool the previous state of versioning
185      */
186     public function enableVersioning($enable = true)
187     {
188         $wasEnabled = $this->versioningIsEnabled;
189         $this->versioningIsEnabled = (bool) $enable;
190         $this->namespaceVersion = '';
191
192         return $wasEnabled;
193     }
194
195     /**
196      * {@inheritdoc}
197      */
198     public function reset()
199     {
200         if ($this->deferred) {
201             $this->commit();
202         }
203         $this->namespaceVersion = '';
204     }
205
206     /**
207      * Like the native unserialize() function but throws an exception if anything goes wrong.
208      *
209      * @param string $value
210      *
211      * @return mixed
212      *
213      * @throws \Exception
214      */
215     protected static function unserialize($value)
216     {
217         if ('b:0;' === $value) {
218             return false;
219         }
220         $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
221         try {
222             if (false !== $value = unserialize($value)) {
223                 return $value;
224             }
225             throw new \DomainException('Failed to unserialize cached value.');
226         } catch (\Error $e) {
227             throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
228         } finally {
229             ini_set('unserialize_callback_func', $unserializeCallbackHandler);
230         }
231     }
232
233     private function getId($key)
234     {
235         CacheItem::validateKey($key);
236
237         if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
238             $this->namespaceVersion = '1'.static::NS_SEPARATOR;
239             try {
240                 foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
241                     $this->namespaceVersion = $v;
242                 }
243                 if ('1'.static::NS_SEPARATOR === $this->namespaceVersion) {
244                     $this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), static::NS_SEPARATOR, 5);
245                     $this->doSave([static::NS_SEPARATOR.$this->namespace => $this->namespaceVersion], 0);
246                 }
247             } catch (\Exception $e) {
248             }
249         }
250
251         if (null === $this->maxIdLength) {
252             return $this->namespace.$this->namespaceVersion.$key;
253         }
254         if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
255             $id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 22));
256         }
257
258         return $id;
259     }
260
261     /**
262      * @internal
263      */
264     public static function handleUnserializeCallback($class)
265     {
266         throw new \DomainException('Class not found: '.$class);
267     }
268 }