GitWrapper::setEnvVar()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GitWrapper;
6
7
use GitWrapper\Event\GitOutputEvent;
8
use GitWrapper\EventSubscriber\AbstractOutputEventSubscriber;
9
use GitWrapper\EventSubscriber\GitLoggerEventSubscriber;
10
use GitWrapper\EventSubscriber\StreamOutputEventSubscriber;
11
use GitWrapper\Exception\GitException;
12
use GitWrapper\Process\GitProcess;
13
use GitWrapper\Strings\GitStrings;
14
use Symfony\Component\EventDispatcher\EventDispatcher;
15
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16
use Symfony\Component\Process\ExecutableFinder;
17
18
/**
19
 * A wrapper class around the Git binary.
20
 *
21
 * A GitWrapper object contains the necessary context to run Git commands such
22
 * as the path to the Git binary and environment variables. It also provides
23
 * helper methods to run Git commands as set up the connection to the GIT_SSH
24
 * wrapper script.
25
 */
26
final class GitWrapper
27
{
28
    /**
29
     * Path to the Git binary.
30
     *
31
     * @var string
32
     */
33
    private $gitBinary;
34
35
    /**
36
     * The timeout of the Git command in seconds.
37
     *
38
     * @var int
39
     */
40
    private $timeout = 60;
41
42
    /**
43
     * Environment variables defined in the scope of the Git command.
44
     *
45
     * @var string[]
46
     */
47
    private $env = [];
48
49
    /**
50
     * @var AbstractOutputEventSubscriber
51
     */
52
    private $outputEventSubscriber;
53
54
    /**
55
     * @var EventDispatcherInterface
56
     */
57
    private $eventDispatcher;
58
59
    public function __construct(?string $gitBinary = null)
60
    {
61
        if ($gitBinary === null) {
62
            $finder = new ExecutableFinder();
63
            $gitBinary = $finder->find('git');
64
            if (! $gitBinary) {
65
                throw new GitException('Unable to find the Git executable.');
66
            }
67
        }
68
69
        $this->setGitBinary($gitBinary);
70
71
        $this->eventDispatcher = new EventDispatcher();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Symfony\Component\E...tcher\EventDispatcher() of type object<Symfony\Component...atcher\EventDispatcher> is incompatible with the declared type object<Symfony\Component...entDispatcherInterface> of property $eventDispatcher.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
72
    }
73
74
    public function getDispatcher(): EventDispatcherInterface
75
    {
76
        return $this->eventDispatcher;
77
    }
78
79
    public function setDispatcher(EventDispatcherInterface $eventDispatcher): void
80
    {
81
        $this->eventDispatcher = $eventDispatcher;
82
    }
83
84
    public function setGitBinary(string $gitBinary): void
85
    {
86
        $this->gitBinary = $gitBinary;
87
    }
88
89
    public function getGitBinary(): string
90
    {
91
        return $this->gitBinary;
92
    }
93
94
    public function setEnvVar(string $var, $value): void
95
    {
96
        $this->env[$var] = $value;
97
    }
98
99
    public function unsetEnvVar(string $var): void
100
    {
101
        unset($this->env[$var]);
102
    }
103
104
    /**
105
     * Returns an environment variable that is defined only in the scope of the
106
     * Git command.
107
     *
108
     * @param string $var The name of the environment variable, e.g. "HOME", "GIT_SSH".
109
     * @param mixed $default The value returned if the environment variable is not set, defaults to
110
     *   null.
111
     */
112
    public function getEnvVar(string $var, $default = null)
113
    {
114
        return $this->env[$var] ?? $default;
115
    }
116
117
    /**
118
     * @return string[]
119
     */
120
    public function getEnvVars(): array
121
    {
122
        return $this->env;
123
    }
124
125
    public function setTimeout(int $timeout): void
126
    {
127
        $this->timeout = $timeout;
128
    }
129
130
    public function getTimeout(): int
131
    {
132
        return $this->timeout;
133
    }
134
135
    /**
136
     * Set an alternate private key used to connect to the repository.
137
     *
138
     * This method sets the GIT_SSH environment variable to use the wrapper
139
     * script included with this library. It also sets the custom GIT_SSH_KEY
140
     * and GIT_SSH_PORT environment variables that are used by the script.
141
     *
142
     * @param string|null $wrapper Path the the GIT_SSH wrapper script, defaults to null which uses the
143
     *   script included with this library.
144
     */
145
    public function setPrivateKey(string $privateKey, int $port = 22, ?string $wrapper = null): void
146
    {
147
        if ($wrapper === null) {
148
            $wrapper = __DIR__ . '/../bin/git-ssh-wrapper.sh';
149
        }
150
151
        if (! $wrapperPath = realpath($wrapper)) {
152
            throw new GitException('Path to GIT_SSH wrapper script could not be resolved: ' . $wrapper);
153
        }
154
155
        if (! $privateKeyPath = realpath($privateKey)) {
156
            throw new GitException('Path private key could not be resolved: ' . $privateKey);
157
        }
158
159
        $this->setEnvVar('GIT_SSH', $wrapperPath);
160
        $this->setEnvVar('GIT_SSH_KEY', $privateKeyPath);
161
        $this->setEnvVar('GIT_SSH_PORT', $port);
162
    }
163
164
    /**
165
     * Unsets the private key by removing the appropriate environment variables.
166
     */
167
    public function unsetPrivateKey(): void
168
    {
169
        $this->unsetEnvVar('GIT_SSH');
170
        $this->unsetEnvVar('GIT_SSH_KEY');
171
        $this->unsetEnvVar('GIT_SSH_PORT');
172
    }
173
174
    /**
175
     * @api
176
     */
177
    public function addOutputEventSubscriber(AbstractOutputEventSubscriber $gitOutputEventSubscriber): void
178
    {
179
        $this->eventDispatcher->addSubscriber($gitOutputEventSubscriber);
180
    }
181
182
    public function addLoggerEventSubscriber(GitLoggerEventSubscriber $gitLoggerEventSubscriber): void
183
    {
184
        $this->eventDispatcher->addSubscriber($gitLoggerEventSubscriber);
185
    }
186
187
    /**
188
     * @api
189
     */
190
    public function removeOutputEventSubscriber(AbstractOutputEventSubscriber $gitOutputEventSubscriber): void
191
    {
192
        $this->eventDispatcher->removeSubscriber($gitOutputEventSubscriber);
193
    }
194
195
    /**
196
     * @api
197
     * Set whether or not to stream real-time output to STDOUT and STDERR.
198
     */
199
    public function streamOutput(bool $streamOutput = true): void
200
    {
201
        if ($streamOutput && ! isset($this->outputEventSubscriber)) {
202
            $this->outputEventSubscriber = new StreamOutputEventSubscriber();
203
            $this->addOutputEventSubscriber($this->outputEventSubscriber);
204
        }
205
206
        if (! $streamOutput && isset($this->outputEventSubscriber)) {
207
            $this->removeOutputEventSubscriber($this->outputEventSubscriber);
208
            unset($this->outputEventSubscriber);
209
        }
210
    }
211
212
    /**
213
     * Returns an object that interacts with a working copy.
214
     *
215
     * @param string $directory Path to the directory containing the working copy.
216
     */
217
    public function workingCopy(string $directory): GitWorkingCopy
218
    {
219
        return new GitWorkingCopy($this, $directory);
220
    }
221
222
    /**
223
     * Returns the version of the installed Git client.
224
     */
225
    public function version(): string
226
    {
227
        return $this->git('--version');
228
    }
229
230
    /**
231
     * Executes a `git init` command.
232
     *
233
     * Create an empty git repository or reinitialize an existing one.
234
     *
235
     * @param mixed[] $options An associative array of command line options.
236
     */
237
    public function init(string $directory, array $options = []): GitWorkingCopy
238
    {
239
        $git = $this->workingCopy($directory);
240
        $git->init($options);
241
        $git->setCloned(true);
242
243
        return $git;
244
    }
245
246
    /**
247
     * Executes a `git clone` command and returns a working copy object.
248
     *
249
     * Clone a repository into a new directory. Use @see GitWorkingCopy::cloneRepository()
250
     * instead for more readable code.
251
     *
252
     * @param string $directory The directory that the repository will be cloned into. If null is
253
     *   passed, the directory will be generated from the URL with @see GitStrings::parseRepositoryName().
254
     * @param mixed[] $options
255
     */
256
    public function cloneRepository(string $repository, ?string $directory = null, array $options = []): GitWorkingCopy
257
    {
258
        if ($directory === null) {
259
            $directory = GitStrings::parseRepositoryName($repository);
260
        }
261
262
        $git = $this->workingCopy($directory);
263
        $git->cloneRepository($repository, $options);
264
        $git->setCloned(true);
265
        return $git;
266
    }
267
268
    /**
269
     * The command is simply a raw command line entry for everything after the Git binary.
270
     * For example, a `git config -l` command would be passed as `config -l` via the first argument of this method.
271
     *
272
     * @return string The STDOUT returned by the Git command.
273
     */
274
    public function git(string $commandLine, ?string $cwd = null): string
275
    {
276
        $command = new GitCommand($commandLine);
277
        $command->executeRaw(is_string($commandLine));
278
        $command->setDirectory($cwd);
279
        return $this->run($command);
280
    }
281
282
    /**
283
     * @return string The STDOUT returned by the Git command.
284
     */
285
    public function run(GitCommand $gitCommand, ?string $cwd = null): string
286
    {
287
        $process = new GitProcess($this, $gitCommand, $cwd);
288
        $process->run(function ($type, $buffer) use ($process, $gitCommand): void {
289
            $event = new GitOutputEvent($this, $process, $gitCommand, $type, $buffer);
290
            $this->eventDispatcher->dispatch($event);
291
        });
292
293
        return $gitCommand->notBypassed() ? $process->getOutput() : '';
294
    }
295
}
296