Passed
Push — master ( 5adf30...14db2b )
by Kevin
02:47
created

Proxy::disableAutoRefresh()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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