Completed
Pull Request — 3.1 (#290)
by
unknown
09:17
created

Reader   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 197
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 26
lcom 1
cbo 4
dl 0
loc 197
rs 10
c 1
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
C findSubContextClasses() 0 40 11
A __construct() 0 4 1
A registerContextReader() 0 3 1
A supportsEnvironment() 0 3 1
B readEnvironmentCallees() 0 28 4
A readContextCallees() 0 12 2
A findAvailableSubContexts() 0 20 3
A loadSubContexts() 0 10 3
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 RecursiveDirectoryIterator;
17
use RecursiveIteratorIterator;
18
use RegexIterator;
19
20
/**
21
 * Read in additional contexts provided by core and contrib.
22
 */
23
final class Reader implements EnvironmentReader {
24
25
  /**
26
   * @var ContextReader[]
27
   */
28
  private $contextReaders = array();
29
30
  /**
31
   * Drupal driver manager.
32
   *
33
   * @var \Drupal\DrupalDriverManager
34
   */
35
  private $drupal;
36
37
  /**
38
   * Configuration parameters for this suite.
39
   */
40
  private $parameters;
41
42
  /**
43
   * Statically cached lists of subcontexts by path.
44
   *
45
   * @var array
46
   */
47
  static protected $subContexts;
48
49
  /**
50
   * Register the Drupal driver manager.
51
   */
52
  public function __construct(DrupalDriverManager $drupal, array $parameters) {
53
    $this->drupal = $drupal;
54
    $this->parameters = $parameters;
55
  }
56
57
  /**
58
   * Registers context loader.
59
   *
60
   * @param ContextReader $contextReader
61
   */
62
  public function registerContextReader(ContextReader $contextReader) {
63
    $this->contextReaders[] = $contextReader;
64
  }
65
66
  /**
67
   * {@inheritdoc}
68
   */
69
  public function supportsEnvironment(Environment $environment) {
70
    return $environment instanceof ContextEnvironment;
71
  }
72
73
  /**
74
   * {@inheritdoc}
75
   */
76
  public function readEnvironmentCallees(Environment $environment) {
77
78
    if (!$environment instanceof ContextEnvironment) {
79
      throw new EnvironmentReadException(sprintf(
80
          'ContextEnvironmentReader does not support `%s` environment.',
81
          get_class($environment)
82
        ), $environment);
83
    }
84
85
    $callees = array();
86
    if (!$environment instanceof UninitializedContextEnvironment) {
87
      return $callees;
88
    }
89
90
    $contextClasses = $this->findSubContextClasses();
91
92
    foreach ($contextClasses as $contextClass) {
93
      $callees = array_merge(
94
        $callees,
95
        $this->readContextCallees($environment, $contextClass)
96
      );
97
98
      // Register context.
99
      $environment->registerContextClass($contextClass, array($this->drupal));
100
    }
101
102
    return $callees;
103
  }
104
105
    /**
106
     * Reads callees from a specific suite's context.
107
     *
108
     * @param ContextEnvironment $environment
109
     * @param string             $contextClass
110
     *
111
     * @return Callee[]
112
     */
113
    private function readContextCallees(ContextEnvironment $environment, $contextClass)
114
    {
115
        $callees = array();
116
        foreach ($this->contextReaders as $loader) {
117
            $callees = array_merge(
118
                $callees,
119
                $loader->readContextCallees($environment, $contextClass)
120
            );
121
        }
122
123
        return $callees;
124
    }
125
126
  /**
127
   * Finds and loads available subcontext classes.
128
   */
129
  private function findSubContextClasses() {
130
    $class_names = array();
131
132
    // Initialize any available sub-contexts.
133
    if (isset($this->parameters['subcontexts'])) {
134
      $paths = array();
135
      // Drivers may specify paths to subcontexts.
136
      if ($this->parameters['subcontexts']['autoload']) {
137
        foreach ($this->drupal->getDrivers() as $name => $driver) {
138
          if ($driver instanceof SubDriverFinderInterface) {
139
            $paths += $driver->getSubDriverPaths();
140
          }
141
        }
142
      }
143
144
      // Additional subcontext locations may be specified manually in behat.yml.
145
      if (isset($this->parameters['subcontexts']['paths'])) {
146
        $paths = array_merge($paths, $this->parameters['subcontexts']['paths']);
147
      }
148
149
      // Load each class.
150
      foreach ($paths as $path) {
151
        if ($subcontexts = $this->findAvailableSubContexts($path)) {
152
          $this->loadSubContexts($subcontexts);
153
        }
154
      }
155
156
      // Find all subcontexts, excluding abstract base classes.
157
      $classes = get_declared_classes();
158
      foreach ($classes as $class) {
159
        $reflect = new \ReflectionClass($class);
160
        if (!$reflect->isAbstract() && $reflect->implementsInterface('Drupal\DrupalExtension\Context\DrupalSubContextInterface')) {
161
          $class_names[] = $class;
162
        }
163
      }
164
165
    }
166
167
    return $class_names;
168
  }
169
170
  /**
171
   * Find Sub-contexts matching a given pattern located at the passed path.
172
   *
173
   * @param string $path
174
   *   Absolute path to the directory to search for sub-contexts.
175
   * @param string $pattern
176
   *   File pattern to match. Defaults to `/^.+\.behat\.inc/i`.
177
   *
178
   * @return array
179
   *   An array of paths.
180
   */
181
  private function findAvailableSubContexts($path, $pattern = '/^.+\.behat\.inc/i') {
182
183
    if (isset(static::$subContexts[$pattern][$path])) {
184
      return static::$subContexts[$pattern][$path];
185
    }
186
187
    static::$subContexts[$pattern][$path] = array();
188
189
    $fileIterator = new RegexIterator(
190
      new RecursiveIteratorIterator(
191
        new RecursiveDirectoryIterator($path)
192
      ), $pattern,
193
      RegexIterator::MATCH
194
    );
195
    foreach ($fileIterator as $found) {
196
      static::$subContexts[$pattern][$path][$found->getRealPath()] = $found->getFileName();
197
    }
198
199
    return static::$subContexts[$pattern][$path];
200
  }
201
202
  /**
203
   * Load each subcontext file.
204
   *
205
   * @param array $subcontexts
206
   *   An array of files to include.
207
   */
208
  private function loadSubContexts($subcontexts) {
209
    foreach ($subcontexts as $path => $subcontext) {
210
      if (!file_exists($path)) {
211
        throw new \RuntimeException(sprintf('Subcontext path %s path does not exist.', $path));
212
      }
213
214
      // Load file.
215
      require_once $path;
216
    }
217
  }
218
219
}
220