Passed
Push — master ( 46782f...96f106 )
by Vitaly
02:48
created

Generator::arrayValue()   B

Complexity

Conditions 4
Paths 2

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 27
rs 8.5806
cc 4
eloc 15
nc 2
nop 1
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
        if (sizeof($items)) {
152
            $this->text('array(');
153
            $this->tabs++;
154
155
            // Iterate array items
156
            foreach ($items as $key => $value) {
157
                // Start array key definition
158
                $this->newLine()->stringValue($key)->text(' => ');
159
160
                // If item value is array - recursion
161
                if (is_array($value)) {
162
                    $this->arrayValue($value)->text(',');
163
                } else {
164
                    $this->stringValue($value)->text(',');
165
                }
166
            }
167
168
            $this->tabs--;
169
            $this->newLine(')');
170
        } else {
171
            $this->text('array()');
172
        }
173
174
        return $this;
175
    }
176
177
    /**
178
     * Add variable definition with array merging.
179
     *
180
     * @param string $name Variable name
181
     * @param array $value Array of key-value items for merging it to other array
182
     * @param string $arrayName Name of array to merge to, if no is specified - $name is used
183
     * @return self Chaining
184
     */
185
    public function defArrayMerge($name, array $value, $arrayName = null)
186
    {
187
        // If no other array is specified - set it to current
188
        if (!isset($arrayName)) {
189
            $arrayName = $name;
190
        }
191
192
        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...
193
    }
194
195
    /**
196
     * Add variable definition.
197
     *
198
     * @param string $name Variable name
199
     * @param string $value Variable default value
200
     * @param string $after String to insert after variable definition
201
     * @param string $end Closing part of variable definition
202
     * @param string $quote Type of quote
203
     * @return Generator Chaining
204
     */
205
    public function defVar($name, $value = null, $after = ' = ', $end = ';', $quote = self::QUOTE_SINGLE)
206
    {
207
        // Output variable definition
208
        $this->newLine($name);
209
210
        // Get variable type
211
        switch (gettype($value)) {
212
            case 'integer':
213
            case 'boolean':
214
            case 'double':
215
                $this->text($after)->text($value)->text($end);
216
                break;
217
            case 'string':
218
                $this->text($after)->stringValue($value, 0, $quote)->text($end);
219
                break;
220
            case 'array':
221
                $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...
222
                break;
223
            case 'NULL':
224
            case 'object':
225
            case 'resource':
226
            default:
227
                $this->text(';');
228
        }
229
230
        return $this;
231
    }
232
233
    /**
234
     * Add class definition.
235
     *
236
     * @param string $name Class name
237
     * @param string $extends Parent class name
238
     * @param array $implements Interfaces names collection
239
     * @return self Chaining
240
     */
241
    public function defClass($name, $extends = null, array $implements = array())
242
    {
243
        // If we define another class, and we were in other class context
244
        if (isset($this->class) && ($name !== $this->class)) {
245
            // Close old class context
246
            $this->endClass();
247
        }
248
249
        // Save new class name
250
        $this->class = $name;
251
252
        // Class definition start
253
        $this->newLine('class ' . $name);
254
255
        // Parent class definition
256
        if (isset($extends)) {
257
            $this->text(' extends ' . $extends);
258
        }
259
260
        // Interfaces
261
        if (sizeof($implements)) {
262
            $this->text(' implements ' . implode(',', $implements));
263
        }
264
265
        $this->newLine('{');
266
267
        $this->tabs++;
268
269
        return $this;
270
    }
271
272
    /**
273
     * Close current class context.
274
     *
275
     * @return self Chaining
276
     */
277
    public function endClass()
278
    {
279
        $this->tabs--;
280
281
        // Close class definition
282
        return $this->newLine('}')
283
            // Add one empty line after class definition
284
        ->newLine('');
285
    }
286
287
    /**
288
     * Define if statement condition.
289
     *
290
     * @param string $condition Condition statement
291
     * @return self Chaining
292
     */
293
    public function defIfCondition($condition)
294
    {
295
        $this->ifConditionLevel++;
296
297
        // Class definition start
298
        $this->newLine('if (' . $condition . ') {');
299
        $this->tabs++;
300
        return $this;
301
    }
302
303
    /**
304
     * Define elseif statement condition.
305
     *
306
     * @param string $condition Condition statement
307
     * @return self Chaining
308
     */
309
    public function defElseIfCondition($condition)
310
    {
311
        $this->tabs--;
312
        // Class definition start
313
        $this->newLine('} elseif (' . $condition . ') {');
314
        $this->tabs++;
315
        return $this;
316
    }
317
318
    /**
319
     * Define else statement.
320
     *
321
     * @return self Chaining
322
     */
323
    public function defElseCondition()
324
    {
325
        $this->tabs--;
326
        // Class definition start
327
        $this->newLine('} else {');
328
        $this->tabs++;
329
        return $this;
330
    }
331
332
    /**
333
     * Close if condition statement.
334
     *
335
     * @return self Chaining
336
     */
337
    public function endIfCondition()
338
    {
339
        $this->tabs--;
340
341
        $this->ifConditionLevel--;
342
343
        // Close class definition
344
        return $this->newLine('}');
345
    }
346
347
    /**
348
     * Add class variable definition.
349
     *
350
     * @param string $name Variable name
351
     * @param string $visibility Variable accessibility level
352
     * @param string $value Variable default value
353
     * @return self Chaining
354
     */
355
    public function defClassVar($name, $visibility = 'public', $value = null)
356
    {
357
        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...
358
            $this->multiComment(array($comment));
359
        }
360
361
        return $this->defvar($visibility . ' ' . $name, $value)->newLine();
362
    }
363
364
    /**
365
     * Add class constant definition.
366
     *
367
     * @param string $name Constant name
368
     * @param string $value Variable default value
369
     * @return self Chaining
370
     */
371
    public function defClassConst($name, $value)
372
    {
373
        return $this->defClassVar(strtoupper($name), 'const', $value);
374
    }
375
376
    /**
377
     * Write file to disk
378
     * @param string $name Path to file
379
     * @param string $format Output file format
380
     */
381
    public function write($name, $format = 'php')
382
    {
383
        $code = $this->flush();
384
385
        if ($format === 'php') {
386
            $code = '<?php ' . $code;
387
        }
388
389
        file_put_contents($name, $code, 0775);
390
    }
391
392
    /**
393
     * Flush internal data and return it.
394
     *
395
     * @return string Current generated code
396
     */
397
    public function flush()
398
    {
399
        // We should use 4 spaces instead of tabs
400
        $code = str_replace("\t", '    ', $this->code);
401
402
        $this->tabs = 0;
403
        $this->code = '';
404
        $this->class = null;
405
406
        return $code;
407
    }
408
409
    /**
410
     * Add function definition.
411
     *
412
     * @param string $name Function name
413
     * @param array $parameters Collection of parameters $typeHint => $paramName
414
     * @return Generator Chaining
415
     */
416
    public function defFunction($name, $parameters = array())
417
    {
418
        // Convert parameters to string
419
        $parameterList = array();
420
        foreach ($parameters as $type => $parameter) {
421
            $parameterList[] = (is_string($type) ? $type.' ' : '') . $parameter;
422
        }
423
        $parameterList = sizeof($parameterList) ? implode(', ', $parameterList) : '';
424
425
        $this->newLine('function ' . $name . '('.$parameterList.')')
426
            ->newLine('{')
427
            ->tabs('');
428
429
        $this->tabs++;
430
431
        return $this;
432
    }
433
434
    /**
435
     * Close current function context.
436
     *
437
     * @return self Chaining
438
     */
439
    public function endFunction()
440
    {
441
        $this->tabs--;
442
443
        return $this->newLine('}')->newLine('');
444
    }
445
446
    /**
447
     * Constructor
448
     * @param string $namespace Code namespace
449
     */
450
    public function __construct($namespace = null)
451
    {
452
        // If namespace is defined - set it
453
        if (isset($namespace)) {
454
            $this->defnamespace($namespace);
455
        }
456
    }
457
458
    /**
459
     * Add namespace declaration
460
     * @param string $name Namespace name
461
     * @return self
462
     */
463
    private function defnamespace($name)
464
    {
465
        return $this->newLine('namespace ' . $name . ';')->newLine();
466
    }
467
}
468
//[PHPCOMPRESSOR(remove,end)]
469