CacheController.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\console\controllers;
  8. use Yii;
  9. use yii\caching\ApcCache;
  10. use yii\caching\CacheInterface;
  11. use yii\console\Controller;
  12. use yii\console\Exception;
  13. use yii\console\ExitCode;
  14. use yii\helpers\Console;
  15. /**
  16. * Allows you to flush cache.
  17. *
  18. * see list of available components to flush:
  19. *
  20. * yii cache
  21. *
  22. * flush particular components specified by their names:
  23. *
  24. * yii cache/flush first second third
  25. *
  26. * flush all cache components that can be found in the system
  27. *
  28. * yii cache/flush-all
  29. *
  30. * Note that the command uses cache components defined in your console application configuration file. If components
  31. * configured are different from web application, web application cache won't be cleared. In order to fix it please
  32. * duplicate web application cache components in console config. You can use any component names.
  33. *
  34. * APC is not shared between PHP processes so flushing cache from command line has no effect on web.
  35. * Flushing web cache could be either done by:
  36. *
  37. * - Putting a php file under web root and calling it via HTTP
  38. * - Using [Cachetool](http://gordalina.github.io/cachetool/)
  39. *
  40. * @author Alexander Makarov <sam@rmcreative.ru>
  41. * @author Mark Jebri <mark.github@yandex.ru>
  42. * @since 2.0
  43. */
  44. class CacheController extends Controller
  45. {
  46. /**
  47. * Lists the caches that can be flushed.
  48. */
  49. public function actionIndex()
  50. {
  51. $caches = $this->findCaches();
  52. if (!empty($caches)) {
  53. $this->notifyCachesCanBeFlushed($caches);
  54. } else {
  55. $this->notifyNoCachesFound();
  56. }
  57. }
  58. /**
  59. * Flushes given cache components.
  60. *
  61. * For example,
  62. *
  63. * ```
  64. * # flushes caches specified by their id: "first", "second", "third"
  65. * yii cache/flush first second third
  66. * ```
  67. */
  68. public function actionFlush()
  69. {
  70. $cachesInput = func_get_args();
  71. if (empty($cachesInput)) {
  72. throw new Exception('You should specify cache components names');
  73. }
  74. $caches = $this->findCaches($cachesInput);
  75. $cachesInfo = [];
  76. $foundCaches = array_keys($caches);
  77. $notFoundCaches = array_diff($cachesInput, array_keys($caches));
  78. if ($notFoundCaches) {
  79. $this->notifyNotFoundCaches($notFoundCaches);
  80. }
  81. if (!$foundCaches) {
  82. $this->notifyNoCachesFound();
  83. return ExitCode::OK;
  84. }
  85. if (!$this->confirmFlush($foundCaches)) {
  86. return ExitCode::OK;
  87. }
  88. foreach ($caches as $name => $class) {
  89. $cachesInfo[] = [
  90. 'name' => $name,
  91. 'class' => $class,
  92. 'is_flushed' => $this->canBeFlushed($class) ? Yii::$app->get($name)->flush() : false,
  93. ];
  94. }
  95. $this->notifyFlushed($cachesInfo);
  96. }
  97. /**
  98. * Flushes all caches registered in the system.
  99. */
  100. public function actionFlushAll()
  101. {
  102. $caches = $this->findCaches();
  103. $cachesInfo = [];
  104. if (empty($caches)) {
  105. $this->notifyNoCachesFound();
  106. return ExitCode::OK;
  107. }
  108. foreach ($caches as $name => $class) {
  109. $cachesInfo[] = [
  110. 'name' => $name,
  111. 'class' => $class,
  112. 'is_flushed' => $this->canBeFlushed($class) ? Yii::$app->get($name)->flush() : false,
  113. ];
  114. }
  115. $this->notifyFlushed($cachesInfo);
  116. }
  117. /**
  118. * Clears DB schema cache for a given connection component.
  119. *
  120. * ```
  121. * # clears cache schema specified by component id: "db"
  122. * yii cache/flush-schema db
  123. * ```
  124. *
  125. * @param string $db id connection component
  126. * @return int exit code
  127. * @throws Exception
  128. * @throws \yii\base\InvalidConfigException
  129. *
  130. * @since 2.0.1
  131. */
  132. public function actionFlushSchema($db = 'db')
  133. {
  134. $connection = Yii::$app->get($db, false);
  135. if ($connection === null) {
  136. $this->stdout("Unknown component \"$db\".\n", Console::FG_RED);
  137. return ExitCode::UNSPECIFIED_ERROR;
  138. }
  139. if (!$connection instanceof \yii\db\Connection) {
  140. $this->stdout("\"$db\" component doesn't inherit \\yii\\db\\Connection.\n", Console::FG_RED);
  141. return ExitCode::UNSPECIFIED_ERROR;
  142. } elseif (!$this->confirm("Flush cache schema for \"$db\" connection?")) {
  143. return ExitCode::OK;
  144. }
  145. try {
  146. $schema = $connection->getSchema();
  147. $schema->refresh();
  148. $this->stdout("Schema cache for component \"$db\", was flushed.\n\n", Console::FG_GREEN);
  149. } catch (\Exception $e) {
  150. $this->stdout($e->getMessage() . "\n\n", Console::FG_RED);
  151. }
  152. }
  153. /**
  154. * Notifies user that given caches are found and can be flushed.
  155. * @param array $caches array of cache component classes
  156. */
  157. private function notifyCachesCanBeFlushed($caches)
  158. {
  159. $this->stdout("The following caches were found in the system:\n\n", Console::FG_YELLOW);
  160. foreach ($caches as $name => $class) {
  161. if ($this->canBeFlushed($class)) {
  162. $this->stdout("\t* $name ($class)\n", Console::FG_GREEN);
  163. } else {
  164. $this->stdout("\t* $name ($class) - can not be flushed via console\n", Console::FG_YELLOW);
  165. }
  166. }
  167. $this->stdout("\n");
  168. }
  169. /**
  170. * Notifies user that there was not found any cache in the system.
  171. */
  172. private function notifyNoCachesFound()
  173. {
  174. $this->stdout("No cache components were found in the system.\n", Console::FG_RED);
  175. }
  176. /**
  177. * Notifies user that given cache components were not found in the system.
  178. * @param array $cachesNames
  179. */
  180. private function notifyNotFoundCaches($cachesNames)
  181. {
  182. $this->stdout("The following cache components were NOT found:\n\n", Console::FG_RED);
  183. foreach ($cachesNames as $name) {
  184. $this->stdout("\t* $name \n", Console::FG_GREEN);
  185. }
  186. $this->stdout("\n");
  187. }
  188. /**
  189. * @param array $caches
  190. */
  191. private function notifyFlushed($caches)
  192. {
  193. $this->stdout("The following cache components were processed:\n\n", Console::FG_YELLOW);
  194. foreach ($caches as $cache) {
  195. $this->stdout("\t* " . $cache['name'] . ' (' . $cache['class'] . ')', Console::FG_GREEN);
  196. if (!$cache['is_flushed']) {
  197. $this->stdout(" - not flushed\n", Console::FG_RED);
  198. } else {
  199. $this->stdout("\n");
  200. }
  201. }
  202. $this->stdout("\n");
  203. }
  204. /**
  205. * Prompts user with confirmation if caches should be flushed.
  206. * @param array $cachesNames
  207. * @return bool
  208. */
  209. private function confirmFlush($cachesNames)
  210. {
  211. $this->stdout("The following cache components will be flushed:\n\n", Console::FG_YELLOW);
  212. foreach ($cachesNames as $name) {
  213. $this->stdout("\t* $name \n", Console::FG_GREEN);
  214. }
  215. return $this->confirm("\nFlush above cache components?");
  216. }
  217. /**
  218. * Returns array of caches in the system, keys are cache components names, values are class names.
  219. * @param array $cachesNames caches to be found
  220. * @return array
  221. */
  222. private function findCaches(array $cachesNames = [])
  223. {
  224. $caches = [];
  225. $components = Yii::$app->getComponents();
  226. $findAll = ($cachesNames === []);
  227. foreach ($components as $name => $component) {
  228. if (!$findAll && !in_array($name, $cachesNames, true)) {
  229. continue;
  230. }
  231. if ($component instanceof CacheInterface) {
  232. $caches[$name] = get_class($component);
  233. } elseif (is_array($component) && isset($component['class']) && $this->isCacheClass($component['class'])) {
  234. $caches[$name] = $component['class'];
  235. } elseif (is_string($component) && $this->isCacheClass($component)) {
  236. $caches[$name] = $component;
  237. } elseif ($component instanceof \Closure) {
  238. $cache = Yii::$app->get($name);
  239. if ($this->isCacheClass($cache)) {
  240. $cacheClass = get_class($cache);
  241. $caches[$name] = $cacheClass;
  242. }
  243. }
  244. }
  245. return $caches;
  246. }
  247. /**
  248. * Checks if given class is a Cache class.
  249. * @param string $className class name.
  250. * @return bool
  251. */
  252. private function isCacheClass($className)
  253. {
  254. return is_subclass_of($className, 'yii\caching\CacheInterface') || $className === 'yii\caching\CacheInterface';
  255. }
  256. /**
  257. * Checks if cache of a certain class can be flushed.
  258. * @param string $className class name.
  259. * @return bool
  260. */
  261. private function canBeFlushed($className)
  262. {
  263. return !is_a($className, ApcCache::className(), true) || PHP_SAPI !== 'cli';
  264. }
  265. }