Security update for permissions_by_term
[yaffs-website] / vendor / drupal / drupal-driver / src / Drupal / Driver / DrushDriver.php
1 <?php
2
3 namespace Drupal\Driver;
4
5 use Drupal\Component\Utility\Random;
6 use Drupal\Driver\Exception\BootstrapException;
7
8 use Symfony\Component\Process\Process;
9
10 /**
11  * Implements DriverInterface.
12  */
13 class DrushDriver extends BaseDriver {
14   /**
15    * Store a drush alias for tests requiring shell access.
16    *
17    * @var string
18    */
19   public $alias;
20
21   /**
22    * Stores the root path to a Drupal installation.
23    *
24    * This is an alternative to using drush aliases.
25    *
26    * @var string
27    */
28   public $root;
29
30   /**
31    * Store the path to drush binary.
32    *
33    * @var string
34    */
35   public $binary;
36
37   /**
38    * Track bootstrapping.
39    */
40   private $bootstrapped = FALSE;
41
42   /**
43    * Random generator.
44    *
45    * @var \Drupal\Component\Utility\Random
46    */
47   private $random;
48
49   /**
50    * Global arguments or options for drush commands.
51    *
52    * @var string
53    */
54   private $arguments = '';
55
56   /**
57    * Set drush alias or root path.
58    *
59    * @param string $alias
60    *   A drush alias.
61    * @param string $root_path
62    *   The root path of the Drupal install. This is an alternative to using
63    *   aliases.
64    * @param string $binary
65    *   The path to the drush binary.
66    * @param \Drupal\Component\Utility\Random $random
67    *   Random generator.
68    *
69    * @throws \Drupal\Driver\Exception\BootstrapException
70    *   Thrown when a required parameter is missing.
71    */
72   public function __construct($alias = NULL, $root_path = NULL, $binary = 'drush', Random $random = NULL) {
73     if (!empty($alias)) {
74       // Trim off the '@' symbol if it has been added.
75       $alias = ltrim($alias, '@');
76
77       $this->alias = $alias;
78     }
79     elseif (!empty($root_path)) {
80       $this->root = realpath($root_path);
81     }
82     else {
83       throw new BootstrapException('A drush alias or root path is required.');
84     }
85
86     $this->binary = $binary;
87
88     if (!isset($random)) {
89       $random = new Random();
90     }
91     $this->random = $random;
92   }
93
94   /**
95    * {@inheritdoc}
96    */
97   public function getRandom() {
98     return $this->random;
99   }
100
101   /**
102    * {@inheritdoc}
103    */
104   public function bootstrap() {
105     // Check that the given alias works.
106     // @todo check that this is a functioning alias.
107     // See http://drupal.org/node/1615450
108     if (!isset($this->alias) && !isset($this->root)) {
109       throw new BootstrapException('A drush alias or root path is required.');
110     }
111     $this->bootstrapped = TRUE;
112   }
113
114   /**
115    * {@inheritdoc}
116    */
117   public function isBootstrapped() {
118     return $this->bootstrapped;
119   }
120
121   /**
122    * {@inheritdoc}
123    */
124   public function userCreate(\stdClass $user) {
125     $arguments = array(
126       sprintf('"%s"', $user->name),
127     );
128     $options = array(
129       'password' => $user->pass,
130       'mail' => $user->mail,
131     );
132     $this->drush('user-create', $arguments, $options);
133     if (isset($user->roles) && is_array($user->roles)) {
134       foreach ($user->roles as $role) {
135         $this->userAddRole($user, $role);
136       }
137     }
138   }
139
140   /**
141    * {@inheritdoc}
142    */
143   public function userDelete(\stdClass $user) {
144     $arguments = array(sprintf('"%s"', $user->name));
145     $options = array(
146       'yes' => NULL,
147       'delete-content' => NULL,
148     );
149     $this->drush('user-cancel', $arguments, $options);
150   }
151
152   /**
153    * {@inheritdoc}
154    */
155   public function userAddRole(\stdClass $user, $role) {
156     $arguments = array(
157       sprintf('"%s"', $role),
158       sprintf('"%s"', $user->name),
159     );
160     $this->drush('user-add-role', $arguments);
161   }
162
163   /**
164    * {@inheritdoc}
165    */
166   public function fetchWatchdog($count = 10, $type = NULL, $severity = NULL) {
167     $options = array(
168       'count' => $count,
169       'type' => $type,
170       'severity' => $severity,
171     );
172     return $this->drush('watchdog-show', array(), $options);
173   }
174
175   /**
176    * {@inheritdoc}
177    */
178   public function clearCache($type = 'all') {
179     $type = array($type);
180     return $this->drush('cache-clear', $type, array());
181   }
182
183   /**
184    * {@inheritdoc}
185    */
186   public function clearStaticCaches() {
187     // The drush driver does each operation as a separate request;
188     // therefore, 'clearStaticCaches' can be a no-op.
189   }
190
191   /**
192    * Decodes JSON object returned by Drush.
193    *
194    * It will clean up any junk that may have appeared before or after the
195    * JSON object. This can happen with remote Drush aliases.
196    *
197    * @param string $output
198    *   The output from Drush.
199    * @return object
200    *   The decoded JSON object.
201    */
202   protected function decodeJsonObject($output) {
203     // Remove anything before the first '{'.
204     $output = preg_replace('/^[^\{]*/', '', $output);
205     // Remove anything after the last '}'.
206     $output = preg_replace('/[^\}]*$/s', '', $output);
207     return json_decode($output);
208   }
209
210   /**
211    * {@inheritdoc}
212    */
213   public function createNode($node) {
214     $result = $this->drush('behat', array('create-node', escapeshellarg(json_encode($node))), array());
215     return $this->decodeJsonObject($result);
216   }
217
218   /**
219    * {@inheritdoc}
220    */
221   public function nodeDelete($node) {
222     $this->drush('behat', array('delete-node', escapeshellarg(json_encode($node))), array());
223   }
224
225   /**
226    * {@inheritdoc}
227    */
228   public function createTerm(\stdClass $term) {
229     $result = $this->drush('behat', array('create-term', escapeshellarg(json_encode($term))), array());
230     return $this->decodeJsonObject($result);
231   }
232
233   /**
234    * {@inheritdoc}
235    */
236   public function termDelete(\stdClass $term) {
237     $this->drush('behat', array('delete-term', escapeshellarg(json_encode($term))), array());
238   }
239
240   /**
241    * {@inheritdoc}
242    */
243   public function isField($entity_type, $field_name) {
244     // If the Behat Drush Endpoint is not installed on the site-under-test,
245     // then the drush() method will throw an exception. In this instance, we
246     // want to treat all potential fields as non-fields.  This allows the
247     // Drush Driver to work with certain built-in Drush capabilities (e.g.
248     // creating users) even if the Behat Drush Endpoint is not available.
249     try {
250       $result = $this->drush('behat', array('is-field', escapeshellarg(json_encode(array($entity_type, $field_name)))), array());
251       return strpos($result, "true\n") !== FALSE;
252     }
253     catch (\Exception $e) {
254       return FALSE;
255     }
256   }
257
258   /**
259    * Sets common drush arguments or options.
260    *
261    * @param string $arguments
262    *   Global arguments to add to every drush command.
263    */
264   public function setArguments($arguments) {
265     $this->arguments = $arguments;
266   }
267
268   /**
269    * Get common drush arguments.
270    */
271   public function getArguments() {
272     return $this->arguments;
273   }
274
275   /**
276    * Parse arguments into a string.
277    *
278    * @param array $arguments
279    *   An array of argument/option names to values.
280    *
281    * @return string
282    *   The parsed arguments.
283    */
284   protected static function parseArguments(array $arguments) {
285     $string = '';
286     foreach ($arguments as $name => $value) {
287       if (is_null($value)) {
288         $string .= ' --' . $name;
289       }
290       else {
291         $string .= ' --' . $name . '=' . $value;
292       }
293     }
294     return $string;
295   }
296
297   /**
298    * Execute a drush command.
299    */
300   public function drush($command, array $arguments = array(), array $options = array()) {
301     $arguments = implode(' ', $arguments);
302
303     // Disable colored output from drush.
304     $options['nocolor'] = TRUE;
305     $string_options = $this->parseArguments($options);
306
307     $alias = isset($this->alias) ? "@{$this->alias}" : '--root=' . $this->root;
308
309     // Add any global arguments.
310     $global = $this->getArguments();
311
312     $process = new Process("{$this->binary} {$alias} {$string_options} {$global} {$command} {$arguments}");
313     $process->setTimeout(3600);
314     $process->run();
315
316     if (!$process->isSuccessful()) {
317       throw new \RuntimeException($process->getErrorOutput());
318     }
319
320     // Some drush commands write to standard error output (for example enable
321     // use drush_log which default to _drush_print_log) instead of returning a
322     // string (drush status use drush_print_pipe).
323     if (!$process->getOutput()) {
324       return $process->getErrorOutput();
325     }
326     else {
327       return $process->getOutput();
328     }
329
330   }
331
332   /**
333    * {@inheritdoc}
334    */
335   public function processBatch() {
336     // Do nothing. Drush should internally handle any needs for processing
337     // batch ops.
338   }
339
340   /**
341    * {@inheritdoc}
342    */
343   public function runCron() {
344     $this->drush('cron');
345   }
346
347   /**
348    * Run Drush commands dynamically from a DrupalContext.
349    */
350   public function __call($name, $arguments) {
351     return $this->drush($name, $arguments);
352   }
353
354 }