RunCommand   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 22
eloc 68
c 3
b 0
f 0
dl 0
loc 189
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A replaceVariables() 0 7 1
A executeScripts() 0 7 4
A executeCommand() 0 30 5
A getTemplateFileName() 0 13 3
A execute() 0 6 2
A configure() 0 17 1
A prepareTemplateFolder() 0 23 6
1
<?php
2
3
namespace Glamorous\Boiler;
4
5
use Glamorous\Boiler\Helpers\Template;
6
use Symfony\Component\Console\Input\InputArgument;
7
use Symfony\Component\Console\Input\InputInterface;
8
use Symfony\Component\Console\Input\InputOption;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Symfony\Component\Finder\Finder;
11
12
class RunCommand extends ConfigurationCommand
13
{
14
    /**
15
     * Input to get arguments from.
16
     *
17
     * @var InputInterface
18
     */
19
    private $input;
20
21
    /**
22
     * Array with the paths.
23
     *
24
     * @var array
25
     */
26
    protected $paths;
27
28
    /**
29
     * Array with all possible variables.
30
     *
31
     * @var array
32
     */
33
    protected $variables = [];
34
35
    /**
36
     * Configure the command options.
37
     */
38
    protected function configure()
39
    {
40
        $this
41
            ->setName('create')
42
            ->setDescription('Create a project based on the given template')
43
            ->addArgument('template', InputArgument::REQUIRED)
44
            ->addOption(
45
                'dir',
46
                null,
47
                InputOption::VALUE_OPTIONAL,
48
                'Which name must the directory to create have?'
49
            )
50
            ->addOption(
51
                'name',
52
                null,
53
                InputOption::VALUE_OPTIONAL,
54
                'Which name must the project have?'
55
            );
56
    }
57
58
    /**
59
     * Execute the command and catch exceptions.
60
     *
61
     * @param InputInterface $input
62
     * @param OutputInterface $output
63
     *
64
     * @return void
65
     */
66
    protected function execute(InputInterface $input, OutputInterface $output): void
67
    {
68
        try {
69
            $this->executeCommand($input, $output);
70
        } catch (BoilerException $exception) {
71
            $output->write('<error>' . $exception->getMessage() . '</error>');
72
        }
73
    }
74
75
    /**
76
     * Replace variables in given string.
77
     *
78
     * @param string $contents
79
     *
80
     * @return string
81
     */
82
    private function replaceVariables(string $contents): string
83
    {
84
        $variables = [
85
            '{#PROJECT_NAME#}' => $this->variables['template_name'],
86
        ];
87
88
        return str_replace(array_keys($variables), array_values($variables), $contents);
89
    }
90
91
    /**
92
     * Execute the scripts from the template.
93
     *
94
     * @param OutputInterface $output
95
     * @param array $template
96
     */
97
    protected function executeScripts(OutputInterface $output, array $template): void
98
    {
99
        foreach ($template['steps'] as $step) {
100
            $output->writeln('<info>Executing ' . $template[$step]['name'] . '</info>');
101
            $scripts = is_array($template[$step]['script']) ? $template[$step]['script'] : [$template[$step]['script']];
102
            foreach ($scripts as $script) {
103
                exec($this->replaceVariables($script));
104
            }
105
        }
106
    }
107
108
    /**
109
     * Internal method to execute the command.
110
     *
111
     * @param InputInterface $input
112
     * @param OutputInterface $output
113
     *
114
     * @throws BoilerException
115
     */
116
    private function executeCommand(InputInterface $input, OutputInterface $output): void
117
    {
118
        $this->input = $input;
119
120
        $this->paths = $this->configuration->getPaths();
121
122
        if (empty($this->paths)) {
123
            throw new BoilerException('No paths configured');
124
        }
125
126
        $templateFileName = $this->getTemplateFileName();
127
128
        $template = Template::getInstance()->searchTemplateByFilenameInGivenPaths($templateFileName, $this->paths);
129
130
        $templateName = $input->getOption('name') ? $input->getOption('name') : $template['name'];
131
        $directoryName = $input->getOption('dir') ?: $templateFileName;
132
133
        if (file_exists($directoryName)) {
134
            throw new BoilerException('Folder already exists');
135
        }
136
137
        $output->writeln('<info>Installing ' . $templateName . '</info>');
138
        $this->variables['template_name'] = $templateName;
139
140
        mkdir($directoryName);
141
        chdir($directoryName);
142
143
        $this->prepareTemplateFolder($templateFileName);
144
145
        $this->executeScripts($output, $template);
146
    }
147
148
    /**
149
     * Prepare the project directory by copying files from the template directory if available.
150
     *
151
     * @param string $name
152
     */
153
    protected function prepareTemplateFolder(string $name): void
154
    {
155
        $finder = new Finder();
156
        foreach ($this->paths as $directory) {
157
            $templateDir = $directory . '/' . $name;
158
            if (!is_dir($templateDir)) {
159
                continue;
160
            }
161
162
            $finder->in($templateDir)
163
                ->notName($name . '.yml')
164
                ->sortByType()
165
                ->ignoreDotFiles(false);
166
167
            foreach ($finder as $file) {
168
                if ($file->isDir()) {
169
                    mkdir($file->getFilename(), 0777, true);
170
                    continue;
171
                }
172
173
                if ($file->isFile()) {
174
                    file_put_contents($file->getRelativePathname(), $this->replaceVariables($file->getContents()));
175
                    continue;
176
                }
177
            }
178
        }//end foreach
179
    }
180
181
    /**
182
     * Get filename of the given template.
183
     *
184
     * @return string
185
     *
186
     * @throws BoilerException
187
     */
188
    private function getTemplateFileName(): string
189
    {
190
        $templateFileName = $this->input->getArgument('template');
191
192
        if (empty($templateFileName)) {
193
            throw new BoilerException('No template given.');
194
        }
195
196
        if (is_array($templateFileName)) {
197
            throw new BoilerException('Only one template is allowed.');
198
        }
199
200
        return $templateFileName;
201
    }
202
}
203