Completed
Push — master ( 6445a9...a23b10 )
by Jonathan
19s queued 10s
created

Reader::findSubContextClasses()   C

Complexity

Conditions 12
Paths 7

Size

Total Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 47
rs 6.9666
c 0
b 0
f 0
cc 12
nc 7
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Drupal\DrupalExtension\Context\Environment\Reader;
4
5
use Behat\Behat\Context\Environment\UninitializedContextEnvironment;
6
use Behat\Behat\Context\Environment\ContextEnvironment;
7
use Behat\Behat\Context\Reader\ContextReader;
8
use Behat\Testwork\Call\Callee;
9
use Behat\Testwork\Environment\Environment;
10
use Behat\Testwork\Environment\Exception\EnvironmentReadException;
11
use Behat\Testwork\Environment\Reader\EnvironmentReader;
12
13
use Drupal\DrupalDriverManager;
14
use Drupal\Driver\SubDriverFinderInterface;
15
16
use Drupal\DrupalExtension\Context\DrupalSubContextInterface;
17
use RecursiveDirectoryIterator;
18
use RecursiveIteratorIterator;
19
use RegexIterator;
20
21
/**
22
 * Read in additional contexts provided by core and contrib.
23
 */
24
final class Reader implements EnvironmentReader
25
{
26
27
  /**
28
   * @var ContextReader[]
29
   */
30
    private $contextReaders = array();
31
32
  /**
33
   * Drupal driver manager.
34
   *
35
   * @var \Drupal\DrupalDriverManager
36
   */
37
    private $drupal;
38
39
  /**
40
   * Configuration parameters for this suite.
41
   */
42
    private $parameters;
43
44
  /**
45
   * Statically cached lists of subcontexts by path.
46
   *
47
   * @var array
48
   */
49
    static protected $subContexts;
50
51
  /**
52
   * Register the Drupal driver manager.
53
   */
54
    public function __construct(DrupalDriverManager $drupal, array $parameters)
55
    {
56
        $this->drupal = $drupal;
57
        $this->parameters = $parameters;
58
    }
59
60
  /**
61
   * Registers context loader.
62
   *
63
   * @param ContextReader $contextReader
64
   */
65
    public function registerContextReader(ContextReader $contextReader)
66
    {
67
        $this->contextReaders[] = $contextReader;
68
    }
69
70
  /**
71
   * {@inheritdoc}
72
   */
73
    public function supportsEnvironment(Environment $environment)
74
    {
75
        return $environment instanceof ContextEnvironment;
76
    }
77
78
  /**
79
   * {@inheritdoc}
80
   */
81
    public function readEnvironmentCallees(Environment $environment)
82
    {
83
84
        if (!$environment instanceof ContextEnvironment) {
85
            throw new EnvironmentReadException(sprintf(
86
                'ContextEnvironmentReader does not support `%s` environment.',
87
                get_class($environment)
88
            ), $environment);
89
        }
90
91
        $callees = array();
92
        if (!$environment instanceof UninitializedContextEnvironment) {
93
            return $callees;
94
        }
95
96
        $contextClasses = $this->findSubContextClasses();
97
98
        foreach ($contextClasses as $contextClass) {
99
            // When executing test scenarios with an examples table the registering of
100
            // contexts is handled differently in newer version of Behat. Starting
101
            // with Behat 3.2.0 the contexts are already registered, and the callees
102
            // are returned by the default reader.
103
            // Work around this and provide compatibility with Behat 3.1.0 as well as
104
            // 3.2.0 and higher by checking if the class already exists before
105
            // registering it and returning the callees.
106
            // @see https://github.com/Behat/Behat/issues/758
107
            if (!$environment->hasContextClass($contextClass)) {
108
                $callees = array_merge(
109
                    $callees,
110
                    $this->readContextCallees($environment, $contextClass)
111
                );
112
113
                // Register context.
114
                $environment->registerContextClass($contextClass, array($this->drupal));
115
            }
116
        }
117
118
        return $callees;
119
    }
120
121
    /**
122
     * Reads callees from a specific suite's context.
123
     *
124
     * @param ContextEnvironment $environment
125
     * @param string             $contextClass
126
     *
127
     * @return Callee[]
128
     */
129
    private function readContextCallees(ContextEnvironment $environment, $contextClass)
130
    {
131
        $callees = array();
132
        foreach ($this->contextReaders as $loader) {
133
            $callees = array_merge(
134
                $callees,
135
                $loader->readContextCallees($environment, $contextClass)
136
            );
137
        }
138
139
        return $callees;
140
    }
141
142
  /**
143
   * Finds and loads available subcontext classes.
144
   */
145
    private function findSubContextClasses()
146
    {
147
        $class_names = array();
148
149
        // Initialize any available sub-contexts.
150
        if (isset($this->parameters['subcontexts'])) {
151
            $paths = array();
152
            // Drivers may specify paths to subcontexts.
153
            if ($this->parameters['subcontexts']['autoload']) {
154
                foreach ($this->drupal->getDrivers() as $name => $driver) {
155
                    if ($driver instanceof SubDriverFinderInterface) {
156
                        $paths += $driver->getSubDriverPaths();
157
                    }
158
                }
159
            }
160
161
            // Additional subcontext locations may be specified manually in behat.yml.
162
            if (isset($this->parameters['subcontexts']['paths'])) {
163
                if (!empty($this->parameters['subcontexts']['paths'])) {
164
                    @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
165
                        'The `subcontexts.paths` parameter is deprecated in Drupal Behat Extension 4.0.0 and will be removed in 4.1.0. Normal Behat contexts should be used instead and loaded via behat.yml.',
166
                        E_USER_DEPRECATED
167
                    );
168
                }
169
                $paths = array_merge($paths, $this->parameters['subcontexts']['paths']);
170
            }
171
172
            // Load each class.
173
            foreach ($paths as $path) {
174
                if ($subcontexts = $this->findAvailableSubContexts($path)) {
175
                    $this->loadSubContexts($subcontexts);
176
                }
177
            }
178
179
            // Find all subcontexts, excluding abstract base classes.
180
            $classes = get_declared_classes();
181
            foreach ($classes as $class) {
182
                $reflect = new \ReflectionClass($class);
183
                if (!$reflect->isAbstract() && $reflect->implementsInterface(DrupalSubContextInterface::class)) {
184
                    @trigger_error('Sub-contexts are deprecated in Drupal Behat Extension 4.0.0 and will be removed in 4.1.0. Class ' . $class . ' is a subcontext. This logic should be moved to a normal Behat context and loaded via behat.yml.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
185
                    $class_names[] = $class;
186
                }
187
            }
188
        }
189
190
        return $class_names;
191
    }
192
193
  /**
194
   * Find Sub-contexts matching a given pattern located at the passed path.
195
   *
196
   * @param string $path
197
   *   Absolute path to the directory to search for sub-contexts.
198
   * @param string $pattern
199
   *   File pattern to match. Defaults to `/^.+\.behat\.inc/i`.
200
   *
201
   * @return array
202
   *   An array of paths.
203
   */
204
    private function findAvailableSubContexts($path, $pattern = '/^.+\.behat\.inc/i')
205
    {
206
207
        if (isset(static::$subContexts[$pattern][$path])) {
208
            return static::$subContexts[$pattern][$path];
209
        }
210
211
        static::$subContexts[$pattern][$path] = array();
212
213
        $fileIterator = new RegexIterator(
214
            new RecursiveIteratorIterator(
215
                new RecursiveDirectoryIterator($path)
216
            ),
217
            $pattern,
218
            RegexIterator::MATCH
219
        );
220
        foreach ($fileIterator as $found) {
221
            static::$subContexts[$pattern][$path][$found->getRealPath()] = $found->getFileName();
222
        }
223
224
        return static::$subContexts[$pattern][$path];
225
    }
226
227
  /**
228
   * Load each subcontext file.
229
   *
230
   * @param array $subcontexts
231
   *   An array of files to include.
232
   */
233
    private function loadSubContexts($subcontexts)
234
    {
235
        foreach ($subcontexts as $path => $subcontext) {
236
            if (!file_exists($path)) {
237
                throw new \RuntimeException(sprintf('Subcontext path %s path does not exist.', $path));
238
            }
239
240
            // Load file.
241
            require_once $path;
242
        }
243
    }
244
}
245