Passed
Push — master ( 6bd319...2d574a )
by Kevin
04:56
created

Proxy::__toString()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 11
rs 10
ccs 5
cts 6
cp 0.8333
crap 3.0416
1
<?php
2
3
namespace Zenstruck\Foundry;
4
5
use Doctrine\Persistence\ObjectManager;
6
use PHPUnit\Framework\Assert;
0 ignored issues
show
Bug introduced by
The type PHPUnit\Framework\Assert was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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