Completed
Push — master ( 6a9b09...82460f )
by Vitaly
04:03
created

Generator::defPublicClassFunction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 4
1
<?php declare(strict_types=1);
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 > 0 ? 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
     * Increase current code indentation.
62
     *
63
     * @param int $amount Indentation amount
64
     *
65
     * @return $this Chaining
66
     */
67
    public function increaseIndentation($amount = 1)
68
    {
69
        $this->tabs += $amount;
70
71
        return $this;
72
    }
73
74
    /**
75
     * Reduce current code indentation.
76
     *
77
     * @param int $amount Indentation amount
78
     *
79
     * @return $this Chaining
80
     */
81
    public function decreaseIndentation($amount = 1)
82
    {
83
        $this->tabs = $this->tabs > $amount ? $this->tabs - $amount : 0;
84
85
        return $this;
86
    }
87
88
    /**
89
     * Add new line to code.
90
     *
91
     * @param string $text Code to add to new line
92
     * @param integer $tabs Tabs count
93
     * @return self
94
     */
95
    public function newLine($text = '', $tabs = null)
96
    {
97
        // If no tabs count is specified set default tabs
98
        if (!isset($tabs)) {
99
            $tabs = $this->tabs;
100
        }
101
102
        return $this->tabs($text, $tabs, "\n");
103
    }
104
105
    /**
106
     * Add single line comment to code
107
     * @param string $text Comment text
108
     * @return self Chaining
109
     */
110
    public function comment($text = '')
111
    {
112
        return isset($text{0}) ? $this->newLine("// " . $text) : $this;
113
    }
114
115
    /**
116
     * Add multi-line comment. If array with one line is passed
117
     * we create special syntax comment in one line, usually
118
     * used for class variable definition in more compact form.
119
     *
120
     * @param array $lines Array of comments lines
121
     * @return self Chaining
122
     */
123
    public function multiComment(array $lines = array())
124
    {
125
        // If array is not empty
126
        if (sizeof($lines)) {
127
            $this->newLine("/**");
128
129
            // Multi-comment with single line
130
            if (sizeof($lines) === 1) {
131
                $this->text(' '.$lines[0].' */');
132
            } else { // Iterate comments lines and if comment line is not empty
133
                foreach ($lines as $line) {
134
                    if (isset($line{0})) {
135
                        $this->newLine(" * " . $line);
136
                    }
137
                }
138
139
                return $this->newLine(" */");
140
            }
141
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
142
        }
143
144
        return $this;
145
    }
146
147
    /**
148
     * Add one line variable definition comment.
149
     *
150
     * @param string $type Variable typeHint
151
     * @param string $description Variable description
152
     * @param string $name Variable name
153
     * @return self Chaining
154
     */
155
    public function commentVar($type, $description, $name = '')
156
    {
157
        return $this->multiComment(array(
158
            '@var ' . trim($type) . (isset($name) ? trim($name) . ' ' : ' ') . trim($description)
159
        ));
160
    }
161
162
    /**
163
     * Add string value definition.
164
     *
165
     * @param string $value String value to add
166
     * @param string $tabs Tabs count
167
     * @param string $quote Type of quote
168
     * @return self Chaining
169
     */
170
    public function stringValue($value, $tabs = null, $quote = self::QUOTE_SINGLE)
171
    {
172
        return $this->tabs($quote . $value . $quote, $tabs);
173
    }
174
175
    /**
176
     * Add array values definition.
177
     *
178
     * @param array $items Array key-value pairs collection
179
     * @return self Chaining
180
     */
181
    public function arrayValue(array $items = array())
182
    {
183
        if (sizeof($items)) {
184
            $this->text('array(');
185
            $this->tabs++;
186
187
            // Iterate array items
188
            foreach ($items as $key => $value) {
189
                // Start array key definition
190
                $this->newLine()->stringValue($key)->text(' => ');
191
192
                // If item value is array - recursion
193
                if (is_array($value)) {
194
                    $this->arrayValue($value)->text(',');
195
                } else {
196
                    $this->stringValue($value)->text(',');
197
                }
198
            }
199
200
            $this->tabs--;
201
            $this->newLine(')');
202
        } else {
203
            $this->text('array()');
204
        }
205
206
        return $this;
207
    }
208
209
    /**
210
     * Add variable definition with array merging.
211
     *
212
     * @param string $name Variable name
213
     * @param array $value Array of key-value items for merging it to other array
214
     * @param string $arrayName Name of array to merge to, if no is specified - $name is used
215
     * @return self Chaining
216
     */
217
    public function defArrayMerge($name, array $value, $arrayName = null)
218
    {
219
        // If no other array is specified - set it to current
220
        if (!isset($arrayName)) {
221
            $arrayName = $name;
222
        }
223
224
        return $this->defvar($name, $value, ' = array_merge( ' . $arrayName . ', ', '')->text(');');
225
    }
226
227
    /**
228
     * Add variable definition.
229
     *
230
     * @param string $name Variable name
231
     * @param mixed $value Variable default value
232
     * @param string $after String to insert after variable definition
233
     * @param string $end Closing part of variable definition
234
     * @param string $quote Type of quote
235
     * @return Generator Chaining
236
     */
237
    public function defVar($name, $value = null, $after = ' = ', $end = ';', $quote = self::QUOTE_SINGLE)
238
    {
239
        // Output variable definition
240
        $this->newLine($name);
241
242
        // Get variable typeHint
243
        switch (gettype($value)) {
244
            case 'integer':
245
            case 'boolean':
246
            case 'double':
247
                $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...
248
                break;
249
            case 'string':
250
                if (strpos($value, 'EOT') !== false) {
251
                    $this->text($after)->stringValue($value, 0, self::QUOTE_NO)->text($end);
252
                } else {
253
                    $this->text($after)->stringValue($value, 0, $quote)->text($end);
254
                }
255
                break;
256
            case 'array':
257
                $this->text($after)->arrayValue($value)->text($end);
258
                break;
259
            case 'NULL':
260
            case 'object':
261
            case 'resource':
262
            default:
263
                $this->text(';');
264
        }
265
266
        return $this;
267
    }
268
269
    /**
270
     * Add trait definition.
271
     *
272
     * @param string $name Trait name
273
     * @return self Chaining
274
     */
275
    public function defTrait($name)
276
    {
277
        // If we define another class, and we were in other class context
278
        if (isset($this->class) && ($name !== $this->class)) {
279
            // Close old class context
280
            $this->endClass();
281
        }
282
283
        // Save new class name
284
        $this->class = $name;
285
286
        // Class definition start
287
        $this->newLine('trait ' . $name);
288
289
        $this->newLine('{');
290
291
        $this->tabs++;
292
293
        return $this;
294
    }
295
296
    /**
297
     * Add class definition.
298
     *
299
     * @param string $name Class name
300
     * @param string $extends Parent class name
301
     * @param array $implements Interfaces names collection
302
     * @return self Chaining
303
     */
304
    public function defClass($name, $extends = null, array $implements = array())
305
    {
306
        // If we define another class, and we were in other class context
307
        if (isset($this->class) && ($name !== $this->class)) {
308
            // Close old class context
309
            $this->endClass();
310
        }
311
312
        // Save new class name
313
        $this->class = $name;
314
315
        // Class definition start
316
        $this->newLine('class ' . $name);
317
318
        // Parent class definition
319
        if (isset($extends)) {
320
            $this->text(' extends ' . $extends);
321
        }
322
323
        // Interfaces
324
        if (sizeof($implements)) {
325
            $this->text(' implements ' . implode(',', $implements));
326
        }
327
328
        $this->newLine('{');
329
330
        $this->tabs++;
331
332
        return $this;
333
    }
334
335
    /**
336
     * Close current class context.
337
     *
338
     * @return self Chaining
339
     */
340
    public function endClass()
341
    {
342
        $this->tabs > 0 ? $this->tabs-- : null;
343
344
        // Close class definition
345
        return $this->newLine('}')
346
            // Add one empty line after class definition
347
        ->newLine('');
348
    }
349
350
    /**
351
     * Define if statement condition.
352
     *
353
     * @param string $condition Condition statement
354
     * @return self Chaining
355
     */
356
    public function defIfCondition($condition)
357
    {
358
        $this->ifConditionLevel++;
359
360
        // Class definition start
361
        $this->newLine('if (' . $condition . ') {');
362
        $this->tabs++;
363
        return $this;
364
    }
365
366
    /**
367
     * Define elseif statement condition.
368
     *
369
     * @param string $condition Condition statement
370
     * @return self Chaining
371
     */
372
    public function defElseIfCondition($condition)
373
    {
374
        $this->tabs--;
375
        // Class definition start
376
        $this->newLine('} elseif (' . $condition . ') {');
377
        $this->tabs++;
378
        return $this;
379
    }
380
381
    /**
382
     * Define else statement.
383
     *
384
     * @return self Chaining
385
     */
386
    public function defElseCondition()
387
    {
388
        $this->tabs--;
389
        // Class definition start
390
        $this->newLine('} else {');
391
        $this->tabs++;
392
        return $this;
393
    }
394
395
    /**
396
     * Close if condition statement.
397
     *
398
     * @return self Chaining
399
     */
400
    public function endIfCondition()
401
    {
402
        if ($this->ifConditionLevel--) {
403
            $this->tabs--;
404
405
            // Close class definition
406
            return $this->newLine('}');
407
        }
408
409
        return $this;
410
    }
411
412
    /**
413
     * Add class variable definition.
414
     *
415
     * @param string $name Variable name
416
     * @param string $visibility Variable accessibility level
417
     * @param mixed $value Variable default value
418
     * @return self Chaining
419
     */
420
    public function defClassVar($name, $visibility = 'public', $value = null)
421
    {
422
        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...
423
            $this->multiComment(array($comment));
424
        }
425
426
        return $this->defvar($visibility . ' ' . $name, $value)->newLine();
427
    }
428
429
    /**
430
     * Add class constant definition.
431
     *
432
     * @param string $name Constant name
433
     * @param string $value Variable default value
434
     * @return self Chaining
435
     */
436
    public function defClassConst($name, $value)
437
    {
438
        return $this->defClassVar(strtoupper($name), 'const', $value);
439
    }
440
441
    /**
442
     * Write file to disk
443
     * @param string $name Path to file
444
     * @param string $format Output file format
445
     */
446
    public function write($name, $format = 'php')
447
    {
448
        $code = $this->flush();
449
450
        if ($format === 'php') {
451
            $code = '<?php ' . $code;
452
        }
453
454
        file_put_contents($name, $code, 0775);
455
    }
456
457
    /**
458
     * Flush internal data and return it.
459
     *
460
     * @return string Current generated code
461
     */
462
    public function flush()
463
    {
464
        // We should use 4 spaces instead of tabs
465
        $code = str_replace("\t", '    ', $this->code);
466
467
        $this->tabs = 0;
468
        $this->code = '';
469
        $this->class = null;
470
471
        return $code;
472
    }
473
474
    /**
475
     * Add class function definition.
476
     *
477
     * @param string $name       Class function name
478
     * @param string $visibility Class function visibility
479
     * @param array  $parameters Class function arguments
480
     * @param array  $comments   Class function multi-line comments
481
     * @param null   $returnType Class function return type PHP7
482
     *
483
     * @return $this
484
     *
485
     */
486
    public function defClassFunction(string $name, string $visibility = 'public', array $parameters = [], array $comments = [], $returnType = null)
487
    {
488
        if ($this->class === null) {
489
            throw new \InvalidArgumentException('Cannot create class function '.$name.' with out class creation');
490
        }
491
492
        $this->defFunction($name, $parameters, $visibility.' ', $comments, $returnType);
493
494
        return $this;
495
    }
496
497
    /**
498
     * @see self::defClassFunction with public visibility
499
     *
500
     * @return $this
501
     */
502
    public function defPublicClassFunction(string $name, array $parameters = [], array $comments = [], $returnType = null)
503
    {
504
        return $this->defClassFunction($name, 'public', $parameters, $comments, $returnType);
505
    }
506
507
    /**
508
     * @see self::defClassFunction with private visibility
509
     *
510
     * @return $this
511
     */
512
    public function defPrivateClassFunction(string $name, array $parameters = [], array $comments = [], $returnType = null)
513
    {
514
        return $this->defClassFunction($name, 'private', $parameters, $comments, $returnType);
515
    }
516
517
    /**
518
     * @see self::defClassFunction with protected visibility
519
     *
520
     * @return $this
521
     */
522
    public function defProtectedClassFunction(string $name, array $parameters = [], array $comments = [], $returnType = null)
523
    {
524
        return $this->defClassFunction($name, 'protected', $parameters, $comments, $returnType);
525
    }
526
527
528
    /**
529
     * Close class function definition.
530
     *
531
     * @return $this Chaining
532
     */
533
    public function endClassFunction()
534
    {
535
        $this->endFunction();
536
537
        return $this;
538
    }
539
540
    /**
541
     * Add function definition.
542
     *
543
     * @param string $name       Function name
544
     * @param array  $parameters Collection of parameters $typeHint => $paramName
545
     * @param string $prefix     Function prefix
546
     * @param array  $comments   Function multi-line comments
547
     * @param string $returnType Function return type PHP7
548
     *
549
     * @return Generator Chaining
550
     */
551
    public function defFunction(string $name, array $parameters = [], string $prefix = '', array $comments = [], string $returnType = null)
552
    {
553
        // Convert parameters to string
554
        $parameterList = array();
555
        foreach ($parameters as $type => $parameter) {
556
            $parameterList[] = (is_string($type) ? $type.' ' : '') . $parameter;
557
        }
558
        $parameterList = sizeof($parameterList) ? implode(', ', $parameterList) : '';
559
560
        $this
561
            ->newLine('')
562
            ->multiComment($comments)
563
            ->newLine($prefix.'function ' . $name . '('.$parameterList.')' . ($returnType  !== null ? ' : ' . $returnType : ''))
564
            ->newLine('{')
565
            ->tabs('');
566
567
        $this->tabs++;
568
569
        return $this;
570
    }
571
572
    /**
573
     * Close current function context.
574
     *
575
     * @return self Chaining
576
     */
577
    public function endFunction()
578
    {
579
        $this->tabs--;
580
581
        return $this->newLine('}');
582
    }
583
584
    /**
585
     * Constructor
586
     * @param string $namespace Code namespace
587
     */
588
    public function __construct($namespace = null)
589
    {
590
        // If namespace is defined - set it
591
        if (isset($namespace)) {
592
            $this->defNamespace($namespace);
593
        }
594
    }
595
596
    /**
597
     * Add namespace declaration.
598
     *
599
     * @param string $name Namespace name
600
     *
601
     * @return $this Chaining
602
     */
603
    public function defNamespace($name)
604
    {
605
        if ($name !== '' && $name !== null) {
606
            $this->newLine('namespace ' . $name . ';')->newLine();
607
        }
608
609
        return $this;
610
    }
611
}
612
//[PHPCOMPRESSOR(remove,end)]
613