Completed
Push — master ( bb289e...4630f9 )
by Tom
03:36
created

Config::newCommand()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 8.439
c 0
b 0
f 0
cc 6
eloc 13
nc 5
nop 2
1
<?php
2
/*
3
 * @author Tom Klingenberg <https://github.com/ktomk>
4
 */
5
6
namespace N98\Magento\Application;
7
8
use Composer\Autoload\ClassLoader;
9
use InvalidArgumentException;
10
use N98\Magento\Application;
11
use N98\Util\ArrayFunctions;
12
use N98\Util\BinaryString;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Formatter\OutputFormatter;
15
use Symfony\Component\Console\Input\ArgvInput;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Output\NullOutput;
18
use Symfony\Component\Console\Output\OutputInterface;
19
20
/**
21
 * Class Config
22
 *
23
 * Class representing the application configuration. Created to factor out configuration related application
24
 * functionality from @see \N98\Magento\Application
25
 *
26
 * @package N98\Magento\Application
27
 */
28
class Config
29
{
30
    const PSR_0 = 'PSR-0';
31
    const PSR_4 = 'PSR-4';
32
33
    const COMMAND_CLASS = 'Symfony\Component\Console\Command\Command';
34
35
    /**
36
     * @var array config data
37
     */
38
    private $config = array();
39
40
    /**
41
     * @var array
42
     */
43
    private $partialConfig = array();
44
45
    /**
46
     * @var ConfigurationLoader
47
     */
48
    private $loader;
49
50
    /**
51
     * @var array
52
     */
53
    private $initConfig = array();
54
55
    /**
56
     * @var boolean
57
     */
58
    private $isPharMode;
59
60
    /**
61
     * @var OutputInterface
62
     */
63
    private $output;
64
65
    /**
66
     * Config constructor.
67
     *
68
     * @param array $initConfig
69
     * @param bool $isPharMode
70
     * @param OutputInterface $output [optional]
71
     */
72
    public function __construct(array $initConfig = array(), $isPharMode = false, OutputInterface $output = null)
73
    {
74
        $this->initConfig = $initConfig;
75
        $this->isPharMode = (bool) $isPharMode;
76
        $this->output = $output ?: new NullOutput();
77
    }
78
79
    /**
80
     * alias magerun command in input from config
81
     *
82
     * @param InputInterface $input
83
     * @return ArgvInput|InputInterface
84
     */
85
    public function checkConfigCommandAlias(InputInterface $input)
0 ignored issues
show
Coding Style introduced by
checkConfigCommandAlias uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
86
    {
87
        foreach ($this->getArray(array('commands', 'aliases')) as $alias) {
88
            if (!is_array($alias)) {
89
                continue;
90
            }
91
            $aliasCommandName = key($alias);
92
            if ($input->getFirstArgument() !== $aliasCommandName) {
93
                continue;
94
            }
95
            $aliasCommandParams = array_slice(
96
                BinaryString::trimExplodeEmpty(' ', $alias[$aliasCommandName]),
97
                1
98
            );
99
            if (0 === count($aliasCommandParams)) {
100
                continue;
101
            }
102
103
            // replace command (?) with aliased data
104
            $oldArgv = $_SERVER['argv'];
105
            $newArgv = array_merge(
106
                array_slice($oldArgv, 0, 2),
107
                $aliasCommandParams,
108
                array_slice($oldArgv, 2)
109
            );
110
            $input = new ArgvInput($newArgv);
111
        }
112
113
        return $input;
114
    }
115
116
    /**
117
     * @param Command $command
118
     */
119
    public function registerConfigCommandAlias(Command $command)
120
    {
121
        foreach ($this->getArray(array('commands', 'aliases')) as $alias) {
122
            if (!is_array($alias)) {
123
                continue;
124
            }
125
126
            $aliasCommandName = key($alias);
127
            $commandString = $alias[$aliasCommandName];
128
            list($originalCommand) = explode(' ', $commandString, 2);
129
            if ($command->getName() !== $originalCommand) {
130
                continue;
131
            }
132
133
            $command->setAliases(array_merge($command->getAliases(), array($aliasCommandName)));
134
        }
135
    }
136
137
    /**
138
     * @param Application $application
139
     */
140
    public function registerCustomCommands(Application $application)
141
    {
142
        foreach ($this->getArray(array('commands', 'customCommands')) as $commandClass) {
143
            $commandName = null;
144
            if (is_array($commandClass)) {
145
                // Support for key => value (name -> class)
146
                $commandName = key($commandClass);
147
                $commandClass = current($commandClass);
148
            }
149
            if (null === $command = $this->newCommand($commandClass, $commandName)) {
150
                $this->output->writeln(
151
                    sprintf(
152
                        '<error>Can not add nonexistent command class "%s" as command to the application</error>',
153
                        $commandClass,
154
                        $commandName
155
                    )
156
                );
157
                $this->debugWriteln(
158
                    'Please check the configuration files contain the correct class-name. If the ' .
159
                    'class-name is correct, check autoloader configurations.'
160
                );
161
            } else {
162
                $this->debugWriteln(
163
                    sprintf(
164
                        '<debug>Add command </debug> <info>%s</info> -> <comment>%s</comment>',
165
                        $command->getName(),
166
                        get_class($command)
167
                    )
168
                );
169
                $application->add($command);
170
            }
171
        }
172
    }
173
174
    /**
175
     * @param string $className
176
     * @param string|null $commandName
177
     * @return Command
178
     * @throws InvalidArgumentException
179
     */
180
    private function newCommand($className, $commandName)
181
    {
182
        /** @var Command $command */
183
        if (!(is_string($className) || is_object($className))) {
184
            throw new InvalidArgumentException(
185
                sprintf('Command classname must be string, %s given', gettype($className))
186
            );
187
        }
188
189
        if (!class_exists($className)) {
190
            return null;
191
        }
192
193
        if (false === is_subclass_of($className, self::COMMAND_CLASS, true)) {
194
            throw new InvalidArgumentException(
195
                sprintf('Class "%s" is not a Command (subclass of "%s")', $className, self::COMMAND_CLASS)
196
            );
197
        }
198
199
        $command = new $className();
200
        if (null !== $commandName) {
201
            $command->setName($commandName);
202
        }
203
204
        return $command;
205
    }
206
207
    /**
208
     * Adds autoloader prefixes from user's config
209
     *
210
     * @param ClassLoader $autoloader
211
     */
212
    public function registerCustomAutoloaders(ClassLoader $autoloader)
213
    {
214
        $mask = '<debug>Registered %s autoloader </debug> <info>%s</info> -> <comment>%s</comment>';
215
216 View Code Duplication
        foreach ($this->getArray('autoloaders') as $prefix => $paths) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
            $paths = (array) $paths;
218
            $this->debugWriteln(sprintf($mask, self::PSR_0, OutputFormatter::escape($prefix), implode(",", $paths)));
219
            $autoloader->add($prefix, $paths);
220
        }
221
222 View Code Duplication
        foreach ($this->getArray('autoloaders_psr4') as $prefix => $paths) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
223
            $paths = (array) $paths;
224
            $this->debugWriteln(sprintf($mask, self::PSR_4, OutputFormatter::escape($prefix), implode(",", $paths)));
225
            $autoloader->addPsr4($prefix, $paths);
226
        }
227
    }
228
229
    /**
230
     * @param array $config
231
     */
232
    public function setConfig(array $config)
233
    {
234
        $this->config = $config;
235
    }
236
237
    /**
238
     * @return array
239
     */
240
    public function getConfig()
241
    {
242
        return $this->config;
243
    }
244
245
    /**
246
     * @param ConfigurationLoader $configurationLoader
247
     */
248
    public function setLoader(ConfigurationLoader $configurationLoader)
249
    {
250
        $this->loader = $configurationLoader;
251
    }
252
253
    /**
254
     * @return ConfigurationLoader
255
     */
256
    public function getLoader()
257
    {
258
        if (!$this->loader) {
259
            $this->loader = $this->createLoader($this->initConfig, $this->isPharMode, $this->output);
260
            $this->initConfig = array();
261
        }
262
263
        return $this->loader;
264
    }
265
266
    public function load()
267
    {
268
        $this->config = $this->getLoader()->toArray();
269
    }
270
271
    /**
272
     * @param bool $loadExternalConfig
273
     */
274
    public function loadPartialConfig($loadExternalConfig)
275
    {
276
        $loader = $this->getLoader();
277
        $this->partialConfig = $loader->getPartialConfig($loadExternalConfig);
278
    }
279
280
    /**
281
     * Get names of sub-folders to be scanned during Magento detection
282
     *
283
     * @return array
284
     */
285
    public function getDetectSubFolders()
286
    {
287
        if (isset($this->partialConfig['detect']['subFolders'])) {
288
            return $this->partialConfig['detect']['subFolders'];
289
        }
290
291
        return array();
292
    }
293
294
    /**
295
     * @param array $initConfig
296
     * @param bool $isPharMode
297
     * @param OutputInterface $output
298
     *
299
     * @return ConfigurationLoader
300
     */
301
    public function createLoader(array $initConfig, $isPharMode, OutputInterface $output)
302
    {
303
        $config = ArrayFunctions::mergeArrays($this->config, $initConfig);
304
305
        $loader = new ConfigurationLoader($config, $isPharMode, $output);
306
307
        return $loader;
308
    }
309
310
    /**
311
     * @param string $message
312
     */
313
    private function debugWriteln($message)
314
    {
315
        $output = $this->output;
316
        if (OutputInterface::VERBOSITY_DEBUG <= $output->getVerbosity()) {
317
            $output->writeln($message);
318
        }
319
    }
320
321
    /**
322
     * Get array from config, default to an empty array if not set
323
     *
324
     * @param string|array $key
325
     * @param array $default [optional]
326
     * @return array
327
     */
328
    private function getArray($key, $default = array())
329
    {
330
        $result = $this->traverse((array) $key);
331
        if (null === $result) {
332
            return $default;
333
        }
334
335
        return $result;
336
    }
337
338
    private function traverse(array $keys)
339
    {
340
        $anchor = &$this->config;
341
        foreach ($keys as $key) {
342
            if (!is_array($anchor)) {
343
                return;
344
            }
345
346
            if (!isset($anchor[$key])) {
347
                return;
348
            }
349
            $anchor = &$anchor[$key];
350
        }
351
352
        return $anchor;
353
    }
354
}
355