Passed
Push — fix/config ( 98677b )
by Arnaud
04:42
created

AbstractCommand::getBuilder()   B

Complexity

Conditions 8
Paths 20

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 8
eloc 17
nc 20
nop 1
dl 0
loc 27
rs 8.4444
c 1
b 1
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Cecil.
7
 *
8
 * Copyright (c) Arnaud Ligny <[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 Cecil\Command;
15
16
use Cecil\Builder;
17
use Cecil\Exception\RuntimeException;
18
use Cecil\Logger\ConsoleLogger;
19
use Cecil\Util;
20
use Symfony\Component\Console\Command\Command;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Style\SymfonyStyle;
24
use Symfony\Component\Filesystem\Filesystem;
25
use Symfony\Component\Process\Process;
26
use Symfony\Component\Yaml\Exception\ParseException;
27
use Symfony\Component\Yaml\Yaml;
28
29
class AbstractCommand extends Command
30
{
31
    const CONFIG_FILE = 'config.yml';
32
    const TMP_DIR = '.cecil';
33
34
    /** @var InputInterface */
35
    protected $input;
36
37
    /** @var OutputInterface */
38
    protected $output;
39
40
    /** @var SymfonyStyle */
41
    protected $io;
42
43
    /** @var Filesystem */
44
    protected $fs;
45
46
    /** @var string */
47
    protected $path;
48
49
    /** @var array */
50
    protected $configFiles;
51
52
    /** @var array */
53
    protected $config;
54
55
    /** @var Builder */
56
    protected $builder;
57
58
    /**
59
     * {@inheritdoc}
60
     */
61
    protected function initialize(InputInterface $input, OutputInterface $output)
62
    {
63
        $this->input = $input;
64
        $this->output = $output;
65
        $this->io = new SymfonyStyle($input, $output);
66
        $this->fs = new Filesystem();
67
68
        if (!in_array($this->getName(), ['self-update'])) {
69
            // working directory
70
            $this->path = getcwd();
71
            if ($input->getArgument('path') !== null) {
72
                $this->path = (string) $input->getArgument('path');
73
            }
74
            if (realpath($this->getPath()) === false) {
75
                $this->fs->mkdir($this->getPath());
76
            }
77
            $this->path = realpath($this->getPath());
78
            // config file(s)
79
            if (!in_array($this->getName(), ['new:site'])) {
80
                // default
81
                $this->configFiles[self::CONFIG_FILE] = realpath(Util::joinFile($this->getPath(), self::CONFIG_FILE));
82
                // from --config=<file>
83
                if ($input->hasOption('config') && $input->getOption('config') !== null) {
84
                    foreach (explode(',', (string) $input->getOption('config')) as $configFile) {
85
                        $this->configFiles[$configFile] = realpath($configFile);
86
                        if (!Util\File::getFS()->isAbsolutePath($configFile)) {
87
                            $this->configFiles[$configFile] = realpath(Util::joinFile($this->getPath(), $configFile));
88
                        }
89
                    }
90
                }
91
                // checks file(s)
92
                foreach ($this->configFiles as $fileName => $filePath) {
93
                    if ($filePath === false || !file_exists($filePath)) {
94
                        unset($this->configFiles[$fileName]);
95
                        $this->getBuilder()->getLogger()->error(\sprintf('Could not find configuration file "%s".', $fileName));
96
                    }
97
                }
98
            }
99
        }
100
101
        parent::initialize($input, $output);
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    public function run(InputInterface $input, OutputInterface $output)
108
    {
109
        if ($output->isDebug()) {
110
            putenv('CECIL_DEBUG=true');
111
112
            return parent::run($input, $output);
113
        }
114
        // simplified error message
115
        try {
116
            return parent::run($input, $output);
117
        } catch (\Exception $e) {
118
            if ($this->io === null) {
119
                $this->io = new SymfonyStyle($input, $output);
120
            }
121
            $this->io->error($e->getMessage());
122
123
            exit(1);
124
        }
125
    }
126
127
    /**
128
     * Returns the working directory.
129
     */
130
    protected function getPath(): ?string
131
    {
132
        return $this->path;
133
    }
134
135
    /**
136
     * Returns config file(s) path.
137
     */
138
    protected function getConfigFiles(): array
139
    {
140
        return array_unique($this->configFiles);
141
    }
142
143
    /**
144
     * Creates or returns a Builder instance.
145
     *
146
     * @throws RuntimeException
147
     */
148
    protected function getBuilder(array $config = []): Builder
149
    {
150
        try {
151
            // config
152
            if ($this->config === null) {
153
                $siteConfig = [];
154
                foreach ($this->getConfigFiles() as $fileName => $filePath) {
155
                    if ($filePath === false || false === $configContent = Util\File::fileGetContents($filePath)) {
156
                        throw new RuntimeException(\sprintf('Can\'t read configuration file "%s".', $fileName));
157
                    }
158
                    $siteConfig = array_replace_recursive($siteConfig, Yaml::parse($configContent));
159
                }
160
                $this->config = array_replace_recursive($siteConfig, $config);
161
            }
162
            // builder
163
            if ($this->builder === null) {
164
                $this->builder = (new Builder($this->config, new ConsoleLogger($this->output)))
165
                    ->setSourceDir($this->getPath())
166
                    ->setDestinationDir($this->getPath());
167
            }
168
        } catch (ParseException $e) {
169
            throw new RuntimeException(\sprintf('Configuration parsing error: %s', $e->getMessage()));
170
        } catch (\Exception $e) {
171
            throw new RuntimeException($e->getMessage());
172
        }
173
174
        return $this->builder;
175
    }
176
177
    /**
178
     * Opens path with editor.
179
     *
180
     * @throws RuntimeException
181
     */
182
    protected function openEditor(string $path, string $editor): void
183
    {
184
        $command = \sprintf('%s "%s"', $editor, $path);
185
        switch (Util\Plateform::getOS()) {
186
            case Util\Plateform::OS_WIN:
187
                $command = \sprintf('start /B "" %s "%s"', $editor, $path);
188
                break;
189
            case Util\Plateform::OS_OSX:
190
                // Typora on macOS
191
                if ($editor == 'typora') {
192
                    $command = \sprintf('open -a typora "%s"', $path);
193
                }
194
                break;
195
        }
196
        $process = Process::fromShellCommandline($command);
197
        $process->run();
198
        if (!$process->isSuccessful()) {
199
            throw new RuntimeException(\sprintf('Can\'t use "%s" editor.', $editor));
200
        }
201
    }
202
}
203