Security update for permissions_by_term
[yaffs-website] / vendor / drupal / drupal-extension / src / Drupal / DrupalExtension / Context / Environment / Reader / Reader.php
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       // When executing test scenarios with an examples table the registering of
94       // contexts is handled differently in newer version of Behat. Starting
95       // with Behat 3.2.0 the contexts are already registered, and the callees
96       // are returned by the default reader.
97       // Work around this and provide compatibility with Behat 3.1.0 as well as
98       // 3.2.0 and higher by checking if the class already exists before
99       // registering it and returning the callees.
100       // @see https://github.com/Behat/Behat/issues/758
101       if (!$environment->hasContextClass($contextClass)) {
102         $callees = array_merge(
103           $callees,
104           $this->readContextCallees($environment, $contextClass)
105         );
106
107         // Register context.
108         $environment->registerContextClass($contextClass, array($this->drupal));
109       }
110     }
111
112     return $callees;
113   }
114
115     /**
116      * Reads callees from a specific suite's context.
117      *
118      * @param ContextEnvironment $environment
119      * @param string             $contextClass
120      *
121      * @return Callee[]
122      */
123     private function readContextCallees(ContextEnvironment $environment, $contextClass)
124     {
125         $callees = array();
126         foreach ($this->contextReaders as $loader) {
127             $callees = array_merge(
128                 $callees,
129                 $loader->readContextCallees($environment, $contextClass)
130             );
131         }
132
133         return $callees;
134     }
135
136   /**
137    * Finds and loads available subcontext classes.
138    */
139   private function findSubContextClasses() {
140     $class_names = array();
141
142     // Initialize any available sub-contexts.
143     if (isset($this->parameters['subcontexts'])) {
144       $paths = array();
145       // Drivers may specify paths to subcontexts.
146       if ($this->parameters['subcontexts']['autoload']) {
147         foreach ($this->drupal->getDrivers() as $name => $driver) {
148           if ($driver instanceof SubDriverFinderInterface) {
149             $paths += $driver->getSubDriverPaths();
150           }
151         }
152       }
153
154       // Additional subcontext locations may be specified manually in behat.yml.
155       if (isset($this->parameters['subcontexts']['paths'])) {
156         $paths = array_merge($paths, $this->parameters['subcontexts']['paths']);
157       }
158
159       // Load each class.
160       foreach ($paths as $path) {
161         if ($subcontexts = $this->findAvailableSubContexts($path)) {
162           $this->loadSubContexts($subcontexts);
163         }
164       }
165
166       // Find all subcontexts, excluding abstract base classes.
167       $classes = get_declared_classes();
168       foreach ($classes as $class) {
169         $reflect = new \ReflectionClass($class);
170         if (!$reflect->isAbstract() && $reflect->implementsInterface('Drupal\DrupalExtension\Context\DrupalSubContextInterface')) {
171           $class_names[] = $class;
172         }
173       }
174
175     }
176
177     return $class_names;
178   }
179
180   /**
181    * Find Sub-contexts matching a given pattern located at the passed path.
182    *
183    * @param string $path
184    *   Absolute path to the directory to search for sub-contexts.
185    * @param string $pattern
186    *   File pattern to match. Defaults to `/^.+\.behat\.inc/i`.
187    *
188    * @return array
189    *   An array of paths.
190    */
191   private function findAvailableSubContexts($path, $pattern = '/^.+\.behat\.inc/i') {
192
193     if (isset(static::$subContexts[$pattern][$path])) {
194       return static::$subContexts[$pattern][$path];
195     }
196
197     static::$subContexts[$pattern][$path] = array();
198
199     $fileIterator = new RegexIterator(
200       new RecursiveIteratorIterator(
201         new RecursiveDirectoryIterator($path)
202       ), $pattern,
203       RegexIterator::MATCH
204     );
205     foreach ($fileIterator as $found) {
206       static::$subContexts[$pattern][$path][$found->getRealPath()] = $found->getFileName();
207     }
208
209     return static::$subContexts[$pattern][$path];
210   }
211
212   /**
213    * Load each subcontext file.
214    *
215    * @param array $subcontexts
216    *   An array of files to include.
217    */
218   private function loadSubContexts($subcontexts) {
219     foreach ($subcontexts as $path => $subcontext) {
220       if (!file_exists($path)) {
221         throw new \RuntimeException(sprintf('Subcontext path %s path does not exist.', $path));
222       }
223
224       // Load file.
225       require_once $path;
226     }
227   }
228
229 }