Passed
Push — fix/serve-timeout ( 697f77...233bc8 )
by Arnaud
04:27
created

AbstractCommand   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 73
dl 0
loc 163
ccs 0
cts 54
cp 0
rs 10
c 1
b 0
f 0
wmc 28

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getPath() 0 3 1
A run() 0 17 4
C initialize() 0 41 12
A getConfigFiles() 0 3 1
A getBuilder() 0 22 5
A openEditor() 0 18 5
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 Builder */
53
    protected $builder;
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    protected function initialize(InputInterface $input, OutputInterface $output)
59
    {
60
        $this->input = $input;
61
        $this->output = $output;
62
        $this->io = new SymfonyStyle($input, $output);
63
        $this->fs = new Filesystem();
64
65
        if (!in_array($this->getName(), ['self-update'])) {
66
            // working directory
67
            $this->path = getcwd();
68
            if ($input->getArgument('path') !== null) {
69
                $this->path = (string) $input->getArgument('path');
70
            }
71
            if (realpath($this->getPath()) === false) {
72
                $this->fs->mkdir($this->getPath());
73
            }
74
            $this->path = realpath($this->getPath());
75
            // config file(s)
76
            if (!in_array($this->getName(), ['new:site'])) {
77
                // default
78
                $this->configFiles[self::CONFIG_FILE] = realpath(Util::joinFile($this->getPath(), self::CONFIG_FILE));
79
                // from --config=<file>
80
                if ($input->hasOption('config') && $input->getOption('config') !== null) {
81
                    foreach (explode(',', (string) $input->getOption('config')) as $configFile) {
82
                        $this->configFiles[$configFile] = realpath($configFile);
83
                        if (!Util\File::getFS()->isAbsolutePath($configFile)) {
84
                            $this->configFiles[$configFile] = realpath(Util::joinFile($this->getPath(), $configFile));
85
                        }
86
                    }
87
                }
88
                // checks file(s)
89
                foreach ($this->configFiles as $fileName => $filePath) {
90
                    if (!$filePath || !file_exists($filePath)) {
91
                        unset($this->configFiles[$fileName]);
92
                        $this->getBuilder()->getLogger()->error(\sprintf('Could not find configuration file "%s".', $fileName));
93
                    }
94
                }
95
            }
96
        }
97
98
        parent::initialize($input, $output);
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104
    public function run(InputInterface $input, OutputInterface $output)
105
    {
106
        if ($output->isDebug()) {
107
            putenv('CECIL_DEBUG=true');
108
109
            return parent::run($input, $output);
110
        }
111
        // simplified error message
112
        try {
113
            return parent::run($input, $output);
114
        } catch (\Exception $e) {
115
            if ($this->io === null) {
116
                $this->io = new SymfonyStyle($input, $output);
117
            }
118
            $this->io->error($e->getMessage());
119
120
            exit(1);
121
        }
122
    }
123
124
    /**
125
     * Returns the working directory.
126
     */
127
    protected function getPath(): ?string
128
    {
129
        return $this->path;
130
    }
131
132
    /**
133
     * Returns config file(s) path.
134
     */
135
    protected function getConfigFiles(): array
136
    {
137
        return array_unique($this->configFiles);
138
    }
139
140
    /**
141
     * Creates or returns a Builder instance.
142
     *
143
     * @throws RuntimeException
144
     */
145
    protected function getBuilder(array $config = []): Builder
146
    {
147
        try {
148
            $siteConfig = [];
149
            foreach ($this->getConfigFiles() as $fileName => $filePath) {
150
                if (false === $configContent = Util\File::fileGetContents($filePath)) {
151
                    throw new RuntimeException(\sprintf('Can\'t read configuration file "%s".', $fileName));
152
                }
153
                $siteConfig = array_replace_recursive($siteConfig, Yaml::parse($configContent));
154
            }
155
            $config = array_replace_recursive($siteConfig, $config);
156
157
            $this->builder = (new Builder($config, new ConsoleLogger($this->output)))
158
                ->setSourceDir($this->getPath())
159
                ->setDestinationDir($this->getPath());
160
        } catch (ParseException $e) {
161
            throw new RuntimeException(\sprintf('Configuration parsing error: %s', $e->getMessage()));
162
        } catch (\Exception $e) {
163
            throw new RuntimeException($e->getMessage());
164
        }
165
166
        return $this->builder;
167
    }
168
169
    /**
170
     * Opens path with editor.
171
     *
172
     * @throws RuntimeException
173
     */
174
    protected function openEditor(string $path, string $editor): void
175
    {
176
        $command = \sprintf('%s "%s"', $editor, $path);
177
        switch (Util\Plateform::getOS()) {
178
            case Util\Plateform::OS_WIN:
179
                $command = \sprintf('start /B "" %s "%s"', $editor, $path);
180
                break;
181
            case Util\Plateform::OS_OSX:
182
                // Typora on macOS
183
                if ($editor == 'typora') {
184
                    $command = \sprintf('open -a typora "%s"', $path);
185
                }
186
                break;
187
        }
188
        $process = Process::fromShellCommandline($command);
189
        $process->run();
190
        if (!$process->isSuccessful()) {
191
            throw new RuntimeException(\sprintf('Can\'t use "%s" editor.', $editor));
192
        }
193
    }
194
}
195