CallDefinition::getArgumentMappings()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php
2
/**
3
 * Copyright (C) 2018 Gerrit Addiks.
4
 * This package (including this file) was released under the terms of the GPL-3.0.
5
 * You should have received a copy of the GNU General Public License along with this program.
6
 * If not, see <http://www.gnu.org/licenses/> or send me a mail so i can send you a copy.
7
 *
8
 * @license GPL-3.0
9
 *
10
 * @author Gerrit Addiks <[email protected]>
11
 */
12
13
namespace Addiks\RDMBundle\Mapping;
14
15
use Addiks\RDMBundle\Mapping\CallDefinitionInterface;
16
use Addiks\RDMBundle\Mapping\MappingInterface;
17
use BackedEnum;
0 ignored issues
show
Bug introduced by
The type BackedEnum 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...
18
use Webmozart\Assert\Assert;
19
use Addiks\RDMBundle\Hydration\HydrationContextInterface;
20
use Symfony\Component\DependencyInjection\ContainerInterface;
21
use Doctrine\ORM\Exception\ORMException;
22
use Doctrine\Common\Util\ClassUtils;
23
use ArgumentCountError;
24
25
final class CallDefinition implements CallDefinitionInterface
26
{
27
28
    /**
29
     * @var ContainerInterface
30
     */
31
    private $container;
32
33
    /**
34
     * @var string|null
35
     */
36
    private $objectReference;
37
38
    /**
39
     * @var string
40
     */
41
    private $routineName;
42
43
    /**
44
     * @var array<MappingInterface>
45
     */
46
    private $argumentMappings = array();
47
48
    /**
49
     * @var bool
50
     */
51
    private $isStaticCall;
52
53
    /**
54
     * @var string
55
     */
56
    private $origin;
57 13
58
    public function __construct(
59
        ContainerInterface $container,
60
        string $routineName,
61
        string $objectReference = null,
62
        array $argumentMappings = array(),
63
        bool $isStaticCall = false,
64
        string $origin = "unknown"
65 13
    ) {
66 13
        $this->routineName = $routineName;
67 13
        $this->objectReference = $objectReference;
68 13
        $this->isStaticCall = $isStaticCall;
69 13
        $this->container = $container;
70
        $this->origin = $origin;
71 13
72
        foreach ($argumentMappings as $argumentMapping) {
73
            /** @var MappingInterface $argumentMapping */
74 13
75
            Assert::isInstanceOf($argumentMapping, MappingInterface::class);
76 13
77
            $this->argumentMappings[] = $argumentMapping;
78
        }
79
    }
80 1
81
    public function __sleep(): array
82
    {
83 1
        return [
84
            'objectReference',
85
            'routineName',
86
            'argumentMappings',
87
            'isStaticCall',
88
            'origin',
89
        ];
90
    }
91 7
92
    public function execute(
93
        HydrationContextInterface $context,
94
        array $dataFromAdditionalColumns
95
    ) {
96 7
        /** @var mixed $result */
97
        $result = null;
98
99 7
        /** @var null|object|string $callee */
100
        $callee = $this->resolveCallee((string)$this->objectReference, $context);
101 7
102
        if ($this->isStaticCall && is_object($callee)) {
103
            $callee = get_class($callee);
104
        }
105
106 7
        /** @var array<mixed> $arguments */
107
        $arguments = $this->resolveArguments(
108
            $context,
109
            $dataFromAdditionalColumns
110
        );
111
112 7
        try {
113
            if (is_null($callee) && !empty($this->objectReference)) {
114
                $result = null;
115 7
116 1
            } elseif (is_null($callee)) {
117
                $result = call_user_func_array($this->routineName, $arguments);
118 6
119 1
            } elseif (is_string($callee)) {
120
                $result = call_user_func_array("{$callee}::{$this->routineName}", $arguments);
121
122 7
            } elseif (property_exists($callee, $this->routineName) && !method_exists($callee, $this->routineName)) {
123
                $result = $callee->{$this->routineName};
124
125
            } elseif ($callee instanceof BackedEnum && $this->routineName === '__toString') {
126
                $result = $callee->value;
127
128
            } else {
129
                $result = call_user_func_array([$callee, $this->routineName], $arguments);
130
            }
131
132
        } catch (ArgumentCountError $exception) {
133
            /** @var string $calleeDescription */
134
            $calleeDescription = "";
135
136
            if (is_object($callee)) {
137
                $calleeDescription = get_class($callee);
138
139
                if (class_exists(ClassUtils::class)) {
140
                    $calleeDescription = ClassUtils::getRealClass($calleeDescription);
141
                }
142
143
            } elseif (is_string($callee)) {
144
                $calleeDescription = $callee;
145
            }
146
147
            if (!empty($calleeDescription)) {
148
                $calleeDescription .= $this->isStaticCall ?'::' :'->';
149
            }
150
151
            throw new ORMException(sprintf(
152
                "Wrong number of arguments passed to routine '%s%s' in %s: %s",
153 7
                $calleeDescription,
154
                $this->routineName,
155
                $this->origin,
156 1
                $exception->getMessage()
157
            ), 0, $exception);
158 1
        }
159
160
        return $result;
161 1
    }
162
163 1
    public function getObjectReference(): ?string
164
    {
165
        return $this->objectReference;
166 1
    }
167
168 1
    public function getRoutineName(): string
169
    {
170
        return $this->routineName;
171 1
    }
172
173 1
    public function getArgumentMappings(): array
174
    {
175
        return $this->argumentMappings;
176
    }
177
178
    public function isStaticCall(): bool
179
    {
180
        return $this->isStaticCall;
181 7
    }
182
183
    /**
184
     * (This return type should be nullable, but there seems to be a bug in current version psalm preventing it.)
185
     *
186 7
     * @return object|string|null
187
     */
188 7
    private function resolveCallee(
189
        string $objectReference,
190 6
        HydrationContextInterface $context
191
    ) {
192 6
        /** @var object|string $callee */
193 1
        $callee = null;
194
195
        if (!empty($objectReference)) {
196 6
            /** @var array<mixed> $hydrationStack */
197
            $hydrationStack = $context->getObjectHydrationStack();
198
199 6
            if ($objectReference[0] === '$') {
200 2
                $objectReference = substr($objectReference, 1);
201
            }
202 4
203 1
            if (in_array($objectReference, ['root', 'entity'])) {
204
                $callee = $context->getEntity();
205 3
206
            } elseif (in_array($objectReference, ['self', 'this'])) {
207 2
                $callee = $hydrationStack[count($hydrationStack) - 1];
208
209 2
            } elseif (in_array($objectReference, ['parent'])) {
210
                $callee = $hydrationStack[count($hydrationStack) - 2];
211 1
212 1
            } elseif ($objectReference[0] === '@') {
213
                /** @var string $serviceId */
214
                $serviceId = substr($objectReference, 1);
215
216
                $callee = $this->container->get($serviceId);
217
218
            } elseif (class_exists($objectReference)) {
219 7
                $callee = $objectReference;
220
221
            } elseif ($context->hasRegisteredValue($objectReference)) {
222
                $callee = $context->getRegisteredValue($objectReference);
223
            }
224
        }
225
226
        return $callee;
227 7
    }
228
229
    /**
230
     * @param array<scalar> $dataFromAdditionalColumns
231
     *
232 7
     * @return array<mixed>
233
     */
234 7
    private function resolveArguments(
235
        HydrationContextInterface $context,
236
        array $dataFromAdditionalColumns
237
    ): array {
238
        /** @var array<mixed> $arguments */
239 7
        $arguments = array();
240
241
        if (array_key_exists('', $dataFromAdditionalColumns)) {
242 7
            $arguments[] = $dataFromAdditionalColumns[''];
243
            unset($dataFromAdditionalColumns['']);
244
        }
245
246
        foreach ($this->argumentMappings as $argumentMapping) {
247
            /** @var MappingInterface $argumentMapping */
248 7
249
            $arguments[] = $argumentMapping->resolveValue(
250
                $context,
251 1
                $dataFromAdditionalColumns
252 1
            );
253
        }
254
255
        return $arguments;
256
    }
257
258
    public function wakeUpCall(ContainerInterface $container): void {
259
        $this->container = $container;
260
    }
261
}
262