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

ArrayHydrator::getHydratorForClass()   B

Complexity

Conditions 10
Paths 3

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 10.0244

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 16
c 1
b 0
f 0
dl 0
loc 29
ccs 15
cts 16
cp 0.9375
rs 7.6666
cc 10
nc 3
nop 1
crap 10.0244

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
2
3
namespace Bdf\Prime\Entity\Hydrator;
4
5
use Bdf\Prime\Entity\Hydrator\Exception\InvalidTypeException;
6
use Bdf\Prime\Entity\ImportableInterface;
7
use Closure;
8
use stdClass;
9
10
use TypeError;
11
use function get_class;
12
use function is_array;
13
use function method_exists;
14
use function property_exists;
15
use function ucfirst;
16
17
/**
18
 * Base hydrator implementation.
19
 * Works like {@link ArrayHydrator}
20
 */
21
class ArrayHydrator implements HydratorInterface
22
{
23
    /**
24
     * Array prefix for protected properties
25
     */
26
    const PROTECTED_PREFIX = "\0*\0";
27
28
    /**
29
     * @var array<class-string, callable(object, array)>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string, callable(object, array)> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, callable(object, array)>.
Loading history...
introduced by
Expected "arrayclassstringcallableobjectarray" but found "array<class-string, callable(object, array)>" for @var tag in member variable comment
Loading history...
30
     */
31
    private $hydratorsCache = [];
32
33
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $object should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $data should have a doc-comment as per coding-style.
Loading history...
34
     * {@inheritdoc}
35
     */
36 518
    public function hydrate($object, array $data)
37
    {
38 518
        return $this->getHydratorForClass(get_class($object))($object, $data);
39
    }
40
41
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $attributes should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $object should have a doc-comment as per coding-style.
Loading history...
42
     * {@inheritdoc}
43
     */
44 13
    public function extract($object, array $attributes = [])
45
    {
46 13
        $values = [];
47 13
        $attributes = array_flip($attributes);
48
49 13
        $privatePrefix = "\0" . get_class($object) . "\0";
50 13
        $privatePrefixLen = strlen($privatePrefix);
51
52 13
        foreach ((array) $object as $name => $property) {
0 ignored issues
show
Coding Style introduced by
A cast statement should not be followed as per the coding-style.
Loading history...
53 13
            if (strpos($name, self::PROTECTED_PREFIX) === 0) {
54 2
                $name = substr($name, 3);
55 13
            } elseif (strpos($name, $privatePrefix) === 0) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
56 2
                $name = substr($name, $privatePrefixLen);
57
            }
58
59 13
            if (!empty($attributes) && !isset($attributes[$name])) {
60 3
                continue;
61
            }
62
63 13
            if ($property instanceof ImportableInterface) {
64 6
                $values[$name] = $property->export();
65
            } else {
66 13
                $values[$name] = $property;
67
            }
68
        }
69
70 13
        return $values;
71
    }
72
73
    /**
74
     * Create or retrieve the hydrator callback for the given entity class
75
     *
76
     * @param class-string $entityClass The entity class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
introduced by
Expected "classstring" but found "class-string" for parameter type
Loading history...
77
     *
78
     * @return callable(object, array)
79
     */
80 518
    private function getHydratorForClass(string $entityClass): callable
0 ignored issues
show
Coding Style introduced by
Private method name "ArrayHydrator::getHydratorForClass" must be prefixed with an underscore
Loading history...
81
    {
82 518
        if (isset($this->hydratorsCache[$entityClass])) {
83 489
            return $this->hydratorsCache[$entityClass];
84
        }
85
86
        $hydrator = static function ($object, array $data) {
87 518
            foreach ($data as $property => $value) {
88
                try {
89 518
                    if (isset($object->$property) && $object->$property instanceof ImportableInterface && is_array($value)) {
90 41
                        $object->$property->import($value);
91 517
                    } elseif (method_exists($object, 'set' . ucfirst($property))) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
92 94
                        $object->{'set' . ucfirst($property)}($value);
93 510
                    } elseif (property_exists($object, $property)) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
94 518
                        $object->$property = $value;
95
                    }
96
                } catch (TypeError $e) {
97 518
                    throw new InvalidTypeException($e);
98
                }
99
            }
100 518
        };
101
102 86
        if ($entityClass !== stdClass::class) {
103
            // Bind to access private properties
104
            // Note: ignore stdClass because all its fields are public
105 85
            $hydrator = Closure::bind($hydrator, null, $entityClass);
106
        }
107
108 86
        return $this->hydratorsCache[$entityClass] = $hydrator;
0 ignored issues
show
Coding Style introduced by
Assignments must be the first block of code on a line
Loading history...
109
    }
110
}
111