Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of PHPUnit.
4
 *
5
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace PHPUnit\Framework\MockObject;
11
 
12
use function array_map;
13
use function explode;
14
use function get_class;
15
use function implode;
16
use function in_array;
17
use function interface_exists;
18
use function is_object;
19
use function sprintf;
20
use function strpos;
21
use function strtolower;
22
use function substr;
23
use Doctrine\Instantiator\Instantiator;
24
use PHPUnit\Framework\SelfDescribing;
25
use PHPUnit\Util\Cloner;
26
use SebastianBergmann\Exporter\Exporter;
27
use stdClass;
28
use Throwable;
29
 
30
/**
31
 * @internal This class is not covered by the backward compatibility promise for PHPUnit
32
 */
33
final class Invocation implements SelfDescribing
34
{
35
    /**
36
     * @var string
37
     */
38
    private $className;
39
 
40
    /**
41
     * @var string
42
     */
43
    private $methodName;
44
 
45
    /**
46
     * @var array
47
     */
48
    private $parameters;
49
 
50
    /**
51
     * @var string
52
     */
53
    private $returnType;
54
 
55
    /**
56
     * @var bool
57
     */
58
    private $isReturnTypeNullable = false;
59
 
60
    /**
61
     * @var bool
62
     */
63
    private $proxiedCall;
64
 
65
    /**
66
     * @var object
67
     */
68
    private $object;
69
 
70
    public function __construct(string $className, string $methodName, array $parameters, string $returnType, object $object, bool $cloneObjects = false, bool $proxiedCall = false)
71
    {
72
        $this->className   = $className;
73
        $this->methodName  = $methodName;
74
        $this->parameters  = $parameters;
75
        $this->object      = $object;
76
        $this->proxiedCall = $proxiedCall;
77
 
78
        if (strtolower($methodName) === '__tostring') {
79
            $returnType = 'string';
80
        }
81
 
82
        if (strpos($returnType, '?') === 0) {
83
            $returnType                 = substr($returnType, 1);
84
            $this->isReturnTypeNullable = true;
85
        }
86
 
87
        $this->returnType = $returnType;
88
 
89
        if (!$cloneObjects) {
90
            return;
91
        }
92
 
93
        foreach ($this->parameters as $key => $value) {
94
            if (is_object($value)) {
95
                $this->parameters[$key] = Cloner::clone($value);
96
            }
97
        }
98
    }
99
 
100
    public function getClassName(): string
101
    {
102
        return $this->className;
103
    }
104
 
105
    public function getMethodName(): string
106
    {
107
        return $this->methodName;
108
    }
109
 
110
    public function getParameters(): array
111
    {
112
        return $this->parameters;
113
    }
114
 
115
    /**
116
     * @throws RuntimeException
117
     *
118
     * @return mixed Mocked return value
119
     */
120
    public function generateReturnValue()
121
    {
122
        if ($this->isReturnTypeNullable || $this->proxiedCall) {
123
            return null;
124
        }
125
 
126
        $intersection               = false;
127
        $union                      = false;
128
        $unionContainsIntersections = false;
129
 
130
        if (strpos($this->returnType, '|') !== false) {
131
            $types = explode('|', $this->returnType);
132
            $union = true;
133
 
134
            if (strpos($this->returnType, '(') !== false) {
135
                $unionContainsIntersections = true;
136
            }
137
        } elseif (strpos($this->returnType, '&') !== false) {
138
            $types        = explode('&', $this->returnType);
139
            $intersection = true;
140
        } else {
141
            $types = [$this->returnType];
142
        }
143
 
144
        $types = array_map('strtolower', $types);
145
 
146
        if (!$intersection && !$unionContainsIntersections) {
147
            if (in_array('', $types, true) ||
148
                in_array('null', $types, true) ||
149
                in_array('mixed', $types, true) ||
150
                in_array('void', $types, true)) {
151
                return null;
152
            }
153
 
154
            if (in_array('true', $types, true)) {
155
                return true;
156
            }
157
 
158
            if (in_array('false', $types, true) ||
159
                in_array('bool', $types, true)) {
160
                return false;
161
            }
162
 
163
            if (in_array('float', $types, true)) {
164
                return 0.0;
165
            }
166
 
167
            if (in_array('int', $types, true)) {
168
                return 0;
169
            }
170
 
171
            if (in_array('string', $types, true)) {
172
                return '';
173
            }
174
 
175
            if (in_array('array', $types, true)) {
176
                return [];
177
            }
178
 
179
            if (in_array('static', $types, true)) {
180
                try {
181
                    return (new Instantiator)->instantiate(get_class($this->object));
182
                } catch (Throwable $t) {
183
                    throw new RuntimeException(
184
                        $t->getMessage(),
185
                        (int) $t->getCode(),
186
                        $t
187
                    );
188
                }
189
            }
190
 
191
            if (in_array('object', $types, true)) {
192
                return new stdClass;
193
            }
194
 
195
            if (in_array('callable', $types, true) ||
196
                in_array('closure', $types, true)) {
197
                return static function (): void
198
                {
199
                };
200
            }
201
 
202
            if (in_array('traversable', $types, true) ||
203
                in_array('generator', $types, true) ||
204
                in_array('iterable', $types, true)) {
205
                $generator = static function (): \Generator
206
                {
207
                    yield from [];
208
                };
209
 
210
                return $generator();
211
            }
212
 
213
            if (!$union) {
214
                try {
215
                    return (new Generator)->getMock($this->returnType, [], [], '', false);
216
                } catch (Throwable $t) {
217
                    if ($t instanceof Exception) {
218
                        throw $t;
219
                    }
220
 
221
                    throw new RuntimeException(
222
                        $t->getMessage(),
223
                        (int) $t->getCode(),
224
                        $t
225
                    );
226
                }
227
            }
228
        }
229
 
230
        if ($intersection && $this->onlyInterfaces($types)) {
231
            try {
232
                return (new Generator)->getMockForInterfaces($types);
233
            } catch (Throwable $t) {
234
                throw new RuntimeException(
235
                    sprintf(
236
                        'Return value for %s::%s() cannot be generated: %s',
237
                        $this->className,
238
                        $this->methodName,
239
                        $t->getMessage(),
240
                    ),
241
                    (int) $t->getCode(),
242
                );
243
            }
244
        }
245
 
246
        $reason = '';
247
 
248
        if ($union) {
249
            $reason = ' because the declared return type is a union';
250
        } elseif ($intersection) {
251
            $reason = ' because the declared return type is an intersection';
252
        }
253
 
254
        throw new RuntimeException(
255
            sprintf(
256
                'Return value for %s::%s() cannot be generated%s, please configure a return value for this method',
257
                $this->className,
258
                $this->methodName,
259
                $reason
260
            )
261
        );
262
    }
263
 
264
    public function toString(): string
265
    {
266
        $exporter = new Exporter;
267
 
268
        return sprintf(
269
            '%s::%s(%s)%s',
270
            $this->className,
271
            $this->methodName,
272
            implode(
273
                ', ',
274
                array_map(
275
                    [$exporter, 'shortenedExport'],
276
                    $this->parameters
277
                )
278
            ),
279
            $this->returnType ? sprintf(': %s', $this->returnType) : ''
280
        );
281
    }
282
 
283
    public function getObject(): object
284
    {
285
        return $this->object;
286
    }
287
 
288
    /**
289
     * @psalm-param non-empty-list<string> $types
290
     */
291
    private function onlyInterfaces(array $types): bool
292
    {
293
        foreach ($types as $type) {
294
            if (!interface_exists($type)) {
295
                return false;
296
            }
297
        }
298
 
299
        return true;
300
    }
301
}