vendor/friendsofsymfony/http-cache/src/CacheInvalidator.php line 298

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the FOSHttpCache package.
  4. *
  5. * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace FOS\HttpCache;
  11. use FOS\HttpCache\Exception\ExceptionCollection;
  12. use FOS\HttpCache\Exception\InvalidArgumentException;
  13. use FOS\HttpCache\Exception\ProxyResponseException;
  14. use FOS\HttpCache\Exception\ProxyUnreachableException;
  15. use FOS\HttpCache\Exception\UnsupportedProxyOperationException;
  16. use FOS\HttpCache\ProxyClient\Invalidation\BanCapable;
  17. use FOS\HttpCache\ProxyClient\Invalidation\ClearCapable;
  18. use FOS\HttpCache\ProxyClient\Invalidation\PurgeCapable;
  19. use FOS\HttpCache\ProxyClient\Invalidation\RefreshCapable;
  20. use FOS\HttpCache\ProxyClient\Invalidation\TagCapable;
  21. use FOS\HttpCache\ProxyClient\ProxyClient;
  22. use FOS\HttpCache\ProxyClient\Symfony;
  23. use Symfony\Component\EventDispatcher\EventDispatcher;
  24. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  25. use Toflar\Psr6HttpCacheStore\Psr6Store;
  26. /**
  27. * Manages HTTP cache invalidation.
  28. *
  29. * @author David de Boer <david@driebit.nl>
  30. * @author David Buchmann <mail@davidbu.ch>
  31. * @author André Rømcke <ar@ez.no>
  32. */
  33. class CacheInvalidator
  34. {
  35. /**
  36. * Value to check support of invalidatePath operation.
  37. */
  38. public const PATH = 'path';
  39. /**
  40. * Value to check support of refreshPath operation.
  41. */
  42. public const REFRESH = 'refresh';
  43. /**
  44. * Value to check support of invalidate and invalidateRegex operations.
  45. */
  46. public const INVALIDATE = 'invalidate';
  47. /**
  48. * Value to check support of invalidateTags operation.
  49. */
  50. public const TAGS = 'tags';
  51. /**
  52. * Value to check support of clearCache operation.
  53. */
  54. public const CLEAR = 'clear';
  55. /**
  56. * @var ProxyClient
  57. */
  58. private $cache;
  59. /**
  60. * @var EventDispatcherInterface
  61. */
  62. private $eventDispatcher;
  63. /**
  64. * Constructor.
  65. *
  66. * @param ProxyClient $cache HTTP cache
  67. */
  68. public function __construct(ProxyClient $cache)
  69. {
  70. $this->cache = $cache;
  71. }
  72. /**
  73. * Check whether this invalidator instance supports the specified
  74. * operation.
  75. *
  76. * Support for PATH means invalidatePath will work, REFRESH means
  77. * refreshPath works, TAGS means that invalidateTags works and
  78. * INVALIDATE is for the invalidate and invalidateRegex methods.
  79. *
  80. * @param string $operation one of the class constants
  81. *
  82. * @return bool
  83. *
  84. * @throws InvalidArgumentException
  85. */
  86. public function supports($operation)
  87. {
  88. switch ($operation) {
  89. case self::PATH:
  90. return $this->cache instanceof PurgeCapable;
  91. case self::REFRESH:
  92. return $this->cache instanceof RefreshCapable;
  93. case self::INVALIDATE:
  94. return $this->cache instanceof BanCapable;
  95. case self::TAGS:
  96. $supports = $this->cache instanceof TagCapable;
  97. if ($supports && $this->cache instanceof Symfony) {
  98. return class_exists(Psr6Store::class);
  99. }
  100. return $supports;
  101. case self::CLEAR:
  102. return $this->cache instanceof ClearCapable;
  103. default:
  104. throw new InvalidArgumentException('Unknown operation '.$operation);
  105. }
  106. }
  107. /**
  108. * Set event dispatcher - may only be called once.
  109. *
  110. * @throws \Exception when trying to override the event dispatcher
  111. */
  112. public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
  113. {
  114. if ($this->eventDispatcher) {
  115. // if you want to set a custom event dispatcher, do so right after instantiating
  116. // the invalidator.
  117. throw new \Exception('You may not change the event dispatcher once it is set.');
  118. }
  119. $this->eventDispatcher = $eventDispatcher;
  120. }
  121. /**
  122. * Get the event dispatcher used by the cache invalidator.
  123. *
  124. * @return EventDispatcherInterface
  125. */
  126. public function getEventDispatcher()
  127. {
  128. if (!$this->eventDispatcher) {
  129. $this->eventDispatcher = new EventDispatcher();
  130. }
  131. return $this->eventDispatcher;
  132. }
  133. /**
  134. * Invalidate a path or URL.
  135. *
  136. * @param string $path Path or URL
  137. * @param array $headers HTTP headers (optional)
  138. *
  139. * @return $this
  140. *
  141. * @throws UnsupportedProxyOperationException
  142. */
  143. public function invalidatePath($path, array $headers = [])
  144. {
  145. if (!$this->cache instanceof PurgeCapable) {
  146. throw UnsupportedProxyOperationException::cacheDoesNotImplement('PURGE');
  147. }
  148. $this->cache->purge($path, $headers);
  149. return $this;
  150. }
  151. /**
  152. * Refresh a path or URL.
  153. *
  154. * @param string $path Path or URL
  155. * @param array $headers HTTP headers (optional)
  156. *
  157. * @see RefreshCapable::refresh()
  158. *
  159. * @return $this
  160. *
  161. * @throws UnsupportedProxyOperationException
  162. */
  163. public function refreshPath($path, array $headers = [])
  164. {
  165. if (!$this->cache instanceof RefreshCapable) {
  166. throw UnsupportedProxyOperationException::cacheDoesNotImplement('REFRESH');
  167. }
  168. $this->cache->refresh($path, $headers);
  169. return $this;
  170. }
  171. /**
  172. * Invalidate all cached objects matching the provided HTTP headers.
  173. *
  174. * Each header is a a POSIX regular expression, for example
  175. * ['X-Host' => '^(www\.)?(this|that)\.com$']
  176. *
  177. * @see BanCapable::ban()
  178. *
  179. * @param array $headers HTTP headers that path must match to be banned
  180. *
  181. * @return $this
  182. *
  183. * @throws UnsupportedProxyOperationException If HTTP cache does not support BAN requests
  184. */
  185. public function invalidate(array $headers)
  186. {
  187. if (!$this->cache instanceof BanCapable) {
  188. throw UnsupportedProxyOperationException::cacheDoesNotImplement('BAN');
  189. }
  190. $this->cache->ban($headers);
  191. return $this;
  192. }
  193. /**
  194. * Remove/Expire cache objects based on cache tags.
  195. *
  196. * @see TagCapable::tags()
  197. *
  198. * @param array $tags Tags that should be removed/expired from the cache
  199. *
  200. * @return $this
  201. *
  202. * @throws UnsupportedProxyOperationException If HTTP cache does not support Tags invalidation
  203. */
  204. public function invalidateTags(array $tags)
  205. {
  206. if (!$this->cache instanceof TagCapable) {
  207. throw UnsupportedProxyOperationException::cacheDoesNotImplement('Tags');
  208. }
  209. $this->cache->invalidateTags($tags);
  210. return $this;
  211. }
  212. /**
  213. * Invalidate URLs based on a regular expression for the URI, an optional
  214. * content type and optional limit to certain hosts.
  215. *
  216. * The hosts parameter can either be a regular expression, e.g.
  217. * '^(www\.)?(this|that)\.com$' or an array of exact host names, e.g.
  218. * ['example.com', 'other.net']. If the parameter is empty, all hosts
  219. * are matched.
  220. *
  221. * @see BanCapable::banPath()
  222. *
  223. * @param string $path Regular expression pattern for URI to
  224. * invalidate
  225. * @param string $contentType Regular expression pattern for the content
  226. * type to limit banning, for instance 'text'
  227. * @param array|string $hosts Regular expression of a host name or list of
  228. * exact host names to limit banning
  229. *
  230. * @return $this
  231. *
  232. * @throws UnsupportedProxyOperationException If HTTP cache does not support BAN requests
  233. */
  234. public function invalidateRegex($path, $contentType = null, $hosts = null)
  235. {
  236. if (!$this->cache instanceof BanCapable) {
  237. throw UnsupportedProxyOperationException::cacheDoesNotImplement('BAN');
  238. }
  239. $this->cache->banPath($path, $contentType, $hosts);
  240. return $this;
  241. }
  242. /**
  243. * Clear the cache completely.
  244. *
  245. * @return $this
  246. *
  247. * @throws UnsupportedProxyOperationException if HTTP cache does not support clearing the cache completely
  248. */
  249. public function clearCache()
  250. {
  251. if (!$this->cache instanceof ClearCapable) {
  252. throw UnsupportedProxyOperationException::cacheDoesNotImplement('CLEAR');
  253. }
  254. $this->cache->clear();
  255. return $this;
  256. }
  257. /**
  258. * Send all pending invalidation requests.
  259. *
  260. * @return int the number of cache invalidations performed per caching server
  261. *
  262. * @throws ExceptionCollection if any errors occurred during flush
  263. */
  264. public function flush()
  265. {
  266. try {
  267. return $this->cache->flush();
  268. } catch (ExceptionCollection $exceptions) {
  269. foreach ($exceptions as $exception) {
  270. $event = new Event();
  271. $event->setException($exception);
  272. if ($exception instanceof ProxyResponseException) {
  273. $this->getEventDispatcher()->dispatch($event, Events::PROXY_RESPONSE_ERROR);
  274. } elseif ($exception instanceof ProxyUnreachableException) {
  275. $this->getEventDispatcher()->dispatch($event, Events::PROXY_UNREACHABLE_ERROR);
  276. }
  277. }
  278. throw $exceptions;
  279. }
  280. }
  281. }