Completed
Pull Request — master (#123)
by
unknown
09:55
created

GitWrapper::getIdleTimeout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace GitWrapper;
4
5
use Symfony\Component\Process\Process;
6
use Symfony\Component\Process\ExecutableFinder;
7
use Symfony\Component\EventDispatcher\EventDispatcher;
8
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
9
10
/**
11
 * A wrapper class around the Git binary.
12
 *
13
 * A GitWrapper object contains the necessary context to run Git commands such
14
 * as the path to the Git binary and environment variables. It also provides
15
 * helper methods to run Git commands as set up the connection to the GIT_SSH
16
 * wrapper script.
17
 */
18
class GitWrapper
19
{
20
    /**
21
     * Symfony event dispatcher object used by this library to dispatch events.
22
     *
23
     * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
24
     */
25
    private $dispatcher;
26
27
    /**
28
     * Path to the Git binary.
29
     *
30
     * @var string
31
     */
32
    protected $gitBinary;
33
34
    /**
35
     * Environment variables defined in the scope of the Git command.
36
     *
37
     * @var array
38
     */
39
    protected $env = array();
40
41
    /**
42
     * The timeout of the Git command in seconds, defaults to 60.
43
     *
44
     * @var int
45
     */
46
    protected $timeout = 60;
47
48
    /**
49
     * The idle timeout of the Git command in seconds, defaults to 60.
50
     *
51
     * @var int
52
     */
53
    protected $idleTimeout = 60;
54
55
    /**
56
     * An array of options passed to the proc_open() function.
57
     *
58
     * @var array
59
     */
60
    protected $procOptions = array();
61
62
    /**
63
     * @var \GitWrapper\Event\GitOutputListenerInterface
64
     */
65
    protected $streamListener;
66
67
    /**
68
     * Constructs a GitWrapper object.
69
     *
70
     * @param string|null $gitBinary
71
     *   The path to the Git binary. Defaults to null, which uses Symfony's
72
     *   ExecutableFinder to resolve it automatically.
73
     *
74
     * @throws \GitWrapper\GitException
75
     *   Throws an exception if the path to the Git binary couldn't be resolved
76
     *   by the ExecutableFinder class.
77
     */
78 336
    public function __construct($gitBinary = null)
79
    {
80 336
        if (null === $gitBinary) {
81
            // @codeCoverageIgnoreStart
82
            $finder = new ExecutableFinder();
83
            $gitBinary = $finder->find('git');
84
            if (!$gitBinary) {
85
                throw new GitException('Unable to find the Git executable.');
86
            }
87
            // @codeCoverageIgnoreEnd
88 168
        }
89
90 336
        $this->setGitBinary($gitBinary);
91 336
    }
92
93
    /**
94
     * Gets the dispatcher used by this library to dispatch events.
95
     *
96
     * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
97
     */
98 244
    public function getDispatcher()
99
    {
100 244
        if (!isset($this->dispatcher)) {
101 240
            $this->dispatcher = new EventDispatcher();
102 120
        }
103 244
        return $this->dispatcher;
104
    }
105
106
    /**
107
     * Sets the dispatcher used by this library to dispatch events.
108
     *
109
     * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
110
     *   The Symfony event dispatcher object.
111
     *
112
     * @return \GitWrapper\GitWrapper
113
     */
114 4
    public function setDispatcher(EventDispatcherInterface $dispatcher)
115
    {
116 4
        $this->dispatcher = $dispatcher;
117 4
        return $this;
118
    }
119
120
    /**
121
     * Sets the path to the Git binary.
122
     *
123
     * @param string $gitBinary
124
     *   Path to the Git binary.
125
     *
126
     * @return \GitWrapper\GitWrapper
127
     */
128 336
    public function setGitBinary($gitBinary)
129
    {
130 336
        $this->gitBinary = $gitBinary;
131 336
        return $this;
132
    }
133
134
    /**
135
     * Returns the path to the Git binary.
136
     *
137
     * @return string
138
     */
139 248
    public function getGitBinary()
140
    {
141 248
        return $this->gitBinary;
142
    }
143
144
    /**
145
     * Sets an environment variable that is defined only in the scope of the Git
146
     * command.
147
     *
148
     * @param string $var
149
     *   The name of the environment variable, e.g. "HOME", "GIT_SSH".
150
     * @param mixed $default
0 ignored issues
show
Bug introduced by
There is no parameter named $default. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
151
     *   The value of the environment variable is not set, defaults to null.
152
     *
153
     * @return \GitWrapper\GitWrapper
154
     */
155 20
    public function setEnvVar($var, $value)
156
    {
157 20
        $this->env[$var] = $value;
158 20
        return $this;
159
    }
160
161
    /**
162
     * Unsets an environment variable that is defined only in the scope of the
163
     * Git command.
164
     *
165
     * @param string $var
166
     *   The name of the environment variable, e.g. "HOME", "GIT_SSH".
167
     *
168
     * @return \GitWrapper\GitWrapper
169
     */
170 8
    public function unsetEnvVar($var)
171
    {
172 8
        unset($this->env[$var]);
173 8
        return $this;
174
    }
175
176
    /**
177
     * Returns an environment variable that is defined only in the scope of the
178
     * Git command.
179
     *
180
     * @param string $var
181
     *   The name of the environment variable, e.g. "HOME", "GIT_SSH".
182
     * @param mixed $default
183
     *   The value returned if the environment variable is not set, defaults to
184
     *   null.
185
     *
186
     * @return mixed
187
     */
188 24
    public function getEnvVar($var, $default = null)
189
    {
190 24
        return isset($this->env[$var]) ? $this->env[$var] : $default;
191
    }
192
193
    /**
194
     * Returns the associative array of environment variables that are defined
195
     * only in the scope of the Git command.
196
     *
197
     * @return array
198
     */
199 244
    public function getEnvVars()
200
    {
201 244
        return $this->env;
202
    }
203
204
    /**
205
     * Sets the timeout of the Git command.
206
     *
207
     * @param int $timeout
208
     *   The timeout in seconds.
209
     *
210
     * @return \GitWrapper\GitWrapper
211
     */
212 4
    public function setTimeout($timeout)
213
    {
214 4
        $this->timeout = (int) $timeout;
215 4
        return $this;
216
    }
217
218
    /**
219
     * Gets the timeout of the Git command.
220
     *
221
     * @return int
222
     *   The timeout in seconds.
223
     */
224 244
    public function getTimeout()
225
    {
226 244
        return $this->timeout;
227
    }
228
229
    /**
230
     * Sets the timeout of the Git command.
231
     *
232
     * @param int $timeout
233
     *   The timeout in seconds.
234
     *
235
     * @return \GitWrapper\GitWrapper
236
     */
237
    public function setIdleTimeout($timeout)
238
    {
239
        $this->idleTimeout = (int) $timeout;
240
        return $this;
241
    }
242
243
    /**
244
     * Gets the idle timeout of the Git command.
245
     *
246
     * @return int
247
     *   The timeout in seconds.
248
     */
249 240
    public function getIdleTimeout()
250
    {
251 240
        return $this->idleTimeout;
252
    }
253
254
    /**
255
     * Sets the options passed to proc_open() when executing the Git command.
256
     *
257
     * @param array $timeout
0 ignored issues
show
Bug introduced by
There is no parameter named $timeout. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
258
     *   The options passed to proc_open().
259
     *
260
     * @return \GitWrapper\GitWrapper
261
     */
262 4
    public function setProcOptions(array $options)
263
    {
264 4
        $this->procOptions = $options;
265 4
        return $this;
266
    }
267
268
    /**
269
     * Gets the options passed to proc_open() when executing the Git command.
270
     *
271
     * @return array
272
     */
273 244
    public function getProcOptions()
274
    {
275 244
        return $this->procOptions;
276
    }
277
278
    /**
279
     * Set an alternate private key used to connect to the repository.
280
     *
281
     * This method sets the GIT_SSH environment variable to use the wrapper
282
     * script included with this library. It also sets the custom GIT_SSH_KEY
283
     * and GIT_SSH_PORT environment variables that are used by the script.
284
     *
285
     * @param string $privateKey
286
     *   Path to the private key.
287
     * @param int $port
288
     *   Port that the SSH server being connected to listens on, defaults to 22.
289
     * @param string|null $wrapper
290
     *   Path the the GIT_SSH wrapper script, defaults to null which uses the
291
     *   script included with this library.
292
     *
293
     * @return \GitWrapper\GitWrapper
294
     *
295
     * @throws \GitWrapper\GitException
296
     *   Thrown when any of the paths cannot be resolved.
297
     */
298 24
    public function setPrivateKey($privateKey, $port = 22, $wrapper = null)
299
    {
300 24
        if (null === $wrapper) {
301 12
            $wrapper = __DIR__ . '/../../bin/git-ssh-wrapper.sh';
302 6
        }
303 24
        if (!$wrapperPath = realpath($wrapper)) {
304 4
            throw new GitException('Path to GIT_SSH wrapper script could not be resolved: ' . $wrapper);
305
        }
306 20
        if (!$privateKeyPath = realpath($privateKey)) {
307 4
            throw new GitException('Path private key could not be resolved: ' . $privateKey);
308
        }
309
310 8
        return $this
311 16
            ->setEnvVar('GIT_SSH', $wrapperPath)
312 16
            ->setEnvVar('GIT_SSH_KEY', $privateKeyPath)
313 16
            ->setEnvVar('GIT_SSH_PORT', (int) $port)
314 8
        ;
315
    }
316
317
    /**
318
     * Unsets the private key by removing the appropriate environment variables.
319
     *
320
     * @return \GitWrapper\GitWrapper
321
     */
322 4
    public function unsetPrivateKey()
323
    {
324 2
        return $this
325 4
            ->unsetEnvVar('GIT_SSH')
326 4
            ->unsetEnvVar('GIT_SSH_KEY')
327 4
            ->unsetEnvVar('GIT_SSH_PORT')
328 2
        ;
329
    }
330
331
    /**
332
     * Adds output listener.
333
     *
334
     * @param \GitWrapper\Event\GitOutputListenerInterface $listener
335
     *
336
     * @return \GitWrapper\GitWrapper
337
     */
338 8
    public function addOutputListener(Event\GitOutputListenerInterface $listener)
339
    {
340 4
        $this
341 8
            ->getDispatcher()
342 8
            ->addListener(Event\GitEvents::GIT_OUTPUT, array($listener, 'handleOutput'))
343
        ;
344 8
        return $this;
345
    }
346
347
    /**
348
     * Adds logger listener listener.
349
     *
350
     * @param Event\GitLoggerListener $listener
351
     *
352
     * @return GitWrapper
353
     */
354 8
    public function addLoggerListener(Event\GitLoggerListener $listener)
355
    {
356 4
        $this
357 8
            ->getDispatcher()
358 8
            ->addSubscriber($listener)
359
        ;
360 8
        return $this;
361
    }
362
363
    /**
364
     * Removes an output listener.
365
     *
366
     * @param \GitWrapper\Event\GitOutputListenerInterface $listener
367
     *
368
     * @return \GitWrapper\GitWrapper
369
     */
370 4
    public function removeOutputListener(Event\GitOutputListenerInterface $listener)
371
    {
372 2
        $this
373 4
            ->getDispatcher()
374 4
            ->removeListener(Event\GitEvents::GIT_OUTPUT, array($listener, 'handleOutput'))
375
        ;
376 4
        return $this;
377
    }
378
379
    /**
380
     * Set whether or not to stream real-time output to STDOUT and STDERR.
381
     *
382
     * @param boolean $streamOutput
383
     *
384
     * @return \GitWrapper\GitWrapper
385
     */
386 4
    public function streamOutput($streamOutput = true)
387
    {
388 4
        if ($streamOutput && !isset($this->streamListener)) {
389 4
            $this->streamListener = new Event\GitOutputStreamListener();
390 4
            $this->addOutputListener($this->streamListener);
391 2
        }
392
393 4
        if (!$streamOutput && isset($this->streamListener)) {
394 4
            $this->removeOutputListener($this->streamListener);
395 4
            unset($this->streamListener);
396 2
        }
397
398 4
        return $this;
399
    }
400
401
    /**
402
     * Returns an object that interacts with a working copy.
403
     *
404
     * @param string $directory
405
     *   Path to the directory containing the working copy.
406
     *
407
     * @return GitWorkingCopy
408
     */
409 212
    public function workingCopy($directory)
410
    {
411 212
        return new GitWorkingCopy($this, $directory);
412
    }
413
414
    /**
415
     * Returns the version of the installed Git client.
416
     *
417
     * @return string
418
     *
419
     * @throws \GitWrapper\GitException
420
     */
421 12
    public function version()
422
    {
423 12
        return $this->git('--version');
424
    }
425
426
    /**
427
     * Parses name of the repository from the path.
428
     *
429
     * For example, passing the "[email protected]:cpliakas/git-wrapper.git"
430
     * repository would return "git-wrapper".
431
     *
432
     * @param string $repository
433
     *   The repository URL.
434
     *
435
     * @return string
436
     */
437 8
    public static function parseRepositoryName($repository)
438
    {
439 8
        $scheme = parse_url($repository, PHP_URL_SCHEME);
440
441 8
        if (null === $scheme) {
442 4
            $parts = explode('/', $repository);
443 4
            $path = end($parts);
444 2
        } else {
445 8
            $strpos = strpos($repository, ':');
446 8
            $path = substr($repository, $strpos + 1);
447
        }
448
449 8
        return basename($path, '.git');
450
    }
451
452
    /**
453
     * Executes a `git init` command.
454
     *
455
     * Create an empty git repository or reinitialize an existing one.
456
     *
457
     * @param string $directory
458
     *   The directory being initialized.
459
     * @param array $options
460
     *   (optional) An associative array of command line options.
461
     *
462
     * @return \GitWrapper\GitWorkingCopy
463
     *
464
     * @throws \GitWrapper\GitException
465
     *
466
     * @see GitWorkingCopy::cloneRepository()
467
     *
468
     * @ingroup commands
469
     */
470 204
    public function init($directory, array $options = array())
471
    {
472 204
        $git = $this->workingCopy($directory);
473 204
        $git->init($options);
474 204
        $git->setCloned(true);
475 204
        return $git;
476
    }
477
478
    /**
479
     * Executes a `git clone` command and returns a working copy object.
480
     *
481
     * Clone a repository into a new directory. Use GitWorkingCopy::clone()
482
     * instead for more readable code.
483
     *
484
     * @param string $repository
485
     *   The Git URL of the repository being cloned.
486
     * @param string $directory
487
     *   The directory that the repository will be cloned into. If null is
488
     *   passed, the directory will automatically be generated from the URL via
489
     *   the GitWrapper::parseRepositoryName() method.
490
     * @param array $options
491
     *   (optional) An associative array of command line options.
492
     *
493
     * @return \GitWrapper\GitWorkingCopy
494
     *
495
     * @throws \GitWrapper\GitException
496
     *
497
     * @see GitWorkingCopy::cloneRepository()
498
     *
499
     * @ingroup commands
500
     */
501 204
    public function cloneRepository($repository, $directory = null, array $options = array())
502
    {
503 204
        if (null === $directory) {
504 4
            $directory = self::parseRepositoryName($repository);
505 2
        }
506 204
        $git = $this->workingCopy($directory);
507 204
        $git->clone($repository, $options);
0 ignored issues
show
Documentation Bug introduced by
The method clone does not exist on object<GitWrapper\GitWorkingCopy>? 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...
508 204
        $git->setCloned(true);
509 204
        return $git;
510
    }
511
512
    /**
513
     * Runs an arbitrary Git command.
514
     *
515
     * The command is simply a raw command line entry for everything after the
516
     * Git binary. For example, a `git config -l` command would be passed as
517
     * `config -l` via the first argument of this method.
518
     *
519
     * Note that no events are thrown by this method.
520
     *
521
     * @param string $commandLine
522
     *   The raw command containing the Git options and arguments. The Git
523
     *   binary should not be in the command, for example `git config -l` would
524
     *   translate to "config -l".
525
     * @param string|null $cwd
526
     *   The working directory of the Git process. Defaults to null which uses
527
     *   the current working directory of the PHP process.
528
     *
529
     * @return string
530
     *   The STDOUT returned by the Git command.
531
     *
532
     * @throws \GitWrapper\GitException
533
     *
534
     * @see GitWrapper::run()
535
     */
536 44
    public function git($commandLine, $cwd = null)
537
    {
538 44
        $command = GitCommand::getInstance($commandLine);
539 44
        $command->setDirectory($cwd);
540 44
        return $this->run($command);
541
    }
542
543
    /**
544
     * Runs a Git command.
545
     *
546
     * @param \GitWrapper\GitCommand $command
547
     *   The Git command being executed.
548
     * @param string|null $cwd
549
     *   Explicitly specify the working directory of the Git process. Defaults
550
     *   to null which automatically sets the working directory based on the
551
     *   command being executed relative to the working copy.
552
     *
553
     * @return string
554
     *   The STDOUT returned by the Git command.
555
     *
556
     * @throws \GitWrapper\GitException
557
     *
558
     * @see Process
559
     */
560 244
    public function run(GitCommand $command, $cwd = null)
561
    {
562 244
        $wrapper = $this;
563 244
        $process = new GitProcess($this, $command, $cwd);
564 240
        $process->setIdleTimeout($this->getIdleTimeout());
565 240
        $process->run(function ($type, $buffer) use ($wrapper, $process, $command) {
566 228
            $event = new Event\GitOutputEvent($wrapper, $process, $command, $type, $buffer);
567 228
            $wrapper->getDispatcher()->dispatch(Event\GitEvents::GIT_OUTPUT, $event);
568 240
        });
569 232
        return $command->notBypassed() ? $process->getOutput() : '';
570
    }
571
572
    /**
573
     * Hackish, allows us to use "clone" as a method name.
574
     *
575
     * $throws \BadMethodCallException
576
     * @throws \GitWrapper\GitException
577
     */
578 208 View Code Duplication
    public function __call($method, $args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
579
    {
580 208
        if ('clone' == $method) {
581 204
            return call_user_func_array(array($this, 'cloneRepository'), $args);
582
        } else {
583 4
            $class = get_called_class();
584 4
            $message = "Call to undefined method $class::$method()";
585 4
            throw new \BadMethodCallException($message);
586
        }
587
    }
588
}
589