ObjectAccessor   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 131
ccs 62
cts 62
cp 1
rs 10
c 0
b 0
f 0
wmc 30

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 2
A exists() 0 7 2
B write() 0 16 6
B read() 0 19 11
A enumerate() 0 13 3
A findPropertyMetadata() 0 4 2
A assertObject() 0 6 2
A assertExists() 0 6 2
1
<?php
2
3
namespace AmaTeam\TreeAccess\Type;
4
5
use AmaTeam\TreeAccess\API\Exception\IllegalTargetException;
6
use AmaTeam\TreeAccess\API\Exception\RuntimeException;
7
use AmaTeam\TreeAccess\API\Metadata\PropertyMetadataInterface;
8
use AmaTeam\TreeAccess\API\TypeAccessorInterface;
9
use AmaTeam\TreeAccess\API\Exception\MissingNodeException;
10
use AmaTeam\TreeAccess\API\Metadata\ManagerInterface;
11
use AmaTeam\TreeAccess\API\NodeInterface;
12
use AmaTeam\TreeAccess\Metadata\Manager;
13
use AmaTeam\TreeAccess\Node;
14
use stdClass;
15
16
/**
17
 * TODO: internals are actually a mess
18
 * TODO: magic methods support
19
 */
20
class ObjectAccessor implements TypeAccessorInterface
21
{
22
    /**
23
     * @var ManagerInterface
24
     */
25
    private $metadataManger;
26
27
    /**
28
     * @param ManagerInterface $metadataManger
29
     */
30 35
    public function __construct(ManagerInterface $metadataManger = null)
31
    {
32 35
        $this->metadataManger = $metadataManger ?: new Manager();
33 35
    }
34
35
    /**
36
     * @param object $object
37
     * @param string $property
38
     * @return NodeInterface
39
     *
40
     * @SuppressWarnings(PHPMD.ElseExpression)
41
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
42
     */
43 15
    public function read(&$object, $property)
44
    {
45 15
        $this->assertExists($object, $property);
46 14
        $metadata = $this->findPropertyMetadata($object, $property);
47 14
        $standard = $object instanceof stdClass;
48 14
        $native = $metadata && !$metadata->isVirtual();
49 14
        if ($standard || $native) {
50 10
            $value = &$object->$property;
51 6
        } elseif ($metadata && $metadata->getGetter() !== null) {
52 5
            $getter = $metadata->getGetter();
53 5
            $value = $getter ? call_user_func([$object, $getter]) : null;
54
        } else {
55 1
            $template = 'Can\'t access property `%s` on target of class `%s`';
56 1
            $message = sprintf($template, $property, get_class($object));
57 1
            throw new IllegalTargetException([$property], $message);
58
        }
59 13
        $readable = $metadata === null || $native || $metadata->getGetter() !== null;
60 13
        $writable = $metadata === null || $native || $metadata->getSetter() !== null;
61 13
        return new Node([$property], $value, $readable, $writable);
62
    }
63
64
    /**
65
     * @param object $object
66
     * @param string $property
67
     * @param mixed $value
68
     * @return NodeInterface
69
     */
70 8
    public function write(&$object, $property, $value)
71
    {
72 8
        $this->assertObject($object);
73 7
        $metadata = $this->findPropertyMetadata($object, $property);
74 7
        $standard = $object instanceof stdClass;
75 7
        $native = $metadata && !$metadata->isVirtual();
76 7
        if ($standard || $native) {
77 4
            $object->$property = $value;
78 4
            return new Node([$property], $value);
79 4
        } elseif ($metadata && $metadata->getSetter() !== null) {
80 3
            call_user_func([$object, $metadata->getSetter()], $value);
81 3
            return new Node([$property], $value);
82
        }
83 1
        $template = 'Can\'t access property `%s` on target of class `%s`';
84 1
        $message = sprintf($template, $property, get_class($object));
85 1
        throw new IllegalTargetException([$property], $message);
86
    }
87
88
    /**
89
     * @inheritDoc
90
     */
91 16
    public function exists($object, $property)
92
    {
93 16
        $this->assertObject($object);
94 16
        if ($this->findPropertyMetadata($object, $property) !== null) {
95 10
            return true;
96
        }
97 10
        return isset($object->$property);
98
    }
99
100
    /**
101
     * @inheritDoc
102
     */
103 4
    public function enumerate($item)
104
    {
105 4
        $this->assertObject($item);
106 4
        $properties = $this->metadataManger->get(get_class($item));
107 4
        $target = [];
108 4
        $names = array_keys($properties);
109 4
        foreach (array_keys(get_object_vars($item)) as $name) {
110 4
            $names[] = $name;
111
        }
112 4
        foreach (array_unique($names) as $name) {
113 4
            $target[$name] = $this->read($item, $name);
114
        }
115 4
        return $target;
116
    }
117
118
    /**
119
     * @param object $object
120
     * @param string $property
121
     * @return PropertyMetadataInterface|null
122
     */
123 20
    private function findPropertyMetadata($object, $property)
124
    {
125 20
        $properties = $this->metadataManger->get(get_class($object));
126 20
        return isset($properties[$property]) ? $properties[$property] : null;
127
    }
128
129
    /**
130
     * @param mixed $item
131
     */
132 21
    private function assertObject($item)
133
    {
134 21
        if (!is_object($item)) {
135 1
            $template = 'Passed `%s` is not an object';
136 1
            $message = sprintf($template, gettype($item));
137 1
            throw new RuntimeException($message);
138
        }
139 20
    }
140
141
    /**
142
     * @param object $item
143
     * @param string $property
144
     */
145 15
    private function assertExists($item, $property)
146
    {
147 15
        if (!$this->exists($item, $property)) {
148 1
            $template = 'Passed object doesn\'t have property `%s`';
149 1
            $message = sprintf($template, $property);
150 1
            throw new MissingNodeException([$property], $message);
151
        }
152 14
    }
153
}
154