Completed
Pull Request — master (#888)
by Robert
03:06
created

ClassAccessorUpdater::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 4
crap 1
1
<?php
2
3
namespace JMS\Serializer\Accessor\Updater;
4
5
use JMS\Serializer\Exception\RuntimeException;
6
use JMS\Serializer\Metadata\ClassMetadata;
7
use JMS\Serializer\Metadata\ClassMetadataUpdaterInterface;
8
use JMS\Serializer\Metadata\PropertyMetadata;
9
10
class ClassAccessorUpdater implements ClassMetadataUpdaterInterface
11
{
12
    /**
13
     * @var string
14
     */
15
    private $defaultType;
16
17
    /**
18
     * @var string
19
     */
20
    private $defaultNaming;
21
22
    /**
23
     * @var array
24
     */
25
    private $getterPrefixes;
26
27
    /**
28
     * @var array
29
     */
30
    private $setterPrefixes;
31
32 538
    public function __construct(
33
        $defaultType = PropertyMetadata::ACCESS_TYPE_PROPERTY,
34
        $defaultNaming = PropertyMetadata::ACCESS_TYPE_NAMING_EXACT,
35
        array $getterPrefixes = ['get', 'is', 'has'],
36
        array $setterPrefixes = ['set', 'update']
37
    )
38
    {
39 538
        $this->defaultType = $defaultType;
40 538
        $this->defaultNaming = $defaultNaming;
41 538
        $this->getterPrefixes = $getterPrefixes;
42 538
        $this->setterPrefixes = $setterPrefixes;
43 538
    }
44
45
    /**
46
     * {@inheritDoc}
47
     */
48 366
    public function update(ClassMetadata $classMetadata)
49
    {
50 366
        foreach ($classMetadata->propertyMetadata as $propertyMetadata) {
51 343
            $this->updateAccessors($propertyMetadata, $classMetadata);
52
        }
53 355
    }
54
55
    /**
56
     * @param string $accessorPrefix
57
     * @param string $propertyName
58
     * @param string $naming
59
     *
60
     * @return string|null
61
     */
62 30
    protected function getAccessorName($accessorPrefix, $propertyName, $naming)
63
    {
64
        switch ($naming) {
65 30
            case PropertyMetadata::ACCESS_TYPE_NAMING_CAMEL_CASE:
66 15
                return $accessorPrefix . preg_replace('/_(\w)/', '\\1', $propertyName) ?: null;
67
68 22
            case PropertyMetadata::ACCESS_TYPE_NAMING_EXACT:
69 21
                return $accessorPrefix . $propertyName;
70
        }
71
72 1
        throw new RuntimeException("Undefined naming type '$naming'.");
73
    }
74
75 31
    protected function getSetter(PropertyMetadata $metadata, $naming)
76
    {
77 31
        if ($metadata->setter || $metadata->readOnly) {
78 14
            return $metadata->setter;
79
        }
80
81 24
        $class = $this->getDeclaringClass($metadata);
82 24
        $name = $metadata->name;
83
84 24
        if (null !== $method = $this->findAccessorName($this->setterPrefixes, $name, $class, $naming)) {
85 16
            return $method;
86
        }
87
88 7
        throw new RuntimeException("Specify public setter for {$class->getName()}::\${$name} (using $naming naming)");
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
89
    }
90
91 22
    protected function getGetter(PropertyMetadata $metadata, $naming)
92
    {
93 22
        if ($metadata->getter) {
94
            return $metadata->getter;
95
        }
96
97 22
        $class = $this->getDeclaringClass($metadata);
98 22
        $name = $metadata->name;
99
100
101 22
        if (null !== $method = $this->findAccessorName($this->getterPrefixes, $name, $class, $naming)) {
102 19
            return $method;
103
        }
104
105 3
        throw new RuntimeException("Specify public getter for {$class->getName()}::\${$name} (using $naming naming)");
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
106
    }
107
108
    /**
109
     * @param PropertyMetadata $metadata
110
     * @return \ReflectionClass
111
     */
112 30
    protected function getDeclaringClass(PropertyMetadata $metadata)
113
    {
114 30
        return $metadata->getReflection()->getDeclaringClass();
115
    }
116
117
    /**
118
     * @param array $prefixes
119
     * @param string $name
120
     * @param \ReflectionClass $class
121
     * @param string $naming
122
     *
123
     * @return null|string
124
     */
125 30
    protected function findAccessorName(array $prefixes, $name, \ReflectionClass $class, $naming)
126
    {
127 30
        foreach ($prefixes as $prefix) {
128 30
            $accessorName = $this->getAccessorName($prefix, $name, $naming);
129 29
            if ($this->hasPublicMethod($class, $accessorName)) {
130 29
                return $accessorName;
131
            }
132
        }
133
134 10
        return null;
135
    }
136
137
    /**
138
     * @param \ReflectionClass $class
139
     * @param string $method
140
     * @return bool
141
     */
142 29
    protected function hasPublicMethod(\ReflectionClass $class, $method)
143
    {
144 29
        return $class->hasMethod($method) && $class->getMethod($method)->isPublic();
145
    }
146
147 343
    protected function updateAccessors(PropertyMetadata $propertyMetadata, ClassMetadata $classMetadata)
148
    {
149
        $type =
150 343
            $propertyMetadata->accessType ?:
151 339
                $classMetadata->accessType ?:
152 343
                    $this->defaultType;
153
154 343
        if ($type !== PropertyMetadata::ACCESS_TYPE_PUBLIC_METHOD) {
155 320
            return;
156
        }
157
158
        $naming =
159 31
            $propertyMetadata->accessTypeNaming ?:
160 28
                $classMetadata->accessTypeNaming ?:
161 31
                    $this->defaultNaming;
162
163 31
        $propertyMetadata->setter = $propertyMetadata->setter ?: $this->getSetter($propertyMetadata, $naming);
164 23
        $propertyMetadata->getter = $propertyMetadata->getter ?: $this->getGetter($propertyMetadata, $naming);
165 20
    }
166
}
167