Application::loadConfig()   B
last analyzed

Complexity

Conditions 8
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 12
rs 8.4444
cc 8
nc 4
nop 2
1
<?php
2
3
/*
4
 * This file is part of PhpSpec, A php toolset to drive emergent
5
 * design by specification.
6
 *
7
 * (c) Marcello Duarte <[email protected]>
8
 * (c) Konstantin Kudryashov <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Doyo\PhpSpec\CodeCoverage\Context;
15
16
use PhpSpec\Console\ConsoleIO;
17
use PhpSpec\Console\ContainerAssembler;
18
use PhpSpec\Exception\Configuration\InvalidConfigurationException;
19
use PhpSpec\Matcher\Matcher;
20
use PhpSpec\ServiceContainer;
21
use Symfony\Component\Console\Application as BaseApplication;
22
use PhpSpec\ServiceContainer\IndexedServiceContainer;
23
use PhpSpec\Extension;
24
use Symfony\Component\Console\Command\Command;
25
use Symfony\Component\Console\Helper\DebugFormatterHelper;
26
use Symfony\Component\Console\Helper\FormatterHelper;
27
use Symfony\Component\Console\Helper\HelperSet;
28
use Symfony\Component\Console\Helper\ProcessHelper;
29
use Symfony\Component\Console\Helper\QuestionHelper;
30
use Symfony\Component\Console\Input\StringInput;
31
use Symfony\Component\Console\Output\StreamOutput;
32
33
/**
34
 * The command line application entry point
35
 *
36
 * @internal
37
 */
38
final class Application extends BaseApplication
39
{
40
    /**
41
     * @var IndexedServiceContainer
42
     */
43
    private $container;
44
45
    public function __construct($config)
46
    {
47
        $container = new IndexedServiceContainer();
48
        $container->set('console.commands.run', new Command());
49
        $container->set('console.input', new StringInput('run --coverage'));
50
        $container->set('console.output', new StreamOutput(fopen('php://memory','+w')));
0 ignored issues
show
Bug introduced by
It seems like fopen('php://memory', '+w') can also be of type false; however, parameter $stream of Symfony\Component\Consol...amOutput::__construct() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

50
        $container->set('console.output', new StreamOutput(/** @scrutinizer ignore-type */ fopen('php://memory','+w')));
Loading history...
51
        $container->set('cli.input', new StringInput('run --coverage'));
52
        $container->set('cli.output', new StreamOutput(fopen('php://memory','+w')));
53
        $container->set('console.helper_set', $this->getDefaultHelperSet());
54
        $this->loadConfig($container, $config);
55
56
        $assembler = new ContainerAssembler();
57
        $assembler->build($container);
58
        $container->set('console.input', new StringInput('run --coverage'));
59
        $this->container = $container;
60
    }
61
62
    /**
63
     * Gets the default helper set with the helpers that should always be available.
64
     *
65
     * @return HelperSet A HelperSet instance
66
     */
67
    protected function getDefaultHelperSet()
68
    {
69
        return new HelperSet([
70
            new FormatterHelper(),
71
            new DebugFormatterHelper(),
72
            new ProcessHelper(),
73
            new QuestionHelper(),
74
        ]);
75
    }
76
77
    /**
78
     * @throws \RuntimeException
79
     */
80
    protected function loadConfig(IndexedServiceContainer $container, array $config)
81
    {
82
        $this->populateContainerParameters($container, $config);
83
84
        foreach ($config as $key => $val) {
85
            if ('extensions' === $key && \is_array($val)) {
86
                foreach ($val as $class => $extensionConfig) {
87
                    $this->loadExtension($container, $class, $extensionConfig ?: []);
88
                }
89
            }
90
            elseif ('matchers' === $key && \is_array($val)) {
91
                $this->registerCustomMatchers($container, $val);
92
            }
93
        }
94
    }
95
96
    /**
97
     * @return IndexedServiceContainer
98
     */
99
    public function getContainer(): IndexedServiceContainer
100
    {
101
        return $this->container;
102
    }
103
104
    private function registerCustomMatchers(IndexedServiceContainer $container, array $matchersClassnames)
105
    {
106
        foreach ($matchersClassnames as $class) {
107
            $this->ensureIsValidMatcherClass($class);
108
109
            $container->define(sprintf('matchers.%s', $class), function () use ($class) {
110
                return new $class();
111
            }, ['matchers']);
112
        }
113
    }
114
115
    private function ensureIsValidMatcherClass(string $class)
116
    {
117
        if (!class_exists($class)) {
118
            throw new InvalidConfigurationException(sprintf('Custom matcher %s does not exist.', $class));
119
        }
120
121
        if (!is_subclass_of($class, Matcher::class)) {
122
            throw new InvalidConfigurationException(sprintf(
123
                'Custom matcher %s must implement %s interface, but it does not.',
124
                $class,
125
                Matcher::class
126
            ));
127
        }
128
    }
129
130
    private function loadExtension(ServiceContainer $container, string $extensionClass, $config)
131
    {
132
        if (!class_exists($extensionClass)) {
133
            throw new InvalidConfigurationException(sprintf('Extension class `%s` does not exist.', $extensionClass));
134
        }
135
136
        if (!\is_array($config)) {
137
            throw new InvalidConfigurationException('Extension configuration must be an array or null.');
138
        }
139
140
        if (!is_a($extensionClass, Extension::class, true)) {
141
            throw new InvalidConfigurationException(sprintf('Extension class `%s` must implement Extension interface', $extensionClass));
142
        }
143
144
        (new $extensionClass)->load($container, $config);
145
    }
146
147
    private function populateContainerParameters(IndexedServiceContainer $container, array $config)
148
    {
149
        foreach ($config as $key => $val) {
150
            if ('extensions' !== $key && 'matchers' !== $key) {
151
                $container->setParam($key, $val);
152
            }
153
        }
154
    }
155
}
156