EnvironmentLoader::addEnvironmentReader()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1.0013

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 8
cts 9
cp 0.8889
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
crap 1.0013
1
<?php
2
/**
3
 * @author Sergii Bondarenko, <[email protected]>
4
 */
5
namespace Behat;
6
7
// Behat extension interface.
8
use Behat\Testwork\ServiceContainer\Extension;
9
// Tools for dependency injection.
10
use Symfony\Component\DependencyInjection\Definition;
11
use Symfony\Component\DependencyInjection\ContainerBuilder;
12
// Behat context extension and tools.
13
use Behat\Behat\Context\ServiceContainer\ContextExtension;
14
use Behat\Behat\Context\Initializer\ContextInitializer;
15
// Behat environment extension and tools.
16
use Behat\Testwork\Environment\ServiceContainer\EnvironmentExtension;
17
use Behat\Testwork\Environment\Reader\EnvironmentReader as EnvironmentExtensionReader;
18
19
/**
20
 * Class EnvironmentLoader.
21
 *
22
 * @package Behat
23
 */
24
final class EnvironmentLoader
25
{
26
    /**
27
     * DI container of the extension.
28
     *
29
     * @var ContainerBuilder
30
     */
31
    private $container;
32
    /**
33
     * Path of the extension.
34
     *
35
     * @var string
36
     */
37
    private $path = '';
38
    /**
39
     * Namespace of the extension.
40
     *
41
     * @var string
42
     */
43
    private $namespace = '';
44
    /**
45
     * Configuration key of the extension in a lowercase.
46
     *
47
     * @var string
48
     */
49
    private $configKey = '';
50
    /**
51
     * Formula: <EXTENSION_NAMESPACE>\<EXTENSION_SUB_NAMESPACE>\<EXTENSION_CONFIG_KEY>.
52
     *
53
     * @var string
54
     */
55
    private $classPath = '';
56
    /**
57
     * Definitions for extending container.
58
     *
59
     * @var Definition[]
60
     */
61
    private $definitions = [];
62
63
    /**
64
     * EnvironmentLoader constructor.
65
     *
66
     * @param Extension $extension
67
     * @param ContainerBuilder $container
68
     * @param array $config
69
     */
70 20
    public function __construct(Extension $extension, ContainerBuilder $container, array $config = [])
71
    {
72 20
        $reflection = new \ReflectionClass($extension);
73
        // Remove the "ServiceContainer" from the namespace of the extension object.
74 20
        $this->namespace = rtrim(str_replace('ServiceContainer', '', $reflection->getNamespaceName()), '\\');
75
        // Remove the name of file and "ServiceContainer" from the path to extension.
76 20
        $this->path = implode(DIRECTORY_SEPARATOR, array_slice(explode(DIRECTORY_SEPARATOR, $reflection->getFileName()), 0, -2));
77 20
        $this->container = $container;
78
        // To not care about string format.
79 20
        $this->configKey = strtolower($extension->getConfigKey());
80
        // Formula: <EXTENSION_NAMESPACE>\<EXTENSION_SUB_NAMESPACE>\<EXTENSION_CONFIG_KEY>.
81 20
        $this->classPath = implode('\\', [
82 20
            $this->namespace,
83
            // Placeholder for sub-namespace of the extension.
84 20
            '%s',
85
            // - Replace all dots, underscores and dashes by dot in the config key of extension.
86
            // - Divide the result array by a dot.
87
            // - Start every string in the result array from a capital letter.
88
            // - Convert an array to string without separators.
89 20
            implode(array_map('ucfirst', explode('.', str_replace(['.', '_', '-'], '.', $this->configKey)))),
90 20
        ]);
91
92
        /** @see EnvironmentReader::__construct() */
93 20
        $this->extendContainer(EnvironmentExtension::READER_TAG, new Definition(
94 20
            sprintf('%s\EnvironmentReader', __NAMESPACE__),
95 20
            [$this->path, $this->namespace]
96 20
        ), 'behat');
97
98 20
        $this->addDefinition(
99 20
            'Context',
100 20
            'Initializer',
101 10
            ContextInitializer::class,
102 10
            ContextExtension::INITIALIZER_TAG,
103 10
            [$config, $this->namespace, $this->path]
104 10
        );
105 10
    }
106
107
    /**
108
     * Implement extension's own environment reader.
109
     *
110
     * @param array $arguments
111
     */
112 2
    public function addEnvironmentReader(array $arguments = [])
113
    {
114
        // Full namespace: <EXTENSION_NAMESPACE>\Environment\<EXTENSION_CONFIG_KEY>EnvironmentReader.
115
        // For example we have registered extension at namespace: "Behat\TqExtension". Class, which
116
        // implements extension interface, located at "Behat\TqExtension\ServiceContainer\TqExtension"
117
        // and its method, "getConfigKey()", returns the "tq" string. In this case the full namespace
118
        // of the reader object will be: "Behat\TqExtension\Environment\TqEnvironmentReader" and its
119
        // constructor will have a set of arguments that were passed to this method.
120 2
        $this->addDefinition(
121 2
            'Environment',
122 2
            'Reader',
123 2
            EnvironmentExtensionReader::class,
124 2
            EnvironmentExtension::READER_TAG,
125 2
            array_merge([$this->namespace, $this->path], $arguments)
126 2
        );
127
    }
128
129
    /**
130
     * Extend DI container by dependencies.
131
     */
132 2
    public function load()
133
    {
134 2
        foreach ($this->definitions as $tag => $definition) {
135 2
            $this->extendContainer($tag, $definition);
136 2
        }
137 2
    }
138
139
    /**
140
     * Add dependency definition for DI container.
141
     *
142
     * @param string $subNamespace
143
     * @param string $objectType
144
     * @param string $interface
145
     * @param string $tag
146
     * @param array $arguments
147
     */
148 10
    private function addDefinition($subNamespace, $objectType, $interface, $tag, array $arguments = [])
149
    {
150 10
        $class = sprintf($this->classPath, $subNamespace) . $subNamespace . $objectType;
151
152 10
        if (!class_exists($class)) {
153 2
            throw new \RuntimeException(sprintf('Class "%s" does not exists!', $class));
154
        }
155
156 10
        if (!in_array($interface, class_implements($class))) {
157
            throw new \RuntimeException(sprintf('Class "%s" must implement the "%s" interface!', $class, $interface));
158
        }
159
160 10
        $this->definitions[$tag] = new Definition($class, $arguments);
161 10
    }
162
163
    /**
164
     * Add dependency to DI container.
165
     *
166
     * @param string $tag
167
     * @param Definition $definition
168
     * @param string $identifier
169
     */
170 10
    private function extendContainer($tag, Definition $definition, $identifier = '')
171
    {
172 10
        if ('' !== $identifier) {
173 10
            $identifier .= '.';
174 10
        }
175
176 10
        $this->container
177 10
          ->setDefinition("$this->configKey.$identifier$tag", $definition)
178 10
          ->addTag($tag);
179 10
    }
180
}
181