Passed
Pull Request — master (#33)
by Vincent
06:12
created

generateExtractOneCaseAttribute()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0987

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 19
ccs 7
cts 9
cp 0.7778
rs 9.8666
cc 3
nc 4
nop 1
crap 3.0987
1
<?php
2
3
namespace Bdf\Prime\Entity\Hydrator;
4
5
use Bdf\Prime\Entity\Hydrator\Exception\HydratorGenerationException;
6
use Bdf\Prime\Entity\Hydrator\Generator\AccessorResolver;
7
use Bdf\Prime\Entity\Hydrator\Generator\AttributeInfo;
8
use Bdf\Prime\Entity\Hydrator\Generator\AttributesResolver;
9
use Bdf\Prime\Entity\Hydrator\Generator\ClassAccessor;
10
use Bdf\Prime\Entity\Hydrator\Generator\CodeGenerator;
11
use Bdf\Prime\Entity\Hydrator\Generator\EmbeddedInfo;
12
use Bdf\Prime\Entity\Hydrator\Generator\TypeAccessor;
13
use Bdf\Prime\Mapper\Mapper;
14
use Bdf\Prime\Mapper\SingleTableInheritanceMapper;
15
use Bdf\Prime\ServiceLocator;
16
17
/**
18
 * Generator for hydrator classes
19
 */
20
class HydratorGenerator
21
{
22
    /**
23
     * The stub hydrator file name
24
     *
25
     * @var string
26
     */
27
    private $stub = __DIR__.'/Generator/stubs/hydrator.php.stub';
28
29
    /**
30
     * @var CodeGenerator
31
     */
32
    private $code;
33
34
    /**
35
     * @var ClassAccessor
36
     */
37
    private $accessor;
38
39
    /**
40
     * @var AccessorResolver
41
     */
42
    private $accessors;
43
44
    /**
45
     * @var AttributesResolver
46
     */
47
    private $resolver;
48
49
    /**
50
     * @var ServiceLocator
51
     */
52
    private $prime;
53
54
    /**
55
     * @var Mapper
56
     */
57
    private $mapper;
58
59
    /**
60
     * @var string
61
     */
62
    private $className;
63
64
    /**
65
     * @var string
66
     */
67
    private $interface = HydratorGeneratedInterface::class;
68
69
    /**
70
     * @var array
71
     */
72
    private $embeddedHydrators = [];
73
74
75
    /**
76
     * HydratorGenerator constructor.
77
     *
78
     * @param ServiceLocator $prime
79
     * @param Mapper $mapper
80
     * @param string $className
81
     *
82
     * @throws HydratorGenerationException
83
     */
84 94
    public function __construct(ServiceLocator $prime, Mapper $mapper, $className)
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line before function; 2 found
Loading history...
85
    {
86 94
        $this->prime = $prime;
87 94
        $this->mapper = $mapper;
88 94
        $this->className = $className;
89
90 94
        $this->code = new CodeGenerator();
91 94
        $this->accessor = $this->makeAccessor();
92 94
        $this->resolver = new AttributesResolver($mapper, $prime);
93 94
        $this->accessors = new AccessorResolver($this->accessor, $this->resolver, $this->code);
94 94
    }
95
96 94
    private function makeAccessor()
0 ignored issues
show
Coding Style introduced by
Private method name "HydratorGenerator::makeAccessor" must be prefixed with an underscore
Loading history...
Coding Style introduced by
Missing doc comment for function makeAccessor()
Loading history...
introduced by
Missing function doc comment
Loading history...
97
    {
98 94
        $subClass = [];
99
100
        // The mapper has inheritance, and it's not the inherited one
101
        if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
Coding Style introduced by
First condition of a multi-line IF statement must directly follow the opening parenthesis
Loading history...
102 94
            $this->mapper instanceof SingleTableInheritanceMapper
0 ignored issues
show
Coding Style introduced by
Each line in a multi-line IF statement must begin with a boolean operator
Loading history...
103 94
            && !in_array(get_class($this->mapper), $this->mapper->getDiscriminatorMap())
104
        ) {
105 5
            $subClass = $this->mapper->getEntityMap();
106
        }
107
108 94
        return new ClassAccessor($this->className, ClassAccessor::SCOPE_INHERIT, $subClass);
109
    }
110
111
    /**
112
     * Get the hydrator namespace
113
     *
114
     * @return string
115
     */
116 94
    public function hydratorNamespace()
117
    {
118 94
        return implode('\\', array_slice(explode('\\', $this->className), 0, -1));
119
    }
120
121
    /**
122
     * Get the hydrator class name, without namespace
123
     *
124
     * @return string
125
     */
126 94
    public function hydratorClassName()
127
    {
128 94
        return 'Hydrator_' . str_replace('\\', '_', $this->className);
129
    }
130
131
    /**
132
     * Get the full class name (namespace + class name)
133
     *
134
     * @return string
135
     */
136 76
    public function hydratorFullClassName()
137
    {
138 76
        return $this->hydratorNamespace() . '\\' . $this->hydratorClassName();
139
    }
140
141
    /**
142
     * Generate the hydrator class code
143
     *
144
     * @return string
145
     */
146 27
    public function generate()
147
    {
148 27
        $this->resolveHydrators();
149
150 27
        return $this->hydratorTemplate();
151
    }
152
153
    /**
154
     * Resolve hydrator properties
155
     */
156 27
    protected function resolveHydrators()
157
    {
158 27
        $classes = [];
159
160 27
        foreach ($this->resolver->rootEmbeddeds() as $embedded) {
161 22
            foreach ($embedded->classes() as $class) {
162
                // For now (1.6) hydrators are used only for hydrate / extract. Remove the isImportable if hydrator are use in mapping context.
163 22
                if ($class === $this->className || !$this->resolver->isEntity($class) || $this->resolver->isImportable($class)) {
164 21
                    continue;
165
                }
166
167 1
                if (!isset($classes[$class])) {
168 1
                    $classes[$class] = true;
169
170 1
                    $property = '__' . str_replace('\\', '_', $class) . '_hydrator';
171 22
                    $this->embeddedHydrators[$class] = $property;
172
                }
173
            }
174
        }
175 27
    }
176
177
    /**
178
     * Get the hydrator class template
179
     *
180
     * @return string
181
     *
182
     * @throws HydratorGenerationException
183
     */
184 27
    protected function hydratorTemplate()
185
    {
186 27
        return $this->code->generate($this->stub, [
187 27
            'namespace'                 => $this->code->namespace($this->hydratorNamespace()),
188 27
            'normalizedEntityClassName' => $this->normalizeClassName($this->className),
189 27
            'hydratorClassName'         => $this->hydratorClassName(),
190 27
            'hydratorInterface'         => $this->normalizeClassName($this->interface),
191 27
            'properties'                => $this->code->properties($this->embeddedHydrators),
192 27
            'constructor'               => $this->code->simpleConstructor($this->embeddedHydrators),
193 27
            'hydrateBody'               => $this->generateHydrateBody(),
194 25
            'extractBody'               => $this->generateExtractBody(),
195 25
            'flatExtractBody'           => $this->generateFlatExtract(),
196 25
            'flatHydrateBody'           => $this->generateFlatHydrate(),
197 25
            'extractOneBody'            => $this->generateExtractOneBody(),
198 25
            'hydrateOneBody'            => $this->generateHydrateOneBody(),
199 25
            'entityClassName'           => $this->className,
200 25
            'embeddedClasses'           => $this->generateEmbeddedClasses(),
201
        ]);
202
    }
203
204
    /**
205
     * Generate the hydrate() method body
206
     *
207
     * @return string
208
     *
209
     * @throws HydratorGenerationException
210
     */
211 27
    protected function generateHydrateBody()
212
    {
213 27
        $out = '';
214
215 27
        foreach ($this->resolver->rootAttributes() as $attribute) {
216
            $out .= <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
217 27
if (array_key_exists('{$attribute->name()}', \$data)) {
218 27
{$this->code->indent($this->generateAttributeHydrate($attribute), 1)}
219
}
220
221
222
PHP;
223
        }
224
225 25
        return $out;
226
    }
227
228
    /**
229
     * Generate the hydration code for one attribute
230
     *
231
     * @param AttributeInfo $attribute
232
     *
233
     * @return string
234
     *
235
     * @throws HydratorGenerationException
236
     */
237 27
    protected function generateAttributeHydrate(AttributeInfo $attribute)
238
    {
239 27
        $out = '';
240
241 27
        $value = '$data[\''.$attribute->name().'\']';
242
243 27
        if ($attribute->isEmbedded()) {
244
            $out .= <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
245 22
if (is_array({$value})) {
246 22
{$this->code->indent($this->generateEmbeddedHydrate($attribute), 1)}
247
} else {
248 22
    {$this->accessor->setter('$object', $attribute->property(), $value)};
249
}
250
PHP;
251
        } else {
252 25
            $out .= $this->accessor->setter('$object', $attribute->property(), $value).';';
253
        }
254
255
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
256
try {
257 25
{$this->code->indent($out, 1)}
258
} catch (\TypeError \$e) {
259 25
    throw new \Bdf\Prime\Entity\Hydrator\Exception\InvalidTypeException(\$e, '{$attribute->type()}');
260
}
261
PHP;
262
    }
263
264
    /**
265
     * Generate embedded hydrator
266
     *
267
     * @param AttributeInfo $attribute
268
     *
269
     * @return string
270
     *
271
     * @throws HydratorGenerationException
272
     */
273 22
    protected function generateEmbeddedHydrate(AttributeInfo $attribute)
274
    {
275
        // We can have multiple entity classes for one attribute : morph
276 22
        $varName = '$__rel_' . str_replace('.', '_', $attribute->name());
277
278 22
        $hydrators = [];
279
280 22
        foreach ($attribute->embedded()->classes() as $class) {
281 22
            if ($this->resolver->isImportable($class)) {
282
                // For other objects (Collections) use import() method
283 21
                $hydrators[$this->normalizeClassName($class)] = "{$varName}->import(\$data['{$attribute->name()}']);";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $varName 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...
284 1
            } elseif ($this->resolver->isEntity($class)) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
285
                // For Entities, use hydrators
286 1
                $hydrators[$this->normalizeClassName($class)] = "{$this->generateEmbeddedHydrator($class)}->hydrate({$varName}, \$data['{$attribute->name()}']);";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $varName 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $class 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
287
            } else {
288 22
                throw new HydratorGenerationException($class, 'Cannot generate embedded hydration for the property "'.$attribute->name().'"');
289
            }
290
        }
291
292
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
293 22
{$varName} = {$this->accessor->getter('$object', $attribute->property())};
294
295 22
{$this->code->switchIntanceOf($varName, $hydrators)}
296
PHP;
297
298
    }
0 ignored issues
show
Coding Style introduced by
Function closing brace must go on the next line following the body; found 1 blank lines before brace
Loading history...
299
300
    /**
301
     * @param string $class
302
     *
303
     * @return string
304
     */
305 1
    protected function generateEmbeddedHydrator($class)
306
    {
307 1
        if ($class === $this->className) {
308
            return '$this';
309
        }
310
311 1
        return '$this->' . $this->embeddedHydrators[$class];
312
    }
313
314
    /**
315
     * Add the root namespace
316
     *
317
     * @param string $className
318
     *
319
     * @return string
320
     */
321 27
    protected function normalizeClassName($className)
322
    {
323 27
        return '\\' . ltrim($className, '\\');
324
    }
325
326
    /**
327
     * Generate the embedded entities classes list
328
     *
329
     * @return string
330
     */
331 25
    protected function generateEmbeddedClasses()
332
    {
333 25
        return $this->code->export(array_keys($this->embeddedHydrators));
334
    }
335
336
    /**
337
     * Generate the {@link HydratorInterface::extract()} method's body
338
     *
339
     * @return string
340
     *
341
     * @throws HydratorGenerationException
342
     */
343 25
    protected function generateExtractBody()
344
    {
345
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
346
if (empty(\$attributes)) {
347 25
{$this->code->indent($this->generateExtractAll(), 1)}
348
} else {
349 25
{$this->code->indent($this->generateExtractSelected(), 1)}
350
}
351
PHP;
352
353
    }
0 ignored issues
show
Coding Style introduced by
Function closing brace must go on the next line following the body; found 1 blank lines before brace
Loading history...
354
355
    /**
356
     * Generate extract method's code for extract all attributes
357
     *
358
     * @return string
359
     *
360
     * @throws HydratorGenerationException
361
     */
362 25
    protected function generateExtractAll()
363
    {
364 25
        $lines = [];
365 25
        $possiblyNotInitialized = [];
366
367 25
        foreach ($this->resolver->rootAttributes() as $attribute) {
368 25
            if ($attribute->isInitializedByDefault()) {
369 25
                $lines[] = "'{$attribute->name()}' => ({$this->generateExtractValue($attribute)})";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
370
            } else {
371 25
                $possiblyNotInitialized[] = "try { \$values['{$attribute->name()}'] = {$this->generateExtractValue($attribute)}; } catch (\Error \$e) { /** Ignore not initialized properties */ }";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
372
            }
373
        }
374
375 25
        if (empty($possiblyNotInitialized)) {
376 25
            return 'return [' . implode(', ', $lines) . '];';
377
        }
378
379
        return '$values = [' . implode(', ', $lines) . '];' . PHP_EOL . PHP_EOL .
380
            implode(PHP_EOL, $possiblyNotInitialized) . PHP_EOL . PHP_EOL .
381
            'return $values;'
382
        ;
0 ignored issues
show
Coding Style introduced by
Space found before semicolon; expected "'return $values;';" but found "'return $values;'
;"
Loading history...
383
    }
384
385
    /**
386
     * Generate extract method's code for extract select attributes
387
     *
388
     * @return string
389
     *
390
     * @throws HydratorGenerationException
391
     */
392 25
    protected function generateExtractSelected()
393
    {
394 25
        $extracts = '';
395
396 25
        foreach ($this->resolver->rootAttributes() as $attribute) {
397 25
            $extractor = "\$values['{$attribute->name()}'] = {$this->generateExtractValue($attribute)};";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
398
399 25
            if (!$attribute->isInitializedByDefault()) {
400
                $extractor = <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
401
try {
402
    {$extractor}
403
} catch (\Error \$e) {
404
    // Ignore not initialized properties
405
}
406
PHP;
407
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
408
            }
409
410
            $extracts .= <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
411 25
if (isset(\$attributes['{$attribute->name()}'])) {
412 25
{$this->code->indent($extractor, 1)}
413
}
414
415
PHP;
416
        }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end foreach"
Loading history...
417
418
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
419
\$attributes = array_flip(\$attributes);
420
\$values = [];
421
422 25
{$extracts}
423
424
return \$values;
425
PHP;
426
    }
427
428
    /**
429
     * Generate the extraction code for one attribute
430
     *
431
     * @param AttributeInfo $attribute
432
     *
433
     * @return string
434
     *
435
     * @throws HydratorGenerationException
436
     */
437 25
    protected function generateExtractValue(AttributeInfo $attribute)
438
    {
439 25
        $line = '';
440
441 25
        if ($attribute->isEmbedded()) {
442 22
            $varName = '$__rel_' . str_replace('.', '_', $attribute->name());
443 22
            $line .= '(' . $varName . ' = '.$this->accessor->getter('$object', $attribute->property()) . ") === null ? null : ";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal ) === null ? null : 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...
444
445 22
            foreach ($attribute->embedded()->classes() as $class) {
446 22
                if ($this->resolver->isImportable($class)) {
447 21
                    $line .= "({$varName} instanceof {$this->normalizeClassName($class)} ? {$varName}->export() : ";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $class 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $varName 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...
448 1
                } elseif ($this->resolver->isEntity($class)) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
449 1
                    $line .= "({$varName} instanceof {$this->normalizeClassName($class)} ? {$this->generateEmbeddedHydrator($class)}->extract({$varName}) : ";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $class 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $varName 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...
450
                } else {
451 22
                    throw new HydratorGenerationException($class, 'Cannot generate embedded hydration for the property "'.$attribute->name().'"');
452
                }
453
            }
454
455 22
            $line .= $varName.str_repeat(')', count($attribute->embedded()->classes()));
456
457 22
            return $line;
458
        }
459
460 23
        $line .= $this->accessor->getter('$object', $attribute->property());
461
462 23
        return $line;
463
    }
464
465
    /**
466
     * Generate the flatExtract (i.e. Mapper::prepareToRepository) method body
467
     *
468
     * @return string
469
     *
470
     * @throws HydratorGenerationException
471
     */
472 25
    protected function generateFlatExtract()
473
    {
474
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
475
if (empty(\$attributes)) {
476 25
{$this->code->indent($this->generateFlatExtractAll(), 1)}
477
} else {
478 25
{$this->code->indent($this->generateFlatExtractSelected(), 1)}
479
}
480
PHP;
481
    }
482
483
    /**
484
     * Generate extract method's code for extract all attributes
485
     *
486
     * @return string
487
     *
488
     * @throws HydratorGenerationException
489
     */
490 25
    protected function generateFlatExtractAll()
491
    {
492 25
        $simpleArray = [];
493 25
        $extractors = [];
494
495 25
        foreach ($this->resolver->attributes() as $attribute) {
496 25
            if ($attribute->isEmbedded()) {
497 16
                $accessor = $this->accessors->embedded($attribute->embedded());
498
                $extractor = <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
499 16
{$accessor->getEmbedded('$__embedded')}
0 ignored issues
show
Coding Style introduced by
Variable "__embedded" is not in valid camel caps format
Loading history...
500 16
\$data['{$attribute->name()}'] = {$accessor->getter('$__embedded', $attribute->property())};
0 ignored issues
show
Coding Style introduced by
Variable "__embedded" is not in valid camel caps format
Loading history...
501
PHP;
502
503 16
                if (!$attribute->isInitializedByDefault()) {
504
                    $extractor = <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
505
try {
506
{$this->code->indent($extractor, 1)}
507
} catch (\Error \$e) {
508
    throw new \Bdf\Prime\Entity\Hydrator\Exception\UninitializedPropertyException('{$this->className}', '{$attribute->property()}');
509
}
510
PHP;
511
                }
512
513 16
                $extractors[] = $extractor;
514 23
            } elseif ($attribute->isInitializedByDefault()) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
515 23
                $simpleArray[] = "'{$attribute->name()}' => ({$this->accessor->getter('$object', $attribute->property())})";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
516
            } else {
517
                $extractors[] = <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
518
try {
519
    \$data['{$attribute->name()}'] = {$this->accessor->getter('$object', $attribute->property())};
520
} catch (\Error \$e) {
521 25
    throw new \Bdf\Prime\Entity\Hydrator\Exception\UninitializedPropertyException('{$attribute->containerClassName()}', '{$attribute->property()}');
522
}
523
PHP;
524
            }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end if"
Loading history...
525
        }
0 ignored issues
show
Coding Style introduced by
End comment for long condition not found; expected "//end foreach"
Loading history...
526
527 25
        $simpleArray = implode(', ', $simpleArray);
528 25
        $extractors = implode(PHP_EOL, $extractors);
529
530
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
531 25
\$data = [{$simpleArray}];
532 25
{$extractors}
533
534
return \$data;
535
PHP;
536
    }
537
538
    /**
539
     * Generate the extract method's code for extract selected attributes
540
     *
541
     * @return string
542
     *
543
     * @throws HydratorGenerationException
544
     */
545 25
    protected function generateFlatExtractSelected()
546
    {
547 25
        $lines = '';
548
549 25
        foreach ($this->resolver->attributes() as $attribute) {
550 25
            if ($attribute->isEmbedded()) {
551 16
                $accessor = $this->accessors->embedded($attribute->embedded());
552
553
                $code = <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
554 16
{$accessor->getEmbedded('$__embedded')}
0 ignored issues
show
Coding Style introduced by
Variable "__embedded" is not in valid camel caps format
Loading history...
555 16
\$data['{$attribute->name()}'] = {$accessor->getter('$__embedded', $attribute->property())};
0 ignored issues
show
Coding Style introduced by
Variable "__embedded" is not in valid camel caps format
Loading history...
556
PHP;
557
            } else {
558 23
                $code = "\$data['{$attribute->name()}'] = {$this->accessor->getter('$object', $attribute->property())};";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
559
            }
560
561
            $lines .= <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
562 25
if (isset(\$attributes['{$attribute->name()}'])) {
563 25
{$this->code->indent($code, 1)}
564
}
565
566
PHP;
567
        }
568
569
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
570
\$data = [];
571
572 25
{$lines}
573
574
return \$data;
575
PHP;
576
    }
577
578
    /**
579
     * Generate the flatHydrate method's body
580
     *
581
     * @return string
582
     *
583
     * @throws HydratorGenerationException
584
     */
585 25
    protected function generateFlatHydrate()
586
    {
587 25
        $types = new TypeAccessor($this->code);
588
589 25
        $out = '';
590 25
        $set = [];
591 25
        $relationKeys = [];
592
593 25
        foreach ($this->mapper->relations() as $property => $metadata) {
594 14
            $relationKeys[$metadata['localKey']] = true;
595
        }
596
597 25
        foreach ($this->resolver->attributes() as $attribute) {
598 25
            $set[$attribute->field()] = $this->generateAttributeFlatHydrate($attribute, $relationKeys, $types);
599
        }
600
601 25
        foreach ($set as $field => $declaration) {
602
            $out .= <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
603 25
if (array_key_exists('{$field}', \$data)) {
604 25
{$this->code->indent($declaration, 1)}
605
}
606
607
608
PHP;
609
        }
610
611 25
        return $types->generateDeclaration().$out;
612
    }
613
614
    /**
615
     * Generate flat hydration for one attribute
616
     *
617
     * @param AttributeInfo $attribute
618
     * @param array $relationKeys
619
     * @param TypeAccessor $types
620
     *
621
     * @return string
622
     *
623
     * @throws HydratorGenerationException
624
     */
625 25
    protected function generateAttributeFlatHydrate(AttributeInfo $attribute, array $relationKeys, TypeAccessor $types)
0 ignored issues
show
Unused Code introduced by
The parameter $relationKeys is not used and could be removed. ( Ignorable by Annotation )

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

625
    protected function generateAttributeFlatHydrate(AttributeInfo $attribute, /** @scrutinizer ignore-unused */ array $relationKeys, TypeAccessor $types)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
The method parameter $relationKeys is never used
Loading history...
626
    {
627 25
        $options = '';
628 25
        if ($attribute->phpOptions()) {
629 5
            $options = "\$this->__metadata->fields['{$attribute->field()}']['phpOptions']";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
630
        }
631
632 25
        $target = "\$value";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal \$value 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...
633 25
        $out = $target.' = '.$types->generateFromDatabase($attribute->type(), '$data[\''.$attribute->field().'\']', $options);
0 ignored issues
show
Bug introduced by
It seems like $attribute->type() can also be of type null; however, parameter $type of Bdf\Prime\Entity\Hydrato...:generateFromDatabase() 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

633
        $out = $target.' = '.$types->generateFromDatabase(/** @scrutinizer ignore-type */ $attribute->type(), '$data[\''.$attribute->field().'\']', $options);
Loading history...
634
635 25
        if (!$attribute->isEmbedded()) {
636 23
            if ($attribute->isNullable()) {
637 23
                return $out."\n".$this->accessor->setter('$object', $attribute->name(), $target, false).';';
638
            }
639
640
            return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
641
{$out}
642
643
if ({$target} !== null) {
644
    {$this->accessor->setter('$object', $attribute->name(), $target, false)};
645
}
646
PHP;
647
        }
648
649 16
        $accessor = $this->accessors
650 16
            ->embedded($attribute->embedded())
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
introduced by
Object operator not indented correctly; expected 10 spaces but found 12
Loading history...
651 16
            ->fullSetter($attribute->property(), $target, '$__embedded', '$data').';'
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
652
        ;
0 ignored issues
show
Coding Style introduced by
Space found before semicolon; expected "';';" but found "';'
;"
Loading history...
653
654 16
        if (!$attribute->isNullable()) {
655
            $accessor = <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
656
if ({$target} !== null) {
657
    {$accessor}
658
}
659
PHP;
660
        }
661
662 16
        return $this->code->lines([$out, $accessor]);
663
    }
664
665
    /**
666
     * Generate the extractOne() method's body
667
     *
668
     * @return string
669
     *
670
     * @throws HydratorGenerationException
671
     */
672 25
    protected function generateExtractOneBody()
673
    {
674 25
        $cases = [];
675
676 25
        foreach ($this->resolver->attributes() as $attribute) {
677 25
            $cases[$attribute->name()] = $this->generateExtractOneCaseAttribute($attribute);
678
        }
679
680 25
        foreach ($this->resolver->embeddeds() as $embedded) {
681 20
            $cases[$embedded->path()] = $this->generateExtractOneCaseEmbedded($embedded);
682
        }
683
684 25
        return $this->code->switch(
685 25
            '$attribute',
686 25
            $cases,
687
            <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
688 25
throw new \Bdf\Prime\Entity\Hydrator\Exception\FieldNotDeclaredException('{$this->className}', \$attribute);
689
PHP
690
        );
691
    }
692
693
    /**
694
     * Generate one case for extractOne switch, for attribute
695
     *
696
     * @param AttributeInfo $attribute
697
     *
698
     * @return string
699
     *
700
     * @throws HydratorGenerationException
701
     */
702 25
    protected function generateExtractOneCaseAttribute(AttributeInfo $attribute)
703
    {
704 25
        if ($attribute->isEmbedded()) {
705 16
            $accessor = $this->accessors->embedded($attribute->embedded());
706
707 16
            $body = $accessor->getEmbedded('$__embedded').$this->code->eol().'return '.$accessor->getter('$__embedded', $attribute->property()).';';
708
        } else {
709 23
            $body = "return {$this->accessor->getter('$object', $attribute->property())};";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this 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...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $attribute 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...
710
        }
711
712 25
        if ($attribute->isInitializedByDefault()) {
713 25
            return $body;
714
        }
715
716
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
717
try {
718
{$this->code->indent($body, 1)}
719
} catch (\Error \$e) {
720
    throw new \Bdf\Prime\Entity\Hydrator\Exception\UninitializedPropertyException('{$attribute->containerClassName()}', '{$attribute->property()}');
721
}
722
PHP;
723
    }
724
725
    /**
726
     * Generate one case for extractOne switch, for embedded
727
     *
728
     * @param EmbeddedInfo $embedded
729
     *
730
     * @return string
731
     *
732
     * @throws HydratorGenerationException
733
     */
734 20
    protected function generateExtractOneCaseEmbedded(EmbeddedInfo $embedded)
735
    {
736 20
        $varName = '$__' . str_replace('.', '_', $embedded->path());
737 20
        $code = $this->accessors->embedded($embedded)->getEmbedded($varName, false);
738 20
        $className = $embedded->isRoot() ? $this->mapper->getEntityClass() : $embedded->parent()->class();
0 ignored issues
show
Coding Style introduced by
The value of a comparison must not be assigned to a variable
Loading history...
739
740
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
741
try {
742 20
{$this->code->indent($this->code->lines([$code, 'return ' . $varName . ';']), 1)}
743
} catch (\Error \$e) {
744 20
    throw new \Bdf\Prime\Entity\Hydrator\Exception\UninitializedPropertyException('{$className}', '{$embedded->property()}');
745
}
746
PHP;
747
    }
748
749
    /**
750
     * Generate hydrateOne() method's body
751
     *
752
     * @return string
753
     *
754
     * @throws HydratorGenerationException
755
     */
756 25
    protected function generateHydrateOneBody()
757
    {
758 25
        $cases = [];
759
760 25
        foreach ($this->resolver->attributes() as $attribute) {
761 25
            $cases[$attribute->name()] = $this->generateHydrateOneCaseAttribute($attribute);
762
        }
763
764 25
        foreach ($this->resolver->embeddeds() as $embedded) {
765 20
            $cases[$embedded->path()] = $this->generateHydrateOneCaseEmbedded($embedded);
766
        }
767
768 25
        return $this->code->switch(
769 25
            '$attribute',
770 25
            $cases,
771
            <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
772 25
throw new \Bdf\Prime\Entity\Hydrator\Exception\FieldNotDeclaredException('{$this->className}', \$attribute);
773
PHP
774
0 ignored issues
show
Coding Style introduced by
Empty lines are not allowed in multi-line function calls
Loading history...
775
        );
776
    }
777
778
    /**
779
     * Generate one case for hydrateOne switch, for attribute
780
     *
781
     * @todo Exception on hydrate unresolved embedded attribute (like MapperHydrator)
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...
782
     *
783
     * @param AttributeInfo $attribute
0 ignored issues
show
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
784
     *
785
     * @return string
786
     *
787
     * @throws HydratorGenerationException
788
     */
789 25
    protected function generateHydrateOneCaseAttribute($attribute)
0 ignored issues
show
introduced by
Type hint "AttributeInfo" missing for $attribute
Loading history...
790
    {
791 25
        if ($attribute->isEmbedded()) {
792 16
            $code = $this->accessors
793 16
                ->embedded($attribute->embedded())
0 ignored issues
show
introduced by
Object operator not indented correctly; expected 14 spaces but found 16
Loading history...
Coding Style introduced by
Space found before object operator
Loading history...
794 16
                ->fullSetter($attribute->property(), '$value', '$__embedded').';'
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
795
            ;
0 ignored issues
show
Coding Style introduced by
Space found before semicolon; expected "';';" but found "';'
;"
Loading history...
796
        } else {
797 23
            $code = $this->accessor->setter('$object', $attribute->property(), '$value', false) . ';';
798
        }
799
800
        // Always surround with try catch because setter can also be typed
801
        return <<<PHP
0 ignored issues
show
Coding Style introduced by
Use of heredoc and nowdoc syntax ("<<<") is not allowed; use standard strings or inline HTML instead
Loading history...
802
try {
803 25
{$this->code->indent($code, 1)}
804
} catch (\TypeError \$e) {
805 25
    throw new \Bdf\Prime\Entity\Hydrator\Exception\InvalidTypeException(\$e, '{$attribute->type()}');
806
}
807
PHP;
808
809
    }
0 ignored issues
show
Coding Style introduced by
Function closing brace must go on the next line following the body; found 1 blank lines before brace
Loading history...
810
811
    /**
812
     * Generate one case for hydrateOne switch, for embedded
813
     *
814
     * @param EmbeddedInfo $embedded
815
     *
816
     * @return string
817
     *
818
     * @throws HydratorGenerationException
819
     */
820 20
    protected function generateHydrateOneCaseEmbedded(EmbeddedInfo $embedded)
821
    {
822 20
        if ($embedded->isRoot()) {
823 20
            return $this->accessor->setter('$object', $embedded->path(), '$value', false).';';
824
        }
825
826 3
        return $this->accessors
827 3
            ->embedded($embedded->parent())
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
introduced by
Object operator not indented correctly; expected 10 spaces but found 12
Loading history...
828 3
            ->fullSetter($embedded->property(), '$value').';'
0 ignored issues
show
Coding Style introduced by
Space found before object operator
Loading history...
829
        ;
0 ignored issues
show
Coding Style introduced by
Space found before semicolon; expected "';';" but found "';'
;"
Loading history...
830
    }
831
}
832