|
16 | 16 |
|
17 | 17 | use phpFastCache\Core\Item\ExtendedCacheItemInterface; |
18 | 18 | use phpFastCache\CacheManager; |
| 19 | +use phpFastCache\Entities\ItemBatch; |
19 | 20 | use phpFastCache\EventManager; |
20 | 21 | use phpFastCache\Exceptions\phpFastCacheCoreException; |
21 | 22 | use phpFastCache\Exceptions\phpFastCacheInvalidArgumentException; |
@@ -70,64 +71,103 @@ public function getItem($key) |
70 | 71 | * @var $item ExtendedCacheItemInterface |
71 | 72 | */ |
72 | 73 | CacheManager::$ReadHits++; |
| 74 | + $cacheSlamsSpendSeconds = 0; |
73 | 75 | $class = new \ReflectionClass((new \ReflectionObject($this))->getNamespaceName() . '\Item'); |
74 | 76 | $item = $class->newInstanceArgs([$this, $key]); |
75 | 77 | $item->setEventManager($this->eventManager); |
76 | | - $driverArray = $this->driverRead($item); |
77 | 78 |
|
78 | | - if ($driverArray) { |
79 | | - if(!is_array($driverArray)){ |
80 | | - throw new phpFastCacheCoreException(sprintf('The driverRead method returned an unexpected variable type: %s', gettype($driverArray))); |
81 | | - } |
82 | | - $item->set($this->driverUnwrapData($driverArray)); |
83 | | - $item->expiresAt($this->driverUnwrapEdate($driverArray)); |
84 | | - |
85 | | - if($this->config['itemDetailedDate']){ |
86 | | - |
87 | | - /** |
88 | | - * If the itemDetailedDate has been |
89 | | - * set after caching, we MUST inject |
90 | | - * a new DateTime object on the fly |
91 | | - */ |
92 | | - $item->setCreationDate($this->driverUnwrapCdate($driverArray) ?: new \DateTime()); |
93 | | - $item->setModificationDate($this->driverUnwrapMdate($driverArray) ?: new \DateTime()); |
94 | | - } |
| 79 | + getItemDriverRead: |
| 80 | + { |
| 81 | + $driverArray = $this->driverRead($item); |
| 82 | + |
| 83 | + if ($driverArray) { |
| 84 | + if(!is_array($driverArray)){ |
| 85 | + throw new phpFastCacheCoreException(sprintf('The driverRead method returned an unexpected variable type: %s', gettype($driverArray))); |
| 86 | + } |
| 87 | + $driverData = $this->driverUnwrapData($driverArray); |
| 88 | + |
| 89 | + if($this->getConfig()[ 'preventCacheSlams' ]){ |
| 90 | + while($driverData instanceof ItemBatch) { |
| 91 | + if($driverData->getItemDate()->getTimestamp() + $this->getConfig()[ 'cacheSlamsTimeout' ] < time()){ |
| 92 | + /** |
| 93 | + * The timeout has been reached |
| 94 | + * Consider that the batch has |
| 95 | + * failed and serve an empty item |
| 96 | + * to avoid to get stuck with a |
| 97 | + * batch item stored in driver |
| 98 | + */ |
| 99 | + goto getItemDriverExpired; |
| 100 | + } |
| 101 | + /** |
| 102 | + * @eventName CacheGetItem |
| 103 | + * @param $this ExtendedCacheItemPoolInterface |
| 104 | + * @param $driverData ItemBatch |
| 105 | + * @param $cacheSlamsSpendSeconds int |
| 106 | + */ |
| 107 | + $this->eventManager->dispatch('CacheGetItemInSlamBatch', $this, $driverData, $cacheSlamsSpendSeconds); |
| 108 | + |
| 109 | + /** |
| 110 | + * Wait for a second before |
| 111 | + * attempting to get exit |
| 112 | + * the current batch process |
| 113 | + */ |
| 114 | + sleep(1); |
| 115 | + $cacheSlamsSpendSeconds++; |
| 116 | + goto getItemDriverRead; |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + $item->set($driverData); |
| 121 | + $item->expiresAt($this->driverUnwrapEdate($driverArray)); |
95 | 122 |
|
96 | | - $item->setTags($this->driverUnwrapTags($driverArray)); |
97 | | - if ($item->isExpired()) { |
98 | | - /** |
99 | | - * Using driverDelete() instead of delete() |
100 | | - * to avoid infinite loop caused by |
101 | | - * getItem() call in delete() method |
102 | | - * As we MUST return an item in any |
103 | | - * way, we do not de-register here |
104 | | - */ |
105 | | - $this->driverDelete($item); |
106 | | - |
107 | | - /** |
108 | | - * Reset the Item |
109 | | - */ |
110 | | - $item->set(null) |
111 | | - ->expiresAfter(abs((int) $this->getConfig()[ 'defaultTtl' ])) |
112 | | - ->setHit(false) |
113 | | - ->setTags([]); |
114 | 123 | if($this->config['itemDetailedDate']){ |
115 | 124 |
|
116 | 125 | /** |
117 | 126 | * If the itemDetailedDate has been |
118 | 127 | * set after caching, we MUST inject |
119 | 128 | * a new DateTime object on the fly |
120 | 129 | */ |
121 | | - $item->setCreationDate(new \DateTime()); |
122 | | - $item->setModificationDate(new \DateTime()); |
| 130 | + $item->setCreationDate($this->driverUnwrapCdate($driverArray) ?: new \DateTime()); |
| 131 | + $item->setModificationDate($this->driverUnwrapMdate($driverArray) ?: new \DateTime()); |
| 132 | + } |
| 133 | + |
| 134 | + $item->setTags($this->driverUnwrapTags($driverArray)); |
| 135 | + |
| 136 | + getItemDriverExpired: |
| 137 | + if ($item->isExpired()) { |
| 138 | + /** |
| 139 | + * Using driverDelete() instead of delete() |
| 140 | + * to avoid infinite loop caused by |
| 141 | + * getItem() call in delete() method |
| 142 | + * As we MUST return an item in any |
| 143 | + * way, we do not de-register here |
| 144 | + */ |
| 145 | + $this->driverDelete($item); |
| 146 | + |
| 147 | + /** |
| 148 | + * Reset the Item |
| 149 | + */ |
| 150 | + $item->set(null) |
| 151 | + ->expiresAfter(abs((int) $this->getConfig()[ 'defaultTtl' ])) |
| 152 | + ->setHit(false) |
| 153 | + ->setTags([]); |
| 154 | + if($this->config['itemDetailedDate']){ |
| 155 | + |
| 156 | + /** |
| 157 | + * If the itemDetailedDate has been |
| 158 | + * set after caching, we MUST inject |
| 159 | + * a new DateTime object on the fly |
| 160 | + */ |
| 161 | + $item->setCreationDate(new \DateTime()); |
| 162 | + $item->setModificationDate(new \DateTime()); |
| 163 | + } |
| 164 | + } else { |
| 165 | + $item->setHit(true); |
123 | 166 | } |
124 | | - } else { |
125 | | - $item->setHit(true); |
| 167 | + }else{ |
| 168 | + $item->expiresAfter(abs((int) $this->getConfig()[ 'defaultTtl' ])); |
126 | 169 | } |
127 | | - }else{ |
128 | | - $item->expiresAfter(abs((int) $this->getConfig()[ 'defaultTtl' ])); |
129 | 170 | } |
130 | | - |
131 | 171 | } |
132 | 172 | } else { |
133 | 173 | throw new phpFastCacheInvalidArgumentException(sprintf('$key must be a string, got type "%s" instead.', gettype($key))); |
@@ -277,6 +317,28 @@ public function save(CacheItemInterface $item) |
277 | 317 | */ |
278 | 318 | $this->eventManager->dispatch('CacheSaveItem', $this, $item); |
279 | 319 |
|
| 320 | + |
| 321 | + if($this->getConfig()[ 'preventCacheSlams' ]){ |
| 322 | + /** |
| 323 | + * @var $itemBatch ExtendedCacheItemInterface |
| 324 | + */ |
| 325 | + $class = new \ReflectionClass((new \ReflectionObject($this))->getNamespaceName() . '\Item'); |
| 326 | + $itemBatch = $class->newInstanceArgs([$this, $item->getKey()]); |
| 327 | + $itemBatch->setEventManager($this->eventManager) |
| 328 | + ->set(new ItemBatch($item->getKey(), new \DateTime())) |
| 329 | + ->expiresAfter($this->getConfig()[ 'cacheSlamsTimeout' ]); |
| 330 | + |
| 331 | + /** |
| 332 | + * To avoid SPL mismatches |
| 333 | + * we have to re-attach the |
| 334 | + * original item to the pool |
| 335 | + */ |
| 336 | + $this->driverWrite($itemBatch); |
| 337 | + $this->detachItem($itemBatch); |
| 338 | + $this->attachItem($item); |
| 339 | + } |
| 340 | + |
| 341 | + |
280 | 342 | if ($this->driverWrite($item) && $this->driverWriteTags($item)) { |
281 | 343 | $item->setHit(true); |
282 | 344 | CacheManager::$WriteHits++; |
|
0 commit comments