Completed
Push — master ( 90f655...0a41f5 )
by Sergii
02:47
created

EnvironmentLoader::addDefinition()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0175

Importance

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