Test Setup Failed
Push — master ( a6c33a...a3b2d5 )
by Php Easy Api
04:08
created

Generator   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 673
Duplicated Lines 0 %

Importance

Changes 4
Bugs 3 Features 0
Metric Value
eloc 205
dl 0
loc 673
rs 2.4
c 4
b 3
f 0
wmc 75

24 Methods

Rating   Name   Duplication   Size   Complexity  
A createPath() 0 5 3
A replacementVariables() 0 8 1
A createClassUse() 0 16 3
A createClassDocument() 0 21 3
A replaceFileContent() 0 18 4
A createMethodParameters() 0 11 2
A createClassPropertyDocument() 0 21 3
B createClassProperty() 0 30 8
A __construct() 0 15 2
A createClassExtend() 0 31 5
A getMethodParameters() 0 5 3
A getStubFile() 0 9 2
A createClassTrait() 0 9 2
A regexEscape() 0 9 1
A getClassInstance() 0 11 3
A createMethod() 0 25 4
A setStubPath() 0 7 2
A createMethodAccessibleProperty() 0 11 2
A getClassString() 0 7 2
A createClass() 0 14 3
A createMethodDocument() 0 19 3
A getAccessibleMethodValue() 0 5 2
B createClassImplements() 0 51 10
A createMethodBody() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like Generator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Generator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Resta\Support;
4
5
use Nette\PhpGenerator\PhpNamespace;
0 ignored issues
show
Bug introduced by
The type Nette\PhpGenerator\PhpNamespace was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Resta\Exception\FileNotFoundException;
7
8
class Generator
9
{
10
    /**
11
     * @var null|array
12
     */
13
    protected $classProperties;
14
15
    /**
16
     * @var null|string
17
     */
18
    protected $data;
19
20
    /**
21
     * @var null|string
22
     */
23
    protected $file;
24
25
    /**
26
     * @var null|object
27
     */
28
    protected $fileSystem;
29
30
    /**
31
     * @var null|string
32
     */
33
    protected $path;
34
35
    /**
36
     * @var null|string
37
     */
38
    protected $name;
39
40
    /**
41
     * @var null|string
42
     */
43
    protected $namespace;
44
45
    /**
46
     * @var string
47
     */
48
    protected $type = 'class';
49
50
    /**
51
     * @var null|string
52
     */
53
    protected $stubPath;
54
55
    /**
56
     * @var array
57
     */
58
    protected $accessibleProperties = array();
59
60
    /**
61
     * @var array
62
     */
63
    protected $methodParameters = array();
64
65
    /**
66
     * @var array
67
     */
68
    protected $loaded = [];
69
70
    /**
71
     * @var null|object
72
     */
73
    protected static $instance;
74
75
    /**
76
     * Generator constructor.
77
     * @param null|string $path
78
     * @param null|string $name
79
     * @param null|object $fileSystem
80
     */
81
    public function __construct($path=null,$name=null,$fileSystem=null)
82
    {
83
        $this->path = $path;
84
85
        $this->name = $name;
86
87
        $this->file = $this->path.''.DIRECTORY_SEPARATOR.''.ucfirst($this->name).'.php';
88
89
        $this->fileSystem = (is_null($fileSystem)) ? files() : $fileSystem;
90
91
        $this->namespace = Utils::getNamespace($this->path);
92
93
        $this->setStubPath();
94
95
        $this->createPath();
96
    }
97
98
    /**
99
     * creates class for generator
100
     *
101
     * @param array $replacement
102
     * @return Generator
103
     *
104
     * @throws FileNotFoundException
105
     */
106
    public function createClass($replacement=array())
107
    {
108
        if(file_exists($this->path)){
109
110
            $content = $this->fileSystem->get($this->getStubFile());
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

110
            /** @scrutinizer ignore-call */ 
111
            $content = $this->fileSystem->get($this->getStubFile());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
111
112
            $this->loaded['createClass'] = true;
113
114
            if($this->fileSystem->put($this->file,$content)!==FALSE){
115
                $this->replaceFileContent($replacement,$this->file);
116
            }
117
        }
118
119
        return $this;
120
    }
121
122
    /**
123
     * creates class document for generator
124
     *
125
     * @param array $documents
126
     *
127
     * @throws FileNotFoundException
128
     */
129
    public function createClassDocument($documents=array())
130
    {
131
        if(isset($this->loaded['createClass'])){
132
133
            $documentString = [];
134
            $documentString[] = '/**';
135
136
            foreach ($documents as $document) {
137
138
                $documentString[] = '
139
* ' . $document . '';
140
141
            }
142
143
            $documentString[] = '
144
*/';
145
146
            $this->replaceFileContent([
147
                'class\s.*\n{' => implode('',$documentString).'
148
'.$this->getClassString()
149
            ],$this->file,true);
150
151
        }
152
153
    }
154
155
    /**
156
     * create class extend object for generator
157
     *
158
     * @param $namespace
159
     * @param $alias
160
     *
161
     * @throws FileNotFoundException
162
     */
163
    public function createClassExtend($namespace, $alias)
164
    {
165
        if(!preg_match('@extends@',$this->getClassString())){
166
167
            if(preg_match('@class\s(.*?).*@',$this->getClassString(),$class)){
168
169
                $statements = explode(' ',$class[0]);
170
171
                $className = $statements[1];
172
173
                if(count($statements)>2){
174
                    $implements = implode(' ',array_slice($statements,2));
175
                }
176
            }
177
            $this->createClassUse([
178
                $namespace
179
            ]);
180
181
            if(isset($implements)){
182
183
                $this->replaceFileContent([
184
                    'class\s.*\n{' =>'class '.$className.' extends '.$alias.' '.$implements.'
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $className does not seem to be defined for all execution paths leading up to this point.
Loading history...
185
{'
186
                ],$this->file,true);
187
            }
188
            else{
189
190
                $this->replaceFileContent([
191
                    'class\s.*\n{' =>'class '.$className.' extends '.$alias.'
192
{'
193
                ],$this->file,true);
194
            }
195
196
        }
197
    }
198
199
    /**
200
     * create class interface object for generator
201
     *
202
     * @param array $implements
203
     *
204
     * @throws FileNotFoundException
205
     */
206
    public function createClassImplements($implements=array())
207
    {
208
        if(!is_null($this->getClassString())){
209
210
            $implementList = [];
211
            $implementUseList = [];
212
213
            foreach($implements as $namespace=>$alias)
214
            {
215
                $implementUseList[] = $namespace;
216
217
                $implementList[] = $alias;
218
            }
219
220
            $this->createClassUse($implementUseList);
221
222
            if(preg_match('@class.*(.*?).*@',$this->getClassString(),$strings)){
223
                $statements = explode(' ',$strings[0]);
224
225
                $className = $statements[1];
226
227
                if(in_array('extends',$statements) && !in_array('implements',$statements)){
228
                    $extendsAliasName = $statements[array_search('extends',$statements)+1];
229
230
                    $this->replaceFileContent([
231
                        'class\s.*\n{' =>'class '.$className.' extends '.$extendsAliasName.' implements '.implode(',',$implementList).'
232
{'
233
                    ],$this->file,true);
234
                }
235
                elseif(in_array('extends',$statements) && in_array('implements',$statements)){
236
237
                    $extendsAliasName = $statements[array_search('extends',$statements)+1];
238
239
                    $this->replaceFileContent([
240
                        'class\s.*\n{' =>'class '.$className.' extends '.$extendsAliasName.' implements '.end($statements).','.implode(',',$implementList).'
241
{'
242
                    ],$this->file,true);
243
                }
244
                elseif(!in_array('extends',$statements) && in_array('implements',$statements)){
245
246
                    $this->replaceFileContent([
247
                        'class\s.*\n{' =>'class '.$className.' implements '.end($statements).','.implode(',',$implementList).'
248
{'
249
                    ],$this->file,true);
250
                }
251
                else{
252
253
                    $this->replaceFileContent([
254
                        'class\s.*\n{' =>'class '.$className.' implements '.implode(',',$implementList).'
255
{'
256
                    ],$this->file,true);
257
258
                }
259
260
            }
261
        }
262
263
    }
264
265
    /**
266
     * creates class property for generator
267
     *
268
     * @param array $properties
269
     * @param bool $loadedMethod
270
     *
271
     * @throws FileNotFoundException
272
     */
273
    public function createClassProperty($properties=array(),$loadedMethod=false)
274
    {
275
        if(is_null($this->classProperties)){
276
            $this->classProperties = $properties;
277
        }
278
279
        if(isset($this->loaded['createMethod'])){
280
            $this->classProperties = $properties;
281
            $loadedMethod = true;
282
        }
283
284
        if($loadedMethod){
285
286
            foreach ($this->classProperties as $property) {
287
288
                if(!preg_match('@'.$this->regexEscape($property).'@',$this->fileSystem->get($this->file))){
289
290
                    if(preg_match('@class\s.*\n{@',$this->fileSystem->get($this->file),$parse)){
291
                        $this->replaceFileContent([
292
                            $parse[0] => $parse[0].' 
293
    '.$property.'
294
    '
295
296
                        ],$this->file,true);
297
                    }
298
                }
299
            }
300
301
            if(isset($this->loaded['createClassPropertyDocument'])){
302
                $this->createClassPropertyDocument($this->loaded['createClassPropertyDocument']);
303
            }
304
        }
305
    }
306
307
    /**
308
     * creates class property document for generator
309
     *
310
     * @param array $properties
311
     *
312
     * @throws FileNotFoundException
313
     */
314
    public function createClassPropertyDocument($properties=array())
315
    {
316
        $this->loaded['createClassPropertyDocument'] = $properties;
317
318
        foreach ($properties as $property=>$documents){
319
320
            $documentString = [];
321
            $documentString[] = '/**';
322
323
            foreach ($documents as $document){
324
                $documentString[] = '
325
     * '.$document.'';
326
            }
327
328
            $documentString[] = '
329
     */';
330
331
            $this->replaceFileContent([
332
                $this->regexEscape($property) => implode('',$documentString).'
333
    '.$property
334
            ],$this->file,true);
335
        }
336
    }
337
338
    /**
339
     * creates class trait for generator
340
     *
341
     * @param $trait
342
     *
343
     * @throws FileNotFoundException
344
     */
345
    public function createClassTrait($trait)
346
    {
347
        if(isset($this->loaded['createClass'])){
348
349
            $this->replaceFileContent([
350
                'class\s.*\n{' => $this->getClassString().' 
351
    use '.$trait.'
352
    '
353
            ],$this->file,true);
354
        }
355
    }
356
357
358
    /**
359
     * creates class use statements for generator
360
     *
361
     * @param array $uses
362
     *
363
     * @throws FileNotFoundException
364
     */
365
    public function createClassUse($uses=array())
366
    {
367
        if(!is_null($this->getClassString())){
368
369
            $useString = [];
370
371
            foreach ($uses as $use) {
372
373
                $useString[] = '
374
use ' . $use . ';';
375
            }
376
377
            $this->replaceFileContent([
378
                'namespace '.$this->regexEscape($this->namespace).';' => 'namespace '.$this->namespace.';               
379
'.implode('',$useString).''
380
            ],$this->file,true);
381
382
        }
383
384
    }
385
386
    /**
387
     * creates method for generator
388
     *
389
     * @param array $methods
390
     *
391
     * @throws FileNotFoundException
392
     */
393
    public function createMethod($methods=array())
394
    {
395
        $list = [];
396
397
        foreach ($methods as $method){
398
399
            if(!preg_match('@function.*'.$method.'@',$this->getClassString())){
400
401
                $list[] = '
402
    '.$this->getAccessibleMethodValue($method).' function '.$method.'('.$this->getMethodParameters($method).')
403
    {
404
        return true;
405
    }
406
            ';
407
            }
408
409
410
        }
411
        if(preg_match('@class\s.*\n{@',$this->fileSystem->get($this->file),$parse)){
412
            $this->replaceFileContent([
413
                $parse[0] => $parse[0].' '.implode('',$list)
414
            ],$this->file,true);
415
416
            $this->createClassProperty([],true);
417
            $this->loaded['createMethod'] = true;
418
        }
419
420
    }
421
422
    /**
423
     * accessible properties method for generator
424
     *
425
     * @param array $methods
426
     * @return void|mixed
427
     *
428
     * @throws FileNotFoundException
429
     */
430
    public function createMethodAccessibleProperty($methods=array())
431
    {
432
        foreach($methods as $method=>$accessibleValue)
433
        {
434
            $this->accessibleProperties[$method] = $accessibleValue;
435
436
            $this->replaceFileContent([
437
438
                'public function '.$method.'' => ''.$this->getAccessibleMethodValue($method).' function '.$method.''
439
440
            ],$this->file,true);
441
        }
442
    }
443
444
    /**
445
     * creates method for generator
446
     *
447
     * @param array $methods
448
     *
449
     * @throws FileNotFoundException
450
     */
451
    public function createMethodBody($methods=array())
452
    {
453
        foreach ($methods as $method=>$body){
454
455
            $this->replaceFileContent([
456
                ''.$this->getAccessibleMethodValue($method).' function '.$method.'\('.$this->getMethodParameters($method).'\)\n.*{\n.*\n.*}' => ''.$this->getAccessibleMethodValue($method).' function '.$method.'('.$this->getMethodParameters($method).')
457
    {
458
        '.$body.'
459
    }'
460
            ],$this->file,true);
461
        }
462
    }
463
464
    /**
465
     * creates method for generator
466
     *
467
     * @param array $methods
468
     *
469
     * @throws FileNotFoundException
470
     */
471
    public function createMethodDocument($methods=array())
472
    {
473
        foreach ($methods as $method=>$documents){
474
475
            $documentString = [];
476
            $documentString[] = '/**';
477
478
            foreach ($documents as $document){
479
                $documentString[] = '
480
     * '.$document.'';
481
            }
482
483
            $documentString[] = '
484
     */';
485
486
            $this->replaceFileContent([
487
                ''.$this->getAccessibleMethodValue($method).' function '.$method.'\('.$this->getMethodParameters($method).'\)' => ''.implode('',$documentString).'
488
    '.$this->getAccessibleMethodValue($method).' function '.$method.'('.$this->getMethodParameters($method,false).')'
489
            ],$this->file,true);
490
        }
491
    }
492
493
    /**
494
     * accessible properties method for generator
495
     *
496
     * @param array $methods
497
     * @return void|mixed
498
     *
499
     * @throws FileNotFoundException
500
     */
501
    public function createMethodParameters($methods=array())
502
    {
503
        foreach($methods as $method=>$parameter)
504
        {
505
            $this->methodParameters[$method] = $parameter;
506
507
            $this->replaceFileContent([
508
509
                ''.$this->getAccessibleMethodValue($method).' function '.$method.'\(\)' => ''.$this->getAccessibleMethodValue($method).' function '.$method.'('.$parameter.')'
510
511
            ],$this->file,true);
512
        }
513
    }
514
515
    /**
516
     * creates directory for generator
517
     *
518
     * @return mixed|void
519
     */
520
    public function createPath()
521
    {
522
        if(!file_exists($this->path)){
523
            if(!$this->fileSystem->makeDirectory($this->path)){
524
                throw new \Error($this->path.' makeDirectory fail');
525
            }
526
        }
527
    }
528
529
    /**
530
     * get accessible method value for generator
531
     *
532
     * @param $method
533
     * @return mixed|string
534
     */
535
    private function getAccessibleMethodValue($method)
536
    {
537
        return  (isset($this->accessibleProperties[$method])) ?
538
            $this->accessibleProperties[$method]
539
            : 'public';
540
    }
541
542
    /**
543
     * get class instance for generator
544
     *
545
     * @return mixed|void
546
     */
547
    public function getClassInstance()
548
    {
549
        if(!isset($this->loaded['createClass'])){
550
551
            if(is_null(self::$instance)){
552
                $class = Utils::getNamespace($this->file);
553
                self::$instance = new $class;
554
            }
555
        }
556
557
        return self::$instance;
558
    }
559
560
    /**
561
     * get class string from generator
562
     *
563
     * @return null|string
564
     *
565
     * @throws FileNotFoundException
566
     */
567
    public function getClassString()
568
    {
569
        if(preg_match('@class\s.*\n{@',$this->fileSystem->get($this->file),$parse)){
570
            return $parse[0];
571
        }
572
573
        return null;
574
    }
575
576
    /**
577
     * get parameters method value for generator
578
     *
579
     * @param $method
580
     * @param bool $regexEscape
581
     * @return mixed|string
582
     */
583
    private function getMethodParameters($method,$regexEscape=true)
584
    {
585
        return  (isset($this->methodParameters[$method])) ?
586
            ($regexEscape) ? $this->regexEscape($this->methodParameters[$method]) : $this->methodParameters[$method]
587
            : '';
588
    }
589
590
    /**
591
     * get stub file for generator
592
     *
593
     * @return string
594
     */
595
    public function getStubFile()
596
    {
597
        $stubFile = $this->stubPath.''.DIRECTORY_SEPARATOR.''.$this->type.'.stub';
598
599
        if(!file_exists($stubFile)){
600
            throw new \Error($stubFile.' path is not available');
601
        }
602
603
        return $stubFile;
604
    }
605
606
    /**
607
     * escapes to regex data for generator
608
     *
609
     * @param $data
610
     * @return mixed
611
     */
612
    public function regexEscape($data)
613
    {
614
        $dataEscape = str_replace('\\','\\\\',$data);
615
        $dataEscape = str_replace('$','\$',$dataEscape);
616
        $dataEscape = str_replace('()','\(\)',$dataEscape);
617
        $dataEscape = str_replace('[]','\[\]',$dataEscape);
618
619
620
        return $dataEscape;
621
    }
622
623
    /**
624
     * replace with replacement variables content of the given file
625
     *
626
     * @param $replacement
627
     * @param $file
628
     * @param bool $default
629
     * @return void
630
     *
631
     * @throws FileNotFoundException
632
     */
633
    private function replaceFileContent($replacement,$file,$default=false)
634
    {
635
        $replacementVariables = ($default) ? $replacement : $this->replacementVariables($replacement);
636
        $content = $this->fileSystem->get($file);
637
638
        foreach ($replacementVariables as $key=>$replacementVariable){
639
640
            if($default){
641
                $search = '/'.$key.'/';
642
            }
643
            else{
644
                $search = '/__'.$key.'__/';
645
            }
646
647
            $replace = $replacementVariable;
648
            $content = preg_replace($search,$replace,$content);
649
        }
650
        $this->fileSystem->replace($file,$content);
651
    }
652
653
    /**
654
     * get replacement variables
655
     *
656
     * @param array $replacement
657
     * @return array
658
     */
659
    private function replacementVariables($replacement=array())
660
    {
661
        $replacement['namespace'] = $this->namespace;
662
        $replacement['class'] = $this->name;
663
664
        return array_map(function($item){
665
            return ucfirst($item);
666
        },$replacement);
667
    }
668
669
    /**
670
     * set stub path
671
     *
672
     * @param $stubPath
673
     */
674
    public function setStubPath($stubPath=null)
675
    {
676
        if(is_null($stubPath)){
677
            $this->stubPath = app()->corePath().'Console/Stubs/generator';
678
        }
679
        else{
680
            $this->stubPath = $stubPath;
681
        }
682
    }
683
684
}
685