Passed
Pull Request — main (#4)
by Michael
03:57
created

CallProxy::getInternal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 2
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MichaelRubel\EnhancedContainer\Core;
6
7
use Illuminate\Support\Str;
8
use Illuminate\Support\Traits\ForwardsCalls;
9
use MichaelRubel\EnhancedContainer\Call;
10
use MichaelRubel\EnhancedContainer\Exceptions\InstanceInteractionException;
11
use MichaelRubel\EnhancedContainer\Traits\InteractsWithContainer;
12
13
class CallProxy implements Call
14
{
15
    use InteractsWithContainer, ForwardsCalls;
0 ignored issues
show
introduced by
The trait MichaelRubel\EnhancedCon...\InteractsWithContainer requires some properties which are not provided by MichaelRubel\EnhancedContainer\Core\CallProxy: $contextual, $map
Loading history...
16
17
    /**
18
     * @var object
19
     */
20
    protected object $instance;
21
22
    /**
23
     * @var object
24
     */
25
    protected object $previous;
26
27
    /**
28
     * @var array
29
     */
30
    protected array $state = [];
31
32
    /**
33
     * CallProxy constructor.
34
     *
35
     * @param  object|string  $class
36
     * @param  array  $dependencies
37
     * @param  string|null  $context
38
     */
39 61
    public function __construct(
40
        object | string $class,
41
        array $dependencies = [],
42
        ?string $context = null
43
    ) {
44 61
        $this->instance = $this->getInstance($class, $dependencies, $context);
45
    }
46
47
    /**
48
     * Gets the internal property by name.
49
     *
50
     * @param  string  $property
51
     *
52
     * @return mixed
53
     */
54 11
    public function getInternal(string $property): mixed
55
    {
56 11
        return $this->{$property};
57
    }
58
59
    /**
60
     * Perform the container call.
61
     *
62
     * @param  object  $service
63
     * @param  string  $method
64
     * @param  array  $parameters
65
     *
66
     * @return mixed
67
     */
68 52
    protected function containerCall(object $service, string $method, array $parameters): mixed
69
    {
70
        try {
71 52
            return app()->call(
72 52
                [$service, $method],
73 52
                $this->getParameters($service, $method, $parameters)
74
            );
75 11
        } catch (\ReflectionException) {
76 3
            return $this->forwardCallTo($service, $method, $parameters);
77
        }
78
    }
79
80
    /**
81
     * @return void
82
     */
83 17
    protected function findForwardedInstance(): void
84
    {
85 17
        $clue = $this->instance::class . Forwarding::CONTAINER_KEY;
86
87 17
        if (app()->bound($clue)) {
88 12
            $newInstance = rescue(fn () => app($clue), report: false);
89
90 12
            if (! is_null($newInstance)) {
91 12
                $this->previous = $this->instance;
92 12
                $this->instance = $newInstance;
93
            }
94
        }
95
    }
96
97
    /**
98
     * @param  string  $name
99
     * @param  string  $type
100
     *
101
     * @return void
102
     */
103 58
    protected function setState(string $name, string $type): void
104
    {
105 58
        $this->state[$name] = $type;
106
    }
107
108
    /**
109
     * @param  string  $name
110
     *
111
     * @return bool
112
     */
113 14
    protected function hasPreviousState(string $name): bool
114
    {
115 14
        return isset($this->state[$name]) && isset($this->previous);
116
    }
117
118
    /**
119
     * Pass the call through container.
120
     *
121
     * @param  string  $method
122
     * @param  array  $parameters
123
     *
124
     * @return mixed
125
     * @throws InstanceInteractionException
126
     */
127 52
    public function __call(string $method, array $parameters): mixed
128
    {
129 52
        if (! method_exists($this->instance, $method)) {
130 12
            if ($this->hasPreviousState($method)) {
131 1
                throw new InstanceInteractionException;
132
            }
133
134 12
            $this->findForwardedInstance();
135
        }
136
137 52
        $this->setState($method, Call::METHOD);
138
139
        try {
140 52
            return $this->containerCall($this->instance, $method, $parameters);
141 11
        } catch (\Error $e) {
142 6
            if (Str::contains($e->getMessage(), 'Call to undefined method')) {
143 4
                $this->findForwardedInstance();
144
145 4
                return $this->containerCall($this->instance, $method, $parameters);
146
            }
147
148 2
            throw $e;
149
        }
150
    }
151
152
    /**
153
     * Get the instance's property.
154
     *
155
     * @param  string  $name
156
     *
157
     * @return mixed
158
     * @throws InstanceInteractionException
159
     */
160 8
    public function __get(string $name): mixed
161
    {
162 8
        if (! property_exists($this->instance, $name)) {
163 2
            if ($this->hasPreviousState($name)) {
164 1
                throw new InstanceInteractionException;
165
            }
166
167 1
            $this->findForwardedInstance();
168
169 1
            $this->setState($name, Call::GET);
170
        }
171
172 8
        return $this->instance->{$name};
173
    }
174
175
    /**
176
     * Set the instance's property.
177
     *
178
     * @param  string  $name
179
     * @param  mixed  $value
180
     */
181 6
    public function __set(string $name, mixed $value): void
182
    {
183 6
        if (! property_exists($this->instance, $name)) {
184 4
            $this->findForwardedInstance();
185
        }
186
187 6
        $this->setState($name, Call::SET);
188
189 6
        $this->instance->{$name} = $value;
190
    }
191
}
192