Passed
Push — master ( 8a6050...5a6ead )
by Andrii
11:18
created

Deployer::findDocker()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 11
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 17
rs 9.9
1
<?php
2
3
namespace hidev\components;
4
5
class Deployer
6
{
7
    private $dir;
8
    private $path;
9
    private $name;
10
    private $tier;
11
    private $dotenv;
12
13
    public function __construct(string $path)
14
    {
15
        $path = trim($path);
16
        if (strncmp($path, '/', 1) !== 0) {
17
            $path = getcwd() . '/' . $path;
18
        }
19
        if (!file_exists($path)) {
20
            throw new \Exception("no env file at $path");
21
        }
22
23
        $this->dir = dirname($path);
24
        $this->path = $path;
25
26
        $this->getComposer()->autoload();
27
28
        $file = basename($path);
29
        $ps = explode('.', $file, 3);
30
        $name = $ps[2];
31
        $ps = explode('-', $name);
32
33
        $this->file = $file;
0 ignored issues
show
Bug Best Practice introduced by
The property file does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
34
        $this->name = $name;
35
        $this->tier = $ps[1] ?? $name;
36
37
        $this->dotenv = $this->createDotenv($this->dir, $this->file);
38
        $this->dotenv->load();
39
40
        $this->checkHost();
41
    }
42
43
    private function checkHost()
44
    {
45
        $dir = basename($this->dir);
46
        $host = $this->getHost();
47
        if ($dir !== $host) {
48
            die("Dir:$dir and Host:$host doesn't match\n");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
49
        }
50
    }
51
52
    /**
53
     * Creates Dotenv object.
54
     * Supports both 2 and 3 version of `phpdotenv`
55
     * @param mixed $dir
56
     * @param mixed $file
57
     * @return Dotenv
58
     */
59
    private function createDotenv($dir, $file)
60
    {
61
        if (method_exists(Dotenv::class, 'create')) {
0 ignored issues
show
Bug introduced by
The type hidev\components\Dotenv was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
62
            return Dotenv::create($dir, $file);
63
        } else {
64
            return new Dotenv($dir, $file);
65
        }
66
    }
67
68
    public function call(string $name = null)
69
    {
70
        $name = $name ?: 'all';
71
        if (!method_exists($this, $name)) {
72
            throw new \Exception("wrong command: $name");
73
        }
74
75
        $this->{$name}();
76
    }
77
78
    public function all()
79
    {
80
        $this->symlinks();
81
        $this->chmod();
82
        $this->phplog();
83
        $this->up();
84
    }
85
86
    public function symlinks()
87
    {
88
        $envSrc = $this->getPath($this->file);
89
        $envDst = $this->getPath('.env');
90
        $docSrc = $this->findDocker();
91
        $docDst = $this->getPath('docker-compose.yml');
92
93
        sys::passthru("ln -sf $envSrc $envDst");
94
        sys::passthru("ln -sf $docSrc $docDst");
95
    }
96
97
    private function findDocker(): string
98
    {
99
        $path = $this->getPath("core/docker-compose.yml.{$this->name}");
100
        if (file_exists($path)) {
101
            return $path;
102
        }
103
        $path = $this->getPath("core/docker-compose.yml.{$this->tier}");
104
        if (file_exists($path)) {
105
            return $path;
106
        }
107
        $tier = $this->getAltTier();
108
        $path = $this->getPath("core/docker-compose.yml.$tier");
109
        if (file_exists($path)) {
110
            return $path;
111
        }
112
113
        throw new \Exception("no docker-compose file: $path");
114
    }
115
116
    private function getAltTier()
117
    {
118
        //return $this->tier === 'beta' ? 'dist' : $this->tier;
119
        return 'dist';
120
    }
121
122
    public function chmod()
123
    {
124
        sys::chmod('a+w', 'runtime public/assets');
125
    }
126
127
    public function phplog()
128
    {
129
        $phplog = $this->getPath('.docker/php/var/log/php/php.log');
130
        $dir = dirname($phplog);
131
        if (!file_exists($dir)) {
132
            sys::mkdir($dir);
133
        }
134
        if (!file_exists($phplog)) {
135
            sys::passthru("sudo touch $phplog");
136
        }
137
        sys::chmod('a+w', $phplog);
138
    }
139
140
    private $composer;
141
142
    private function getComposer()
143
    {
144
        if ($this->composer === null) {
145
            $this->composer = new Composer($this->dir);
146
        }
147
148
        return $this->composer;
149
    }
150
151
    private function up()
152
    {
153
        $this->refresh();
154
        $this->getSystemd()->up($this->genSystemdConfig());
155
    }
156
157
    private function down()
158
    {
159
        $this->getSystemd()->down();
160
    }
161
162
    private function restart()
163
    {
164
        $this->refresh();
165
        $this->getSystemd()->restart();
166
        $this->getSystemd()->status();
167
    }
168
169
    private function status()
170
    {
171
        $this->refresh();
172
        $this->getSystemd()->status();
173
    }
174
175
    private $systemd;
176
177
    private function getSystemd()
178
    {
179
        if ($this->systemd === null) {
180
            $this->systemd = new Systemd($this->getHost());
181
        }
182
183
        return $this->systemd;
184
    }
185
186
    public function refresh()
187
    {
188
        $this->getComposer()->dump();
189
    }
190
191
    private $systemdTemplate = 'core/systemd.service';
192
193
    private function genSystemdConfig()
194
    {
195
        return $this->genByTemplate($this->systemdTemplate);
196
    }
197
198
    private function genByTemplate($file): string
199
    {
200
        $tpl = file_get_contents($this->findTemplatePath($file));
0 ignored issues
show
Bug introduced by
$this->findTemplatePath($file) of type void is incompatible with the type string expected by parameter $filename of file_get_contents(). ( Ignorable by Annotation )

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

200
        $tpl = file_get_contents(/** @scrutinizer ignore-type */ $this->findTemplatePath($file));
Loading history...
Bug introduced by
Are you sure the usage of $this->findTemplatePath($file) targeting hidev\components\Deployer::findTemplatePath() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
201
        $subs = [];
202
        foreach ($this->getConfig() as $key => $value) {
203
            $subs["{{$key}}"] = $value;
204
        }
205
206
        return strtr($tpl, $subs);
207
    }
208
209
    /**
210
     * undocumented function
211
     *
212
     * @return void
213
     */
214
    private function findTemplatePath($file)
215
    {
216
        $path = $this->getPath($file);
217
        if (file_exists($path)) {
218
            return $path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path returns the type string which is incompatible with the documented return type void.
Loading history...
219
        }
220
        $path = __DIR__ . DIRECTORY_SEPARATOR . $file;
221
        if (file_exists($path)) {
222
            return $path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path returns the type string which is incompatible with the documented return type void.
Loading history...
223
        }
224
        $path = __DIR__ . DIRECTORY_SEPARATOR . basename($file);
225
        if (file_exists($path)) {
226
            return $path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path returns the type string which is incompatible with the documented return type void.
Loading history...
227
        }
228
229
        throw new \Exception("failed find `$file` template");
230
    }
231
232
    public function getConfig(): array
233
    {
234
        return array_merge(
235
            [
236
                'HOST'  => $this->getHost(),
237
                'DIR'   => $this->dir,
238
            ],
239
            $this->getEnv(),
240
        );
241
    }
242
243
    public function getEnv(): array
244
    {
245
        return $_ENV;
246
    }
247
248
    public function getHost(): string
249
    {
250
        $hosts = $this->getHosts();
251
        return $_ENV['HOST'] ?? reset($hosts);
252
    }
253
254
    public function getHosts(): array
255
    {
256
        if (!empty($_ENV['HOSTS'])) {
257
            $hosts = explode(',', $_ENV['HOSTS']);
258
        } elseif (!empty($_ENV['HOST'])) {
259
            $hosts = [$_ENV['HOSTS']];
260
        } else {
261
            throw new \Exception('no HOST given');
262
        }
263
        foreach ($hosts as &$host) {
264
            $host = trim($host);
265
        }
266
267
        return $hosts;
268
    }
269
270
    public function getPath(string $file): string
271
    {
272
        if (strncmp($file, '/', 1) === 0) {
273
            $path = $file;
274
        } else {
275
            $path = $this->dir . DIRECTORY_SEPARATOR . $file;
276
        }
277
278
        return $this->extractCwd($path);
279
    }
280
281
    private function extractCwd(string $path): string
282
    {
283
        $cwd = $this->getCwd();
284
        $len = strlen($cwd);
285
        if (strncmp($path, $cwd, $len) === 0) {
286
            $path = substr($path, $len+1);
287
        }
288
289
        return $path;
290
    }
291
292
    private $cwd;
293
294
    private function getCwd(): string
295
    {
296
        if ($this->cwd === null) {
297
            $this->cwd = \getcwd();
298
        }
299
300
        return $this->cwd;
301
    }
302
}
303