Passed
Branch master (33b347)
by Sébastien
06:52
created

EntityGenerator::generateEntityStubMethods()   C

Complexity

Conditions 13
Paths 70

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 13

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 40
ccs 22
cts 22
cp 1
rs 6.6166
c 0
b 0
f 0
cc 13
nc 70
nop 0
crap 13

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
use Doctrine\Inflector\Inflector as InflectorObject;
30
use Doctrine\Inflector\InflectorFactory;
31
32
/**
33
 * Generic class used to generate PHP5 entity classes from Mapper.
34
 *
35
 *     [php]
36
 *     $mapper = $service->mappers()->build('Entity);
37
 *
38
 *     $generator = new EntityGenerator();
39
 *     $generator->setGenerateStubMethods(true);
40
 *     $generator->setRegenerateEntityIfExists(false);
41
 *     $generator->setUpdateEntityIfExists(true);
42
 *     $generator->generate($mapper, '/path/to/generate/entities');
43
 *
44
 *
45
 * @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...
46
 * @since   2.0
0 ignored issues
show
introduced by
Tag value indented incorrectly; expected 1 space but found 3
Loading history...
Coding Style introduced by
The tag in position 2 should be the @link tag
Loading history...
47
 * @author  Benjamin Eberlei <[email protected]>
0 ignored issues
show
introduced by
Tag value indented incorrectly; expected 1 space but found 2
Loading history...
Coding Style introduced by
The tag in position 3 should be the @since tag
Loading history...
48
 * @author  Guilherme Blanco <[email protected]>
0 ignored issues
show
introduced by
Tag value indented incorrectly; expected 1 space but found 2
Loading history...
49
 * @author  Jonathan Wage <[email protected]>
0 ignored issues
show
introduced by
Tag value indented incorrectly; expected 1 space but found 2
Loading history...
50
 * @author  Roman Borschel <[email protected]>
0 ignored issues
show
introduced by
Tag value indented incorrectly; expected 1 space but found 2
Loading history...
51
 */
52
class EntityGenerator
53
{
54
    /**
55
     * Specifies class fields should be protected.
56
     */
57
    const FIELD_VISIBLE_PROTECTED = 'protected';
58
59
    /**
60
     * Specifies class fields should be private.
61
     */
62
    const FIELD_VISIBLE_PRIVATE = 'private';
63
64
    /**
65
     * The prime service locator
66
     *
67
     * @var ServiceLocator
68
     */
69
    private $prime;
70
71
    /**
72
     * The inflector instance
73
     *
74
     * @var InflectorObject
75
     */
76
    private $inflector;
77
78
    /**
79
     * The mapper info
80
     *
81
     * @var MapperInfo
82
     */
83
    private $mapperInfo;
84
85
    /**
86
     * The extension to use for written php files.
87
     *
88
     * @var string
89
     */
90
    private $extension = '.php';
91
92
    /**
93
     * Whether or not the current Mapper instance is new or old.
94
     *
95
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
96
     */
97
    private $isNew = true;
98
99
    /**
100
     * @var array
101
     */
102
    private $staticReflection = [];
103
104
    /**
105
     * Number of spaces to use for indention in generated code.
106
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @var tag in member variable comment
Loading history...
107
    private $numSpaces = 4;
108
109
    /**
110
     * The actual spaces to use for indention.
111
     *
112
     * @var string
113
     */
114
    private $spaces = '    ';
115
116
    /**
117
     * The class all generated entities should extend.
118
     *
119
     * @var string
120
     */
121
    private $classToExtend;
122
123
    /**
124
     * The interfaces all generated entities should implement.
125
     *
126
     * @var array
127
     */
128
    private $interfaces = [];
129
130
    /**
131
     * The traits
132
     *
133
     * @var array
134
     */
135
    private $traits = [];
136
137
    /**
138
     * Whether or not to generate sub methods.
139
     *
140
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
141
     */
142
    private $generateEntityStubMethods = true;
143
144
    /**
145
     * Whether or not to update the entity class if it exists already.
146
     *
147
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
148
     */
149
    private $updateEntityIfExists = false;
150
151
    /**
152
     * Whether or not to re-generate entity class if it exists already.
153
     *
154
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
155
     */
156
    private $regenerateEntityIfExists = false;
157
158
    /**
159
     * The name of get methods will not contains the 'get' prefix
160
     *
161
     * @var boolean
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for @var tag in member variable comment
Loading history...
162
     */
163
    private $useGetShortcutMethod = true;
164
165
    /**
166
     * Visibility of the field
167
     *
168
     * @var string
169
     */
170
    private $fieldVisibility = self::FIELD_VISIBLE_PROTECTED;
171
172
    /**
173
     * @var string
174
     */
175
    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...
176
'<?php
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
177
178
<namespace><useStatement><entityAnnotation>
179
<entityClassName>
180
{
181
<entityTraits><entityBody>
182
}
183
';
184
185
    /**
186
     * @var string
187
     */
188
    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...
189
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
190
 * <description>
191
 *
192
 * @return <variableType>
193
 */
194
public function <methodName>()
195
{
196
<spaces>return $this-><fieldName>;
197
}
198
';
199
200
    /**
201
     * @var string
202
     */
203
    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...
204
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
205
 * <description>
206
 *
207
 * @param <variableType> $<variableName>
208
 *
209
 * @return $this
210
 */
211
public function <methodName>(<methodTypeHint>$<variableName><variableDefault>)
212
{
213
<spaces>$this-><fieldName> = $<variableName>;
214
215
<spaces>return $this;
216
}
217
';
218
219
    /**
220
     * @var string
221
     */
222
    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...
223
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
224
 * <description>
225
 *
226
 * @param <variableType> $<variableName>
227
 *
228
 * @return $this
229
 */
230
public function <methodName>(<methodTypeHint>$<variableName>)
231
{
232
<spaces>$this-><fieldName>[] = $<variableName>;
233
234
<spaces>return $this;
235
}
236
';
237
238
    /**
239
     * @var string
240
     */
241
    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...
242
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
243
 * <description>
244
 */
245
public function <methodName>()
246
{
247
<spaces><content>
248
}
249
';
250
251
    /**
252
     * @var string
253
     */
254
    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...
255
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
256
 * Constructor
257
 */
258
public function __construct()
259
{
260
<spaces><collections>
261
}
262
';
263
264
    /**
265
     * @var string
266
     */
267
    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...
268
'/**
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
269
 * Constructor
270
 *
271
 * @param array $data
272
 */
273
public function __construct(array $data = [])
274
{
275
<spaces><initialize>$this->import($data);
276
}
277
';
278
279
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $prime should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $inflector should have a doc-comment as per coding-style.
Loading history...
280
     * Set prime service locator
281
     */
282 21
    public function __construct(ServiceLocator $prime, ?InflectorObject $inflector = null)
283
    {
284 21
        $this->prime = $prime;
285 21
        $this->inflector = $inflector ?? InflectorFactory::create()->build();
0 ignored issues
show
Coding Style introduced by
Operation must be bracketed
Loading history...
286 21
    }
287
288
    /**
289
     * Generates and writes entity classes
290
     *
291
     * @param Mapper $mapper
292
     * @param string $file    Entity file name
293
     *
294
     * @return string|false If no generation
295
     * 
296
     * @api
297
     */
298 20
    public function generate($mapper, $file = null)
0 ignored issues
show
introduced by
Type hint "Mapper" missing for $mapper
Loading history...
299
    {
300 20
        $this->isNew = !$file || !file_exists($file) || $this->regenerateEntityIfExists;
0 ignored issues
show
Coding Style introduced by
Boolean operators are not allowed outside of control structure conditions
Loading history...
Coding Style introduced by
The value of a boolean operation must not be assigned to a variable
Loading history...
301
302
        // If entity doesn't exist or we're re-generating the entities entirely
303 20
        if ($this->isNew || !$file) {
304 20
            return $this->generateEntityClass($mapper);
305
        // 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...
306
        } elseif ($this->updateEntityIfExists && $file) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
307
            return $this->generateUpdatedEntityClass($mapper, $file);
308
        }
309
        
310
        return false;
311
    }
312
    
313
    /**
314
     * Generates a PHP5 Doctrine 2 entity class from the given Mapper instance.
315
     *
316
     * @param Mapper $mapper
317
     *
318
     * @return string
319
     */
320 20
    public function generateEntityClass(Mapper $mapper)
321
    {
322 20
        $this->mapperInfo = $mapper->info();
323
        
324 20
        $this->staticReflection[$this->mapperInfo->className()] = ['properties' => [], 'methods' => []];
325
        
326
        $placeHolders = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
327 20
            '<namespace>',
328
            '<useStatement>',
329
            '<entityAnnotation>',
330
            '<entityClassName>',
331
            '<entityTraits>',
332
            '<entityBody>'
0 ignored issues
show
introduced by
A comma should follow the last multiline array item. Found: '<entityBody>'
Loading history...
333
        );
334
335
        $replacements = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
336 20
            $this->generateEntityNamespace(),
337 20
            $this->generateEntityUse(),
338 20
            $this->generateEntityDocBlock(),
339 20
            $this->generateEntityClassName(),
340 20
            $this->generateEntityTraits(),
341 20
            $this->generateEntityBody()
0 ignored issues
show
introduced by
A comma should follow the last multiline array item. Found: )
Loading history...
342
        );
343
344 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...
345
346 20
        return str_replace('<spaces>', $this->spaces, $code);
347
    }
348
349
    /**
350
     * Generates the updated code for the given Mapper and entity at path.
351
     *
352
     * @param Mapper $mapper
353
     * @param string $file
354
     *
355
     * @return string
356
     */
357
    public function generateUpdatedEntityClass(Mapper $mapper, $file)
358
    {
359
        $this->mapperInfo = $mapper->info();
360
        
361
        $currentCode = file_get_contents($file);
362
363
        $this->parseTokensInEntityFile($currentCode);
364
        
365
        $body = $this->generateEntityBody();
366
        $body = str_replace('<spaces>', $this->spaces, $body);
367
        $last = strrpos($currentCode, '}');
368
369
        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...
370
    }
371
372
    /**
373
     * @return string
374
     */
375 20
    protected function generateEntityNamespace()
376
    {
377 20
        if ($this->hasNamespace($this->mapperInfo->className())) {
378 20
            return 'namespace ' . $this->getNamespace($this->mapperInfo->className()) .';' . "\n\n";
379
        }
380
    }
381
382
    /**
383
     * Generate use part
384
     * 
385
     * @return string
386
     */
387 20
    protected function generateEntityUse()
388
    {
389 20
        $use = [];
390
        
391 20
        if ($this->hasNamespace($this->getClassToExtend())) {
392 2
            $use[$this->getClassToExtend()] = 'use ' . $this->getClassToExtend() . ';';
393
        }
394
        
395 20
        foreach ($this->interfaces as $interface) {
396 3
            if ($this->hasNamespace($interface)) {
397 3
                $use[$interface] = 'use ' . $interface . ';';
398
            }
399
        }
400
        
401 20
        foreach ($this->traits as $trait) {
402 2
            if ($this->hasNamespace($trait)) {
403 2
                $use[$trait] = 'use ' . $trait . ';';
404
            }
405
        }
406
        
407 20
        foreach ($this->mapperInfo->objects() as $info) {
408 19
            $className = $info->className();
409 19
            if (!$info->belongsToRoot()) {
410 2
                continue;
411
            }
412
413 19
            if ($this->hasNamespace($className)) {
414 19
                $use[$className] = 'use '.$className.';';
415
            }
416
417 19
            if ($info->wrapper() !== null) {
418 1
                $repository = $this->prime->repository($className);
419 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

419
                $wrapperClass = $repository->collectionFactory()->wrapperClass(/** @scrutinizer ignore-type */ $info->wrapper());
Loading history...
420
421 1
                if ($this->hasNamespace($wrapperClass)) {
422 19
                    $use[$wrapperClass] = 'use '.$wrapperClass.';';
423
                }
424
            }
425
        }
426
        
427 20
        if (!$use) {
428 1
            return '';
429
        }
430
        
431 19
        sort($use);
432
        
433 19
        return implode("\n", $use) . "\n\n";
434
    }
435
    
436
    /**
437
     * @return string
438
     */
439 20
    protected function generateEntityClassName()
440
    {
441 20
        return 'class ' . $this->getClassName($this->mapperInfo->className()) .
442 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...
443 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...
444
    }
445
446
    /**
447
     * @return string
448
     */
449 20
    protected function generateEntityTraits()
450
    {
451 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...
452 18
            return '';
453
        }
454
        
455 2
        $traits = '';
456
        
457 2
        foreach ($this->traits as $trait) {
458 2
            $traits .= $this->spaces . 'use ' . $this->getRelativeClassName($trait) . ';' . "\n";
459
        }
460
        
461 2
        return $traits . "\n";
462
    }
463
464
    /**
465
     * @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...
466
     *
467
     * @return string
468
     */
469 20
    protected function generateEntityBody()
470
    {
471 20
        $fieldMappingProperties = $this->generateEntityFieldMappingProperties();
472 20
        $embeddedProperties = $this->generateEntityEmbeddedProperties();
473 20
        $stubMethods = $this->generateEntityStubMethods ? $this->generateEntityStubMethods() : null;
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style introduced by
The value of a comparison must not be assigned to a variable
Loading history...
474
475 20
        $code = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
476
477 20
        if ($fieldMappingProperties) {
478 20
            $code[] = $fieldMappingProperties;
479
        }
480
481 20
        if ($embeddedProperties) {
482 19
            $code[] = $embeddedProperties;
483
        }
484
485 20
        $code[] = $this->generateEntityConstructor();
486
487 20
        if ($stubMethods) {
488 19
            $code[] = $stubMethods;
489
        }
490
491 20
        return implode("\n", $code);
492
    }
493
494
    /**
495
     * @return string
496
     */
497 20
    protected function generateEntityConstructor()
498
    {
499 20
        $initializable = in_array(InitializableInterface::class, $this->interfaces);
500 20
        $isImportable  = in_array(ImportableInterface::class, $this->interfaces)
501 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...
502
503 20
        $collections = [];
504
505 20
        foreach ($this->mapperInfo->objects() as $property) {
506 19
            if (!$property->belongsToRoot()) {
507 2
                continue;
508
            }
509
510 19
            if ($property->isRelation()) {
511 18
                if (!$property->isArray()) {
512 18
                    $collections[$property->name()] = '$this->'.$property->name().' = new '.$this->getRelativeClassName($property->className()).'();';
513 17
                } elseif ($property->wrapper() === 'collection') { // @todo handle other wrapper types
0 ignored issues
show
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...
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
introduced by
There should be no white space after an opening "{"
Loading history...
514 18
                    $collections[$property->name()] = '$this->'.$property->name().' = '.$this->getRelativeClassName($property->className()).'::collection();';
515
                }
516
            } else {
517 19
                $collections[$property->name()] = '$this->'.$property->name().' = new '.$this->getRelativeClassName($property->className()).'();';
518
            }
519
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
520 20
        foreach ($this->mapperInfo->properties() as $property) {
521 20
            if ($property->isDateTime() && $property->hasDefault()) {
522 1
                $constructorArgs = '';
523
                // Add the default timezone from the property type.
524 1
                if ($timezone = $property->getTimezone()) {
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...
525 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...
526
                }
527
528 20
                $collections[$property->name()] = '$this->'.$property->name().' = new '.$property->phpType().'('.$constructorArgs.');';
529
            }
530
        }
531
        
532 20
        $methods = [];
533
        
534 20
        if (!$this->hasMethod('__construct')) {
535 18
            if ($isImportable) {
536 2
                $buffer = '';
537
538 2
                if ($initializable) {
539 1
                    $buffer = '$this->initialize();'."\n".$this->spaces;
540 1
                } elseif ($collections) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
541 1
                    $buffer = implode("\n".$this->spaces, $collections)."\n".$this->spaces;
542
                }
543
544 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...
545 16
            } elseif ($collections && !$initializable) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
546 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...
547
            }
548
        }
549
        
550 20
        if (!$this->hasMethod('initialize') && $initializable) {
551 1
            $methods[] = $this->generateMethod('{@inheritdoc}', 'initialize', implode("\n".$this->spaces, $collections));
552
        }
553
554 20
        return implode("\n", $methods);
555
    }
556
557
    /**
558
     * @param Mapper $mapper
0 ignored issues
show
Coding Style introduced by
Superfluous parameter comment
Loading history...
introduced by
Doc comment for parameter $mapper does not match actual variable name <undefined>
Loading history...
559
     *
560
     * @return string
561
     */
562 20
    protected function generateEntityDocBlock()
563
    {
564 20
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
565 20
        $lines[] = '/**';
566 20
        $lines[] = ' * ' . $this->getClassName($this->mapperInfo->className());
567 20
        $lines[] = ' */';
568
        
569 20
        return implode("\n", $lines);
570
    }
571
    
572
    /**
573
     * @return string
574
     */
575 19
    protected function generateEntityStubMethods()
576
    {
577 19
        $methods = [];
578
        
579 19
        foreach ($this->mapperInfo->properties() as $property) {
580 19
            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...
581 19
                $methods[] = $code;
582
            }
583
584 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...
585 19
                $methods[] = $code;
586
            }
587
        }
588
589 19
        foreach ($this->mapperInfo->objects() as $property) {
590 18
            if (!$property->belongsToRoot()) {
591 2
                continue;
592
            }
593
594 18
            if (!$property->isArray() || $property->wrapper() !== null) {
595 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...
596 18
                    $methods[] = $code;
597
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
598 18
                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...
599 18
                    $methods[] = $code;
600
                }
601
            } else {
602 15
                if ($code = $this->generateEntityStubMethod('add', $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...
603 15
                    $methods[] = $code;
604
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
605 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...
606 15
                    $methods[] = $code;
607
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
608 15
                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...
609 18
                    $methods[] = $code;
610
                }
611
            }
612
        }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end foreach"
Loading history...
613
614 19
        return implode("\n", $methods);
615
    }
616
617
    /**
618
     * @return string
619
     */
620 20
    protected function generateEntityFieldMappingProperties()
621
    {
622 20
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
623
624 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...
625 20
            if ($this->hasProperty($property->name())) {
626 2
                continue;
627
            }
628
            
629 20
            $default = '';
630 20
            if ($property->hasDefault() && !$property->isDateTime()) {
631 1
                $default = ' = '.$this->stringfyValue(
632 1
                    $property->convert($property->getDefault())
633
                );
634 20
            } elseif ($property->isArray()) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
635 14
                $default = ' = []';
636
            }
637
638 20
            $lines[] = $this->generateFieldMappingPropertyDocBlock($property);
639 20
            $lines[] = $this->spaces.$this->fieldVisibility.' $'.$property->name().$default.";\n";
640
        }
641
642 20
        return implode("\n", $lines);
643
    }
644
645
    /**
646
     * @return string
647
     */
648 20
    protected function generateEntityEmbeddedProperties()
649
    {
650 20
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
651
652 20
        foreach ($this->mapperInfo->objects() as $property) {
653 19
            if (!$property->belongsToRoot() || $this->hasProperty($property->name())) {
654 4
                continue;
655
            }
656
            
657 19
            if (!$property->isRelation()) {
658 2
                $lines[] = $this->generateEmbeddedPropertyDocBlock($property);
659 2
                $lines[] = $this->spaces.$this->fieldVisibility.' $'.$property->name().";\n";
660
            } else {
661 18
                $name = $property->name();
662
663
                // Do not initialize the property if it's a wrapper
664 18
                if ($property->isArray() && $property->wrapper() === null) {
665 14
                    $name .= ' = []';
666
                }
667
668 18
                $lines[] = $this->generateEmbeddedPropertyDocBlock($property);
669 19
                $lines[] = $this->spaces.$this->fieldVisibility.' $'.$name.";\n";
670
            }
671
        }
672
673 20
        return implode("\n", $lines);
674
    }
675
676
    /**
677
     * @param string            $type
678
     * @param InfoInterface     $propertyInfo
679
     * @param string|null       $defaultValue
680
     *
681
     * @return string
682
     */
683 19
    protected function generateEntityStubMethod($type, InfoInterface $propertyInfo, $defaultValue = null)
684
    {
685 19
        $fieldName = $propertyInfo->name();
686
687
        // The hint flag help algorythm to determine the hint info for object parameter.
688
        // It should be 'array' for collection but the add method need the object hint.
689
        // setItems(array $items)
690
        // addItem(Item $item)
691 19
        $hintOne = false;
692
693 19
        if ($type === 'get' && $this->useGetShortcutMethod === true) {
694 18
            $variableName = $this->inflector->camelize($fieldName);
695 18
            $methodName = $variableName;
696
        } else {
697 19
            $methodName = $type . $this->inflector->classify($fieldName);
698 19
            $variableName = $this->inflector->camelize($fieldName);
699
        }
700
        
701 19
        if ($type === 'add') {
702 15
            $methodName = $this->inflector->singularize($methodName);
703 15
            $variableName = $this->inflector->singularize($variableName);
704 15
            $hintOne = true;
705
        }
706
707 19
        if ($this->hasMethod($methodName)) {
708
            return '';
709
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
710 19
        $this->staticReflection[$this->mapperInfo->className()]['methods'][] = strtolower($methodName);
711
712 19
        if ($propertyInfo->isObject()) {
713 18
            $variableType = $this->getRelativeClassName($propertyInfo->className());
0 ignored issues
show
Bug introduced by
The method className() does not exist on Bdf\Prime\Mapper\Info\InfoInterface. It seems like you code against a sub-type of Bdf\Prime\Mapper\Info\InfoInterface such as Bdf\Prime\Mapper\Info\ObjectPropertyInfo. ( Ignorable by Annotation )

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

713
            $variableType = $this->getRelativeClassName($propertyInfo->/** @scrutinizer ignore-call */ className());
Loading history...
714 18
            $methodTypeHint =  $variableType.' ';
0 ignored issues
show
Coding Style introduced by
Expected 1 space after "="; 2 found
Loading history...
715
        } else {
716 19
            $variableType = $propertyInfo->phpType();
0 ignored issues
show
Bug introduced by
The method phpType() does not exist on Bdf\Prime\Mapper\Info\InfoInterface. It seems like you code against a sub-type of Bdf\Prime\Mapper\Info\InfoInterface such as 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

716
            /** @scrutinizer ignore-call */ 
717
            $variableType = $propertyInfo->phpType();
Loading history...
717 19
            $methodTypeHint = null;
718
        }
719
720 19
        if ($propertyInfo->isArray() && $hintOne === false) {
721 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\InfoInterface. It seems like you code against a sub-type of Bdf\Prime\Mapper\Info\InfoInterface such as Bdf\Prime\Mapper\Info\ObjectPropertyInfo. ( Ignorable by Annotation )

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

721
            if ($propertyInfo->isObject() && $propertyInfo->/** @scrutinizer ignore-call */ wrapper() !== null) {
Loading history...
722 1
                $repository = $this->prime->repository($propertyInfo->className());
723 1
                $wrapperClass = $this->getRelativeClassName($repository->collectionFactory()->wrapperClass($propertyInfo->wrapper()));
724
725 1
                $methodTypeHint = $wrapperClass.' ';
726 1
                $variableType .= '[]|'.$wrapperClass;
727
            } else {
728 15
                $methodTypeHint = 'array ';
729
730 15
                if ($variableType !== 'array') {
731 15
                    $variableType .= '[]';
732
                }
733
            }
734
        }
735
736
        $replacements = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
737 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...
738 19
          '<methodTypeHint>'    => $methodTypeHint,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
739 19
          '<variableType>'      => $variableType,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
740 19
          '<variableName>'      => $variableName,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
741 19
          '<methodName>'        => $methodName,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
742 19
          '<fieldName>'         => $fieldName,
0 ignored issues
show
Coding Style introduced by
Array key not indented correctly; expected 12 spaces but found 10
Loading history...
743 19
          '<variableDefault>'   => ($defaultValue !== null ) ? (' = '.$defaultValue) : ''
0 ignored issues
show
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...
introduced by
A comma should follow the last multiline array item. Found: ''
Loading history...
744
        );
745
746 19
        $method = str_replace(
747 19
            array_keys($replacements),
748 19
            array_values($replacements),
749 19
            $this->getMethodTemplate($type)
750
        );
751
752 19
        return $this->prefixCodeWithSpaces($method);
753
    }
754
755
    /**
756
     * Get the template of the method
757
     *
758
     * @param string $prefix
759
     *
760
     * @return string
761
     *
762
     * @throws \LogicException
763
     */
764 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...
765
    {
766
        switch ($prefix) {
767 19
            case 'get':
768 19
                return static::$getMethodTemplate;
0 ignored issues
show
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...
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
769
770 19
            case 'add':
771 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...
772
773 19
            case 'set':
774 19
                return static::$setMethodTemplate;
0 ignored issues
show
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...
introduced by
Case breaking statement indented incorrectly; expected 14 spaces, found 16
Loading history...
775
        }
776
777
        throw new \LogicException('No template found for method "'.$prefix.'"');
778
    }
779
780
    /**
781
     * @param string $description
782
     * @param string $methodName
783
     * @param string $content
784
     *
785
     * @return string
786
     */
787 1
    protected function generateMethod($description, $methodName, $content = null)
788
    {
789 1
        if ($this->hasMethod($methodName)) {
790
            return '';
791
        }
792
        
793 1
        $this->staticReflection[$this->mapperInfo->className()]['methods'][] = $methodName;
794
795
        $replacements = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
796 1
            '<description>' => $description,
797 1
            '<methodName>'  => $methodName,
798 1
            '<content>'     => $content,
799
        );
800
801 1
        $method = str_replace(
802 1
            array_keys($replacements),
803 1
            array_values($replacements),
804 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...
805
        );
806
807 1
        return $this->prefixCodeWithSpaces($method);
808
    }
809
810
    /**
811
     * @param PropertyInfo $property
812
     *
813
     * @return string
814
     */
815 20
    protected function generateFieldMappingPropertyDocBlock($property)
0 ignored issues
show
introduced by
Type hint "PropertyInfo" missing for $property
Loading history...
816
    {
817 20
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
818 20
        $lines[] = $this->spaces . '/**';
819 20
        $lines[] = $this->spaces . ' * @var '.$property->phpType();
820 20
        $lines[] = $this->spaces . ' */';
821
822 20
        return implode("\n", $lines);
823
    }
824
825
    /**
826
     * @param ObjectPropertyInfo $property
827
     *
828
     * @return string
829
     */
830 19
    protected function generateEmbeddedPropertyDocBlock($property)
0 ignored issues
show
introduced by
Type hint "ObjectPropertyInfo" missing for $property
Loading history...
831
    {
832 19
        $className = $property->className();
833 19
        if ($className) {
834 19
            $className = $this->getRelativeClassName($className);
835
836 19
            if ($property->isArray()) {
837 15
                if ($property->wrapper() !== null) {
0 ignored issues
show
introduced by
The condition $property->wrapper() !== null is always true.
Loading history...
838 1
                    $repository = $this->prime->repository($property->className());
839 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

839
                    $className = $this->getRelativeClassName($repository->collectionFactory()->wrapperClass(/** @scrutinizer ignore-type */ $property->wrapper())).'|'.$className.'[]';
Loading history...
840
                } else {
841 19
                    $className .= '[]';
842
                }
843
            }
844
        } else {
845
            $className = '{type}';
846
        }
847
        
848 19
        $lines = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
849 19
        $lines[] = $this->spaces . '/**';
850 19
        $lines[] = $this->spaces . ' * @var '.$className;
851 19
        $lines[] = $this->spaces . ' */';
852
853 19
        return implode("\n", $lines);
854
    }
855
    
856
    //
857
    //---------- 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...
858
    //
859
860
    /**
861
     * @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...
862
     *
863
     * @param string $src
0 ignored issues
show
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
864
     *
865
     * @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...
866
     */
867
    protected function parseTokensInEntityFile($src)
868
    {
869
        $tokens = token_get_all($src);
870
        $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...
871
        $lastSeenClass = false;
872
873
        $inNamespace = false;
874
        $inClass = false;
875
876
        for ($i = 0; $i < count($tokens); $i++) {
0 ignored issues
show
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...
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...
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...
877
            $token = $tokens[$i];
878
            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...
879
                continue;
880
            }
881
882
            if ($inNamespace) {
883
                if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
884
                    $lastSeenNamespace .= $token[1];
885
                } elseif (is_string($token) && in_array($token, array(';', '{'))) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
886
                    $inNamespace = false;
887
                }
888
            }
889
890
            if ($inClass) {
891
                $inClass = false;
892
                $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...
893
                $this->staticReflection[$lastSeenClass]['properties'] = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
894
                $this->staticReflection[$lastSeenClass]['methods'] = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
895
            }
896
897
            if ($token[0] == T_NAMESPACE) {
898
                $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...
899
                $inNamespace = true;
900
            } 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
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
                $inClass = true;
902
            } elseif ($token[0] == T_FUNCTION) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
903
                if ($tokens[$i+2][0] == T_STRING) {
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...
904
                    $this->staticReflection[$lastSeenClass]['methods'][] = strtolower($tokens[$i+2][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
                } elseif ($tokens[$i+2] == "&" && $tokens[$i+3][0] == T_STRING) {
0 ignored issues
show
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...
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...
906
                    $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
Expected 1 space after "+"; 0 found
Loading history...
Coding Style introduced by
Operation must be bracketed
Loading history...
907
                }
908
            } elseif (in_array($token[0], array(T_VAR, T_PUBLIC, T_PRIVATE, T_PROTECTED)) && $tokens[$i+2][0] != T_FUNCTION) {
0 ignored issues
show
introduced by
If the line declaring an array spans longer than 80 characters, each element should be broken into its own line
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
Operation must be bracketed
Loading history...
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
Coding Style introduced by
Expected 1 space after "+"; 0 found
Loading history...
909
                $this->staticReflection[$lastSeenClass]['properties'][] = substr($tokens[$i+2][1], 1);
0 ignored issues
show
Coding Style introduced by
Expected 1 space after "+"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space before "+"; 0 found
Loading history...
Coding Style introduced by
Operation must be bracketed
Loading history...
910
            }
911
        }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end for"
Loading history...
912
    }
913
914
    /**
915
     * @param string $property
916
     *
917
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
918
     */
919 20
    protected function hasProperty($property)
920
    {
921 20
        if ($this->classToExtend || (!$this->isNew && class_exists($this->mapperInfo->className()))) {
922
            // 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...
923 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...
924 2
            if ($reflClass->hasProperty($property)) {
925 2
                return true;
926
            }
927
        }
928
929
        // check traits for existing property
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
930 20
        foreach ($this->getTraitsReflections() as $trait) {
931
            if ($trait->hasProperty($property)) {
932
                return true;
933
            }
934
        }
935
936
        return (
937 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...
938 20
            in_array($property, $this->staticReflection[$this->mapperInfo->className()]['properties'])
939
        );
940
    }
941
942
    /**
943
     * @param string $method
944
     *
945
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
946
     */
947 20
    protected function hasMethod($method)
948
    {
949 20
        if ($this->classToExtend || (!$this->isNew && class_exists($this->mapperInfo->className()))) {
950
            // 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...
951 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...
952
953 2
            if ($reflClass->hasMethod($method)) {
954 2
                return true;
955
            }
956
        }
957
958
        // check traits for existing method
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
959 20
        foreach ($this->getTraitsReflections() as $trait) {
960
            if ($trait->hasMethod($method)) {
961
                return true;
962
            }
963
        }
964
965
        return (
966 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...
967 20
            in_array(strtolower($method), $this->staticReflection[$this->mapperInfo->className()]['methods'])
968
        );
969
    }
970
971
    /**
972
     * Get class name relative to use
973
     * 
974
     * @param string $className
975
     * @return string
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
976
     */
977 19
    protected function getRelativeClassName($className)
978
    {
979 19
        $className = ltrim($className, '\\');
980
        
981 19
        if ($this->hasNamespace($className)) {
982 19
            return $this->getClassName($className);
983
        } else {
984 2
            return '\\' . $className;
985
        }
986
    }
987
    
988
    /**
989
     * Get the class short name
990
     * 
991
     * @param string $className
992
     *
993
     * @return string
994
     */
995 20
    protected function getClassName($className)
996
    {
997 20
        $parts = explode('\\', $className);
998 20
        return array_pop($parts);
999
    }
1000
1001
    /**
1002
     * @param string $className
1003
     *
1004
     * @return string
1005
     */
1006 20
    protected function getNamespace($className)
1007
    {
1008 20
        $parts = explode('\\', $className);
1009 20
        array_pop($parts);
1010
        
1011 20
        return implode('\\', $parts);
1012
    }
1013
    
1014
    /**
1015
     * @param string $className
1016
     *
1017
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
1018
     */
1019 20
    protected function hasNamespace($className)
1020
    {
1021 20
        return strrpos($className, '\\') != 0;
1022
    }
1023
1024
    /**
1025
     * @return string
1026
     */
1027 2
    protected function getClassToExtendName()
1028
    {
1029 2
        $refl = new \ReflectionClass($this->getClassToExtend());
1030
1031 2
        return $this->getRelativeClassName($refl->getName());
1032
    }
1033
1034
    /**
1035
     * @return string
1036
     */
1037 3
    protected function getInterfacesToImplement()
1038
    {
1039 3
        $interfaces = [];
1040
        
1041 3
        foreach ($this->interfaces as $interface) {
1042 3
            $refl = new \ReflectionClass($interface);
1043
1044 3
            $interfaces[] = $this->getRelativeClassName($refl->getName());
1045
        }
1046
        
1047 3
        return implode(', ', $interfaces);
1048
    }
1049
    
1050
    /**
1051
     * @param Mapper $mapper
0 ignored issues
show
Coding Style introduced by
Superfluous parameter comment
Loading history...
introduced by
Doc comment for parameter $mapper does not match actual variable name <undefined>
Loading history...
1052
     *
1053
     * @return \ReflectionClass[]
1054
     */
1055 20
    protected function getTraitsReflections()
1056
    {
1057 20
        if ($this->isNew) {
1058 20
            return [];
1059
        }
1060
        
1061
        $reflClass = new \ReflectionClass($this->mapperInfo->className());
1062
1063
        $traits = array();
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
1064
1065
        while ($reflClass !== false) {
1066
            $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

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