Completed
Pull Request — master (#85)
by
unknown
02:21
created

DrushDriver::nodeDeleteMultiple()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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 userDeleteMultiple(array $uids) {
156
    $this->getCore()->userDeleteMultiple($uids);
0 ignored issues
show
Documentation Bug introduced by
The method getCore does not exist on object<Drupal\Driver\DrushDriver>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
157
  }
158
159
  /**
160
   * {@inheritdoc}
161
   */
162
  public function userAddRole(\stdClass $user, $role) {
163
    $arguments = array(
164
      sprintf('"%s"', $role),
165
      sprintf('"%s"', $user->name),
166
    );
167
    $this->drush('user-add-role', $arguments);
168
  }
169
170
  /**
171
   * {@inheritdoc}
172
   */
173
  public function fetchWatchdog($count = 10, $type = NULL, $severity = NULL) {
174
    $options = array(
175
      'count' => $count,
176
      'type' => $type,
177
      'severity' => $severity,
178
    );
179
    return $this->drush('watchdog-show', array(), $options);
180
  }
181
182
  /**
183
   * {@inheritdoc}
184
   */
185
  public function clearCache($type = 'all') {
186
    $type = array($type);
187
    return $this->drush('cache-clear', $type, array());
188
  }
189
190
  /**
191
   * {@inheritdoc}
192
   */
193
  public function clearStaticCaches() {
194
    // The drush driver does each operation as a separate request;
195
    // therefore, 'clearStaticCaches' can be a no-op.
196
  }
197
198
  /**
199
   * Decodes JSON object returned by Drush.
200
   *
201
   * It will clean up any junk that may have appeared before or after the
202
   * JSON object. This can happen with remote Drush aliases.
203
   *
204
   * @param string $output
205
   *   The output from Drush.
206
   * @return object
207
   *   The decoded JSON object.
208
   */
209
  protected function decodeJsonObject($output) {
210
    // Remove anything before the first '{'.
211
    $output = preg_replace('/^[^\{]*/', '', $output);
212
    // Remove anything after the last '}'.
213
    $output = preg_replace('/[^\}]*$/s', '', $output);
214
    return json_decode($output);
215
  }
216
217
  /**
218
   * {@inheritdoc}
219
   */
220
  public function createNode($node) {
221
    $result = $this->drush('behat', array('create-node', escapeshellarg(json_encode($node))), array());
222
    return $this->decodeJsonObject($result);
223
  }
224
225
  /**
226
   * {@inheritdoc}
227
   */
228
  public function nodeDelete($node) {
229
    $this->drush('behat', array('delete-node', escapeshellarg(json_encode($node))), array());
230
  }
231
232
  /**
233
   * {@inheritdoc}
234
   */
235
  public function nodeDeleteMultiple(array $nids) {
236
    $this->getCore()->nodeDeleteMultiple($nids);
0 ignored issues
show
Documentation Bug introduced by
The method getCore does not exist on object<Drupal\Driver\DrushDriver>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
237
  }
238
239
  /**
240
   * {@inheritdoc}
241
   */
242
  public function createTerm(\stdClass $term) {
243
    $result = $this->drush('behat', array('create-term', escapeshellarg(json_encode($term))), array());
244
    return $this->decodeJsonObject($result);
245
  }
246
247
  /**
248
   * {@inheritdoc}
249
   */
250
  public function termDelete(\stdClass $term) {
251
    $this->drush('behat', array('delete-term', escapeshellarg(json_encode($term))), array());
252
  }
253
254
  /**
255
   * {@inheritdoc}
256
   */
257
  public function isField($entity_type, $field_name) {
258
    // If the Behat Drush Endpoint is not installed on the site-under-test,
259
    // then the drush() method will throw an exception. In this instance, we
260
    // want to treat all potential fields as non-fields.  This allows the
261
    // Drush Driver to work with certain built-in Drush capabilities (e.g.
262
    // creating users) even if the Behat Drush Endpoint is not available.
263
    try {
264
      $result = $this->drush('behat', array(
265
        'is-field',
266
        escapeshellarg(json_encode(array($entity_type, $field_name))),
267
      ),
268
        array()
269
      );
270
      return strpos($result, "true\n") !== FALSE;
271
    }
272
    catch (\Exception $e) {
273
      return FALSE;
274
    }
275
  }
276
277
  /**
278
   * Sets common drush arguments or options.
279
   *
280
   * @param string $arguments
281
   *   Global arguments to add to every drush command.
282
   */
283
  public function setArguments($arguments) {
284
    $this->arguments = $arguments;
285
  }
286
287
  /**
288
   * Get common drush arguments.
289
   */
290
  public function getArguments() {
291
    return $this->arguments;
292
  }
293
294
  /**
295
   * Parse arguments into a string.
296
   *
297
   * @param array $arguments
298
   *   An array of argument/option names to values.
299
   *
300
   * @return string
301
   *   The parsed arguments.
302
   */
303
  protected static function parseArguments(array $arguments) {
304
    $string = '';
305
    foreach ($arguments as $name => $value) {
306
      if (is_null($value)) {
307
        $string .= ' --' . $name;
308
      }
309
      else {
310
        $string .= ' --' . $name . '=' . $value;
311
      }
312
    }
313
    return $string;
314
  }
315
316
  /**
317
   * Execute a drush command.
318
   */
319
  public function drush($command, array $arguments = array(), array $options = array()) {
320
    $arguments = implode(' ', $arguments);
321
322
    // Disable colored output from drush.
323
    $options['nocolor'] = TRUE;
324
    $string_options = $this->parseArguments($options);
325
326
    $alias = isset($this->alias) ? "@{$this->alias}" : '--root=' . $this->root;
327
328
    // Add any global arguments.
329
    $global = $this->getArguments();
330
331
    $process = new Process("{$this->binary} {$alias} {$string_options} {$global} {$command} {$arguments}");
332
    $process->setTimeout(3600);
333
    $process->run();
334
335
    if (!$process->isSuccessful()) {
336
      throw new \RuntimeException($process->getErrorOutput());
337
    }
338
339
    // Some drush commands write to standard error output (for example enable
340
    // use drush_log which default to _drush_print_log) instead of returning a
341
    // string (drush status use drush_print_pipe).
342
    if (!$process->getOutput()) {
343
      return $process->getErrorOutput();
344
    }
345
    else {
346
      return $process->getOutput();
347
    }
348
349
  }
350
351
  /**
352
   * {@inheritdoc}
353
   */
354
  public function processBatch() {
355
    // Do nothing. Drush should internally handle any needs for processing
356
    // batch ops.
357
  }
358
359
  /**
360
   * {@inheritdoc}
361
   */
362
  public function runCron() {
363
    $this->drush('cron');
364
  }
365
366
  /**
367
   * Run Drush commands dynamically from a DrupalContext.
368
   */
369
  public function __call($name, $arguments) {
370
    return $this->drush($name, $arguments);
371
  }
372
373
}
374