1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\Porter\Commands; |
4
|
|
|
|
5
|
|
|
use SilverStripe\Porter\Helpers\ValidationHelper; |
6
|
|
|
use Symfony\Component\Filesystem\Filesystem; |
7
|
|
|
use Symfony\Component\Console\Command\Command; |
8
|
|
|
use Symfony\Component\Console\Input\InputOption; |
9
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
10
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
11
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
12
|
|
|
use Symfony\Component\Yaml\Exception\RuntimeException; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Class CreateModuleCommand |
16
|
|
|
*/ |
17
|
|
|
class CreateModuleCommand extends Command |
18
|
|
|
{ |
19
|
|
|
const ARGUMENTS_MODULE_NAME = 'module-name'; |
20
|
|
|
const ARGUMENTS_MODULE_NAMESPACE = 'module-namespace'; |
21
|
|
|
const ARGUMENTS_MODULE_PATH = 'module-path'; |
22
|
|
|
const OPTIONS_SS3 = 'ss3'; |
23
|
|
|
const OPTIONS_NON_VENDOR = 'nonVendor'; |
24
|
|
|
const OPTIONS_TRAVIS_CI = 'withTravisCI'; |
25
|
|
|
const OPTIONS_CIRCLE_CI = 'withCircleCI'; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var string |
29
|
|
|
*/ |
30
|
|
|
private $moduleName; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var string |
34
|
|
|
*/ |
35
|
|
|
private $namespace; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var string |
39
|
|
|
*/ |
40
|
|
|
private $moduleType = 'silverstripe-vendormodule'; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var string |
44
|
|
|
*/ |
45
|
|
|
private $modulePath; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var Filesystem |
49
|
|
|
*/ |
50
|
|
|
private $fileSystem; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var string The target frameowkr version |
54
|
|
|
*/ |
55
|
|
|
private $frameworkVersion = 'ss4'; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var string |
59
|
|
|
*/ |
60
|
|
|
private $separator = DIRECTORY_SEPARATOR; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Configures the command |
64
|
|
|
*/ |
65
|
|
View Code Duplication |
protected function configure() |
|
|
|
|
66
|
|
|
{ |
67
|
|
|
$this->setName('create-module') |
68
|
|
|
->setDescription('Sets up a new SilverStripe module skeleton at a' |
69
|
|
|
. ' path you specify (defaults to using getcwd())') |
70
|
|
|
->addArgument(self::ARGUMENTS_MODULE_NAME, InputArgument::REQUIRED) |
71
|
|
|
->addArgument(self::ARGUMENTS_MODULE_NAMESPACE, InputArgument::REQUIRED) |
72
|
|
|
->addOption(self::OPTIONS_NON_VENDOR, null, InputOption::VALUE_NONE) |
73
|
|
|
->addOption(self::OPTIONS_SS3, null, InputOption::VALUE_NONE) |
74
|
|
|
->addOption(self::OPTIONS_TRAVIS_CI, null, InputOption::VALUE_NONE) |
75
|
|
|
->addOption(self::OPTIONS_CIRCLE_CI, null, InputOption::VALUE_NONE); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Executes this command |
80
|
|
|
* @param InputInterface $input |
81
|
|
|
* @param OutputInterface $output |
82
|
|
|
* @return int|null|void |
83
|
|
|
*/ |
84
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
85
|
|
|
{ |
86
|
|
|
$this->setArguments($input); |
87
|
|
|
$output->writeln( |
88
|
|
|
"Creating SilverStripe module named {$this->moduleName} " |
89
|
|
|
. "at {$this->getModulePath()}" |
90
|
|
|
); |
91
|
|
|
$this->preCopyOptions($input); |
92
|
|
|
$this->copySkeleton(); |
93
|
|
|
$output->writeln(' - Skeleton copied'); |
94
|
|
|
$this->setupComposerJson(); |
95
|
|
|
$output->writeln(' - composer.json updated'); |
96
|
|
|
$this->postCopyOptions($input); |
97
|
|
|
$output->writeln(' - Options copied'); |
98
|
|
|
$output->writeln(' - Done'); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Applies any pre copy options |
103
|
|
|
* @param InputInterface $input |
104
|
|
|
*/ |
105
|
|
|
protected function preCopyOptions(InputInterface $input) |
106
|
|
|
{ |
107
|
|
|
if ($input->getOption(self::OPTIONS_NON_VENDOR)) { |
108
|
|
|
$this->moduleType = 'silverstripe-module'; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
if ($ss3 = $input->getOption(self::OPTIONS_SS3)) { |
|
|
|
|
112
|
|
|
$this->frameworkVersion = 'ss3'; |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Applies any post copy options |
118
|
|
|
* @param InputInterface $input |
119
|
|
|
*/ |
120
|
|
|
protected function postCopyOptions(InputInterface $input) |
121
|
|
|
{ |
122
|
|
|
$source = $this->getSourcePath('options'); |
123
|
|
|
$target = $this->getTargetPath(); |
124
|
|
|
$uri = function ($folder, $endPoint) { |
125
|
|
|
return "{$folder}{$this->separator}{$endPoint}"; |
126
|
|
|
}; |
127
|
|
|
|
128
|
|
View Code Duplication |
if ($withTravis = $input->getOption(self::OPTIONS_TRAVIS_CI)) { |
|
|
|
|
129
|
|
|
$file = '.travis.yml'; |
130
|
|
|
$this->getFilesystem()->copy( |
131
|
|
|
$uri($source, $file), |
132
|
|
|
$uri($target, $file) |
133
|
|
|
); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
View Code Duplication |
if ($withCircleCI = $input->getOption(self::OPTIONS_CIRCLE_CI)) { |
|
|
|
|
137
|
|
|
$folder = '.circleci'; |
138
|
|
|
$this->getFilesystem()->mirror( |
139
|
|
|
$uri($source, $folder), |
140
|
|
|
$uri($target, $folder) |
141
|
|
|
); |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Sets the argument values to their respective properties |
147
|
|
|
* @param InputInterface $input |
148
|
|
|
* @throws RuntimeException |
149
|
|
|
*/ |
150
|
|
View Code Duplication |
protected function setArguments(InputInterface $input) |
|
|
|
|
151
|
|
|
{ |
152
|
|
|
$this->moduleName = $input->getArgument(self::ARGUMENTS_MODULE_NAME); |
153
|
|
|
$this->namespace = $input->getArgument(self::ARGUMENTS_MODULE_NAMESPACE); |
154
|
|
|
|
155
|
|
|
ValidationHelper::validateModuleName($this->moduleName); |
156
|
|
|
ValidationHelper::validateNamespace($this->namespace); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Copies the skeleton to the root directory |
161
|
|
|
*/ |
162
|
|
|
protected function copySkeleton() |
163
|
|
|
{ |
164
|
|
|
$this->getFilesystem()->mirror( |
165
|
|
|
$this->getSourcePath(), |
166
|
|
|
$this->getTargetPath() |
167
|
|
|
); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Copies the configured values to the composer.json file |
172
|
|
|
*/ |
173
|
|
|
protected function setupComposerJson() |
174
|
|
|
{ |
175
|
|
|
$search = [ |
176
|
|
|
'$moduleName', |
177
|
|
|
'$moduleType', |
178
|
|
|
'$namespace' |
179
|
|
|
]; |
180
|
|
|
$replace = [ |
181
|
|
|
$this->moduleName, |
182
|
|
|
$this->moduleType, |
183
|
|
|
$this->namespace |
184
|
|
|
]; |
185
|
|
|
|
186
|
|
|
$contents = str_ireplace($search, $replace, $this->getComposerFileContents()); |
187
|
|
|
$this->getFilesystem()->dumpFile($this->getComposerFilePath(), $contents); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Returns the path to the composer file |
192
|
|
|
* @return string |
193
|
|
|
*/ |
194
|
|
|
public function getComposerFilePath() |
195
|
|
|
{ |
196
|
|
|
return $this->getTargetPath() . DIRECTORY_SEPARATOR . 'composer.json'; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Returns the content of the composer file |
201
|
|
|
* @return bool|string |
202
|
|
|
*/ |
203
|
|
|
public function getComposerFileContents() |
204
|
|
|
{ |
205
|
|
|
return file_get_contents($this->getComposerFilePath()); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Gets or sets the file system property |
210
|
|
|
* @return Filesystem |
211
|
|
|
*/ |
212
|
|
|
public function getFilesystem() |
213
|
|
|
{ |
214
|
|
|
if (!$this->fileSystem) { |
215
|
|
|
$this->fileSystem = new Filesystem(); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
return $this->fileSystem; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Returns the path to the given sub-dir. Defaults to assets skeleton |
223
|
|
|
* @param string $subDir |
224
|
|
|
* @return string |
225
|
|
|
*/ |
226
|
|
|
public function getSourcePath($subDir = 'assets') |
227
|
|
|
{ |
228
|
|
|
$porterDir = __DIR__; |
229
|
|
|
return "{$porterDir}{$this->separator}{$subDir}{$this->separator}{$this->frameworkVersion}-skeleton"; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Gets destination path for the module skeleton |
234
|
|
|
* @return string |
235
|
|
|
*/ |
236
|
|
|
public function getTargetPath() |
237
|
|
|
{ |
238
|
|
|
$folderName = substr($this->moduleName, stripos($this->moduleName, DIRECTORY_SEPARATOR) + 1); |
239
|
|
|
return $this->getModulePath() . $this->separator . $folderName; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Returns the module path. Defaults to use getcwd(); |
244
|
|
|
* @return string |
245
|
|
|
*/ |
246
|
|
|
public function getModulePath() |
247
|
|
|
{ |
248
|
|
|
if (!$this->modulePath) { |
249
|
|
|
$this->modulePath = getcwd(); |
250
|
|
|
} |
251
|
|
|
return $this->modulePath; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* @param string $modulePath |
256
|
|
|
* @return CreateModuleCommand |
257
|
|
|
*/ |
258
|
|
|
public function setModulePath($modulePath) |
259
|
|
|
{ |
260
|
|
|
$this->modulePath = $modulePath; |
261
|
|
|
return $this; |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.