From ff80968387a191b017620caff35b694275636e76 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 31 Aug 2014 14:23:31 +0400 Subject: [PATCH] cache controller adjustments and improvements related to #4792, close #4874 --- framework/console/controllers/CacheController.php | 210 ++++++++++++++++++--- .../console/controllers/CacheControllerTest.php | 98 ++++++++++ 2 files changed, 282 insertions(+), 26 deletions(-) create mode 100644 tests/unit/framework/console/controllers/CacheControllerTest.php diff --git a/framework/console/controllers/CacheController.php b/framework/console/controllers/CacheController.php index a6286dd..ac60868 100644 --- a/framework/console/controllers/CacheController.php +++ b/framework/console/controllers/CacheController.php @@ -9,13 +9,26 @@ namespace yii\console\controllers; use Yii; use yii\console\Controller; -use yii\console\Exception; use yii\caching\Cache; +use yii\helpers\Console; +use yii\console\Exception; /** * Allows you to flush cache. * + * ~~~ + * #see list of available components to flush + * yii cache + * + * #flush particular components specified by their names + * yii cache/flush first second third + * + * #flush all cache components that can be found in the system + * yii cache/flush-all + * ~~~ + * * @author Alexander Makarov + * @author Mark Jebri * @since 2.0 */ class CacheController extends Controller @@ -25,43 +38,188 @@ class CacheController extends Controller */ public function actionIndex() { - $caches = []; - $components = Yii::$app->getComponents(); - foreach ($components as $name => $component) { - if ($component instanceof Cache) { - $caches[$name] = get_class($component); - } elseif (is_array($component) && isset($component['class']) && strpos($component['class'], 'Cache') !== false) { - $caches[$name] = $component['class']; - } - } + $caches = $this->findCaches(); + if (!empty($caches)) { - echo "The following caches can be flushed:\n\n"; - foreach ($caches as $name => $class) { - echo " * $name: $class\n"; - } + $this->notifyCachesCanBeFlushed($caches); } else { - echo "No cache is used.\n"; + $this->notifyNoCachesFound(); } } /** - * Flushes cache. - * @param string $component Name of the cache application component to use. + * Flushes given cache components. + * For example, * - * @throws \yii\console\Exception + * ~~~ + * # flushes caches specified by their id: "first", "second", "third" + * yii cache/flush first second third + * ~~~ + * */ - public function actionFlush($component = 'cache') + public function actionFlush() { - /* @var $cache Cache */ - $cache = Yii::$app->get($component, false); - if (!$cache || !$cache instanceof Cache) { - throw new Exception('Application component "'.$component.'" is not defined or not a cache.'); + $cachesInput = func_get_args(); + + if (empty($cachesInput)) { + throw new Exception("You should specify cache components names"); + } + + $caches = $this->findCaches($cachesInput); + $cachesInfo = []; + + $foundCaches = array_keys($caches); + $notFoundCaches = array_diff($cachesInput, array_keys($caches)); + + if ($notFoundCaches) { + $this->notifyNotFoundCaches($notFoundCaches); } - if (!$cache->flush()) { - throw new Exception('Unable to flush cache.'); + if (!$foundCaches) { + $this->notifyNoCachesFound(); + return static::EXIT_CODE_NORMAL; } - echo "\nDone.\n"; + if (!$this->confirmFlush($foundCaches)) { + return static::EXIT_CODE_NORMAL; + } + + foreach ($caches as $name => $class) { + $cachesInfo[] = [ + 'name' => $name, + 'class' => $class, + 'is_flushed' => Yii::$app->get($name)->flush(), + ]; + } + + $this->notifyFlushed($cachesInfo); } + + /** + * Flushes all caches registered in the system. + */ + public function actionFlushAll() + { + $caches = $this->findCaches(); + $cachesInfo = []; + + if (empty($caches)) { + $this->notifyNoCachesFound(); + return static::EXIT_CODE_NORMAL; + } + + foreach ($caches as $name => $class) { + $cachesInfo[] = [ + 'name' => $name, + 'class' => $class, + 'is_flushed' => Yii::$app->get($name)->flush(), + ]; + } + + $this->notifyFlushed($cachesInfo); + } + + /** + * Notifies user that given caches are found and can be flushed. + * @param array $caches array of cache component classes + */ + private function notifyCachesCanBeFlushed($caches) + { + $this->stdout("The following caches were found in the system:\n\n", Console::FG_YELLOW); + + foreach ($caches as $name => $class) { + $this->stdout("\t* $name ($class)\n", Console::FG_GREEN); + } + + $this->stdout("\n"); + } + + /** + * Notifies user that there was not found any cache in the system. + */ + private function notifyNoCachesFound() + { + $this->stdout("No cache components were found in the system.\n", Console::FG_RED); + } + + /** + * Notifies user that given cache components were not found in the system. + * @param array $cachesNames + */ + private function notifyNotFoundCaches($cachesNames) + { + $this->stdout("The following cache components were NOT found:\n\n", Console::FG_RED); + + foreach ($cachesNames as $name) { + $this->stdout("\t * $name \n", Console::FG_GREEN); + } + + $this->stdout("\n"); + } + + /** + * + * @param array $caches + */ + private function notifyFlushed($caches) + { + $this->stdout("The following cache components were processed:\n\n", Console::FG_YELLOW); + + foreach ($caches as $cache) { + $this->stdout("\t* " . $cache['name'] ." (" . $cache['class'] . ")", Console::FG_GREEN); + + if (!$cache['is_flushed']) { + $this->stdout(" - not flushed\n", Console::FG_RED); + } else { + $this->stdout("\n"); + } + } + + $this->stdout("\n"); + } + + /** + * Prompts user with confirmation if caches should be flushed. + * @param array $cachesNames + * @return boolean + */ + private function confirmFlush($cachesNames) + { + $this->stdout("The following cache components will be flushed:\n\n", Console::FG_YELLOW); + + foreach ($cachesNames as $name) { + $this->stdout("\t * $name \n", Console::FG_GREEN); + } + + return $this->confirm("\nFlush above cache components?"); + } + + /** + * Returns array of caches in the system, keys are cache components names, values are class names. + * @param array $cachesNames caches to be found + * @return array + */ + private function findCaches(array $cachesNames = []) + { + $caches = []; + $components = Yii::$app->getComponents(); + $findAll = ($cachesNames == []); + + foreach ($components as $name => $component) { + if (!$findAll && !in_array($name, $cachesNames)) { + continue; + } + + if ($component instanceof Cache) { + $caches[$name] = get_class($component); + } elseif (is_array($component) && isset($component['class']) && strpos($component['class'], 'Cache') !== false) { + $caches[$name] = $component['class']; + } elseif (is_string($component) && strpos($component, 'Cache') !== false) { + $caches[$name] = $component; + } + } + + return $caches; + } + } diff --git a/tests/unit/framework/console/controllers/CacheControllerTest.php b/tests/unit/framework/console/controllers/CacheControllerTest.php new file mode 100644 index 0000000..ad10c0a --- /dev/null +++ b/tests/unit/framework/console/controllers/CacheControllerTest.php @@ -0,0 +1,98 @@ +_cacheController = Yii::createObject([ + 'class' => 'yiiunit\framework\console\controllers\CacheConsoledController', + 'interactive' => false, + ],[null, null]); //id and module are null + + $this->mockApplication([ + 'components' => [ + 'firstCache' => 'yii\caching\ArrayCache', + 'secondCache' => 'yii\caching\ArrayCache', + ], + ]); + } + + public function testFlushOne() + { + Yii::$app->firstCache->set('firstKey', 'firstValue'); + Yii::$app->firstCache->set('secondKey', 'secondValue'); + Yii::$app->secondCache->set('thirdKey', 'thirdValue'); + + $this->_cacheController->actionFlush('firstCache'); + + $this->assertFalse(Yii::$app->firstCache->get('firstKey'),'first cache data should be flushed'); + $this->assertFalse(Yii::$app->firstCache->get('secondKey'),'first cache data should be flushed'); + $this->assertEquals('thirdValue', Yii::$app->secondCache->get('thirdKey'), 'second cache data should not be flushed'); + } + + public function testFlushBoth() + { + Yii::$app->firstCache->set('firstKey', 'firstValue'); + Yii::$app->firstCache->set('secondKey', 'secondValue'); + Yii::$app->secondCache->set('thirdKey', 'secondValue'); + + $this->_cacheController->actionFlush('firstCache', 'secondCache'); + + $this->assertFalse(Yii::$app->firstCache->get('firstKey'),'first cache data should be flushed'); + $this->assertFalse(Yii::$app->firstCache->get('secondKey'),'first cache data should be flushed'); + $this->assertFalse(Yii::$app->secondCache->get('thirdKey'), 'second cache data should be flushed'); + } + + public function testNotFoundFlush() + { + Yii::$app->firstCache->set('firstKey', 'firstValue'); + + $this->_cacheController->actionFlush('notExistingCache'); + + $this->assertEquals('firstValue', Yii::$app->firstCache->get('firstKey'), 'first cache data should not be flushed'); + } + + /** + * @expectedException yii\console\Exception + */ + public function testNothingToFlushException() + { + $this->_cacheController->actionFlush(); + } + + public function testFlushAll() + { + Yii::$app->firstCache->set('firstKey', 'firstValue'); + Yii::$app->secondCache->set('thirdKey', 'secondValue'); + + $this->_cacheController->actionFlushAll(); + + $this->assertFalse(Yii::$app->firstCache->get('firstKey'),'first cache data should be flushed'); + $this->assertFalse(Yii::$app->secondCache->get('thirdKey'), 'second cache data should be flushed'); + + } + +} + +class CacheConsoledController extends CacheController +{ + + public function stdout($string) + { + } + +}