Completed
Push — master ( 3da74f...d372d6 )
by Christian
02:54
created

Git::getGitDir()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.2
c 0
b 0
f 0
cc 3
eloc 14
nc 3
nop 0
1
<?php
2
3
namespace uuf6429\ElderBrother\Vcs\Adapter;
4
5
use Symfony\Component\Process\Process;
6
use uuf6429\ElderBrother\Event\Git as GitEvent;
7
8
class Git extends Adapter
9
{
10
    /**
11
     * @var string|null
12
     */
13
    protected $gitDir;
14
15
    /**
16
     * {@inheritdoc}
17
     */
18
    public function isAvailable()
19
    {
20
        return is_dir($this->getHookPath());
21
    }
22
23
    /**
24
     * {@inheritdoc}
25
     */
26
    public function isInstalled()
27
    {
28
        return file_exists($this->getInstallLockFile());
29
    }
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function install()
35
    {
36
        $hookFiles = $this->getHookFiles();
37
38
        // create install lock
39
        fclose(fopen($this->getInstallLockFile(), 'x'));
40
41
        foreach ($hookFiles as $event => $file) {
42
            // back up existing hook
43
            if (file_exists($file)) {
44
                if (!rename($file, $file . '.bak')) {
45
                    throw new \RuntimeException("Could not back up hook file: $file");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
46
                }
47
            }
48
49
            // create new hook file
50
            if (!file_put_contents(
51
                $file,
52
                sprintf(
53
                    '#!/bin/sh%sphp -f %s -- run -e %s%s',
54
                    PHP_EOL,
55
                    escapeshellarg(str_replace($this->getGitDir(), '', ELDER_BROTHER_BIN)),
56
                    escapeshellarg($event),
57
                    PHP_EOL
58
                )
59
            )) {
60
                throw new \RuntimeException("Could not create hook file: $file");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
61
            }
62
            if (!chmod($file, 0755)) {
63
                throw new \RuntimeException("Could not make hook file executable: $file");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
64
            }
65
        }
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function uninstall()
72
    {
73
        $hookFiles = $this->getHookFiles();
74
75
        foreach ($hookFiles as $file) {
76
            // remove hook file
77
            if (!unlink($file)) {
78
                throw new \RuntimeException("Could not remove hook file: $file");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
79
            }
80
81
            // restore backed up hook
82
            if (file_exists($file . '.bak')) {
83
                if (!rename($file . '.bak', $file) || !chmod($file, 0755)) {
84
                    throw new \RuntimeException("Could not properly restore hook file: $file");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $file instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
85
                }
86
            }
87
        }
88
89
        // remove install lock
90
        unlink($this->getInstallLockFile());
91
    }
92
93
    /**
94
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
95
     */
96
    protected function getGitDir()
97
    {
98
        if (!$this->gitDir) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->gitDir of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
99
            $process = new Process('git rev-parse --git-dir', PROJECT_ROOT);
100
            $process->run();
101
102
            if (!$process->isSuccessful()) {
103
                $this->logger->warning(
104
                    sprintf(
105
                        "Failed to retrieve git project root:\n$ %s (exit: %d)\n> %s",
106
                        $process->getCommandLine(),
107
                        $process->getExitCode(),
108
                        implode("\n> ", explode(PHP_EOL, $process->getOutput()))
109
                    )
110
                );
111
            } else {
112
                $this->gitDir = realpath(trim($process->getOutput())) . '/';
113
            }
114
        }
115
116
        return $this->gitDir;
117
    }
118
119
    /**
120
     * @return string
121
     */
122
    protected function getHookPath()
123
    {
124
        return $this->getGitDir() . 'hooks/';
125
    }
126
127
    /**
128
     * @return string
129
     */
130
    protected function getInstallLockFile()
131
    {
132
        return $this->getHookPath() . 'ebi.lock';
133
    }
134
135
    /**
136
     * @return string[]
137
     */
138
    protected function getHookFiles()
139
    {
140
        $hookPath = $this->getHookPath();
141
142
        $result = [];
143
        foreach ((new \ReflectionClass(GitEvent::class))->getConstants() as $eventName) {
144
            $hook = explode(':', $eventName, 2);
145
146
            if (count($hook) === 2 && $hook[0] === 'git') {
147
                $result[$eventName] = $hookPath . $hook[1];
148
            }
149
        }
150
151
        return $result;
152
    }
153
}
154