Completed
Push — develop ( 0438cb...146147 )
by Neomerx
04:10 queued 02:30
created

ApplicationCommand::executeClear()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3.0261

Importance

Changes 0
Metric Value
dl 0
loc 24
ccs 12
cts 14
cp 0.8571
rs 8.9713
c 0
b 0
f 0
cc 3
eloc 15
nc 3
nop 2
crap 3.0261
1
<?php namespace Limoncello\Application\Commands;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Limoncello\Application\Contracts\Settings\CacheSettingsProviderInterface;
20
use Limoncello\Application\Exceptions\ConfigurationException;
21
use Limoncello\Contracts\Application\ApplicationSettingsInterface;
22
use Limoncello\Contracts\Commands\CommandInterface;
23
use Limoncello\Contracts\Commands\IoInterface;
24
use Limoncello\Contracts\FileSystem\FileSystemInterface;
25
use Limoncello\Contracts\Settings\SettingsProviderInterface;
26
use Psr\Container\ContainerInterface;
27
28
/**
29
 * @package Limoncello\Application
30
 */
31
class ApplicationCommand implements CommandInterface
32
{
33
    /** Argument name */
34
    const ARG_ACTION = 'action';
35
36
    /** Command action */
37
    const ACTION_CLEAR_CACHE = 'clear-cache';
38
39
    /** Command action */
40
    const ACTION_CREATE_CACHE = 'cache';
41
42
    /**
43
     * @inheritdoc
44
     */
45 1
    public static function getName(): string
46
    {
47 1
        return 'l:app';
48
    }
49
50
    /**
51
     * @inheritdoc
52
     */
53 1
    public static function getDescription(): string
54
    {
55 1
        return 'Creates and cleans application cache.';
56
    }
57
58
    /**
59
     * @inheritdoc
60
     */
61 1
    public static function getHelp(): string
62
    {
63 1
        return 'This command creates and cleans caches for routes, settings and etc.';
64
    }
65
66
    /**
67
     * @inheritdoc
68
     */
69 1
    public static function getArguments(): array
70
    {
71 1
        $cache = static::ACTION_CREATE_CACHE;
72 1
        $clear = static::ACTION_CLEAR_CACHE;
73
74
        return [
75
            [
76 1
                static::ARGUMENT_NAME        => static::ARG_ACTION,
77 1
                static::ARGUMENT_DESCRIPTION => "Action such as `$cache` or `$clear`.",
78 1
                static::ARGUMENT_MODE        => static::ARGUMENT_MODE__REQUIRED,
79
            ],
80
        ];
81
    }
82
83
    /**
84
     * @inheritdoc
85
     */
86 1
    public static function getOptions(): array
87
    {
88 1
        return [];
89
    }
90
91
    /**
92
     * @inheritdoc
93
     */
94 1
    public static function execute(ContainerInterface $container, IoInterface $inOut): void
95
    {
96 1
        (new static())->run($container, $inOut);
97
    }
98
99
    /**
100
     * @param ContainerInterface $container
101
     * @param IoInterface        $inOut
102
     *
103
     * @return void
104
     */
105 4
    protected function run(ContainerInterface $container, IoInterface $inOut): void
106
    {
107 4
        $action = $inOut->getArgument(static::ARG_ACTION);
108
        switch ($action) {
109 4
            case static::ACTION_CREATE_CACHE:
110 1
                $this->executeCache($container, $inOut);
111
                break;
112 3
            case static::ACTION_CLEAR_CACHE:
113 2
                $this->executeClear($container, $inOut);
114 1
                break;
115
            default:
116 1
                $inOut->writeError("Unsupported action `$action`." . PHP_EOL);
117 1
                break;
118
        }
119
    }
120
121
    /**
122
     * @param ContainerInterface $container
123
     * @param IoInterface        $inOut
124
     *
125
     * @return void
126
     */
127 2
    protected function executeClear(ContainerInterface $container, IoInterface $inOut): void
128
    {
129 2
        assert($inOut);
130
131 2
        $appSettings    = $this->getApplicationSettings($container);
132 2
        $cacheDir       = $appSettings[ApplicationSettingsInterface::KEY_CACHE_FOLDER];
133 2
        $cacheCallable  = $appSettings[ApplicationSettingsInterface::KEY_CACHE_CALLABLE];
134 2
        list (, $class) = $this->parseCacheCallable($cacheCallable);
135
136 2
        if ($class === null) {
137
            // parsing of cache callable failed (most likely error in settings)
138 1
            throw new ConfigurationException();
139
        }
140
141 1
        $path = $cacheDir . DIRECTORY_SEPARATOR . $class . '.php';
142
143 1
        $fileSystem = $this->getFileSystem($container);
144 1
        if ($fileSystem->exists($path) === true) {
145
            $fileSystem->delete($path);
146
            $inOut->writeInfo("Cache file deleted `$path`.", IoInterface::VERBOSITY_VERBOSE);
0 ignored issues
show
Unused Code introduced by
The call to IoInterface::writeInfo() has too many arguments starting with \Limoncello\Contracts\Co...face::VERBOSITY_VERBOSE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
147
        } else {
148 1
            $inOut->writeInfo('Cache already clean.');
149
        }
150
    }
151
152
    /**
153
     * @param ContainerInterface $container
154
     * @param IoInterface        $inOut
155
     *
156
     * @return void
157
     */
158 1
    protected function executeCache(ContainerInterface $container, IoInterface $inOut): void
159
    {
160 1
        assert($inOut);
161
162 1
        $appSettings   = $this->getApplicationSettings($container);
163 1
        $cacheDir      = $appSettings[ApplicationSettingsInterface::KEY_CACHE_FOLDER];
164 1
        $cacheCallable = $appSettings[ApplicationSettingsInterface::KEY_CACHE_CALLABLE];
165 1
        list ($namespace, $class, $method) = $this->parseCacheCallable($cacheCallable);
166 1
        if ($class === null || $namespace === null || $method === null) {
167
            // parsing of cache callable failed (most likely error in settings)
168 1
            throw new ConfigurationException();
169
        }
170
171
        /** @var CacheSettingsProviderInterface $settingsProvider */
172
        $settingsProvider = $container->get(CacheSettingsProviderInterface::class);
173
        $settingsData     = $settingsProvider->serialize();
174
        $content          = $this->composeContent($settingsData, $namespace, $class, $method);
175
176
        $path = $cacheDir . DIRECTORY_SEPARATOR . $class . '.php';
177
        $this->getFileSystem($container)->write($path, $content);
178
179
        $inOut->writeInfo('Cache created.');
180
        $inOut->writeInfo("Cache written to `$path`.", IoInterface::VERBOSITY_VERBOSE);
0 ignored issues
show
Unused Code introduced by
The call to IoInterface::writeInfo() has too many arguments starting with \Limoncello\Contracts\Co...face::VERBOSITY_VERBOSE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
181
    }
182
183
    /**
184
     * @param mixed $mightBeCallable
185
     *
186
     * @return array
187
     *
188
     * @SuppressWarnings(PHPMD.ElseExpression)
189
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
190
     */
191 6
    protected function parseCacheCallable($mightBeCallable): array
192
    {
193 6
        if (is_string($mightBeCallable) === true &&
194 6
            count($nsClassMethod = explode('::', $mightBeCallable, 2)) === 2 &&
195 6
            count($nsClass = explode('\\', $nsClassMethod[0])) > 1
196
        ) {
197 1
            $canBeClass     = array_pop($nsClass);
198 1
            $canBeNamespace = array_filter($nsClass);
199 1
            $canBeMethod    = $nsClassMethod[1];
200 5
        } elseif (is_array($mightBeCallable) === true &&
201 5
            count($mightBeCallable) === 2 &&
202 5
            count($nsClass = explode('\\', $mightBeCallable[0])) > 1
203
        ) {
204 2
            $canBeClass     = array_pop($nsClass);
205 2
            $canBeNamespace = array_filter($nsClass);
206 2
            $canBeMethod    = $mightBeCallable[1];
207
        } else {
208 3
            return [null, null, null];
209
        }
210
211 3
        foreach (array_merge($canBeNamespace, [$canBeClass, $canBeMethod]) as $value) {
212
            // is string might have a-z, A-Z, _, numbers but has at least one a-z or A-Z.
213 3
            if (is_string($value) === false ||
214 3
                preg_match('/^\\w+$/i', $value) !== 1 ||
215 3
                preg_match('/^[a-z]+$/i', $value) !== 1
216
            ) {
217 3
                return [null, null, null];
218
            }
219
        }
220
221 2
        $namespace = implode('\\', $canBeNamespace);
222 2
        $class     = $canBeClass;
223 2
        $method    = $canBeMethod;
224
225 2
        return [$namespace, $class, $method];
226
    }
227
228
    /**
229
     * @param mixed  $value
230
     * @param string $className
231
     * @param string $methodName
232
     * @param string $namespace
233
     *
234
     * @return string
235
     */
236
    protected function composeContent(
237
        $value,
238
        string $namespace,
239
        string $className,
240
        string $methodName
241
    ): string {
242
        $now  = date(DATE_RFC2822);
243
        $data = var_export($value, true);
244
245
        assert(
246
            $data !== null,
247
            'It seems the data are not exportable. It is likely to be caused by class instances ' .
248
                'that do not implement ` __set_state` magic method required by `var_export`. ' .
249
                'See http://php.net/manual/en/language.oop5.magic.php#object.set-state for more details.'
250
        );
251
252
        $content = <<<EOT
253
<?php namespace $namespace;
254
255
// THIS FILE IS AUTO GENERATED. DO NOT EDIT IT MANUALLY.
256
// Generated at: $now
257
258
class $className
259
{
260
    const DATA = $data;
261
262
    public static function $methodName()
263
    {
264
        return static::DATA;
265
    }
266
}
267
268
EOT;
269
270
        return $content;
271
    }
272
273
    /**
274
     * @param ContainerInterface $container
275
     *
276
     * @return array
277
     */
278 3
    protected function getApplicationSettings(ContainerInterface $container): array
279
    {
280
        /** @var SettingsProviderInterface $settingsProvider */
281 3
        $settingsProvider = $container->get(SettingsProviderInterface::class);
282 3
        $appSettings      = $settingsProvider->get(ApplicationSettingsInterface::class);
283
284 3
        return $appSettings;
285
    }
286
287
    /**
288
     * @param ContainerInterface $container
289
     *
290
     * @return FileSystemInterface
291
     */
292 1
    protected function getFileSystem(ContainerInterface $container): FileSystemInterface
293
    {
294 1
        return $container->get(FileSystemInterface::class);
295
    }
296
}
297