Passed
Pull Request — master (#29)
by Sébastien
12:35 queued 04:51
created

EntityGenerator::generateEntityNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 7
ccs 3
cts 4
cp 0.75
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2.0625
1
<?php
0 ignored issues
show
introduced by
The PHP open tag must be followed by exactly one blank line
Loading history...
2
/*
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a file comment
Loading history...
introduced by
Namespaced classes, interfaces and traits should not begin with a file doc comment
Loading history...
Coding Style introduced by
Empty line required before block comment
Loading history...
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Bdf\Prime\Entity;
21
22
use Bdf\Prime\Mapper\Info\InfoInterface;
23
use Bdf\Prime\Mapper\Info\ObjectPropertyInfo;
24
use Bdf\Prime\Mapper\Info\PropertyInfo;
25
use Bdf\Prime\Mapper\Mapper;
26
use Bdf\Prime\Mapper\Info\MapperInfo;
27
use Bdf\Prime\ServiceLocator;
28
use Doctrine\Common\Inflector\Inflector;
29
30
/**
31
 * Generic class used to generate PHP5 entity classes from Mapper.
32
 *
33
 *     [php]
34
 *     $mapper = $service->mappers()->build('Entity);
35
 *
36
 *     $generator = new EntityGenerator();
37
 *     $generator->setGenerateStubMethods(true);
38
 *     $generator->setRegenerateEntityIfExists(false);
39
 *     $generator->setUpdateEntityIfExists(true);
40
 *     $generator->generate($mapper, '/path/to/generate/entities');
41
 *
42
 *
43
 * @link    www.doctrine-project.org
0 ignored issues
show
Coding Style introduced by
There must be exactly one blank line before the tags in a doc comment
Loading history...
Coding Style introduced by
The tag in position 1 should be the @author tag
Loading history...
introduced by
Tag value indented incorrectly; expected 1 space but found 4
Loading history...
44
 * @since   2.0
0 ignored issues
show
Coding Style introduced by
The tag in position 2 should be the @link tag
Loading history...
introduced by
Tag value indented incorrectly; expected 1 space but found 3
Loading history...
45
 * @author  Benjamin Eberlei <[email protected]>
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @since tag
Loading history...
introduced by
Tag value indented incorrectly; expected 1 space but found 2
Loading history...
46
 * @author  Guilherme Blanco <[email protected]>
0 ignored issues
show
introduced by
Tag value indented incorrectly; expected 1 space but found 2
Loading history...
47
 * @author  Jonathan Wage <[email protected]>
0 ignored issues
show
introduced by
Tag value indented incorrectly; expected 1 space but found 2
Loading history...
48
 * @author  Roman Borschel <[email protected]>
0 ignored issues
show
introduced by
Tag value indented incorrectly; expected 1 space but found 2
Loading history...
49
 */
50
class EntityGenerator
51
{
52
    /**
53
     * Specifies class fields should be protected.
54
     */
55
    const FIELD_VISIBLE_PROTECTED = 'protected';
56
57
    /**
58
     * Specifies class fields should be private.
59
     */
60
    const FIELD_VISIBLE_PRIVATE = 'private';
61
62
    /**
63
     * The prime service locator
64
     *
65
     * @var ServiceLocator
66
     */
67
    private $prime;
68
69
    /**
70
     * The mapper info
71
     *
72
     * @var MapperInfo
73
     */
74
    private $mapperInfo;
75
76
    /**
77
     * The extension to use for written php files.
78
     *
79
     * @var string
80
     */
81
    private $extension = '.php';
82
83
    /**
84
     * Whether or not the current Mapper instance is new or old.
85
     *
86
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
87
     */
88
    private $isNew = true;
89
90
    /**
91
     * @var array
92
     */
93
    private $staticReflection = [];
94
95
    /**
96
     * Number of spaces to use for indention in generated code.
97
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @var tag in member variable comment
Loading history...
98
    private $numSpaces = 4;
99
100
    /**
101
     * The actual spaces to use for indention.
102
     *
103
     * @var string
104
     */
105
    private $spaces = '    ';
106
107
    /**
108
     * The class all generated entities should extend.
109
     *
110
     * @var string
111
     */
112
    private $classToExtend;
113
114
    /**
115
     * The interfaces all generated entities should implement.
116
     *
117
     * @var array
118
     */
119
    private $interfaces = [];
120
121
    /**
122
     * The traits
123
     *
124
     * @var array
125
     */
126
    private $traits = [];
127
128
    /**
129
     * Whether or not to generate sub methods.
130
     *
131
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
132
     */
133
    private $generateEntityStubMethods = true;
134
135
    /**
136
     * Whether or not to update the entity class if it exists already.
137
     *
138
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
139
     */
140
    private $updateEntityIfExists = false;
141
142
    /**
143
     * Whether or not to re-generate entity class if it exists already.
144
     *
145
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
146
     */
147
    private $regenerateEntityIfExists = false;
148
149
    /**
150
     * The name of get methods will not contains the 'get' prefix
151
     *
152
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
153
     */
154
    private $useGetShortcutMethod = true;
155
156
    /**
157
     * Visibility of the field
158
     *
159
     * @var string
160
     */
161
    private $fieldVisibility = self::FIELD_VISIBLE_PROTECTED;
162
163
    /**
164
     * @var string
165
     */
166
    private static $classTemplate =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
167
'<?php
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
168
169
<namespace><useStatement><entityAnnotation>
170
<entityClassName>
171
{
172
<entityTraits><entityBody>
173
}
174
';
175
176
    /**
177
     * @var string
178
     */
179
    private static $getMethodTemplate =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
180
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
181
 * <description>
182
 *
183
 * @return <variableType>
184
 */
185
public function <methodName>()
186
{
187
<spaces>return $this-><fieldName>;
188
}
189
';
190
191
    /**
192
     * @var string
193
     */
194
    private static $setMethodTemplate =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
195
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
196
 * <description>
197
 *
198
 * @param <variableType> $<variableName>
199
 *
200
 * @return $this
201
 */
202
public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
203
{
204
<spaces>$this-><fieldName> = $<variableName>;
205
206
<spaces>return $this;
207
}
208
';
209
210
    /**
211
     * @var string
212
     */
213
    private static $addMethodTemplate =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
214
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
215
 * <description>
216
 *
217
 * @param <variableType> $<variableName>
218
 *
219
 * @return $this
220
 */
221
public function <methodName>(<methodTypeHint>$<variableName>)
222
{
223
<spaces>$this-><fieldName>[] = $<variableName>;
224
225
<spaces>return $this;
226
}
227
';
228
229
    /**
230
     * @var string
231
     */
232
    private static $methodTemplate =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
233
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
234
 * <description>
235
 */
236
public function <methodName>()
237
{
238
<spaces><content>
239
}
240
';
241
242
    /**
243
     * @var string
244
     */
245
    private static $constructorMethodTemplate =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
246
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
247
 * Constructor
248
 */
249
public function __construct()
250
{
251
<spaces><collections>
252
}
253
';
254
255
    /**
256
     * @var string
257
     */
258
    private static $importableConstructorMethodTemplate =
0 ignored issues
show
Coding Style introduced by
Multi-line assignments must have the equal sign on the second line
Loading history...
259
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
260
 * Constructor
261
 *
262
 * @param array $data
263
 */
264
public function __construct(array $data = [])
265
{
266
<spaces><initialize>$this->import($data);
267
}
268
';
269
270
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $prime should have a doc-comment as per coding-style.
Loading history...
271
     * Set prime service locator
272
     */
273 21
    public function __construct(ServiceLocator $prime)
274
    {
275 21
        $this->prime = $prime;
276 21
    }
277
278
    /**
279
     * Generates and writes entity classes
280
     *
281
     * @param Mapper $mapper
282
     * @param string $file    Entity file name
283
     *
284
     * @return string|false If no generation
285
     * 
286
     * @api
287
     */
288 20
    public function generate($mapper, $file = null)
0 ignored issues
show
introduced by
Type hint "Mapper" missing for $mapper
Loading history...
289
    {
290 20
        $this->isNew = !$file || !file_exists($file) || $this->regenerateEntityIfExists;
0 ignored issues
show
Coding Style introduced by
The value of a boolean operation must not be assigned to a variable
Loading history...
Coding Style introduced by
Boolean operators are not allowed outside of control structure conditions
Loading history...
291
292
        // If entity doesn't exist or we're re-generating the entities entirely
293 20
        if ($this->isNew || !$file) {
294 20
            return $this->generateEntityClass($mapper);
295
        // If entity exists and we're allowed to update the entity class
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 12 spaces, found 8
Loading history...
296
        } elseif ($this->updateEntityIfExists && $file) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
297
            return $this->generateUpdatedEntityClass($mapper, $file);
298
        }
299
        
300
        return false;
301
    }
302
    
303
    /**
304
     * Generates a PHP5 Doctrine 2 entity class from the given Mapper instance.
305
     *
306
     * @param Mapper $mapper
307
     *
308
     * @return string
309
     */
310 20
    public function generateEntityClass(Mapper $mapper)
311
    {
312 20
        $this->mapperInfo = $mapper->info();
313
        
314 20
        $this->staticReflection[$this->mapperInfo->className()] = ['properties' => [], 'methods' => []];
315
        
316
        $placeHolders = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
317 20
            '<namespace>',
318
            '<useStatement>',
319
            '<entityAnnotation>',
320
            '<entityClassName>',
321
            '<entityTraits>',
322
            '<entityBody>'
0 ignored issues
show
introduced by
A comma should follow the last multiline array item. Found: '<entityBody>'
Loading history...
323
        );
324
325
        $replacements = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
326 20
            $this->generateEntityNamespace(),
327 20
            $this->generateEntityUse(),
328 20
            $this->generateEntityDocBlock(),
329 20
            $this->generateEntityClassName(),
330 20
            $this->generateEntityTraits(),
331 20
            $this->generateEntityBody()
0 ignored issues
show
introduced by
A comma should follow the last multiline array item. Found: )
Loading history...
332
        );
333
334 20
        $code = str_replace($placeHolders, $replacements, static::$classTemplate);
0 ignored issues
show
Bug introduced by
Since $classTemplate is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $classTemplate to at least protected.
Loading history...
335
336 20
        return str_replace('<spaces>', $this->spaces, $code);
337
    }
338
339
    /**
340
     * Generates the updated code for the given Mapper and entity at path.
341
     *
342
     * @param Mapper $mapper
343
     * @param string $file
344
     *
345
     * @return string
346
     */
347
    public function generateUpdatedEntityClass(Mapper $mapper, $file)
348
    {
349
        $this->mapperInfo = $mapper->info();
350
        
351
        $currentCode = file_get_contents($file);
352
353
        $this->parseTokensInEntityFile($currentCode);
354
        
355
        $body = $this->generateEntityBody();
356
        $body = str_replace('<spaces>', $this->spaces, $body);
357
        $last = strrpos($currentCode, '}');
358
359
        return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : '') . "}\n";
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
360
    }
361
362
    /**
363
     * @return string
364
     */
365 20
    protected function generateEntityNamespace(): string
366
    {
367 20
        if ($this->hasNamespace($this->mapperInfo->className())) {
368 20
            return 'namespace ' . $this->getNamespace($this->mapperInfo->className()) .';' . "\n\n";
369
        }
370
371
        return '';
372
    }
373
374
    /**
375
     * Generate use part
376
     * 
377
     * @return string
378
     */
379 20
    protected function generateEntityUse()
380
    {
381 20
        $use = [];
382
        
383 20
        if ($this->hasNamespace($this->getClassToExtend())) {
384 2
            $use[$this->getClassToExtend()] = 'use ' . $this->getClassToExtend() . ';';
385
        }
386
        
387 20
        foreach ($this->interfaces as $interface) {
388 3
            if ($this->hasNamespace($interface)) {
389 3
                $use[$interface] = 'use ' . $interface . ';';
390
            }
391
        }
392
        
393 20
        foreach ($this->traits as $trait) {
394 2
            if ($this->hasNamespace($trait)) {
395 2
                $use[$trait] = 'use ' . $trait . ';';
396
            }
397
        }
398
        
399 20
        foreach ($this->mapperInfo->objects() as $info) {
400 19
            $className = $info->className();
401 19
            if (!$info->belongsToRoot()) {
402 2
                continue;
403
            }
404
405 19
            if ($this->hasNamespace($className)) {
406 19
                $use[$className] = 'use '.$className.';';
407
            }
408
409 19
            if ($info->wrapper() !== null) {
410 1
                $repository = $this->prime->repository($className);
411 1
                $wrapperClass = $repository->collectionFactory()->wrapperClass($info->wrapper());
0 ignored issues
show
Bug introduced by
It seems like $info->wrapper() can also be of type callable; however, parameter $wrapper of Bdf\Prime\Collection\Col...Factory::wrapperClass() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

411
                $wrapperClass = $repository->collectionFactory()->wrapperClass(/** @scrutinizer ignore-type */ $info->wrapper());
Loading history...
412
413 1
                if ($this->hasNamespace($wrapperClass)) {
414 19
                    $use[$wrapperClass] = 'use '.$wrapperClass.';';
415
                }
416
            }
417
        }
418
        
419 20
        if (!$use) {
420 1
            return '';
421
        }
422
        
423 19
        sort($use);
424
        
425 19
        return implode("\n", $use) . "\n\n";
426
    }
427
    
428
    /**
429
     * @return string
430
     */
431 20
    protected function generateEntityClassName()
432
    {
433 20
        return 'class ' . $this->getClassName($this->mapperInfo->className()) .
434 20
            ($this->classToExtend ? ' extends ' . $this->getClassToExtendName() : null) .
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
435 20
            ($this->interfaces ? ' implements ' . $this->getInterfacesToImplement() : null);
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
436
    }
437
438
    /**
439
     * @return string
440
     */
441 20
    protected function generateEntityTraits()
442
    {
443 20
        if (!$this->traits) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->traits of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
444 18
            return '';
445
        }
446
        
447 2
        $traits = '';
448
        
449 2
        foreach ($this->traits as $trait) {
450 2
            $traits .= $this->spaces . 'use ' . $this->getRelativeClassName($trait) . ';' . "\n";
451
        }
452
        
453 2
        return $traits . "\n";
454
    }
455
456
    /**
457
     * @param Mapper $mapper
0 ignored issues
show
introduced by
Doc comment for parameter $mapper does not match actual variable name <undefined>
Loading history...
Coding Style introduced by
Superfluous parameter comment
Loading history...
458
     *
459
     * @return string
460
     */
461 20
    protected function generateEntityBody()
462
    {
463 20
        $fieldMappingProperties = $this->generateEntityFieldMappingProperties();
464 20
        $embeddedProperties = $this->generateEntityEmbeddedProperties();
465 20
        $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods() : null;
0 ignored issues
show
Coding Style introduced by
The value of a comparison must not be assigned to a variable
Loading history...
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
466
467 20
        $code = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
468
469 20
        if ($fieldMappingProperties) {
470 20
            $code[] = $fieldMappingProperties;
471
        }
472
473 20
        if ($embeddedProperties) {
474 19
            $code[] = $embeddedProperties;
475
        }
476
477 20
        $code[] = $this->generateEntityConstructor();
478
479 20
        if ($stubMethods) {
480 19
            $code[] = $stubMethods;
481
        }
482
483 20
        return implode("\n", $code);
484
    }
485
486
    /**
487
     * @return string
488
     */
489 20
    protected function generateEntityConstructor()
490
    {
491 20
        $initializable = in_array(InitializableInterface::class, $this->interfaces);
492 20
        $isImportable  = in_array(ImportableInterface::class, $this->interfaces)
493 20
                    || is_subclass_of($this->classToExtend, ImportableInterface::class);
0 ignored issues
show
Coding Style introduced by
Boolean operators are not allowed outside of control structure conditions
Loading history...
494
495 20
        $collections = [];
496
497 20
        foreach ($this->mapperInfo->objects() as $property) {
498 19
            if (!$property->belongsToRoot()) {
499 2
                continue;
500
            }
501
502 19
            if ($property->isRelation()) {
503 18
                if (!$property->isArray()) {
504 18
                    $collections[$property->name()] = '$this->'.$property->name().' = new '.$this->getRelativeClassName($property->className()).'();';
505 17
                } elseif ($property->wrapper() === 'collection') { // @todo handle other wrapper types
0 ignored issues
show
introduced by
There should be no white space after an opening "{"
Loading history...
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
Coding Style introduced by
Comments may not appear after statements
Loading history...
506 18
                    $collections[$property->name()] = '$this->'.$property->name().' = '.$this->getRelativeClassName($property->className()).'::collection();';
507
                }
508
            } else {
509 19
                $collections[$property->name()] = '$this->'.$property->name().' = new '.$this->getRelativeClassName($property->className()).'();';
510
            }
511
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
512 20
        foreach ($this->mapperInfo->properties() as $property) {
513 20
            if ($property->isDateTime() && $property->hasDefault()) {
514 1
                $constructorArgs = '';
515
                // Add the default timezone from the property type.
516 1
                if ($timezone = $property->getTimezone()) {
0 ignored issues
show
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
517 1
                    $constructorArgs = "'now', new \DateTimeZone('$timezone')";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $timezone instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
518
                }
519
520 20
                $collections[$property->name()] = '$this->'.$property->name().' = new '.$property->phpType().'('.$constructorArgs.');';
521
            }
522
        }
523
        
524 20
        $methods = [];
525
        
526 20
        if (!$this->hasMethod('__construct')) {
527 18
            if ($isImportable) {
528 2
                $buffer = '';
529
530 2
                if ($initializable) {
531 1
                    $buffer = '$this->initialize();'."\n".$this->spaces;
532 1
                } elseif ($collections) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
533 1
                    $buffer = implode("\n".$this->spaces, $collections)."\n".$this->spaces;
534
                }
535
536 2
                $methods[] = $this->prefixCodeWithSpaces(str_replace("<initialize>", $buffer, static::$importableConstructorMethodTemplate));
0 ignored issues
show
Bug introduced by
Since $importableConstructorMethodTemplate is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $importableConstructorMethodTemplate to at least protected.
Loading history...
Coding Style Comprehensibility introduced by
The string literal <initialize> does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
537 16
            } elseif ($collections && !$initializable) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
538 16
                $methods[] = $this->prefixCodeWithSpaces(str_replace("<collections>", implode("\n".$this->spaces, $collections), static::$constructorMethodTemplate));
0 ignored issues
show
Bug introduced by
Since $constructorMethodTemplate is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $constructorMethodTemplate to at least protected.
Loading history...
Coding Style Comprehensibility introduced by
The string literal <collections> does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
539
            }
540
        }
541
        
542 20
        if (!$this->hasMethod('initialize') && $initializable) {
543 1
            $methods[] = $this->generateMethod('{@inheritdoc}', 'initialize', implode("\n".$this->spaces, $collections));
544
        }
545
546 20
        return implode("\n", $methods);
547
    }
548
549
    /**
550
     * @param Mapper $mapper
0 ignored issues
show
introduced by
Doc comment for parameter $mapper does not match actual variable name <undefined>
Loading history...
Coding Style introduced by
Superfluous parameter comment
Loading history...
551
     *
552
     * @return string
553
     */
554 20
    protected function generateEntityDocBlock()
555
    {
556 20
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
557 20
        $lines[] = '/**';
558 20
        $lines[] = ' * ' . $this->getClassName($this->mapperInfo->className());
559 20
        $lines[] = ' */';
560
        
561 20
        return implode("\n", $lines);
562
    }
563
    
564
    /**
565
     * @return string
566
     */
567 19
    protected function generateEntityStubMethods()
568
    {
569 19
        $methods = [];
570
        
571 19
        foreach ($this->mapperInfo->properties() as $property) {
572 19
            if ($code = $this->generateEntityStubMethod('set', $property)) {
0 ignored issues
show
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
573 19
                $methods[] = $code;
574
            }
575
576 19
            if ($code = $this->generateEntityStubMethod('get', $property)) {
0 ignored issues
show
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
577 19
                $methods[] = $code;
578
            }
579
        }
580
581 19
        foreach ($this->mapperInfo->objects() as $property) {
582 18
            if (!$property->belongsToRoot()) {
583 2
                continue;
584
            }
585
586 18
            if (!$property->isArray() || $property->wrapper() !== null) {
587 18
                if ($code = $this->generateEntityStubMethod('set', $property)) {
0 ignored issues
show
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
588 18
                    $methods[] = $code;
589
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
590 18
                if ($code = $this->generateEntityStubMethod('get', $property)) {
0 ignored issues
show
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
591 18
                    $methods[] = $code;
592
                }
593
            } else {
594 15
                if ($code = $this->generateEntityStubMethod('add', $property)) {
0 ignored issues
show
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
595 15
                    $methods[] = $code;
596
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
597 15
                if ($code = $this->generateEntityStubMethod('set', $property)) {
0 ignored issues
show
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
598 15
                    $methods[] = $code;
599
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
600 15
                if ($code = $this->generateEntityStubMethod('get', $property)) {
0 ignored issues
show
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
Coding Style introduced by
Variable assignment found within a condition. Did you mean to do a comparison ?
Loading history...
601 18
                    $methods[] = $code;
602
                }
603
            }
604
        }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end foreach"
Loading history...
605
606 19
        return implode("\n", $methods);
607
    }
608
609
    /**
610
     * @return string
611
     */
612 20
    protected function generateEntityFieldMappingProperties()
613
    {
614 20
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
615
616 20
        foreach ($this->mapperInfo->properties() as  $property) {
0 ignored issues
show
Coding Style introduced by
There should be 1 space after "as" as per the coding-style, but found 2.
Loading history...
617 20
            if ($this->hasProperty($property->name())) {
618 2
                continue;
619
            }
620
            
621 20
            $default = '';
622 20
            if ($property->hasDefault() && !$property->isDateTime()) {
623 1
                $default = ' = '.$this->stringfyValue(
624 1
                    $property->convert($property->getDefault())
625
                );
626 20
            } elseif ($property->isArray()) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
627 14
                $default = ' = []';
628
            }
629
630 20
            $lines[] = $this->generateFieldMappingPropertyDocBlock($property);
631 20
            $lines[] = $this->spaces.$this->fieldVisibility.' $'.$property->name().$default.";\n";
632
        }
633
634 20
        return implode("\n", $lines);
635
    }
636
637
    /**
638
     * @return string
639
     */
640 20
    protected function generateEntityEmbeddedProperties()
641
    {
642 20
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
643
644 20
        foreach ($this->mapperInfo->objects() as $property) {
645 19
            if (!$property->belongsToRoot() || $this->hasProperty($property->name())) {
646 4
                continue;
647
            }
648
            
649 19
            if (!$property->isRelation()) {
650 2
                $lines[] = $this->generateEmbeddedPropertyDocBlock($property);
651 2
                $lines[] = $this->spaces.$this->fieldVisibility.' $'.$property->name().";\n";
652
            } else {
653 18
                $name = $property->name();
654
655
                // Do not initialize the property if it's a wrapper
656 18
                if ($property->isArray() && $property->wrapper() === null) {
657 14
                    $name .= ' = []';
658
                }
659
660 18
                $lines[] = $this->generateEmbeddedPropertyDocBlock($property);
661 19
                $lines[] = $this->spaces.$this->fieldVisibility.' $'.$name.";\n";
662
            }
663
        }
664
665 20
        return implode("\n", $lines);
666
    }
667
668
    /**
669
     * @param string            $type
670
     * @param InfoInterface     $propertyInfo
671
     * @param string|null       $defaultValue
672
     *
673
     * @return string
674
     */
675 19
    protected function generateEntityStubMethod($type, InfoInterface $propertyInfo, $defaultValue = null)
676
    {
677 19
        $fieldName = $propertyInfo->name();
678
679
        // The hint flag help algorythm to determine the hint info for object parameter.
680
        // It should be 'array' for collection but the add method need the object hint.
681
        // setItems(array $items)
682
        // addItem(Item $item)
683 19
        $hintOne = false;
684
685 19
        if ($type === 'get' && $this->useGetShortcutMethod === true) {
686 18
            $variableName = Inflector::camelize($fieldName);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Inflector\Inflector::camelize() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

686
            $variableName = /** @scrutinizer ignore-deprecated */ Inflector::camelize($fieldName);
Loading history...
687 18
            $methodName = $variableName;
688
        } else {
689 19
            $methodName = $type . Inflector::classify($fieldName);
690 19
            $variableName = Inflector::camelize($fieldName);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Inflector\Inflector::camelize() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

690
            $variableName = /** @scrutinizer ignore-deprecated */ Inflector::camelize($fieldName);
Loading history...
691
        }
692
        
693 19
        if ($type === 'add') {
694 15
            $methodName = Inflector::singularize($methodName);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Inflector\Inflector::singularize() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

694
            $methodName = /** @scrutinizer ignore-deprecated */ Inflector::singularize($methodName);
Loading history...
695 15
            $variableName = Inflector::singularize($variableName);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Inflector\Inflector::singularize() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

695
            $variableName = /** @scrutinizer ignore-deprecated */ Inflector::singularize($variableName);
Loading history...
696 15
            $hintOne = true;
697
        }
698
699 19
        if ($this->hasMethod($methodName)) {
700
            return '';
701
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
702 19
        $this->staticReflection[$this->mapperInfo->className()]['methods'][] = strtolower($methodName);
703
704 19
        if ($propertyInfo->isObject()) {
705
            /** @var ObjectPropertyInfo $propertyInfo */
0 ignored issues
show
Coding Style introduced by
Block comments must be started with /*
Loading history...
Coding Style introduced by
Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
706 18
            $variableType = $this->getRelativeClassName($propertyInfo->className());
707 18
            $methodTypeHint =  $variableType.' ';
0 ignored issues
show
Coding Style introduced by
Expected 1 space after "="; 2 found
Loading history...
708
        } else {
709
            /** @var PropertyInfo $propertyInfo */
0 ignored issues
show
Coding Style introduced by
Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead
Loading history...
Coding Style introduced by
Block comments must be started with /*
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
710 19
            $variableType = $propertyInfo->phpType();
711 19
            $methodTypeHint = '';
712
        }
713
714 19
        if ($propertyInfo->isArray() && $hintOne === false) {
715 16
            if ($propertyInfo->isObject() && $propertyInfo->wrapper() !== null) {
0 ignored issues
show
Bug introduced by
The method wrapper() does not exist on Bdf\Prime\Mapper\Info\PropertyInfo. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

715
            if ($propertyInfo->isObject() && $propertyInfo->/** @scrutinizer ignore-call */ wrapper() !== null) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
716
                /** @var ObjectPropertyInfo $propertyInfo */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Block comments must be started with /*
Loading history...
Coding Style introduced by
Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
717 1
                $repository = $this->prime->repository($propertyInfo->className());
718 1
                $wrapperClass = $this->getRelativeClassName($repository->collectionFactory()->wrapperClass($propertyInfo->wrapper()));
0 ignored issues
show
Bug introduced by
It seems like $propertyInfo->wrapper() can also be of type callable; however, parameter $wrapper of Bdf\Prime\Collection\Col...Factory::wrapperClass() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

718
                $wrapperClass = $this->getRelativeClassName($repository->collectionFactory()->wrapperClass(/** @scrutinizer ignore-type */ $propertyInfo->wrapper()));
Loading history...
719
720 1
                $methodTypeHint = $wrapperClass.' ';
721 1
                $variableType .= '[]|'.$wrapperClass;
722
            } else {
723 15
                $methodTypeHint = 'array ';
724
725 15
                if ($variableType !== 'array') {
726 15
                    $variableType .= '[]';
727
                }
728
            }
729
        }
730
731
        $replacements = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
732 19
          '<description>'       => ucfirst($type).' '.$variableName,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
733 19
          '<methodTypeHint>'    => $methodTypeHint,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
734 19
          '<variableType>'      => $variableType,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
735 19
          '<variableName>'      => $variableName,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
736 19
          '<methodName>'        => $methodName,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
737 19
          '<fieldName>'         => $fieldName,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
738 19
          '<variableDefault>'   => ($defaultValue !== null ) ? (' = '.$defaultValue) : ''
0 ignored issues
show
introduced by
A comma should follow the last multiline array item. Found: ''
Loading history...
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
introduced by
There should be no white space before a closing ")"
Loading history...
739
        );
740
741 19
        $method = str_replace(
742 19
            array_keys($replacements),
743 19
            array_values($replacements),
744 19
            $this->getMethodTemplate($type)
745
        );
746
747 19
        return $this->prefixCodeWithSpaces($method);
748
    }
749
750
    /**
751
     * Get the template of the method
752
     *
753
     * @param string $prefix
754
     *
755
     * @return string
756
     *
757
     * @throws \LogicException
758
     */
759 19
    private function getMethodTemplate($prefix)
0 ignored issues
show
Coding Style introduced by
Private method name "EntityGenerator::getMethodTemplate" must be prefixed with an underscore
Loading history...
760
    {
761
        switch ($prefix) {
762 19
            case 'get':
763 19
                return static::$getMethodTemplate;
0 ignored issues
show
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
Bug introduced by
Since $getMethodTemplate is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $getMethodTemplate to at least protected.
Loading history...
764
765 19
            case 'add':
766 15
                return static::$addMethodTemplate;
0 ignored issues
show
Bug introduced by
Since $addMethodTemplate is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $addMethodTemplate to at least protected.
Loading history...
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
767
768 19
            case 'set':
769 19
                return static::$setMethodTemplate;
0 ignored issues
show
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
Bug introduced by
Since $setMethodTemplate is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $setMethodTemplate to at least protected.
Loading history...
770
        }
771
772
        throw new \LogicException('No template found for method "'.$prefix.'"');
773
    }
774
775
    /**
776
     * @param string $description
777
     * @param string $methodName
778
     * @param string $content
779
     *
780
     * @return string
781
     */
782 1
    protected function generateMethod(string $description, string $methodName, string $content)
783
    {
784 1
        if ($this->hasMethod($methodName)) {
785
            return '';
786
        }
787
        
788 1
        $this->staticReflection[$this->mapperInfo->className()]['methods'][] = $methodName;
789
790
        $replacements = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
791 1
            '<description>' => $description,
792 1
            '<methodName>'  => $methodName,
793 1
            '<content>'     => $content,
794
        );
795
796 1
        $method = str_replace(
797 1
            array_keys($replacements),
798 1
            array_values($replacements),
799 1
            static::$methodTemplate
0 ignored issues
show
Bug introduced by
Since $methodTemplate is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $methodTemplate to at least protected.
Loading history...
800
        );
801
802 1
        return $this->prefixCodeWithSpaces($method);
803
    }
804
805
    /**
806
     * @param PropertyInfo $property
807
     *
808
     * @return string
809
     */
810 20
    protected function generateFieldMappingPropertyDocBlock($property)
0 ignored issues
show
introduced by
Type hint "PropertyInfo" missing for $property
Loading history...
811
    {
812 20
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
813 20
        $lines[] = $this->spaces . '/**';
814 20
        $lines[] = $this->spaces . ' * @var '.$property->phpType();
815 20
        $lines[] = $this->spaces . ' */';
816
817 20
        return implode("\n", $lines);
818
    }
819
820
    /**
821
     * @param ObjectPropertyInfo $property
822
     *
823
     * @return string
824
     */
825 19
    protected function generateEmbeddedPropertyDocBlock($property)
0 ignored issues
show
introduced by
Type hint "ObjectPropertyInfo" missing for $property
Loading history...
826
    {
827 19
        $className = $property->className();
828 19
        if ($className) {
829 19
            $className = $this->getRelativeClassName($className);
830
831 19
            if ($property->isArray()) {
832 15
                if ($property->wrapper() !== null) {
833 1
                    $repository = $this->prime->repository($property->className());
834 1
                    $className = $this->getRelativeClassName($repository->collectionFactory()->wrapperClass($property->wrapper())).'|'.$className.'[]';
0 ignored issues
show
Bug introduced by
It seems like $property->wrapper() can also be of type callable; however, parameter $wrapper of Bdf\Prime\Collection\Col...Factory::wrapperClass() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

834
                    $className = $this->getRelativeClassName($repository->collectionFactory()->wrapperClass(/** @scrutinizer ignore-type */ $property->wrapper())).'|'.$className.'[]';
Loading history...
835
                } else {
836 19
                    $className .= '[]';
837
                }
838
            }
839
        } else {
840
            $className = '{type}';
841
        }
842
        
843 19
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
844 19
        $lines[] = $this->spaces . '/**';
845 19
        $lines[] = $this->spaces . ' * @var '.$className;
846 19
        $lines[] = $this->spaces . ' */';
847
848 19
        return implode("\n", $lines);
849
    }
850
    
851
    //
852
    //---------- tools methods
0 ignored issues
show
Coding Style introduced by
No space found before comment text; expected "// ---------- tools methods" but found "//---------- tools methods"
Loading history...
853
    //
854
855
    /**
856
     * @todo this won't work if there is a namespace in brackets and a class outside of it.
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
857
     *
858
     * @param string $src
0 ignored issues
show
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
859
     *
860
     * @return void
0 ignored issues
show
introduced by
If there is no return value for a function, there must not be a @return tag.
Loading history...
861
     */
862
    protected function parseTokensInEntityFile($src)
863
    {
864
        $tokens = token_get_all($src);
865
        $lastSeenNamespace = "";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
866
        $lastSeenClass = false;
867
868
        $inNamespace = false;
869
        $inClass = false;
870
871
        for ($i = 0; $i < count($tokens); $i++) {
0 ignored issues
show
Coding Style Performance introduced by
The use of count() inside a loop condition is not allowed; assign the return value to a variable and use the variable in the loop condition instead
Loading history...
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
872
            $token = $tokens[$i];
873
            if (in_array($token[0], array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT))) {
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
introduced by
If the line declaring an array spans longer than 80 characters, each element should be broken into its own line
Loading history...
874
                continue;
875
            }
876
877
            if ($inNamespace) {
878
                if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
879
                    $lastSeenNamespace .= $token[1];
880
                } elseif (is_string($token) && in_array($token, array(';', '{'))) {
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
881
                    $inNamespace = false;
882
                }
883
            }
884
885
            if ($inClass) {
886
                $inClass = false;
887
                $lastSeenClass = $lastSeenNamespace . ($lastSeenNamespace ? '\\' : '') . $token[1];
0 ignored issues
show
Coding Style introduced by
The value of a comparison must not be assigned to a variable
Loading history...
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
888
                $this->staticReflection[$lastSeenClass]['properties'] = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
889
                $this->staticReflection[$lastSeenClass]['methods'] = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
890
            }
891
892
            if ($token[0] == T_NAMESPACE) {
893
                $lastSeenNamespace = "";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
894
                $inNamespace = true;
895
            } elseif ($token[0] == T_CLASS && $tokens[$i-1][0] != T_DOUBLE_COLON) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
Coding Style introduced by
Operation must be bracketed
Loading history...
Coding Style introduced by
Expected 1 space before "-"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "-"; 0 found
Loading history...
896
                $inClass = true;
897
            } elseif ($token[0] == T_FUNCTION) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
898
                if ($tokens[$i+2][0] == T_STRING) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "+"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "+"; 0 found
Loading history...
Coding Style introduced by
Operation must be bracketed
Loading history...
899
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+2][1]);
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "+"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "+"; 0 found
Loading history...
Coding Style introduced by
Operation must be bracketed
Loading history...
900
                } elseif ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal & does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
Coding Style introduced by
Expected 1 space before "+"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "+"; 0 found
Loading history...
Coding Style introduced by
Operation must be bracketed
Loading history...
901
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+3][1]);
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "+"; 0 found
Loading history...
Coding Style introduced by
Operation must be bracketed
Loading history...
Coding Style introduced by
Expected 1 space after "+"; 0 found
Loading history...
902
                }
903
            } elseif (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
Coding Style introduced by
Operation must be bracketed
Loading history...
Coding Style introduced by
Expected 1 space before "+"; 0 found
Loading history...
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
Coding Style introduced by
Expected 1 space after "+"; 0 found
Loading history...
introduced by
If the line declaring an array spans longer than 80 characters, each element should be broken into its own line
Loading history...
904
                $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
0 ignored issues
show
Coding Style introduced by
Operation must be bracketed
Loading history...
Coding Style introduced by
Expected 1 space after "+"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space before "+"; 0 found
Loading history...
905
            }
906
        }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end for"
Loading history...
907
    }
908
909
    /**
910
     * @param string $property
911
     *
912
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
913
     */
914 20
    protected function hasProperty($property)
915
    {
916 20
        if ($this->classToExtend || (!$this->isNew && class_exists($this->mapperInfo->className()))) {
917
            // don't generate property if its already on the base class.
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
918 2
            $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $this->mapperInfo->className());
0 ignored issues
show
Coding Style introduced by
The value of a comparison must not be assigned to a variable
Loading history...
919 2
            if ($reflClass->hasProperty($property)) {
920 2
                return true;
921
            }
922
        }
923
924
        // check traits for existing property
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
925 20
        foreach ($this->getTraitsReflections() as $trait) {
926
            if ($trait->hasProperty($property)) {
927
                return true;
928
            }
929
        }
930
931
        return (
932 20
            isset($this->staticReflection[$this->mapperInfo->className()]) &&
0 ignored issues
show
Coding Style introduced by
Boolean operators are not allowed outside of control structure conditions
Loading history...
933 20
            in_array($property, $this->staticReflection[$this->mapperInfo->className()]['properties'])
934
        );
935
    }
936
937
    /**
938
     * @param string $method
939
     *
940
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
941
     */
942 20
    protected function hasMethod($method)
943
    {
944 20
        if ($this->classToExtend || (!$this->isNew && class_exists($this->mapperInfo->className()))) {
945
            // don't generate method if its already on the base class.
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
946 2
            $reflClass = new \ReflectionClass($this->getClassToExtend() ?: $this->mapperInfo->className());
0 ignored issues
show
Coding Style introduced by
The value of a comparison must not be assigned to a variable
Loading history...
947
948 2
            if ($reflClass->hasMethod($method)) {
949 2
                return true;
950
            }
951
        }
952
953
        // check traits for existing method
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
954 20
        foreach ($this->getTraitsReflections() as $trait) {
955
            if ($trait->hasMethod($method)) {
956
                return true;
957
            }
958
        }
959
960
        return (
961 20
            isset($this->staticReflection[$this->mapperInfo->className()]) &&
0 ignored issues
show
Coding Style introduced by
Boolean operators are not allowed outside of control structure conditions
Loading history...
962 20
            in_array(strtolower($method), $this->staticReflection[$this->mapperInfo->className()]['methods'])
963
        );
964
    }
965
966
    /**
967
     * Get class name relative to use
968
     * 
969
     * @param string $className
970
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
971
     */
972 19
    protected function getRelativeClassName($className)
973
    {
974 19
        $className = ltrim($className, '\\');
975
        
976 19
        if ($this->hasNamespace($className)) {
977 19
            return $this->getClassName($className);
978
        } else {
979 2
            return '\\' . $className;
980
        }
981
    }
982
    
983
    /**
984
     * Get the class short name
985
     * 
986
     * @param string $className
987
     *
988
     * @return string
989
     */
990 20
    protected function getClassName($className)
991
    {
992 20
        $parts = explode('\\', $className);
993 20
        return array_pop($parts);
994
    }
995
996
    /**
997
     * @param string $className
998
     *
999
     * @return string
1000
     */
1001 20
    protected function getNamespace($className)
1002
    {
1003 20
        $parts = explode('\\', $className);
1004 20
        array_pop($parts);
1005
        
1006 20
        return implode('\\', $parts);
1007
    }
1008
    
1009
    /**
1010
     * @param string $className
1011
     *
1012
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
1013
     */
1014 20
    protected function hasNamespace($className)
1015
    {
1016 20
        return strrpos($className, '\\') != 0;
1017
    }
1018
1019
    /**
1020
     * @return string
1021
     */
1022 2
    protected function getClassToExtendName()
1023
    {
1024 2
        $refl = new \ReflectionClass($this->getClassToExtend());
1025
1026 2
        return $this->getRelativeClassName($refl->getName());
1027
    }
1028
1029
    /**
1030
     * @return string
1031
     */
1032 3
    protected function getInterfacesToImplement()
1033
    {
1034 3
        $interfaces = [];
1035
        
1036 3
        foreach ($this->interfaces as $interface) {
1037 3
            $refl = new \ReflectionClass($interface);
1038
1039 3
            $interfaces[] = $this->getRelativeClassName($refl->getName());
1040
        }
1041
        
1042 3
        return implode(', ', $interfaces);
1043
    }
1044
    
1045
    /**
1046
     * @param Mapper $mapper
0 ignored issues
show
introduced by
Doc comment for parameter $mapper does not match actual variable name <undefined>
Loading history...
Coding Style introduced by
Superfluous parameter comment
Loading history...
1047
     *
1048
     * @return \ReflectionClass[]
1049
     */
1050 20
    protected function getTraitsReflections()
1051
    {
1052 20
        if ($this->isNew) {
1053 20
            return [];
1054
        }
1055
        
1056
        $reflClass = new \ReflectionClass($this->mapperInfo->className());
1057
1058
        $traits = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
1059
1060
        while ($reflClass !== false) {
1061
            $traits = array_merge($traits, $reflClass->getTraits());
0 ignored issues
show
Bug introduced by
It seems like $reflClass->getTraits() can also be of type null; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1061
            $traits = array_merge($traits, /** @scrutinizer ignore-type */ $reflClass->getTraits());
Loading history...
1062
1063
            $reflClass = $reflClass->getParentClass();
1064
        }
1065
1066
        return $traits;
1067
    }
1068
    
1069
    /**
1070
     * @param string $code
1071
     * @param int    $num
0 ignored issues
show
Coding Style introduced by
Expected "integer" but found "int" for parameter type
Loading history...
1072
     *
1073
     * @return string
1074
     */
1075 20
    protected function prefixCodeWithSpaces($code, $num = 1)
1076
    {
1077 20
        $lines = explode("\n", $code);
1078
1079 20
        foreach ($lines as $key => $value) {
1080 20
            if ( ! empty($value)) {
0 ignored issues
show
introduced by
There should be no white space after an opening "("
Loading history...
introduced by
A unary operator statement must not be followed by a space
Loading history...
Coding Style introduced by
Expected 0 spaces after opening bracket; 1 found
Loading history...
Coding Style introduced by
First condition of a multi-line IF statement must directly follow the opening parenthesis
Loading history...
1081 20
                $lines[$key] = str_repeat($this->spaces, $num) . $lines[$key];
1082
            }
1083
        }
1084
1085 20
        return implode("\n", $lines);
1086
    }
1087
    
1088
    /**
1089
     * Get string representation of a value
1090
     * 
1091
     * @param mixed $value
1092
     *
1093
     * @return string
1094
     */
1095 1
    protected function stringfyValue($value)
1096
    {
1097 1
        if (is_array($value)) {
1098
            if (empty($value)) {
1099
                return '[]';
1100
            }
1101
            
1102
            return var_export($value, true);
1103
        }
1104
        
1105 1
        if (null === $value) {
1106
            return 'null';
1107
        }
1108
        
1109 1
        if (is_string($value)) {
1110 1
            return "'" . $value . "'";
1111
        }
1112
        
1113
        if (is_bool($value)) {
1114
            return $value ? 'true' : 'false';
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
1115
        }
1116
1117
        return $value;
1118
    }
1119
1120
    //---------------------- mutators
0 ignored issues
show
Coding Style introduced by
No space found before comment text; expected "// ---------------------- mutators" but found "//---------------------- mutators"
Loading history...
1121
1122
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $numSpaces should have a doc-comment as per coding-style.
Loading history...
1123
     * Sets the number of spaces the exported class should have.
1124
     *
1125
     * @api
1126
     */
1127 1
    public function setNumSpaces(int $numSpaces): void
1128
    {
1129 1
        $this->spaces = str_repeat(' ', $numSpaces);
1130 1
        $this->numSpaces = $numSpaces;
1131 1
    }
1132
1133
    /**
1134
     * Gets the indentation spaces
1135
     */
1136 1
    public function getNumSpaces(): int
1137
    {
1138 1
        return $this->numSpaces;
1139
    }
1140
1141
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $extension should have a doc-comment as per coding-style.
Loading history...
1142
     * Sets the extension to use when writing php files to disk.
1143
     *
1144
     * @api
1145
     */
1146 1
    public function setExtension(string $extension): void
1147
    {
1148 1
        $this->extension = $extension;
1149 1
    }
1150
1151
    /**
1152
     * Get the file extension
1153
     */
1154 1
    public function getExtension(): string
1155
    {
1156 1
        return $this->extension;
1157
    }
1158
1159
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $classToExtend should have a doc-comment as per coding-style.
Loading history...
1160
     * Sets the name of the class the generated classes should extend from.
1161
     *
1162
     * @api
1163
     */
1164 3
    public function setClassToExtend(string $classToExtend): void
1165
    {
1166 3
        $this->classToExtend = $classToExtend;
1167 3
    }
1168
1169
    /**
1170
     * Get the class to extend
1171
     */
1172 21
    public function getClassToExtend(): ?string
1173
    {
1174 21
        return $this->classToExtend;
1175
    }
1176
1177
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $interface should have a doc-comment as per coding-style.
Loading history...
1178
     * Add interface to implement
1179
     *
1180
     * @api
1181
     */
1182 2
    public function addInterface(string $interface)
1183
    {
1184 2
        $this->interfaces[$interface] = $interface;
1185 2
    }
1186
1187
    /**
1188
     * Sets the interfaces
1189
     *
1190
     * @param string[] $interfaces
1191
     *
1192
     * @api
1193
     */
1194 2
    public function setInterfaces(array $interfaces): void
1195
    {
1196 2
        $this->interfaces = $interfaces;
1197 2
    }
1198
1199
    /**
1200
     * Get the registered interfaces
1201
     */
1202 1
    public function getInterfaces(): array
1203
    {
1204 1
        return $this->interfaces;
1205
    }
1206
1207
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $trait should have a doc-comment as per coding-style.
Loading history...
1208
     * Add trait
1209
     *
1210
     * @api
1211
     */
1212 2
    public function addTrait(string $trait)
1213
    {
1214 2
        $this->traits[$trait] = $trait;
1215 2
    }
1216
1217
    /**
1218
     * Sets the traits
1219
     *
1220
     * @param string[] $traits
1221
     *
1222
     * @api
1223
     */
1224 1
    public function setTraits(array $traits): void
1225
    {
1226 1
        $this->traits = $traits;
1227 1
    }
1228
1229
    /**
1230
     * Get the registered traits
1231
     */
1232 1
    public function getTraits(): array
1233
    {
1234 1
        return $this->traits;
1235
    }
1236
1237
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $visibility should have a doc-comment as per coding-style.
Loading history...
1238
     * Sets the class fields visibility for the entity (can either be private or protected).
1239
     *
1240
     * @throws \InvalidArgumentException
1241
     *
1242
     * @api
1243
     */
1244 2
    public function setFieldVisibility(string $visibility): void
1245
    {
1246 2
        if ($visibility !== static::FIELD_VISIBLE_PRIVATE && $visibility !== static::FIELD_VISIBLE_PROTECTED) {
1247
            throw new \InvalidArgumentException('Invalid provided visibility (only private and protected are allowed): ' . $visibility);
1248
        }
1249
1250 2
        $this->fieldVisibility = $visibility;
1251 2
    }
1252
1253
    /**
1254
     * Get the field visibility
1255
     */
1256 1
    public function getFieldVisibility(): string
1257
    {
1258 1
        return $this->fieldVisibility;
1259
    }
1260
1261
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $bool should have a doc-comment as per coding-style.
Loading history...
1262
     * Sets whether or not to try and update the entity if it already exists.
1263
     *
1264
     * @api
1265
     */
1266 1
    public function setUpdateEntityIfExists(bool $bool): void
1267
    {
1268 1
        $this->updateEntityIfExists = $bool;
1269 1
    }
1270
1271
    /**
1272
     * Get the flag for updating the entity
1273
     */
1274 1
    public function getUpdateEntityIfExists(): bool
1275
    {
1276 1
        return $this->updateEntityIfExists;
1277
    }
1278
1279
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $bool should have a doc-comment as per coding-style.
Loading history...
1280
     * Sets whether or not to regenerate the entity if it exists.
1281
     *
1282
     * @api
1283
     */
1284 1
    public function setRegenerateEntityIfExists(bool $bool): void
1285
    {
1286 1
        $this->regenerateEntityIfExists = $bool;
1287 1
    }
1288
1289
    /**
1290
     * Get the flag for regenerating entity
1291
     */
1292 1
    public function getRegenerateEntityIfExists(): bool
1293
    {
1294 1
        return $this->regenerateEntityIfExists;
1295
    }
1296
1297
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $bool should have a doc-comment as per coding-style.
Loading history...
1298
     * Sets whether or not to generate stub methods for the entity.
1299
     *
1300
     * @api
1301
     */
1302 2
    public function setGenerateStubMethods(bool $bool): void
1303
    {
1304 2
        $this->generateEntityStubMethods = $bool;
1305 2
    }
1306
1307
    /**
1308
     * Get the flag for generating stub methods
1309
     */
1310 1
    public function getGenerateStubMethods(): bool
1311
    {
1312 1
        return $this->generateEntityStubMethods;
1313
    }
1314
1315
    /**
1316
     * Sets whether or not the get mehtod will be suffixed by 'get'.
1317
     *
1318
     * @param bool $bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for parameter type
Loading history...
1319
     *
1320
     * @return void
0 ignored issues
show
introduced by
If there is no return value for a function, there must not be a @return tag.
Loading history...
1321
     *
1322
     * @api
1323
     */
1324 2
    public function useGetShortcutMethod($bool = true)
1325
    {
1326 2
        $this->useGetShortcutMethod = $bool;
1327 2
    }
1328
1329
    /**
1330
     * Get the flag for get mehtod name.
1331
     */
1332 1
    public function getUseGetShortcutMethod(): bool
1333
    {
1334 1
        return $this->useGetShortcutMethod;
1335
    }
1336
}