Completed
Push — cache-command-apc-improvement ( 166db9 )
by Alexander
10:06
created

CacheController::notifyCachesCanBeFlushed()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 0
cts 10
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 1
crap 12
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
8
namespace yii\console\controllers;
9
10
use Yii;
11
use yii\caching\ApcCache;
12
use yii\console\Controller;
13
use yii\caching\Cache;
14
use yii\helpers\Console;
15
use yii\console\Exception;
16
17
/**
18
 * Allows you to flush cache.
19
 *
20
 * see list of available components to flush:
21
 *
22
 *     yii cache
23
 *
24
 * flush particular components specified by their names:
25
 *
26
 *     yii cache/flush first second third
27
 *
28
 * flush all cache components that can be found in the system
29
 *
30
 *     yii cache/flush-all
31
 *
32
 * Note that the command uses cache components defined in your console application configuration file. If components
33
 * configured are different from web application, web application cache won't be cleared. In order to fix it please
34
 * duplicate web application cache components in console config. You can use any component names.
35
 *
36
 * APC is not shared between PHP processes so flushing cache from command line has no effect on web.
37
 * Flushing web cache could be either done by:
38
 *
39
 * - Putting a php file under web root and calling it via HTTP
40
 * - Using [Cachetool](http://gordalina.github.io/cachetool/)
41
 *
42
 * @author Alexander Makarov <[email protected]>
43
 * @author Mark Jebri <[email protected]>
44
 * @since 2.0
45
 */
46
class CacheController extends Controller
47
{
48
    /**
49
     * Lists the caches that can be flushed.
50
     */
51
    public function actionIndex()
52
    {
53
        $caches = $this->findCaches();
54
55
        if (!empty($caches)) {
56
            $this->notifyCachesCanBeFlushed($caches);
57
        } else {
58
            $this->notifyNoCachesFound();
59
        }
60
    }
61
62
    /**
63
     * Flushes given cache components.
64
     * For example,
65
     *
66
     * ```
67
     * # flushes caches specified by their id: "first", "second", "third"
68
     * yii cache/flush first second third
69
     * ```
70
     *
71
     */
72 4
    public function actionFlush()
73
    {
74 4
        $cachesInput = func_get_args();
75
76 4
        if (empty($cachesInput)) {
77 1
            throw new Exception('You should specify cache components names');
78
        }
79
80 3
        $caches = $this->findCaches($cachesInput);
81 3
        $cachesInfo = [];
82
83 3
        $foundCaches = array_keys($caches);
84 3
        $notFoundCaches = array_diff($cachesInput, array_keys($caches));
85
86 3
        if ($notFoundCaches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $notFoundCaches of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
87 1
            $this->notifyNotFoundCaches($notFoundCaches);
88 1
        }
89
90 3
        if (!$foundCaches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $foundCaches of type array<integer|string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
91 1
            $this->notifyNoCachesFound();
92 1
            return static::EXIT_CODE_NORMAL;
93
        }
94
95 2
        if (!$this->confirmFlush($foundCaches)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->confirmFlush($foundCaches) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
96
            return static::EXIT_CODE_NORMAL;
97
        }
98
99 2
        foreach ($caches as $name => $class) {
100 2
            $cachesInfo[] = [
101 2
                'name' => $name,
102 2
                'class' => $class,
103 2
                'is_flushed' => $this->isApcCache($class) ? false : Yii::$app->get($name)->flush(),
104
            ];
105 2
        }
106
107 2
        $this->notifyFlushed($cachesInfo);
108 2
    }
109
110
    /**
111
     * Flushes all caches registered in the system.
112
     */
113 1
    public function actionFlushAll()
114
    {
115 1
        $caches = $this->findCaches();
116 1
        $cachesInfo = [];
117
118 1
        if (empty($caches)) {
119
            $this->notifyNoCachesFound();
120
            return static::EXIT_CODE_NORMAL;
121
        }
122
123 1
        foreach ($caches as $name => $class) {
124 1
            $cachesInfo[] = [
125 1
                'name' => $name,
126 1
                'class' => $class,
127 1
                'is_flushed' => $this->isApcCache($class) ? false : Yii::$app->get($name)->flush(),
128
            ];
129 1
        }
130
131 1
        $this->notifyFlushed($cachesInfo);
132 1
    }
133
134
    /**
135
     * Clears DB schema cache for a given connection component.
136
     *
137
     * ```
138
     * # clears cache schema specified by component id: "db"
139
     * yii cache/flush-schema db
140
     * ```
141
     *
142
     * @param string $db id connection component
143
     * @return int exit code
144
     * @throws Exception
145
     * @throws \yii\base\InvalidConfigException
146
     *
147
     * @since 2.0.1
148
     */
149 1
    public function actionFlushSchema($db = 'db')
150
    {
151 1
        $connection = Yii::$app->get($db, false);
152 1
        if ($connection === null) {
153
            $this->stdout("Unknown component \"$db\".\n", Console::FG_RED);
154
            return self::EXIT_CODE_ERROR;
155
        }
156
157 1
        if (!$connection instanceof \yii\db\Connection) {
158
            $this->stdout("\"$db\" component doesn't inherit \\yii\\db\\Connection.\n", Console::FG_RED);
159
            return self::EXIT_CODE_ERROR;
160 1
        } elseif (!$this->confirm("Flush cache schema for \"$db\" connection?")) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->confirm("Flush ca...\"{$db}\" connection?") of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
161
            return static::EXIT_CODE_NORMAL;
162
        }
163
164
        try {
165 1
            $schema = $connection->getSchema();
166 1
            $schema->refresh();
167 1
            $this->stdout("Schema cache for component \"$db\", was flushed.\n\n", Console::FG_GREEN);
168 1
        } catch (\Exception $e) {
169
            $this->stdout($e->getMessage() . "\n\n", Console::FG_RED);
170
        }
171 1
    }
172
173
    /**
174
     * Notifies user that given caches are found and can be flushed.
175
     * @param array $caches array of cache component classes
176
     */
177
    private function notifyCachesCanBeFlushed($caches)
178
    {
179
        $this->stdout("The following caches were found in the system:\n\n", Console::FG_YELLOW);
180
181
        foreach ($caches as $name => $class) {
182
            if ($this->isApcCache($class)) {
183
                $this->stdout("\t* $name ($class) - can not be flushed via console\n", Console::FG_YELLOW);
184
            } else {
185
                $this->stdout("\t* $name ($class)\n", Console::FG_GREEN);
186
            }
187
        }
188
189
        $this->stdout("\n");
190
    }
191
192
    /**
193
     * Notifies user that there was not found any cache in the system.
194
     */
195 1
    private function notifyNoCachesFound()
196
    {
197 1
        $this->stdout("No cache components were found in the system.\n", Console::FG_RED);
198 1
    }
199
200
    /**
201
     * Notifies user that given cache components were not found in the system.
202
     * @param array $cachesNames
203
     */
204 1
    private function notifyNotFoundCaches($cachesNames)
205
    {
206 1
        $this->stdout("The following cache components were NOT found:\n\n", Console::FG_RED);
207
208 1
        foreach ($cachesNames as $name) {
209 1
            $this->stdout("\t* $name \n", Console::FG_GREEN);
210 1
        }
211
212 1
        $this->stdout("\n");
213 1
    }
214
215
    /**
216
     *
217
     * @param array $caches
218
     */
219 3
    private function notifyFlushed($caches)
220
    {
221 3
        $this->stdout("The following cache components were processed:\n\n", Console::FG_YELLOW);
222
223 3
        foreach ($caches as $cache) {
224 3
            $this->stdout("\t* " . $cache['name'] .' (' . $cache['class'] . ')', Console::FG_GREEN);
225
226 3
            if (!$cache['is_flushed']) {
227
                $this->stdout(" - not flushed\n", Console::FG_RED);
228
            } else {
229 3
                $this->stdout("\n");
230
            }
231 3
        }
232
233 3
        $this->stdout("\n");
234 3
    }
235
236
    /**
237
     * Prompts user with confirmation if caches should be flushed.
238
     * @param array $cachesNames
239
     * @return bool
240
     */
241 2
    private function confirmFlush($cachesNames)
242
    {
243 2
        $this->stdout("The following cache components will be flushed:\n\n", Console::FG_YELLOW);
244
245 2
        foreach ($cachesNames as $name) {
246 2
            $this->stdout("\t* $name \n", Console::FG_GREEN);
247 2
        }
248
249 2
        return $this->confirm("\nFlush above cache components?");
250
    }
251
252
    /**
253
     * Returns array of caches in the system, keys are cache components names, values are class names.
254
     * @param array $cachesNames caches to be found
255
     * @return array
256
     */
257 4
    private function findCaches(array $cachesNames = [])
258
    {
259 4
        $caches = [];
260 4
        $components = Yii::$app->getComponents();
261 4
        $findAll = ($cachesNames === []);
262
263 4
        foreach ($components as $name => $component) {
264 4
            if (!$findAll && !in_array($name, $cachesNames, true)) {
265 3
                continue;
266
            }
267
268 3
            if ($component instanceof Cache) {
269
                $caches[$name] = get_class($component);
270 3
            } elseif (is_array($component) && isset($component['class']) && $this->isCacheClass($component['class'])) {
271
                $caches[$name] = $component['class'];
272 3
            } elseif (is_string($component) && $this->isCacheClass($component)) {
273 3
                $caches[$name] = $component;
274 3
            }
275 4
        }
276
277 4
        return $caches;
278
    }
279
280
    /**
281
     * Checks if given class is a Cache class.
282
     * @param string $className class name.
283
     * @return bool
284
     */
285 3
    private function isCacheClass($className)
286
    {
287 3
        return is_subclass_of($className, Cache::className());
288
    }
289
290
    /**
291
     * Checks if it's APC cache
292
     * @param string $className class name.
293
     * @return bool
294
     */
295 3
    private function isApcCache($className)
296
    {
297 3
        return is_a($className, ApcCache::className(), true);
298
    }
299
}
300