GitCommand::setCache()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
/*
3
 * This file is part of the GitCommandBundle package.
4
 *
5
 * (c) Paul Schweppe <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace VersionControl\GitCommandBundle\GitCommands;
12
13
use Doctrine\Common\Cache\CacheProvider;
14
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
15
use Symfony\Component\Process\Exception\LogicException;
16
use Symfony\Component\Process\Exception\RuntimeException;
17
use Symfony\Component\Process\Process;
18
use Symfony\Component\Process\ProcessBuilder;
19
use Symfony\Component\Stopwatch\Stopwatch;
20
use VersionControl\GitCommandBundle\GitCommands\Command\GitUserInterface;
21
use VersionControl\GitCommandBundle\Service\SshProcessInterface;
22
use VersionControl\GitCommandBundle\Service\SftpProcessInterface;
23
use VersionControl\GitCommandBundle\GitCommands\Exception\InvalidArgumentException;
24
use VersionControl\GitCommandBundle\Logger\GitCommandLogger;
25
use VersionControl\GitCommandBundle\Event\GitAlterFilesEvent;
26
use VersionControl\GitCommandBundle\GitCommands\Exception\RunGitCommandException;
27
28
/**
29
 * @author Paul Schweppe <[email protected]>
30
 */
31
class GitCommand
32
{
33
    /**
34
     * @var string
35
     */
36
    protected $gitPath;
37
38
    /**
39
     * @var GitUserInterface
40
     */
41
    private $gitUser;
42
43
    /**
44
     * The git gitEnvironment entity.
45
     *
46
     * @var GitEnvironmentInterface
47
     */
48
    protected $gitEnvironment;
49
50
    /**
51
     * @var EventDispatcherInterface
52
     */
53
    public $dispatcher;
54
55
    /**
56
     * Git Command Logger.
57
     *
58
     * @var GitCommandLogger
59
     */
60
    protected $logger;
61
62
    /**
63
     * Symfony's debugging Stopwatch.
64
     *
65
     * @var Stopwatch|null
66
     */
67
    private $stopwatch;
68
69
    /**
70
     * SSH Process.
71
     *
72
     * @var SshProcessInterface
73
     */
74
    private $sshProcess;
75
76
    /**
77
     * Sftp Process.
78
     *
79
     * @var SftpProcessInterface
80
     */
81
    private $sftpProcess;
82
83
    /**
84
     * Cache in memory.
85
     *
86
     * @var CacheProvider
87
     */
88
    private $cache;
89
90
    /**
91
     * Last Exit code of local command.
92
     *
93
     * @var int
94
     */
95
    private $exitCode;
96
97
    /**
98
     * Wrapper function to run shell commands. Supports local and remote commands
99
     * depending on the gitEnvironment details.
100
     *
101
     * @param string $command command to run
102
     * @param bool $cacheCommand command to run
103
     * @param bool $trim do not trim response. Maybe need for some command responses
104
     *
105
     * @return string Result of command
106
     *
107
     * @throws RuntimeException
108
     * @throws LogicException
109
     * @throws \RuntimeException
110
     * @throws RunGitCommandException
111
     */
112
    public function runCommand($command, $cacheCommand = true, $trim = true): string
113
    {
114
        if ($this->stopwatch) {
115
            $this->stopwatch->start('git_request', 'version_control');
116
        }
117
118
        $fullCommand = sprintf('cd %s && %s', $this->gitPath, $command);
119
        $cacheId = md5($this->gitEnvironment->getId() . $fullCommand);
120
121
        if ($this->gitEnvironment->getSsh() === false) {
122
            //Run local commands
123
            $start = microtime(true);
124
            $response = $this->runLocalCommand($command);
125
126
            $this->logCommand($fullCommand, 'local', array(), $start);
127
128
            return $trim === true ? trim($response) : $response;
129
        }
130
131
        //Run remote command over ssh
132
        if ($cacheCommand === true) {
133
            $response = $this->cache->fetch($cacheId);
134
            if ($response === false) {
135
                $response = $this->runRemoteCommand($fullCommand);
136
                $this->cache->save($cacheId, $response);
137
            }
138
        } else {
139
            $response = $this->runRemoteCommand($fullCommand);
140
        }
141
142
        return $trim === true ? trim($response) : $response;
143
    }
144
145
    /**
146
     * Run remote command over ssh.
147
     *
148
     * @param string $fullCommand
149
     *
150
     * @return string Commands response
151
     */
152
    private function runRemoteCommand($fullCommand): string
153
    {
154
        $start = microtime(true);
155
156
        $this->sshProcess->run(
157
            array($fullCommand),
158
            $this->gitEnvironment->getHost(),
159
            $this->gitEnvironment->getUsername(),
160
            22,
161
            $this->gitEnvironment->getPassword(),
162
            null,
163
            $this->gitEnvironment->getPrivateKey(),
164
            $this->gitEnvironment->getPrivateKeyPassword()
165
        );
166
167
        $this->logCommand(
168
            $fullCommand,
169
            'remote',
170
            array('host' => $this->gitEnvironment->getHost()),
171
            $start,
172
            $this->sshProcess->getStdout(),
173
            $this->sshProcess->getStderr(),
174
            $this->sshProcess->getExitStatus()
0 ignored issues
show
Bug introduced by
It seems like $this->sshProcess->getExitStatus() can also be of type false; however, parameter $exitStatus of VersionControl\GitComman...itCommand::logCommand() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
            /** @scrutinizer ignore-type */ $this->sshProcess->getExitStatus()
Loading history...
175
        );
176
177
        return $this->sshProcess->getStdout();
178
    }
179
180
    /**
181
     * Run local command.
182
     *
183
     * @param string|array $command
184
     *
185
     * @return string Commands response
186
     * @throws RuntimeException
187
     * @throws RunGitCommandException
188
     * @throws LogicException
189
     * @throws RunGitCommandException
190
     */
191
    private function runLocalCommand($command)
192
    {
193
        $fullCommand = sprintf('cd %s && %s', $this->gitPath, $command);
0 ignored issues
show
Bug introduced by
It seems like $command can also be of type array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

193
        $fullCommand = sprintf('cd %s && %s', $this->gitPath, /** @scrutinizer ignore-type */ $command);
Loading history...
194
195
        //Run local commands
196
        if (is_array($command)) {
197
            //$finalCommands = array_merge(array('cd',$this->gitPath,'&&'),$command);
198
            $builder = new ProcessBuilder($command);
199
            $builder->setPrefix('cd ' . $this->gitPath . ' && ');
200
            $process = $builder->getProcess();
201
        } else {
202
            $process = new Process($fullCommand);
203
        }
204
205
        //Run Proccess
206
        $process->run();
207
208
        $this->exitCode = $process->getExitCode();
209
210
        $response = '';
211
        // executes after the command finishes
212
        if ($process->isSuccessful()) {
213
            $response = $process->getOutput();
214
            if (trim($process->getErrorOutput()) !== '') {
215
                $response = $process->getErrorOutput();
216
            }
217
        } else {
218
            if (trim($process->getErrorOutput()) !== '') {
219
                throw new RunGitCommandException($process->getErrorOutput());
220
            }
221
        }
222
223
        return $response;
224
    }
225
226
    public function getLastExitStatus()
227
    {
228
        if ($this->gitEnvironment->getSsh() === true) {
229
            return $this->sshProcess->getExitStatus();
230
        }
231
232
        return $this->exitCode;
233
    }
234
235
    /**
236
     * Gets the git path.
237
     *
238
     * @return string
239
     */
240
    public function getGitPath(): string
241
    {
242
        return $this->gitPath;
243
    }
244
245
    /**
246
     * Sets the git path.
247
     *
248
     * @param string $gitPath
249
     *
250
     * @return GitCommand
251
     */
252
    protected function setGitPath(string $gitPath)
253
    {
254
        $this->gitPath = rtrim(trim($gitPath), DIRECTORY_SEPARATOR);
255
256
        return $this;
257
    }
258
259
    /**
260
     * Sets the Git Environment.
261
     *
262
     * @param GitEnvironmentInterface $gitEnvironment
263
     *
264
     * @return GitCommand
265
     */
266
    public function setGitEnvironment(GitEnvironmentInterface $gitEnvironment)
267
    {
268
        $this->gitEnvironment = $gitEnvironment;
269
        $this->setGitPath($this->gitEnvironment->getPath());
270
271
        return $this;
272
    }
273
274
    /**
275
     * Allows you to override the git Environment.
276
     *
277
     * @param GitEnvironmentInterface $gitEnvironment
278
     *
279
     * @return GitCommand
280
     */
281
    public function overRideGitEnvironment(GitEnvironmentInterface $gitEnvironment)
282
    {
283
        $this->gitEnvironment = $gitEnvironment;
284
        $this->setGitPath($this->gitEnvironment->getPath());
285
286
        return $this;
287
    }
288
289
    /**
290
     * Gets git Environment.
291
     *
292
     * @return GitEnvironmentInterface
293
     */
294
    public function getGitEnvironment()
295
    {
296
        return $this->gitEnvironment;
297
    }
298
299
    public function getDispatcher()
300
    {
301
        return $this->dispatcher;
302
    }
303
304
    public function setDispatcher(EventDispatcherInterface $dispatcher)
305
    {
306
        $this->dispatcher = $dispatcher;
307
308
        return $this;
309
    }
310
311
    public function triggerGitAlterFilesEvent($eventName = 'git.alter_files')
312
    {
313
        $event = new GitAlterFilesEvent($this->gitEnvironment, array());
314
        $this->dispatcher->dispatch($eventName, $event);
315
    }
316
317
    public function getLogger()
318
    {
319
        return $this->logger;
320
    }
321
322
    public function setLogger(GitCommandLogger $logger)
323
    {
324
        $this->logger = $logger;
325
326
        return $this;
327
    }
328
329
    /**
330
     * Sets a stopwatch instance for debugging purposes.
331
     *
332
     * @param Stopwatch $stopwatch
333
     */
334
    public function setStopwatch(Stopwatch $stopwatch = null)
335
    {
336
        $this->stopwatch = $stopwatch;
337
    }
338
339
    /**
340
     * Log the query if we have an instance of ElasticaLogger.
341
     *
342
     * @param string $command
343
     * @param string $method
344
     * @param array $data
345
     * @param int $start
346
     * @param string $response
347
     * @param string $error
348
     * @param int $exitStatus
349
     */
350
    public function logCommand(
351
        $command,
352
        $method,
353
        $data,
354
        $start,
355
        $response = '',
356
        $error = '',
357
        $exitStatus = 0
358
    ) {
359
        if (!$this->logger || !$this->logger instanceof GitCommandLogger) {
360
            return;
361
        }
362
        $time = microtime(true) - $start;
363
364
        $this->logger->logCommand($command, $method, $data, $time, $response, $error, $exitStatus);
365
    }
366
367
    /**
368
     * Sets the SSH Process.
369
     *
370
     * @param SshProcessInterface $sshProcess
371
     *
372
     * @return GitCommand
373
     */
374
    public function setSshProcess(SshProcessInterface $sshProcess)
375
    {
376
        $this->sshProcess = $sshProcess;
377
378
        return $this;
379
    }
380
381
    /**
382
     * Sets the SFTP Process.
383
     *
384
     * @param SftpProcessInterface $sftpProcess
385
     *
386
     * @return GitCommand
387
     */
388
    public function setSftpProcess(SftpProcessInterface $sftpProcess)
389
    {
390
        $this->sftpProcess = $sftpProcess;
391
392
        return $this;
393
    }
394
395
    /**
396
     * Gets the SFTP Process.
397
     *
398
     * @return SftpProcessInterface
399
     */
400
    public function getSftpProcess()
401
    {
402
        $this->sftpProcess->setGitEnviroment($this->gitEnvironment);
403
404
        return $this->sftpProcess;
405
    }
406
407
    public function setCache(CacheProvider $cache)
408
    {
409
        $this->cache = $cache;
410
    }
411
412
    /**
413
     * Get git command groups.
414
     *
415
     * @param string $name
416
     *
417
     * @return Command\GitBranchCommand|Command\GitCommitCommand|Command\GitDiffCommand|Command\GitFilesCommand|Command\GitInitCommand|Command\GitLogCommand|Command\GitTagCommand
418
     *
419
     * @throws InvalidArgumentException
420
     */
421
    public function command($name)
422
    {
423
        switch (trim($name)) {
424
            case 'branch':
425
                $command = new Command\GitBranchCommand($this);
426
                break;
427
            case 'tag':
428
                $command = new Command\GitTagCommand($this);
429
                break;
430
            case 'commit':
431
                $command = new Command\GitCommitCommand($this);
432
                break;
433
            case 'diff':
434
                $command = new Command\GitDiffCommand($this);
435
                break;
436
            case 'files':
437
                $command = new Command\GitFilesCommand($this);
438
                break;
439
            case 'init':
440
                $command = new Command\GitInitCommand($this);
441
                break;
442
            case 'log':
443
                $command = new Command\GitLogCommand($this);
444
                break;
445
            case 'status':
446
                $command = new Command\GitStatusCommand($this);
447
                break;
448
            case 'sync':
449
                $command = new Command\GitSyncCommand($this);
450
                break;
451
            case 'undo':
452
                $command = new Command\GitUndoCommand($this);
453
                break;
454
            default:
455
                throw new InvalidArgumentException(sprintf('Unknown command instance called: "%s"', $name));
456
        }
457
458
        return $command;
459
    }
460
461
    /**
462
     * @return GitUserInterface
463
     */
464
    public function getGitUser(): GitUserInterface
465
    {
466
        return $this->gitUser;
467
    }
468
469
    /**
470
     * @param GitUserInterface $gitUser
471
     */
472
    public function setGitUser(GitUserInterface $gitUser): void
473
    {
474
        $this->gitUser = $gitUser;
475
    }
476
}
477