Installer::checkSignature()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the dotfiles project.
7
 *
8
 *     (c) Anthonius Munthi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Dotfiles\Plugins\Composer;
15
16
use Dotfiles\Core\Config\Config;
17
use Dotfiles\Core\Util\CommandProcessor;
18
use Dotfiles\Core\Util\Downloader;
19
use Dotfiles\Core\Util\Toolkit;
20
use Psr\Log\LoggerInterface;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Process\Process;
23
24
class Installer
25
{
26
    public const SCRIPT_URL = 'https://getcomposer.org/installer';
27
28
    public const SIG_URL = 'https://composer.github.io/installer.sig';
29
30
    /**
31
     * @var Config
32
     */
33
    private $config;
34
35
    /**
36
     * @var Downloader
37
     */
38
    private $downloader;
39
40
    /**
41
     * @var LoggerInterface
42
     */
43
    private $logger;
44
45
    /**
46
     * @var OutputInterface
47
     */
48
    private $output;
49
50
    /**
51
     * @var CommandProcessor
52
     */
53
    private $processor;
54
55
    public function __construct(
56
        OutputInterface $output,
57
        LoggerInterface $logger,
58
        Config $config,
59
        Downloader $downloader,
60
        CommandProcessor $processor
61
    ) {
62
        $this->config = $config;
63
        $this->logger = $logger;
64
        $this->output = $output;
65
        $this->downloader = $downloader;
66
        $this->processor = $processor;
67
    }
68
69
    /**
70
     * @param bool $force
71
     */
72
    public function run($force = false): void
73
    {
74
        $config = $this->config;
75
        $targetDir = $config->get('dotfiles.bin_dir');
76
        $targetFile = $targetDir.DIRECTORY_SEPARATOR.$config->get('composer.file_name');
77
        $this->debug('begin installation');
78
        $this->debug('target file: '.$targetFile);
79
80
        $this->debug('checking existing composer installation');
81
        if (is_file($targetFile) && !$force) {
82
            $this->output->writeln('Composer already installed, skipping');
83
84
            return;
85
        }
86
87
        $downloader = $this->downloader;
88
        $scriptFile = $config->get('dotfiles.temp_dir').'/composer/composer.php';
89
        $sigFile = $config->get('dotfiles.temp_dir').'/composer/composer.sig';
90
        Toolkit::ensureFileDir($scriptFile);
91
        Toolkit::ensureFileDir($sigFile);
92
93
        if (!is_file($sigFile)) {
94
            $downloader->run(static::SIG_URL, $sigFile);
95
        }
96
        if (!is_file($scriptFile)) {
97
            $this->debug('start downloading composer');
98
            $downloader->run(static::SCRIPT_URL, $scriptFile);
99
        }
100
        if (!$this->checkSignature($scriptFile, $sigFile)) {
101
            return;
102
        }
103
104
        // start executing script
105
        $this->executeInstallScript(
106
            $scriptFile,
107
            $config->get('dotfiles.bin_dir'),
108
            $config->get('composer.file_name')
109
        );
110
        if (is_file($targetFile)) {
111
            $this->output->writeln('composer successfully installed to <comment>'.$targetFile.'</comment>');
112
        }
113
    }
114
115
    private function checkSignature($scriptFile, $sigFile)
116
    {
117
        $actual = hash_file('SHA384', $scriptFile);
118
        $expected = trim(file_get_contents($sigFile));
119
        if ($expected !== $actual) {
120
            $this->output->writeln('<error>Signature Invalid</error>');
121
            unlink($scriptFile);
122
            unlink($sigFile);
123
124
            return false;
125
        }
126
        $this->debug('signature valid');
127
128
        return true;
129
    }
130
131
    private function debug($message, $context = array()): void
132
    {
133
        $message = '<comment>composer:</comment> '.$message;
134
        $this->logger->debug($message, $context);
135
    }
136
137
    private function executeInstallScript($scriptFile, $installDir, $fileName): void
138
    {
139
        $cmd = array(
140
            'php',
141
            $scriptFile,
142
            '--ansi',
143
            '--install-dir='.$installDir,
144
            '--filename='.$fileName,
145
        );
146
        $cmd = implode(' ', $cmd);
147
148
        $process = $this->processor->create($cmd);
149
        $process->setTimeout(3600);
150
        $process->setIdleTimeout(60);
151
        $process->run(function ($type, $buffer): void {
152
            //@codeCoverageIgnoreStart
153
            if (Process::ERR === $type) {
154
                $this->logger->error($buffer);
155
            } else {
156
                $this->debug($buffer);
157
            }
158
            //@codeCoverageIgnoreEnd
159
        });
160
    }
161
}
162