Completed
Push — master ( e11cdd...019752 )
by Vitaly
02:50
created

Generator   C

Complexity

Total Complexity 61

Size/Duplication

Total Lines 496
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 14
Bugs 3 Features 8
Metric Value
wmc 61
c 14
b 3
f 8
lcom 1
cbo 0
dl 0
loc 496
rs 6.018

25 Methods

Rating   Name   Duplication   Size   Complexity  
A text() 0 6 1
A tabs() 0 10 3
A newLine() 0 9 2
A comment() 0 4 2
B multiComment() 0 23 5
A commentVar() 0 6 2
A stringValue() 0 4 1
B arrayValue() 0 27 4
A defArrayMerge() 0 9 2
D defVar() 0 31 10
A defTrait() 0 17 3
B defClass() 0 30 5
A endClass() 0 9 1
A defIfCondition() 0 9 1
A defElseIfCondition() 0 8 1
A defElseCondition() 0 8 1
A endIfCondition() 0 11 2
A defClassVar() 0 8 3
A defClassConst() 0 4 1
A write() 0 10 2
A flush() 0 11 1
A defFunction() 0 17 4
A endFunction() 0 6 1
A __construct() 0 7 2
A defNamespace() 0 4 1

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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
//[PHPCOMPRESSOR(remove,start)]
3
namespace samsonphp\generator;
4
5
class Generator
6
{
7
    /** Single quote for string value **/
8
    const QUOTE_SINGLE = "'";
9
10
    /** Double quote for string value **/
11
    const QUOTE_DOUBLE = '"';
12
13
    /** No quote for string heredoc value **/
14
    const QUOTE_NO = '';
15
16
    /** @var string Generated code */
17
    public $code = '';
18
19
    /** @var integer Level of code line tabbing for new lines */
20
    public $tabs = 0;
21
22
    /** @var string Current class name */
23
    public $class;
24
25
    /** @var int Current conditions nesting level */
26
    public $ifConditionLevel = 0;
27
28
    /**
29
     * Add simple text to current code position
30
     * @param string $text Text to add
31
     * @return self
32
     */
33
    public function text($text = '')
34
    {
35
        $this->code .= $text;
36
37
        return $this;
38
    }
39
40
    /**
41
     * Add current tabbing level to current line.
42
     *
43
     * @param string $endText Text to add after tabs
44
     * @param integer $tabs Amount of tabs to add
45
     * @param string $startText Text to add before tabs
46
     * @return Generator Chaining
47
     */
48
    public function tabs($endText = '', $tabs = null, $startText = '')
49
    {
50
        // Generate tabs array
51
        $tabs = isset($tabs) && $tabs ? array_fill(0, $tabs, "\t") : array();
52
53
        // Add necessary amount of tabs to line and append text
54
        $this->text($startText.implode('', $tabs) . $endText);
55
56
        return $this;
57
    }
58
59
    /**
60
     * Add new line to code.
61
     *
62
     * @param string $text Code to add to new line
63
     * @param integer $tabs Tabs count
64
     * @return self
65
     */
66
    public function newLine($text = '', $tabs = null)
67
    {
68
        // If no tabs count is specified set default tabs
69
        if (!isset($tabs)) {
70
            $tabs = $this->tabs;
71
        }
72
73
        return $this->tabs($text, $tabs, "\n");
74
    }
75
76
    /**
77
     * Add single line comment to code
78
     * @param string $text Comment text
79
     * @return self Chaining
80
     */
81
    public function comment($text = '')
82
    {
83
        return isset($text{0}) ? $this->newLine("// " . $text) : $this;
84
    }
85
86
    /**
87
     * Add multi-line comment. If array with one line is passed
88
     * we create special syntax comment in one line, usually
89
     * used for class variable definition in more compact form.
90
     *
91
     * @param array $lines Array of comments lines
92
     * @return self Chaining
93
     */
94
    public function multiComment(array $lines = array())
95
    {
96
        // If array is not empty
97
        if (sizeof($lines)) {
98
            $this->newLine("/**");
99
100
            // Multi-comment with single line
101
            if (sizeof($lines) === 1) {
102
                $this->text(' '.$lines[0].' */');
103
            } else { // Iterate comments lines and if comment line is not empty
104
                foreach ($lines as $line) {
105
                    if (isset($line{0})) {
106
                        $this->newLine(" * " . $line);
107
                    }
108
                }
109
110
                return $this->newLine(" */");
111
            }
112
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
113
        }
114
115
        return $this;
116
    }
117
118
    /**
119
     * Add one line variable definition comment.
120
     *
121
     * @param string $type Variable type
122
     * @param string $description Variable description
123
     * @param string $name Variable name
124
     * @return self Chaining
125
     */
126
    public function commentVar($type, $description, $name = '')
127
    {
128
        return $this->multiComment(array(
129
            '@var ' . trim($type) . (isset($name) ? trim($name) . ' ' : ' ') . trim($description)
130
        ));
131
    }
132
133
    /**
134
     * Add string value definition.
135
     *
136
     * @param string $value String value to add
137
     * @param string $tabs Tabs count
138
     * @param string $quote Type of quote
139
     * @return self Chaining
140
     */
141
    public function stringValue($value, $tabs = null, $quote = self::QUOTE_SINGLE)
142
    {
143
        return $this->tabs($quote . $value . $quote, $tabs);
144
    }
145
146
    /**
147
     * Add array values definition.
148
     *
149
     * @param array $items Array key-value pairs collection
150
     * @return self Chaining
151
     */
152
    public function arrayValue(array $items = array())
153
    {
154
        if (sizeof($items)) {
155
            $this->text('array(');
156
            $this->tabs++;
157
158
            // Iterate array items
159
            foreach ($items as $key => $value) {
160
                // Start array key definition
161
                $this->newLine()->stringValue($key)->text(' => ');
162
163
                // If item value is array - recursion
164
                if (is_array($value)) {
165
                    $this->arrayValue($value)->text(',');
166
                } else {
167
                    $this->stringValue($value)->text(',');
168
                }
169
            }
170
171
            $this->tabs--;
172
            $this->newLine(')');
173
        } else {
174
            $this->text('array()');
175
        }
176
177
        return $this;
178
    }
179
180
    /**
181
     * Add variable definition with array merging.
182
     *
183
     * @param string $name Variable name
184
     * @param array $value Array of key-value items for merging it to other array
185
     * @param string $arrayName Name of array to merge to, if no is specified - $name is used
186
     * @return self Chaining
187
     */
188
    public function defArrayMerge($name, array $value, $arrayName = null)
189
    {
190
        // If no other array is specified - set it to current
191
        if (!isset($arrayName)) {
192
            $arrayName = $name;
193
        }
194
195
        return $this->defvar($name, $value, ' = array_merge( ' . $arrayName . ', ', '')->text(');');
196
    }
197
198
    /**
199
     * Add variable definition.
200
     *
201
     * @param string $name Variable name
202
     * @param mixed $value Variable default value
203
     * @param string $after String to insert after variable definition
204
     * @param string $end Closing part of variable definition
205
     * @param string $quote Type of quote
206
     * @return Generator Chaining
207
     */
208
    public function defVar($name, $value = null, $after = ' = ', $end = ';', $quote = self::QUOTE_SINGLE)
209
    {
210
        // Output variable definition
211
        $this->newLine($name);
212
213
        // Get variable type
214
        switch (gettype($value)) {
215
            case 'integer':
216
            case 'boolean':
217
            case 'double':
218
                $this->text($after)->text($value)->text($end);
0 ignored issues
show
Documentation introduced by
$value is of type integer|boolean|double, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
219
                break;
220
            case 'string':
221
                if (strpos($value, 'EOT') !== false) {
222
                    $this->text($after)->stringValue($value, 0, self::QUOTE_NO)->text($end);
223
                } else {
224
                    $this->text($after)->stringValue($value, 0, $quote)->text($end);
225
                }
226
                break;
227
            case 'array':
228
                $this->text($after)->arrayValue($value)->text($end);
229
                break;
230
            case 'NULL':
231
            case 'object':
232
            case 'resource':
233
            default:
234
                $this->text(';');
235
        }
236
237
        return $this;
238
    }
239
240
    /**
241
     * Add trait definition.
242
     *
243
     * @param string $name Trait name
244
     * @return self Chaining
245
     */
246
    public function defTrait($name)
247
    {
248
        // If we define another class, and we were in other class context
249
        if (isset($this->class) && ($name !== $this->class)) {
250
            // Close old class context
251
            $this->endClass();
252
        }
253
254
        // Save new class name
255
        $this->class = $name;
256
257
        $this->newLine('{');
258
259
        $this->tabs++;
260
261
        return $this;
262
    }
263
264
    /**
265
     * Add class definition.
266
     *
267
     * @param string $name Class name
268
     * @param string $extends Parent class name
269
     * @param array $implements Interfaces names collection
270
     * @return self Chaining
271
     */
272
    public function defClass($name, $extends = null, array $implements = array())
273
    {
274
        // If we define another class, and we were in other class context
275
        if (isset($this->class) && ($name !== $this->class)) {
276
            // Close old class context
277
            $this->endClass();
278
        }
279
280
        // Save new class name
281
        $this->class = $name;
282
283
        // Class definition start
284
        $this->newLine('class ' . $name);
285
286
        // Parent class definition
287
        if (isset($extends)) {
288
            $this->text(' extends ' . $extends);
289
        }
290
291
        // Interfaces
292
        if (sizeof($implements)) {
293
            $this->text(' implements ' . implode(',', $implements));
294
        }
295
296
        $this->newLine('{');
297
298
        $this->tabs++;
299
300
        return $this;
301
    }
302
303
    /**
304
     * Close current class context.
305
     *
306
     * @return self Chaining
307
     */
308
    public function endClass()
309
    {
310
        $this->tabs--;
311
312
        // Close class definition
313
        return $this->newLine('}')
314
            // Add one empty line after class definition
315
        ->newLine('');
316
    }
317
318
    /**
319
     * Define if statement condition.
320
     *
321
     * @param string $condition Condition statement
322
     * @return self Chaining
323
     */
324
    public function defIfCondition($condition)
325
    {
326
        $this->ifConditionLevel++;
327
328
        // Class definition start
329
        $this->newLine('if (' . $condition . ') {');
330
        $this->tabs++;
331
        return $this;
332
    }
333
334
    /**
335
     * Define elseif statement condition.
336
     *
337
     * @param string $condition Condition statement
338
     * @return self Chaining
339
     */
340
    public function defElseIfCondition($condition)
341
    {
342
        $this->tabs--;
343
        // Class definition start
344
        $this->newLine('} elseif (' . $condition . ') {');
345
        $this->tabs++;
346
        return $this;
347
    }
348
349
    /**
350
     * Define else statement.
351
     *
352
     * @return self Chaining
353
     */
354
    public function defElseCondition()
355
    {
356
        $this->tabs--;
357
        // Class definition start
358
        $this->newLine('} else {');
359
        $this->tabs++;
360
        return $this;
361
    }
362
363
    /**
364
     * Close if condition statement.
365
     *
366
     * @return self Chaining
367
     */
368
    public function endIfCondition()
369
    {
370
        if ($this->ifConditionLevel--) {
371
            $this->tabs--;
372
373
            // Close class definition
374
            return $this->newLine('}');
375
        }
376
377
        return $this;
378
    }
379
380
    /**
381
     * Add class variable definition.
382
     *
383
     * @param string $name Variable name
384
     * @param string $visibility Variable accessibility level
385
     * @param mixed $value Variable default value
386
     * @return self Chaining
387
     */
388
    public function defClassVar($name, $visibility = 'public', $value = null)
389
    {
390
        if (isset($comment) && isset($comment{0})) {
0 ignored issues
show
Bug introduced by
The variable $comment seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
391
            $this->multiComment(array($comment));
392
        }
393
394
        return $this->defvar($visibility . ' ' . $name, $value)->newLine();
395
    }
396
397
    /**
398
     * Add class constant definition.
399
     *
400
     * @param string $name Constant name
401
     * @param string $value Variable default value
402
     * @return self Chaining
403
     */
404
    public function defClassConst($name, $value)
405
    {
406
        return $this->defClassVar(strtoupper($name), 'const', $value);
407
    }
408
409
    /**
410
     * Write file to disk
411
     * @param string $name Path to file
412
     * @param string $format Output file format
413
     */
414
    public function write($name, $format = 'php')
415
    {
416
        $code = $this->flush();
417
418
        if ($format === 'php') {
419
            $code = '<?php ' . $code;
420
        }
421
422
        file_put_contents($name, $code, 0775);
423
    }
424
425
    /**
426
     * Flush internal data and return it.
427
     *
428
     * @return string Current generated code
429
     */
430
    public function flush()
431
    {
432
        // We should use 4 spaces instead of tabs
433
        $code = str_replace("\t", '    ', $this->code);
434
435
        $this->tabs = 0;
436
        $this->code = '';
437
        $this->class = null;
438
439
        return $code;
440
    }
441
442
    /**
443
     * Add function definition.
444
     *
445
     * @param string $name Function name
446
     * @param array $parameters Collection of parameters $typeHint => $paramName
447
     * @return Generator Chaining
448
     */
449
    public function defFunction($name, $parameters = array())
450
    {
451
        // Convert parameters to string
452
        $parameterList = array();
453
        foreach ($parameters as $type => $parameter) {
454
            $parameterList[] = (is_string($type) ? $type.' ' : '') . $parameter;
455
        }
456
        $parameterList = sizeof($parameterList) ? implode(', ', $parameterList) : '';
457
458
        $this->newLine('function ' . $name . '('.$parameterList.')')
459
            ->newLine('{')
460
            ->tabs('');
461
462
        $this->tabs++;
463
464
        return $this;
465
    }
466
467
    /**
468
     * Close current function context.
469
     *
470
     * @return self Chaining
471
     */
472
    public function endFunction()
473
    {
474
        $this->tabs--;
475
476
        return $this->newLine('}')->newLine('');
477
    }
478
479
    /**
480
     * Constructor
481
     * @param string $namespace Code namespace
482
     */
483
    public function __construct($namespace = null)
484
    {
485
        // If namespace is defined - set it
486
        if (isset($namespace)) {
487
            $this->defnamespace($namespace);
488
        }
489
    }
490
491
    /**
492
     * Add namespace declaration
493
     * @param string $name Namespace name
494
     * @return self
495
     */
496
    public function defNamespace($name)
497
    {
498
        return $this->newLine('namespace ' . $name . ';')->newLine();
499
    }
500
}
501
//[PHPCOMPRESSOR(remove,end)]
502