Passed
Push — master ( 3581a7...46782f )
by Vitaly
02:09
created

Generator   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 459
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 10
Bugs 2 Features 6
Metric Value
wmc 55
c 10
b 2
f 6
lcom 1
cbo 0
dl 0
loc 459
rs 6.8

24 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
A arrayValue() 0 23 3
A defArrayMerge() 0 9 2
D defVar() 0 27 9
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 9 1
A defClassVar() 0 8 3
A defClassConst() 0 4 1
A write() 0 10 2
A flush() 0 11 1
A endFunction() 0 6 1
A __construct() 0 7 2
A defnamespace() 0 4 1
A defFunction() 0 17 4

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

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...
189
    }
190
191
    /**
192
     * Add variable definition.
193
     *
194
     * @param string $name Variable name
195
     * @param string $value Variable default value
196
     * @param string $after String to insert after variable definition
197
     * @param string $end Closing part of variable definition
198
     * @param string $quote Type of quote
199
     * @return Generator Chaining
200
     */
201
    public function defVar($name, $value = null, $after = ' = ', $end = ';', $quote = self::QUOTE_SINGLE)
202
    {
203
        // Output variable definition
204
        $this->newLine($name);
205
206
        // Get variable type
207
        switch (gettype($value)) {
208
            case 'integer':
209
            case 'boolean':
210
            case 'double':
211
                $this->text($after)->text($value)->text($end);
212
                break;
213
            case 'string':
214
                $this->text($after)->stringValue($value, 0, $quote)->text($end);
215
                break;
216
            case 'array':
217
                $this->text($after)->arrayValue($value)->text($end);
0 ignored issues
show
Documentation introduced by
$value is of type null, but the function expects a array.

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...
218
                break;
219
            case 'NULL':
220
            case 'object':
221
            case 'resource':
222
            default:
223
                $this->text(';');
224
        }
225
226
        return $this;
227
    }
228
229
    /**
230
     * Add class definition.
231
     *
232
     * @param string $name Class name
233
     * @param string $extends Parent class name
234
     * @param array $implements Interfaces names collection
235
     * @return self Chaining
236
     */
237
    public function defClass($name, $extends = null, array $implements = array())
238
    {
239
        // If we define another class, and we were in other class context
240
        if (isset($this->class) && ($name !== $this->class)) {
241
            // Close old class context
242
            $this->endClass();
243
        }
244
245
        // Save new class name
246
        $this->class = $name;
247
248
        // Class definition start
249
        $this->newLine('class ' . $name);
250
251
        // Parent class definition
252
        if (isset($extends)) {
253
            $this->text(' extends ' . $extends);
254
        }
255
256
        // Interfaces
257
        if (sizeof($implements)) {
258
            $this->text(' implements ' . implode(',', $implements));
259
        }
260
261
        $this->newLine('{');
262
263
        $this->tabs++;
264
265
        return $this;
266
    }
267
268
    /**
269
     * Close current class context.
270
     *
271
     * @return self Chaining
272
     */
273
    public function endClass()
274
    {
275
        $this->tabs--;
276
277
        // Close class definition
278
        return $this->newLine('}')
279
            // Add one empty line after class definition
280
        ->newLine('');
281
    }
282
283
    /**
284
     * Define if statement condition.
285
     *
286
     * @param string $condition Condition statement
287
     * @return self Chaining
288
     */
289
    public function defIfCondition($condition)
290
    {
291
        $this->ifConditionLevel++;
292
293
        // Class definition start
294
        $this->newLine('if (' . $condition . ') {');
295
        $this->tabs++;
296
        return $this;
297
    }
298
299
    /**
300
     * Define elseif statement condition.
301
     *
302
     * @param string $condition Condition statement
303
     * @return self Chaining
304
     */
305
    public function defElseIfCondition($condition)
306
    {
307
        $this->tabs--;
308
        // Class definition start
309
        $this->newLine('} elseif (' . $condition . ') {');
310
        $this->tabs++;
311
        return $this;
312
    }
313
314
    /**
315
     * Define else statement.
316
     *
317
     * @return self Chaining
318
     */
319
    public function defElseCondition()
320
    {
321
        $this->tabs--;
322
        // Class definition start
323
        $this->newLine('} else {');
324
        $this->tabs++;
325
        return $this;
326
    }
327
328
    /**
329
     * Close if condition statement.
330
     *
331
     * @return self Chaining
332
     */
333
    public function endIfCondition()
334
    {
335
        $this->tabs--;
336
337
        $this->ifConditionLevel--;
338
339
        // Close class definition
340
        return $this->newLine('}');
341
    }
342
343
    /**
344
     * Add class variable definition.
345
     *
346
     * @param string $name Variable name
347
     * @param string $visibility Variable accessibility level
348
     * @param string $value Variable default value
349
     * @return self Chaining
350
     */
351
    public function defClassVar($name, $visibility = 'public', $value = null)
352
    {
353
        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...
354
            $this->multiComment(array($comment));
355
        }
356
357
        return $this->defvar($visibility . ' ' . $name, $value)->newLine();
358
    }
359
360
    /**
361
     * Add class constant definition.
362
     *
363
     * @param string $name Constant name
364
     * @param string $value Variable default value
365
     * @return self Chaining
366
     */
367
    public function defClassConst($name, $value)
368
    {
369
        return $this->defClassVar(strtoupper($name), 'const', $value);
370
    }
371
372
    /**
373
     * Write file to disk
374
     * @param string $name Path to file
375
     * @param string $format Output file format
376
     */
377
    public function write($name, $format = 'php')
378
    {
379
        $code = $this->flush();
380
381
        if ($format === 'php') {
382
            $code = '<?php ' . $code;
383
        }
384
385
        file_put_contents($name, $code, 0775);
386
    }
387
388
    /**
389
     * Flush internal data and return it.
390
     *
391
     * @return string Current generated code
392
     */
393
    public function flush()
394
    {
395
        // We should use 4 spaces instead of tabs
396
        $code = str_replace("\t", '    ', $this->code);
397
398
        $this->tabs = 0;
399
        $this->code = '';
400
        $this->class = null;
401
402
        return $code;
403
    }
404
405
    /**
406
     * Add function definition.
407
     *
408
     * @param string $name Function name
409
     * @param array $parameters Collection of parameters $typeHint => $paramName
410
     * @return Generator Chaining
411
     */
412
    public function defFunction($name, $parameters = array())
413
    {
414
        // Convert parameters to string
415
        $parameterList = array();
416
        foreach ($parameters as $type => $parameter) {
417
            $parameterList[] = (is_string($type) ? $type.' ' : '') . $parameter;
418
        }
419
        $parameterList = sizeof($parameterList) ? implode(', ', $parameterList) : '';
420
421
        $this->newLine('function ' . $name . '('.$parameterList.')')
422
            ->newLine('{')
423
            ->tabs('');
424
425
        $this->tabs++;
426
427
        return $this;
428
    }
429
430
    /**
431
     * Close current function context.
432
     *
433
     * @return self Chaining
434
     */
435
    public function endFunction()
436
    {
437
        $this->tabs--;
438
439
        return $this->newLine('}')->newLine('');
440
    }
441
442
    /**
443
     * Constructor
444
     * @param string $namespace Code namespace
445
     */
446
    public function __construct($namespace = null)
447
    {
448
        // If namespace is defined - set it
449
        if (isset($namespace)) {
450
            $this->defnamespace($namespace);
451
        }
452
    }
453
454
    /**
455
     * Add namespace declaration
456
     * @param string $name Namespace name
457
     * @return self
458
     */
459
    private function defnamespace($name)
460
    {
461
        return $this->newLine('namespace ' . $name . ';')->newLine();
462
    }
463
}
464
//[PHPCOMPRESSOR(remove,end)]
465