Completed
Push — master ( 2d0949...d58be5 )
by Neomerx
08:30
created

ApplicationCommand::createFileSystem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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
    public static function getName(): string
46
    {
47
        return 'l:app';
48
    }
49
50
    /**
51
     * @inheritdoc
52
     */
53
    public static function getDescription(): string
54
    {
55
        return 'Creates and cleans application cache.';
56
    }
57
58
    /**
59
     * @inheritdoc
60
     */
61
    public static function getHelp(): string
62
    {
63
        return 'This command creates and cleans caches for routes, settings and etc.';
64
    }
65
66
    /**
67
     * @inheritdoc
68
     */
69
    public static function getArguments(): array
70
    {
71
        $cache = static::ACTION_CREATE_CACHE;
72
        $clear = static::ACTION_CLEAR_CACHE;
73
74
        return [
75
            [
76
                static::ARGUMENT_NAME        => static::ARG_ACTION,
77
                static::ARGUMENT_DESCRIPTION => "Action such as `$cache` or `$clear`.",
78
                static::ARGUMENT_MODE        => static::ARGUMENT_MODE__REQUIRED,
79
            ],
80
        ];
81
    }
82
83
    /**
84
     * @inheritdoc
85
     */
86
    public static function getOptions(): array
87
    {
88
        return [];
89
    }
90
91
    /**
92
     * @inheritdoc
93
     */
94
    public static function execute(ContainerInterface $container, IoInterface $inOut)
95
    {
96
        (new static())->run($container, $inOut);
97
    }
98
99
    /**
100
     * @param ContainerInterface $container
101
     * @param IoInterface        $inOut
102
     *
103
     * @return void
104
     */
105
    protected function run(ContainerInterface $container, IoInterface $inOut)
106
    {
107
        $action = $inOut->getArgument(static::ARG_ACTION);
108
        switch ($action) {
109
            case static::ACTION_CREATE_CACHE:
110
                $this->executeCache($container, $inOut);
111
                break;
112
            case static::ACTION_CLEAR_CACHE:
113
                $this->executeClear($container, $inOut);
114
                break;
115
            default:
116
                $inOut->writeError("Unsupported action `$action`." . PHP_EOL);
117
                break;
118
        }
119
    }
120
121
    /**
122
     * @param ContainerInterface $container
123
     * @param IoInterface        $inOut
124
     *
125
     * @return void
126
     */
127
    protected function executeClear(ContainerInterface $container, IoInterface $inOut)
128
    {
129
        assert($inOut);
130
131
        $appSettings    = $this->getApplicationSettings($container);
132
        $cacheDir       = $appSettings[ApplicationSettingsInterface::KEY_CACHE_FOLDER];
133
        $cacheCallable  = $appSettings[ApplicationSettingsInterface::KEY_CACHE_CALLABLE];
134
        list (, $class) = $this->parseCacheCallable($cacheCallable);
135
136
        if ($class === null) {
137
            // parsing of cache callable failed (most likely error in settings)
138
            throw new ConfigurationException();
139
        }
140
141
        $path = $cacheDir . DIRECTORY_SEPARATOR . $class . '.php';
142
143
        $this->getFileSystem($container)->delete($path);
144
    }
145
146
    /**
147
     * @param ContainerInterface $container
148
     * @param IoInterface        $inOut
149
     *
150
     * @return void
151
     */
152
    protected function executeCache(ContainerInterface $container, IoInterface $inOut)
153
    {
154
        assert($inOut);
155
156
        $appSettings   = $this->getApplicationSettings($container);
157
        $cacheDir      = $appSettings[ApplicationSettingsInterface::KEY_CACHE_FOLDER];
158
        $cacheCallable = $appSettings[ApplicationSettingsInterface::KEY_CACHE_CALLABLE];
159
        list ($namespace, $class, $method) = $this->parseCacheCallable($cacheCallable);
160
        if ($class === null || $namespace === null || $method === null) {
161
            // parsing of cache callable failed (most likely error in settings)
162
            throw new ConfigurationException();
163
        }
164
165
        /** @var CacheSettingsProviderInterface $settingsProvider */
166
        $settingsProvider = $container->get(CacheSettingsProviderInterface::class);
167
        $settingsData     = $settingsProvider->serialize();
168
        $content          = $this->composeContent($settingsData, $namespace, $class, $method);
169
170
        $path = $cacheDir . DIRECTORY_SEPARATOR . $class . '.php';
171
        $this->getFileSystem($container)->write($path, $content);
172
    }
173
174
    /**
175
     * @param mixed $mightBeCallable
176
     *
177
     * @return array
178
     *
179
     * @SuppressWarnings(PHPMD.ElseExpression)
180
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
181
     */
182
    protected function parseCacheCallable($mightBeCallable): array
183
    {
184
        if (is_string($mightBeCallable) === true &&
185
            count($nsClassMethod = explode('::', $mightBeCallable, 2)) === 2 &&
186
            count($nsClass = explode('\\', $nsClassMethod[0])) > 1
187
        ) {
188
            $canBeClass     = array_pop($nsClass);
189
            $canBeNamespace = array_filter($nsClass);
190
            $canBeMethod    = $nsClassMethod[1];
191
        } elseif (is_array($mightBeCallable) === true &&
192
            count($mightBeCallable) === 2 &&
193
            count($nsClass = explode('\\', $mightBeCallable[0])) > 1
194
        ) {
195
            $canBeClass     = array_pop($nsClass);
196
            $canBeNamespace = array_filter($nsClass);
197
            $canBeMethod    = $mightBeCallable[1];
198
        } else {
199
            return [null, null, null];
200
        }
201
202
        foreach (array_merge($canBeNamespace, [$canBeClass, $canBeMethod]) as $value) {
203
            // is string might have a-z, A-Z, _, numbers but has at least one a-z or A-Z.
204
            if (is_string($value) === false ||
205
                preg_match('/^\\w+$/i', $value) !== 1 ||
206
                preg_match('/^[a-z]+$/i', $value) !== 1
207
            ) {
208
                return [null, null, null];
209
            }
210
        }
211
212
        $namespace = implode('\\', $canBeNamespace);
213
        $class     = $canBeClass;
214
        $method    = $canBeMethod;
215
216
        return [$namespace, $class, $method];
217
    }
218
219
    /**
220
     * @param mixed  $value
221
     * @param string $className
222
     * @param string $methodName
223
     * @param string $namespace
224
     *
225
     * @return string
226
     */
227
    protected function composeContent(
228
        $value,
229
        string $namespace,
230
        string $className,
231
        string $methodName
232
    ): string {
233
        $now     = date(DATE_RFC2822);
234
        $data    = var_export($value, true);
235
        $content = <<<EOT
236
<?php namespace $namespace;
237
238
// THIS FILE IS AUTO GENERATED. DO NOT EDIT IT MANUALLY.
239
// Generated at: $now
240
241
class $className
242
{
243
    const DATA = $data;
244
245
    public static function $methodName()
246
    {
247
        return static::DATA;
248
    }
249
}
250
251
EOT;
252
253
        return $content;
254
    }
255
256
    /**
257
     * @param ContainerInterface $container
258
     *
259
     * @return array
260
     */
261
    protected function getApplicationSettings(ContainerInterface $container): array
262
    {
263
        /** @var SettingsProviderInterface $settingsProvider */
264
        $settingsProvider = $container->get(SettingsProviderInterface::class);
265
        $appSettings      = $settingsProvider->get(ApplicationSettingsInterface::class);
266
267
        return $appSettings;
268
    }
269
270
    /**
271
     * @param ContainerInterface $container
272
     *
273
     * @return FileSystemInterface
274
     */
275
    protected function getFileSystem(ContainerInterface $container): FileSystemInterface
276
    {
277
        return $container->get(FileSystemInterface::class);
278
    }
279
}
280