Passed
Push — master ( f7067e...90d382 )
by Arnaud
06:09
created

AbstractCommand::getPath()   B

Complexity

Conditions 7
Paths 15

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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