Completed
Push — master ( 4041f3...e9f71d )
by Vitaly
02:22
created

Generator::write()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 2
Metric Value
c 4
b 0
f 2
dl 0
loc 10
rs 9.4285
cc 2
eloc 5
nc 2
nop 2
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
17
    /** @var string Generated code */
18
    public $code = '';
19
20
    /** @var integer Level of code line tabbing for new lines */
21
    public $tabs = 0;
22
23
    /** @var string Current class name */
24
    public $class;
25
26
    /** @var int Current conditions nesting level */
27
    public $ifConditionLevel = 0;
28
29
    /**
30
     * Add simple text to current code position
31
     * @param string $text Text to add
32
     * @return self
33
     */
34
    public function text($text = '')
35
    {
36
        $this->code .= $text;
37
38
        return $this;
39
    }
40
41
    /**
42
     * Add current tabbing level to current line.
43
     *
44
     * @param string $endText Text to add after tabs
45
     * @param integer $tabs Amount of tabs to add
46
     * @param string $startText Text to add before tabs
47
     * @return Generator Chaining
48
     */
49
    public function tabs($endText = '', $tabs = null, $startText = '')
50
    {
51
        // Generate tabs array
52
        $tabs = isset($tabs) && $tabs ? array_fill(0, $tabs, "\t") : array();
53
54
        // Add necessary amount of tabs to line and append text
55
        $this->text($startText.implode('', $tabs) . $endText);
56
57
        return $this;
58
    }
59
60
    /**
61
     * Add new line to code.
62
     *
63
     * @param string $text Code to add to new line
64
     * @param integer $tabs Tabs count
65
     * @return self
66
     */
67
    public function newLine($text = '', $tabs = null)
68
    {
69
        // If no tabs count is specified set default tabs
70
        if (!isset($tabs)) {
71
            $tabs = $this->tabs;
72
        }
73
74
        return $this->tabs($text, $tabs, "\n");
75
    }
76
77
    /**
78
     * Add single line comment to code
79
     * @param string $text Comment text
80
     * @return self Chaining
81
     */
82
    public function comment($text = '')
83
    {
84
        return isset($text{0}) ? $this->newLine("// " . $text) : $this;
85
    }
86
87
    /**
88
     * Add multi-line comment. If array with one line is passed
89
     * we create special syntax comment in one line, usually
90
     * used for class variable definition in more compact form.
91
     *
92
     * @param array $lines Array of comments lines
93
     * @return self Chaining
94
     */
95
    public function multiComment(array $lines = array())
96
    {
97
        // If array is not empty
98
        if (sizeof($lines)) {
99
            $this->newLine("/**");
100
101
            // Multi-comment with single line
102
            if (sizeof($lines) === 1) {
103
                $this->text(' '.$lines[0].' */');
104
            } else { // Iterate comments lines and if comment line is not empty
105
                foreach ($lines as $line) {
106
                    if (isset($line{0})) {
107
                        $this->newLine(" * " . $line);
108
                    }
109
                }
110
111
                return $this->newLine(" */");
112
            }
113
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
114
        }
115
116
        return $this;
117
    }
118
119
    /**
120
     * Add one line variable definition comment.
121
     *
122
     * @param string $type Variable type
123
     * @param string $description Variable description
124
     * @param string $name Variable name
125
     * @return self Chaining
126
     */
127
    public function commentVar($type, $description, $name = '')
128
    {
129
        return $this->multiComment(array(
130
            '@var ' . trim($type) . (isset($name) ? trim($name) . ' ' : ' ') . trim($description)
131
        ));
132
    }
133
134
    /**
135
     * Add string value definition.
136
     *
137
     * @param string $value String value to add
138
     * @param string $tabs Tabs count
139
     * @param string $quote Type of quote
140
     * @return self Chaining
141
     */
142
    public function stringValue($value, $tabs = null, $quote = self::QUOTE_SINGLE)
143
    {
144
        return $this->tabs($quote . $value . $quote, $tabs);
145
    }
146
147
    /**
148
     * Add array values definition.
149
     *
150
     * @param array $items Array key-value pairs collection
151
     * @return self Chaining
152
     */
153
    public function arrayValue(array $items = array())
154
    {
155
        if (sizeof($items)) {
156
            $this->text('array(');
157
            $this->tabs++;
158
159
            // Iterate array items
160
            foreach ($items as $key => $value) {
161
                // Start array key definition
162
                $this->newLine()->stringValue($key)->text(' => ');
163
164
                // If item value is array - recursion
165
                if (is_array($value)) {
166
                    $this->arrayValue($value)->text(',');
167
                } else {
168
                    $this->stringValue($value)->text(',');
169
                }
170
            }
171
172
            $this->tabs--;
173
            $this->newLine(')');
174
        } else {
175
            $this->text('array()');
176
        }
177
178
        return $this;
179
    }
180
181
    /**
182
     * Add variable definition with array merging.
183
     *
184
     * @param string $name Variable name
185
     * @param array $value Array of key-value items for merging it to other array
186
     * @param string $arrayName Name of array to merge to, if no is specified - $name is used
187
     * @return self Chaining
188
     */
189
    public function defArrayMerge($name, array $value, $arrayName = null)
190
    {
191
        // If no other array is specified - set it to current
192
        if (!isset($arrayName)) {
193
            $arrayName = $name;
194
        }
195
196
        return $this->defvar($name, $value, ' = array_merge( ' . $arrayName . ', ', '')->text(');');
197
    }
198
199
    /**
200
     * Add variable definition.
201
     *
202
     * @param string $name Variable name
203
     * @param mixed $value Variable default value
204
     * @param string $after String to insert after variable definition
205
     * @param string $end Closing part of variable definition
206
     * @param string $quote Type of quote
207
     * @return Generator Chaining
208
     */
209
    public function defVar($name, $value = null, $after = ' = ', $end = ';', $quote = self::QUOTE_SINGLE)
210
    {
211
        // Output variable definition
212
        $this->newLine($name);
213
214
        // Get variable type
215
        switch (gettype($value)) {
216
            case 'integer':
217
            case 'boolean':
218
            case 'double':
219
                $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...
220
                break;
221
            case 'string':
222
                if (strpos($value, 'EOT') !== false) {
223
                    $this->text($after)->stringValue($value, 0, self::QUOTE_NO)->text($end);
224
                } else {
225
                    $this->text($after)->stringValue($value, 0, $quote)->text($end);
226
                }
227
                break;
228
            case 'array':
229
                $this->text($after)->arrayValue($value)->text($end);
230
                break;
231
            case 'NULL':
232
            case 'object':
233
            case 'resource':
234
            default:
235
                $this->text(';');
236
        }
237
238
        return $this;
239
    }
240
241
    /**
242
     * Add trait definition.
243
     *
244
     * @param string $name Trait name
245
     * @return self Chaining
246
     */
247
    public function defTrait($name)
248
    {
249
        // If we define another class, and we were in other class context
250
        if (isset($this->class) && ($name !== $this->class)) {
251
            // Close old class context
252
            $this->endClass();
253
        }
254
255
        // Save new class name
256
        $this->class = $name;
257
258
        // Class definition start
259
        $this->newLine('trait ' . $name);
260
261
        $this->newLine('{');
262
263
        $this->tabs++;
264
265
        return $this;
266
    }
267
268
    /**
269
     * Add class definition.
270
     *
271
     * @param string $name Class name
272
     * @param string $extends Parent class name
273
     * @param array $implements Interfaces names collection
274
     * @return self Chaining
275
     */
276
    public function defClass($name, $extends = null, array $implements = array())
277
    {
278
        // If we define another class, and we were in other class context
279
        if (isset($this->class) && ($name !== $this->class)) {
280
            // Close old class context
281
            $this->endClass();
282
        }
283
284
        // Save new class name
285
        $this->class = $name;
286
287
        // Class definition start
288
        $this->newLine('class ' . $name);
289
290
        // Parent class definition
291
        if (isset($extends)) {
292
            $this->text(' extends ' . $extends);
293
        }
294
295
        // Interfaces
296
        if (sizeof($implements)) {
297
            $this->text(' implements ' . implode(',', $implements));
298
        }
299
300
        $this->newLine('{');
301
302
        $this->tabs++;
303
304
        return $this;
305
    }
306
307
    /**
308
     * Close current class context.
309
     *
310
     * @return self Chaining
311
     */
312
    public function endClass()
313
    {
314
        $this->tabs--;
315
316
        // Close class definition
317
        return $this->newLine('}')
318
            // Add one empty line after class definition
319
        ->newLine('');
320
    }
321
322
    /**
323
     * Define if statement condition.
324
     *
325
     * @param string $condition Condition statement
326
     * @return self Chaining
327
     */
328
    public function defIfCondition($condition)
329
    {
330
        $this->ifConditionLevel++;
331
332
        // Class definition start
333
        $this->newLine('if (' . $condition . ') {');
334
        $this->tabs++;
335
        return $this;
336
    }
337
338
    /**
339
     * Define elseif statement condition.
340
     *
341
     * @param string $condition Condition statement
342
     * @return self Chaining
343
     */
344
    public function defElseIfCondition($condition)
345
    {
346
        $this->tabs--;
347
        // Class definition start
348
        $this->newLine('} elseif (' . $condition . ') {');
349
        $this->tabs++;
350
        return $this;
351
    }
352
353
    /**
354
     * Define else statement.
355
     *
356
     * @return self Chaining
357
     */
358
    public function defElseCondition()
359
    {
360
        $this->tabs--;
361
        // Class definition start
362
        $this->newLine('} else {');
363
        $this->tabs++;
364
        return $this;
365
    }
366
367
    /**
368
     * Close if condition statement.
369
     *
370
     * @return self Chaining
371
     */
372
    public function endIfCondition()
373
    {
374
        if ($this->ifConditionLevel--) {
375
            $this->tabs--;
376
377
            // Close class definition
378
            return $this->newLine('}');
379
        }
380
381
        return $this;
382
    }
383
384
    /**
385
     * Add class variable definition.
386
     *
387
     * @param string $name Variable name
388
     * @param string $visibility Variable accessibility level
389
     * @param mixed $value Variable default value
390
     * @return self Chaining
391
     */
392
    public function defClassVar($name, $visibility = 'public', $value = null)
393
    {
394
        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...
395
            $this->multiComment(array($comment));
396
        }
397
398
        return $this->defvar($visibility . ' ' . $name, $value)->newLine();
399
    }
400
401
    /**
402
     * Add class constant definition.
403
     *
404
     * @param string $name Constant name
405
     * @param string $value Variable default value
406
     * @return self Chaining
407
     */
408
    public function defClassConst($name, $value)
409
    {
410
        return $this->defClassVar(strtoupper($name), 'const', $value);
411
    }
412
413
    /**
414
     * Write file to disk
415
     * @param string $name Path to file
416
     * @param string $format Output file format
417
     */
418
    public function write($name, $format = 'php')
419
    {
420
        $code = $this->flush();
421
422
        if ($format === 'php') {
423
            $code = '<?php ' . $code;
424
        }
425
426
        file_put_contents($name, $code, 0775);
427
    }
428
429
    /**
430
     * Flush internal data and return it.
431
     *
432
     * @return string Current generated code
433
     */
434
    public function flush()
435
    {
436
        // We should use 4 spaces instead of tabs
437
        $code = str_replace("\t", '    ', $this->code);
438
439
        $this->tabs = 0;
440
        $this->code = '';
441
        $this->class = null;
442
443
        return $code;
444
    }
445
446
    /**
447
     * Add function definition.
448
     *
449
     * @param string $name Function name
450
     * @param array $parameters Collection of parameters $typeHint => $paramName
451
     * @return Generator Chaining
452
     */
453
    public function defFunction($name, $parameters = array())
454
    {
455
        // Convert parameters to string
456
        $parameterList = array();
457
        foreach ($parameters as $type => $parameter) {
458
            $parameterList[] = (is_string($type) ? $type.' ' : '') . $parameter;
459
        }
460
        $parameterList = sizeof($parameterList) ? implode(', ', $parameterList) : '';
461
462
        $this->newLine('function ' . $name . '('.$parameterList.')')
463
            ->newLine('{')
464
            ->tabs('');
465
466
        $this->tabs++;
467
468
        return $this;
469
    }
470
471
    /**
472
     * Close current function context.
473
     *
474
     * @return self Chaining
475
     */
476
    public function endFunction()
477
    {
478
        $this->tabs--;
479
480
        return $this->newLine('}')->newLine('');
481
    }
482
483
    /**
484
     * Constructor
485
     * @param string $namespace Code namespace
486
     */
487
    public function __construct($namespace = null)
488
    {
489
        // If namespace is defined - set it
490
        if (isset($namespace)) {
491
            $this->defnamespace($namespace);
492
        }
493
    }
494
495
    /**
496
     * Add namespace declaration
497
     * @param string $name Namespace name
498
     * @return self
499
     */
500
    public function defNamespace($name)
501
    {
502
        return $this->newLine('namespace ' . $name . ';')->newLine();
503
    }
504
}
505
//[PHPCOMPRESSOR(remove,end)]
506