This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace DrawMyAttention\ResourceGenerator\Commands; |
||
4 | |||
5 | use Illuminate\Support\Str; |
||
6 | use Illuminate\Console\Command; |
||
7 | use Illuminate\Support\Composer; |
||
8 | use Illuminate\Filesystem\Filesystem; |
||
9 | |||
10 | class ResourceMakeCommand extends Command |
||
11 | { |
||
12 | /** |
||
13 | * The name and signature of the console command. |
||
14 | * |
||
15 | * @var string |
||
16 | */ |
||
17 | protected $signature = 'make:resource {name : The model name} {attributes?}'; |
||
18 | |||
19 | /** |
||
20 | * The console command description. |
||
21 | * |
||
22 | * @var string |
||
23 | */ |
||
24 | protected $description = 'Create a new model, migration, controller and add routes'; |
||
25 | |||
26 | /** |
||
27 | * The filesystem instance. |
||
28 | * |
||
29 | * @var \Illuminate\Filesystem\Filesystem |
||
30 | */ |
||
31 | private $files; |
||
32 | |||
33 | /** |
||
34 | * @var Composer |
||
35 | */ |
||
36 | private $composer; |
||
37 | |||
38 | /** |
||
39 | * @var array The data types that can be created in a migration. |
||
40 | */ |
||
41 | private $dataTypes = [ |
||
42 | 'string', 'integer', 'boolean', 'bigIncrements', 'bigInteger', |
||
43 | 'binary', 'boolean', 'char', 'date', 'dateTime', 'float', 'increments', |
||
44 | 'json', 'jsonb', 'longText', 'mediumInteger', 'mediumText', 'nullableTimestamps', |
||
45 | 'smallInteger', 'tinyInteger', 'softDeletes', 'text', 'time', 'timestamp', |
||
46 | 'timestamps', 'rememberToken', |
||
47 | ]; |
||
48 | |||
49 | private $fakerMethods = [ |
||
50 | 'string' => ['method' => 'words', 'parameters' => '2, true'], |
||
51 | 'integer' => ['method' => 'randomNumber', 'parameters' => ''], |
||
52 | ]; |
||
53 | |||
54 | /** |
||
55 | * @var array $columnProperties Properties that can be applied to a table column. |
||
56 | */ |
||
57 | private $columnProperties = [ |
||
58 | 'unsigned', 'index', 'nullable' |
||
59 | ]; |
||
60 | |||
61 | /** |
||
62 | * Create a new command instance. |
||
63 | * |
||
64 | * @param Filesystem $files |
||
65 | * @param Composer $composer |
||
66 | */ |
||
67 | public function __construct(Filesystem $files, Composer $composer) |
||
68 | { |
||
69 | parent::__construct(); |
||
70 | |||
71 | $this->files = $files; |
||
72 | |||
73 | $this->composer = $composer; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * Execute the console command. |
||
78 | * |
||
79 | * @return mixed |
||
80 | */ |
||
81 | public function handle() |
||
82 | { |
||
83 | $name = trim($this->input->getArgument('name')); |
||
84 | |||
85 | $this->createModel($name); |
||
86 | |||
87 | $this->createMigration($name); |
||
88 | |||
89 | $this->createController($name); |
||
90 | |||
91 | $this->appendRoutes($name); |
||
92 | |||
93 | $this->createModelFactory($name); |
||
94 | } |
||
95 | |||
96 | private function createModelFactory($name) |
||
97 | { |
||
98 | $model = $this->modelName($name); |
||
99 | |||
100 | $stub = $this->files->get(__DIR__ . '/../Stubs/factory.stub'); |
||
101 | |||
102 | $stub = str_replace('CLASSNAME', $model, $stub); |
||
103 | |||
104 | $class = 'App\\' . $model; |
||
105 | $model = new $class; |
||
106 | |||
107 | $stub = str_replace( |
||
108 | 'ATTRIBUTES', |
||
109 | $this->buildFakerAttributes($model->migrationAttributes()), |
||
110 | $stub |
||
111 | ); |
||
112 | |||
113 | $this->files->append(database_path('factories/ModelFactory.php'), $stub); |
||
114 | |||
115 | $this->info('Created model factory'); |
||
116 | |||
117 | return true; |
||
118 | } |
||
119 | |||
120 | public function buildFakerAttributes($attributes) |
||
121 | { |
||
122 | $faker = ''; |
||
123 | |||
124 | foreach ($attributes as $attribute) { |
||
125 | |||
126 | $formatter = |
||
127 | $this->fakerMethods[$this->getFieldTypeFromProperties($attribute['properties'])]; |
||
128 | |||
129 | $method = $formatter['method']; |
||
130 | $parameters = $formatter['parameters']; |
||
131 | |||
132 | $faker .= "'".$attribute['name']."' => \$faker->".$method."(".$parameters.")," . PHP_EOL . ' '; |
||
133 | |||
134 | } |
||
135 | |||
136 | return rtrim($faker); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * Create and store a new Model to the filesystem. |
||
141 | * |
||
142 | * @param string $name |
||
143 | * @return bool |
||
144 | */ |
||
145 | private function createModel($name) |
||
146 | { |
||
147 | $modelName = $this->modelName($name); |
||
148 | |||
149 | $filename = $modelName . '.php'; |
||
150 | |||
151 | if ($this->files->exists(app_path($filename))) { |
||
152 | $this->error('Model already exists!'); |
||
153 | return false; |
||
154 | } |
||
155 | |||
156 | $model = $this->buildModel($name); |
||
157 | |||
158 | $this->files->put(app_path('/' . $filename), $model); |
||
159 | |||
160 | $this->info($modelName . ' Model created'); |
||
161 | |||
162 | return true; |
||
163 | } |
||
164 | |||
165 | private function createMigration($name) |
||
166 | { |
||
167 | $filename = $this->buildMigrationFilename($name); |
||
168 | |||
169 | if ($this->files->exists(database_path($filename))) { |
||
170 | $this->error('Migration already exists!'); |
||
171 | return false; |
||
172 | } |
||
173 | |||
174 | $migration = $this->buildMigration($name); |
||
175 | |||
176 | $this->files->put( |
||
177 | database_path('/migrations/' . $filename), |
||
178 | $migration |
||
179 | ); |
||
180 | |||
181 | if (env('APP_ENV') != 'testing') { |
||
182 | $this->composer->dumpAutoloads(); |
||
183 | } |
||
184 | |||
185 | $this->info('Created migration ' . $filename); |
||
186 | |||
187 | return true; |
||
188 | } |
||
189 | |||
190 | private function createController($modelName) |
||
191 | { |
||
192 | $filename = ucfirst($modelName) . 'Controller.php'; |
||
193 | |||
194 | if ($this->files->exists(app_path('Http/' . $filename))) { |
||
195 | $this->error('Controller already exists!'); |
||
196 | return false; |
||
197 | } |
||
198 | |||
199 | $stub = $this->files->get(__DIR__ . '/../Stubs/controller.stub'); |
||
200 | |||
201 | $stub = str_replace('MyModelClass', ucfirst($modelName), $stub); |
||
202 | $stub = str_replace('myModelInstance', Str::camel($modelName), $stub); |
||
203 | $stub = str_replace('template', strtolower($modelName), $stub); |
||
204 | |||
205 | $this->files->put(app_path('Http/Controllers/' . $filename), $stub); |
||
206 | |||
207 | $this->info('Created controller ' . $filename); |
||
208 | |||
209 | return true; |
||
210 | } |
||
211 | |||
212 | private function appendRoutes($modelName) |
||
213 | { |
||
214 | $modelTitle = ucfirst($modelName); |
||
215 | |||
216 | $modelName = strtolower($modelName); |
||
217 | |||
218 | $newRoutes = $this->files->get(__DIR__ . '/../Stubs/routes.stub'); |
||
219 | |||
220 | $newRoutes = str_replace('|MODEL_TITLE|', $modelTitle, $newRoutes); |
||
221 | |||
222 | $newRoutes = str_replace('|MODEL_NAME|', $modelName, $newRoutes); |
||
223 | |||
224 | $newRoutes = str_replace('|CONTROLLER_NAME|', $modelTitle . 'Controller', $newRoutes); |
||
225 | |||
226 | $this->files->append( |
||
227 | app_path('Http/routes.php'), |
||
228 | $newRoutes |
||
229 | ); |
||
230 | |||
231 | $this->info('Added routes for ' . $modelTitle); |
||
232 | } |
||
233 | |||
234 | protected function buildMigration($name) |
||
235 | { |
||
236 | $stub = $this->files->get(__DIR__ . '/../Stubs/migration.stub'); |
||
237 | |||
238 | $className = 'Create' . Str::plural($name). 'Table'; |
||
239 | |||
240 | $stub = str_replace('MIGRATION_CLASS_PLACEHOLDER', $className, $stub); |
||
241 | |||
242 | $table = strtolower(Str::plural($name)); |
||
243 | |||
244 | $stub = str_replace('TABLE_NAME_PLACEHOLDER', $table, $stub); |
||
245 | |||
246 | $class = 'App\\' . $name; |
||
247 | $model = new $class; |
||
248 | |||
249 | $stub = str_replace('MIGRATION_COLUMNS_PLACEHOLDER', $this->buildTableColumns($model->migrationAttributes()), $stub); |
||
250 | |||
251 | return $stub; |
||
252 | } |
||
253 | |||
254 | protected function buildModel($name) |
||
255 | { |
||
256 | $stub = $this->files->get(__DIR__ . '/../Stubs/model.stub'); |
||
257 | |||
258 | $stub = $this->replaceClassName($name, $stub); |
||
259 | |||
260 | $stub = $this->addMigrationAttributes($this->argument('attributes'), $stub); |
||
261 | |||
262 | $stub = $this->addModelAttributes('fillable', $this->argument('attributes'), $stub); |
||
263 | |||
264 | $stub = $this->addModelAttributes('hidden', $this->argument('attributes'), $stub); |
||
265 | |||
266 | return $stub; |
||
267 | } |
||
268 | |||
269 | public function convertModelToTableName($model) |
||
270 | { |
||
271 | return Str::plural(Str::snake($model)); |
||
272 | } |
||
273 | |||
274 | public function buildMigrationFilename($model) |
||
275 | { |
||
276 | $table = $this->convertModelToTableName($model); |
||
277 | |||
278 | return date('Y_m_d_his') . '_create_' . $table . '_table.php'; |
||
279 | } |
||
280 | |||
281 | private function replaceClassName($name, $stub) |
||
282 | { |
||
283 | return str_replace('NAME_PLACEHOLDER', $name, $stub); |
||
284 | } |
||
285 | |||
286 | private function addMigrationAttributes($text, $stub) |
||
287 | { |
||
288 | $attributesAsArray = $this->parseAttributesFromInputString($text); |
||
289 | $attributesAsText = $this->convertArrayToString($attributesAsArray); |
||
290 | |||
291 | return str_replace('MIGRATION_ATTRIBUTES_PLACEHOLDER', $attributesAsText, $stub); |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * Convert a pipe-separated list of attributes to an array. |
||
296 | * |
||
297 | * @param string $text The Pipe separated attributes |
||
298 | * @return array |
||
299 | */ |
||
300 | public function parseAttributesFromInputString($text) |
||
301 | { |
||
302 | $parts = explode('|', $text); |
||
303 | |||
304 | $attributes = []; |
||
305 | |||
306 | foreach ($parts as $part) { |
||
307 | $components = explode(':', $part); |
||
308 | $attributes[$components[0]] = |
||
309 | isset($components[1]) ? explode(',', $components[1]) : []; |
||
310 | } |
||
311 | |||
312 | return $attributes; |
||
313 | |||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Convert a PHP array into a string version. |
||
318 | * |
||
319 | * @param $array |
||
320 | * |
||
321 | * @return string |
||
322 | */ |
||
323 | public function convertArrayToString($array) |
||
324 | { |
||
325 | $string = '['; |
||
326 | |||
327 | foreach ($array as $name => $properties) { |
||
328 | $string .= '['; |
||
329 | $string .= "'name' => '" . $name . "',"; |
||
330 | |||
331 | $string .= "'properties' => ["; |
||
332 | foreach ($properties as $property) { |
||
333 | $string .= "'".$property."', "; |
||
334 | } |
||
335 | $string = rtrim($string, ', '); |
||
336 | $string .= ']'; |
||
337 | |||
338 | $string .= '],'; |
||
339 | } |
||
340 | |||
341 | $string = rtrim($string, ','); |
||
342 | |||
343 | $string .= ']'; |
||
344 | |||
345 | |||
346 | return $string; |
||
347 | } |
||
348 | |||
349 | public function addModelAttributes($name, $attributes, $stub) |
||
350 | { |
||
351 | $attributes = '[' . collect($this->parseAttributesFromInputString($attributes)) |
||
352 | ->filter(function($attribute) use ($name) { |
||
353 | return in_array($name, $attribute); |
||
354 | })->map(function ($attributes, $name) { |
||
355 | return "'" . $name . "'"; |
||
356 | })->values()->implode(', ') . ']'; |
||
357 | |||
358 | |||
359 | return str_replace(strtoupper($name) . '_PLACEHOLDER', $attributes, $stub); |
||
360 | } |
||
361 | |||
362 | public function buildTableColumns($attributes) |
||
363 | { |
||
364 | |||
365 | return rtrim(collect($attributes)->reduce(function($column, $attribute) { |
||
366 | |||
367 | $fieldType = $this->getFieldTypeFromProperties($attribute['properties']); |
||
368 | |||
369 | if ($length = $this->typeCanDefineSize($fieldType)) { |
||
370 | $length = $this->extractFieldLengthValue($attribute['properties']); |
||
371 | } |
||
372 | |||
373 | $properties = $this->extractAttributePropertiesToApply($attribute['properties']); |
||
374 | |||
375 | return $column . $this->buildSchemaColumn($fieldType, $attribute['name'], $length, $properties); |
||
376 | |||
377 | })); |
||
378 | |||
379 | } |
||
380 | |||
381 | /** |
||
382 | * Get the column field type based from the properties of the field being created. |
||
383 | * |
||
384 | * @param array $properties |
||
385 | * @return string |
||
386 | */ |
||
387 | private function getFieldTypeFromProperties($properties) |
||
388 | { |
||
389 | $type = array_intersect($properties, $this->dataTypes); |
||
390 | |||
391 | // If the properties that were given in the command |
||
392 | // do not explicitly define a data type, or there |
||
393 | // is no matching data type found, the column |
||
394 | // should be cast to a string. |
||
395 | |||
396 | if (! $type) { |
||
0 ignored issues
–
show
|
|||
397 | return 'string'; |
||
398 | } |
||
399 | |||
400 | return $type[0]; |
||
401 | } |
||
402 | |||
403 | /** |
||
404 | * Can the data type have it's size controlled within the migration? |
||
405 | * |
||
406 | * @param string $type |
||
407 | * @return bool |
||
408 | */ |
||
409 | private function typeCanDefineSize($type) |
||
410 | { |
||
411 | return $type == 'string' || $type == 'char'; |
||
412 | } |
||
413 | |||
414 | /** |
||
415 | * Extract a numeric length value from all properties specified for the attribute. |
||
416 | * |
||
417 | * @param array $properties |
||
418 | * @return int $length |
||
419 | */ |
||
420 | private function extractFieldLengthValue($properties) |
||
421 | { |
||
422 | foreach ($properties as $property) { |
||
423 | if (is_numeric($property)) { |
||
424 | return $property; |
||
425 | } |
||
426 | } |
||
427 | |||
428 | return 0; |
||
429 | } |
||
430 | |||
431 | /** |
||
432 | * Get the column properties that should be applied to the column. |
||
433 | * |
||
434 | * @param $properties |
||
435 | * @return array |
||
436 | */ |
||
437 | private function extractAttributePropertiesToApply($properties) |
||
438 | { |
||
439 | return array_intersect($properties, $this->columnProperties); |
||
440 | } |
||
441 | |||
442 | /** |
||
443 | * Create a Schema Builder column. |
||
444 | * |
||
445 | * @param string $fieldType The type of column to create |
||
446 | * @param string $name Name of the column to create |
||
447 | * @param int $length Field length |
||
448 | * @param array $traits Additional properties to apply to the column |
||
449 | * @return string |
||
450 | */ |
||
451 | private function buildSchemaColumn($fieldType, $name, $length = 0, $traits = []) |
||
452 | { |
||
453 | return sprintf("\$table->%s('%s'%s)%s;" . PHP_EOL . ' ', |
||
454 | $fieldType, |
||
455 | $name, |
||
456 | $length > 0 ? ", $length" : '', |
||
457 | implode('', array_map(function ($trait) { |
||
458 | return '->' . $trait . '()'; |
||
459 | }, $traits)) |
||
460 | ); |
||
461 | } |
||
462 | |||
463 | /** |
||
464 | * Build a Model name from a word. |
||
465 | * |
||
466 | * @param string $name |
||
467 | * @return string |
||
468 | */ |
||
469 | private function modelName($name) |
||
470 | { |
||
471 | return ucfirst($name); |
||
472 | } |
||
473 | |||
474 | } |
||
475 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.