Passed
Pull Request — master (#1)
by Kevin
02:38
created

Proxy::fetchObject()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 2
rs 10
1
<?php
2
3
namespace Zenstruck\Foundry;
4
5
use Doctrine\Persistence\ObjectManager;
6
use PHPUnit\Framework\Assert;
7
8
/**
9
 * @author Kevin Bond <[email protected]>
10
 */
11
final class Proxy
12
{
13
    /** @var object */
14
    private $object;
15
16
    /** @var string */
17
    private $class;
18
19
    /** @var bool */
20
    private $autoRefresh = false;
21
22
    /** @var bool */
23
    private $persisted = false;
24
25 258
    public function __construct(object $object)
26
    {
27 258
        $this->object = $object;
28 258
        $this->class = \get_class($object);
29 258
    }
30
31 126
    public function __call(string $method, array $arguments)
32
    {
33 126
        return $this->object()->{$method}(...$arguments);
34
    }
35
36 4
    public function __get(string $name)
37
    {
38 4
        return $this->object()->{$name};
39
    }
40
41 4
    public function __set(string $name, $value): void
42
    {
43 4
        $this->object()->{$name} = $value;
44 4
    }
45
46 4
    public function __unset(string $name): void
47
    {
48 4
        unset($this->object()->{$name});
49 4
    }
50
51 4
    public function __isset(string $name): bool
52
    {
53 4
        return isset($this->object()->{$name});
54
    }
55
56 8
    public function __toString(): string
57
    {
58 8
        if (!\method_exists($this->object, '__toString')) {
59 4
            if (\PHP_VERSION_ID < 70400) {
60
                return '(no __toString)';
61
            }
62
63 4
            throw new \RuntimeException(\sprintf('Proxied object "%s" cannot be converted to a string.', $this->class));
64
        }
65
66 4
        return $this->object()->__toString();
67
    }
68
69 52
    public static function persisted(object $object): self
70
    {
71 52
        $proxy = new self($object);
72 52
        $proxy->persisted = $proxy->autoRefresh = true;
73
74 52
        return $proxy;
75
    }
76
77 178
    public function isPersisted(): bool
78
    {
79 178
        return $this->persisted;
80
    }
81
82 158
    public function object(): object
83
    {
84 158
        if ($this->autoRefresh && $this->persisted) {
85 78
            $this->refresh();
86
        }
87
88 158
        return $this->object;
89
    }
90
91 186
    public function save(): self
92
    {
93 186
        $this->objectManager()->persist($this->object);
94 186
        $this->objectManager()->flush();
95 186
        $this->autoRefresh = $this->persisted = true;
96
97 186
        return $this;
98
    }
99
100 4
    public function remove(): self
101
    {
102 4
        $this->objectManager()->remove($this->object);
103 4
        $this->objectManager()->flush();
104 4
        $this->autoRefresh = $this->persisted = false;
105
106 4
        return $this;
107
    }
108
109 86
    public function refresh(): self
110
    {
111 86
        if (!$this->persisted) {
112 4
            throw new \RuntimeException(\sprintf('Cannot refresh unpersisted object (%s).', $this->class));
113
        }
114
115 82
        if ($this->objectManager()->contains($this->object)) {
116 78
            $this->objectManager()->refresh($this->object);
117
118 78
            return $this;
119
        }
120
121 8
        if (!$object = $this->fetchObject()) {
122 4
            throw new \RuntimeException('The object no longer exists.');
123
        }
124
125 4
        $this->object = $object;
126
127 4
        return $this;
128
    }
129
130
    /**
131
     * @param mixed $value
132
     */
133 12
    public function forceSet(string $property, $value): self
134
    {
135 12
        return $this->forceSetAll([$property => $value]);
136
    }
137
138 12
    public function forceSetAll(array $properties): self
139
    {
140 12
        $object = $this->object();
141
142 12
        foreach ($properties as $property => $value) {
143 12
            Instantiator::forceSet($object, $property, $value);
144
        }
145
146 12
        return $this;
147
    }
148
149
    /**
150
     * @return mixed
151
     */
152 4
    public function forceGet(string $property)
153
    {
154 4
        return Instantiator::forceGet($this->object(), $property);
155
    }
156
157 4
    public function repository(): RepositoryProxy
158
    {
159 4
        return Factory::configuration()->repositoryFor($this->class);
160
    }
161
162 182
    public function enableAutoRefresh(): self
163
    {
164 182
        if (!$this->persisted) {
165
            throw new \RuntimeException(\sprintf('Cannot enable auto-refresh on unpersisted object (%s).', $this->class));
166
        }
167
168 182
        $this->autoRefresh = true;
169
170 182
        return $this;
171
    }
172
173 186
    public function disableAutoRefresh(): self
174
    {
175 186
        $this->autoRefresh = false;
176
177 186
        return $this;
178
    }
179
180 4
    public function withoutAutoRefresh(callable $callback): self
181
    {
182 4
        $this->disableAutoRefresh();
183
184 4
        $this->executeCallback($callback);
185
186 4
        return $this->isPersisted() ? $this->enableAutoRefresh() : $this;
187
    }
188
189 4
    public function assertPersisted(string $message = 'The object is not persisted.'): self
190
    {
191 4
        Assert::assertNotNull($this->fetchObject(), $message);
192
193 4
        return $this;
194
    }
195
196 4
    public function assertNotPersisted(string $message = 'The object is persisted but it should not be.'): self
197
    {
198 4
        Assert::assertNull($this->fetchObject(), $message);
199
200 4
        return $this;
201
    }
202
203
    /**
204
     * @internal
205
     */
206 8
    public function executeCallback(callable $callback, ...$arguments): void
207
    {
208 8
        $object = $this;
209 8
        $parameters = (new \ReflectionFunction($callback))->getParameters();
210
211 8
        if (isset($parameters[0]) && $parameters[0]->getType() && $this->class === $parameters[0]->getType()->getName()) {
212 8
            $object = $object->object();
213
        }
214
215 8
        $callback($object, ...$arguments);
216 8
    }
217
218 16
    private function fetchObject(): ?object
219
    {
220 16
        $id = $this->objectManager()->getClassMetadata($this->class)->getIdentifierValues($this->object);
221
222 16
        return empty($id) ? null : $this->objectManager()->find($this->class, $id);
223
    }
224
225 186
    private function objectManager(): ObjectManager
226
    {
227 186
        return Factory::configuration()->objectManagerFor($this->class);
228
    }
229
}
230