Completed
Pull Request — master (#467)
by Marco
23:32
created

assertByRefVariableValueSame()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProxyManagerTest\Functional;
6
7
use Generator;
8
use PHPUnit\Framework\ExpectationFailedException;
9
use PHPUnit\Framework\TestCase;
10
use ProxyManager\Factory\AccessInterceptorValueHolderFactory;
11
use ProxyManager\Proxy\AccessInterceptorInterface;
12
use ProxyManager\Proxy\AccessInterceptorValueHolderInterface;
13
use ProxyManagerTest\Assert;
14
use ProxyManagerTestAsset\BaseClass;
15
use ProxyManagerTestAsset\BaseInterface;
16
use ProxyManagerTestAsset\CallableInterface;
17
use ProxyManagerTestAsset\ClassWithCounterConstructor;
18
use ProxyManagerTestAsset\ClassWithDynamicArgumentsMethod;
19
use ProxyManagerTestAsset\ClassWithMethodWithByRefVariadicFunction;
20
use ProxyManagerTestAsset\ClassWithMethodWithVariadicFunction;
21
use ProxyManagerTestAsset\ClassWithParentHint;
22
use ProxyManagerTestAsset\ClassWithPublicArrayPropertyAccessibleViaMethod;
23
use ProxyManagerTestAsset\ClassWithPublicProperties;
24
use ProxyManagerTestAsset\ClassWithSelfHint;
25
use ProxyManagerTestAsset\EmptyClass;
26
use ProxyManagerTestAsset\OtherObjectAccessClass;
27
use ProxyManagerTestAsset\VoidCounter;
28
use ReflectionClass;
29
use stdClass;
30
use function array_values;
31
use function get_class;
32
use function random_int;
33
use function serialize;
34
use function ucfirst;
35
use function uniqid;
36
use function unserialize;
37
38
/**
39
 * Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator} produced objects
40
 *
41
 * @group Functional
42
 * @coversNothing
43
 */
44
final class AccessInterceptorValueHolderFunctionalTest extends TestCase
45
{
46
    /**
47
     * @param mixed[] $params
48
     * @param mixed   $expectedValue
49
     *
50
     * @dataProvider getProxyMethods
51
     */
52
    public function testMethodCalls(object $instance, string $method, array $params, $expectedValue) : void
53
    {
54
        $proxy    = (new AccessInterceptorValueHolderFactory())->createProxy($instance);
55
        $callback = [$proxy, $method];
56
57
        self::assertIsCallable($callback);
58
        self::assertSame($instance, $proxy->getWrappedValueHolderValue());
59
        self::assertSame($expectedValue, $callback(...array_values($params)));
60
61
        $listener = $this->createMock(CallableInterface::class);
62
        $listener
63
            ->expects(self::once())
64
            ->method('__invoke')
65
            ->with($proxy, $instance, $method, $params, false);
66
67
        $proxy->setMethodPrefixInterceptor(
68
            $method,
69
            static function (
70
                AccessInterceptorInterface $proxy,
71
                object $instance,
72
                string $method,
73
                array $params,
74
                bool & $returnEarly
75
            ) use ($listener) : void {
76
                $listener->__invoke($proxy, $instance, $method, $params, $returnEarly);
0 ignored issues
show
Bug introduced by
The method __invoke() does not seem to exist on object<PHPUnit\Framework\MockObject\MockObject>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
77
            }
78
        );
79
80
        self::assertSame($expectedValue, $callback(...array_values($params)));
81
82
        $random = uniqid('', true);
83
84
        $proxy->setMethodPrefixInterceptor(
85
            $method,
86
            static function (
87
                AccessInterceptorInterface $proxy,
88
                object $instance,
89
                string $method,
90
                array $params,
91
                bool & $returnEarly
92
            ) use ($random) : string {
93
                $returnEarly = true;
94
95
                return $random;
96
            }
97
        );
98
99
        self::assertSame($random, $callback(...array_values($params)));
100
    }
101
102
    /**
103
     * @param mixed[] $params
104
     * @param mixed   $expectedValue
105
     *
106
     * @dataProvider getProxyMethods
107
     */
108
    public function testMethodCallsWithSuffixListener(
109
        object $instance,
110
        string $method,
111
        array $params,
112
        $expectedValue
113
    ) : void {
114
        $proxy    = (new AccessInterceptorValueHolderFactory())->createProxy($instance);
115
        $callback = [$proxy, $method];
116
117
        self::assertIsCallable($callback);
118
119
        $listener = $this->createMock(CallableInterface::class);
120
        $listener
121
            ->expects(self::once())
122
            ->method('__invoke')
123
            ->with($proxy, $instance, $method, $params, $expectedValue, false);
124
125
        $proxy->setMethodSuffixInterceptor(
126
            $method,
127
            /** @param mixed $returnValue */
128
            static function (
129
                AccessInterceptorInterface $proxy,
130
                object $instance,
131
                string $method,
132
                array $params,
133
                $returnValue,
134
                bool & $returnEarly
135
            ) use ($listener) : void {
136
                $listener->__invoke($proxy, $instance, $method, $params, $returnValue, $returnEarly);
0 ignored issues
show
Bug introduced by
The method __invoke() does not seem to exist on object<PHPUnit\Framework\MockObject\MockObject>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
137
            }
138
        );
139
140
        self::assertSame($expectedValue, $callback(...array_values($params)));
141
142
        $random = uniqid('', true);
143
144
        $proxy->setMethodSuffixInterceptor(
145
            $method,
146
            /** @param mixed $returnValue */
147
            static function (
148
                AccessInterceptorInterface $proxy,
149
                object $instance,
150
                string $method,
151
                array $params,
152
                $returnValue,
153
                bool & $returnEarly
154
            ) use ($random) : string {
155
                $returnEarly = true;
156
157
                return $random;
158
            }
159
        );
160
161
        self::assertSame($random, $callback(...array_values($params)));
162
    }
163
164
    /**
165
     * @param mixed[] $params
166
     * @param mixed   $expectedValue
167
     *
168
     * @dataProvider getProxyMethods
169
     */
170
    public function testMethodCallsAfterUnSerialization(
171
        object $instance,
172
        string $method,
173
        array $params,
174
        $expectedValue
175
    ) : void {
176
        /** @var AccessInterceptorValueHolderInterface $proxy */
177
        $proxy    = unserialize(serialize((new AccessInterceptorValueHolderFactory())->createProxy($instance)));
178
        $callback = [$proxy, $method];
179
180
        self::assertIsCallable($callback);
181
        self::assertSame($expectedValue, $callback(...array_values($params)));
182
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
183
    }
184
185
    /**
186
     * @param mixed[] $params
187
     * @param mixed   $expectedValue
188
     *
189
     * @dataProvider getProxyMethods
190
     */
191
    public function testMethodCallsAfterCloning(
192
        object $instance,
193
        string $method,
194
        array $params,
195
        $expectedValue
196
    ) : void {
197
        $proxy    = (new AccessInterceptorValueHolderFactory())->createProxy($instance);
198
        $cloned   = clone $proxy;
199
        $callback = [$cloned, $method];
200
201
        self::assertIsCallable($callback);
202
        self::assertNotSame($proxy->getWrappedValueHolderValue(), $cloned->getWrappedValueHolderValue());
203
        self::assertSame($expectedValue, $callback(...array_values($params)));
204
        self::assertEquals($instance, $cloned->getWrappedValueHolderValue());
205
    }
206
207
    /**
208
     * @param mixed $propertyValue
209
     *
210
     * @dataProvider getPropertyAccessProxies
211
     */
212
    public function testPropertyReadAccess(
213
        object $instance,
214
        AccessInterceptorValueHolderInterface $proxy,
215
        string $publicProperty,
216
        $propertyValue
217
    ) : void {
218
        self::assertSame($propertyValue, $proxy->$publicProperty);
219
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
220
    }
221
222
    /**
223
     * @dataProvider getPropertyAccessProxies
224
     */
225
    public function testPropertyWriteAccess(
226
        object $instance,
0 ignored issues
show
Unused Code introduced by
The parameter $instance is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
227
        AccessInterceptorValueHolderInterface $proxy,
228
        string $publicProperty
229
    ) : void {
230
        $newValue               = uniqid('', true);
231
        $proxy->$publicProperty = $newValue;
232
233
        self::assertSame($newValue, $proxy->$publicProperty);
234
235
        $wrappedValue = $proxy->getWrappedValueHolderValue();
236
237
        self::assertNotNull($wrappedValue);
238
        self::assertSame($newValue, $wrappedValue->$publicProperty);
239
    }
240
241
    /**
242
     * @dataProvider getPropertyAccessProxies
243
     */
244
    public function testPropertyExistence(
245
        object $instance,
246
        AccessInterceptorValueHolderInterface $proxy,
247
        string $publicProperty
248
    ) : void {
249
        self::assertSame(isset($instance->$publicProperty), isset($proxy->$publicProperty));
250
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
251
252
        $proxy->getWrappedValueHolderValue()->$publicProperty = null;
253
        self::assertFalse(isset($proxy->$publicProperty));
254
    }
255
256
    /**
257
     * @dataProvider getPropertyAccessProxies
258
     */
259
    public function testPropertyUnset(
260
        object $instance,
261
        AccessInterceptorValueHolderInterface $proxy,
262
        string $publicProperty
263
    ) : void {
264
        $instance = $proxy->getWrappedValueHolderValue() ?: $instance;
265
        unset($proxy->$publicProperty);
266
267
        self::assertFalse(isset($instance->$publicProperty));
268
        self::assertFalse(isset($proxy->$publicProperty));
269
    }
270
271
    /**
272
     * Verifies that accessing a public property containing an array behaves like in a normal context
273
     */
274
    public function testCanWriteToArrayKeysInPublicProperty() : void
275
    {
276
        $instance  = new ClassWithPublicArrayPropertyAccessibleViaMethod();
277
        $proxy     = (new AccessInterceptorValueHolderFactory())->createProxy($instance);
278
279
        $proxy->arrayProperty['foo'] = 'bar';
280
281
        self::assertSame('bar', $proxy->getArrayProperty()['foo']);
282
283
        $proxy->arrayProperty = ['tab' => 'taz'];
284
285
        self::assertSame(['tab' => 'taz'], $proxy->getArrayProperty());
286
    }
287
288
    /**
289
     * Verifies that public properties retrieved via `__get` don't get modified in the object state
290
     */
291
    public function testWillNotModifyRetrievedPublicProperties() : void
292
    {
293
        $instance  = new ClassWithPublicProperties();
294
        $proxy    = (new AccessInterceptorValueHolderFactory())->createProxy($instance);
295
        $variable = $proxy->property0;
296
297
        self::assertByRefVariableValueSame('property0', $variable);
298
299
        $variable = 'foo';
300
301
        self::assertByRefVariableValueSame('property0', $proxy->property0);
302
        self::assertByRefVariableValueSame('foo', $variable);
303
    }
304
305
    /**
306
     * Verifies that public properties references retrieved via `__get` modify in the object state
307
     */
308
    public function testWillModifyByRefRetrievedPublicProperties() : void
309
    {
310
        $instance  = new ClassWithPublicProperties();
311
        $proxy    = (new AccessInterceptorValueHolderFactory())->createProxy($instance);
312
        $variable = &$proxy->property0;
313
314
        self::assertByRefVariableValueSame('property0', $variable);
315
316
        $variable = 'foo';
317
318
        self::assertByRefVariableValueSame('foo', $proxy->property0);
319
        self::assertByRefVariableValueSame('foo', $variable);
320
    }
321
322
    /**
323
     * @group 115
324
     * @group 175
325
     */
326
    public function testWillBehaveLikeObjectWithNormalConstructor() : void
327
    {
328
        $instance = new ClassWithCounterConstructor(10);
329
330
        self::assertSame(10, $instance->amount, 'Verifying that test asset works as expected');
331
        self::assertSame(10, $instance->getAmount(), 'Verifying that test asset works as expected');
332
        $instance->__construct(3);
333
        self::assertSame(13, $instance->amount, 'Verifying that test asset works as expected');
334
        self::assertSame(13, $instance->getAmount(), 'Verifying that test asset works as expected');
335
336
        $proxyName = get_class(
337
            (new AccessInterceptorValueHolderFactory())
338
                ->createProxy(new ClassWithCounterConstructor(0))
339
        );
340
341
        $proxy = new $proxyName(15);
342
343
        self::assertSame(15, $proxy->amount, 'Verifying that the proxy constructor works as expected');
344
        self::assertSame(15, $proxy->getAmount(), 'Verifying that the proxy constructor works as expected');
345
        $proxy->__construct(5);
346
        self::assertSame(20, $proxy->amount, 'Verifying that the proxy constructor works as expected');
347
        self::assertSame(20, $proxy->getAmount(), 'Verifying that the proxy constructor works as expected');
348
    }
349
350
    public function testWillForwardVariadicArguments() : void
351
    {
352
        $factory      = new AccessInterceptorValueHolderFactory();
353
        $targetObject = new ClassWithMethodWithVariadicFunction();
354
355
        $object = $factory->createProxy(
356
            $targetObject,
357
            [
358
                'bar' => static function () : string {
359
                    return 'Foo Baz';
360
                },
361
            ]
362
        );
363
364
        self::assertNull($object->bar);
365
        self::assertNull($object->baz);
366
367
        $object->foo('Ocramius', 'Malukenho', 'Danizord');
368
        self::assertSame('Ocramius', $object->bar);
369
        self::assertSame(['Malukenho', 'Danizord'], Assert::readAttribute($object, 'baz'));
370
    }
371
372
    /**
373
     * @group 265
374
     */
375
    public function testWillForwardVariadicByRefArguments() : void
376
    {
377
        $object = (new AccessInterceptorValueHolderFactory())->createProxy(
378
            new ClassWithMethodWithByRefVariadicFunction(),
379
            [
380
                'bar' => static function () : string {
381
                    return 'Foo Baz';
382
                },
383
            ]
384
        );
385
386
        $arguments = ['Ocramius', 'Malukenho', 'Danizord'];
387
388
        self::assertSame(
389
            ['Ocramius', 'changed', 'Danizord'],
390
            (new ClassWithMethodWithByRefVariadicFunction())->tuz(...$arguments),
391
            'Verifying that the implementation of the test asset is correct before proceeding'
392
        );
393
        self::assertSame(['Ocramius', 'changed', 'Danizord'], $object->tuz(...$arguments));
394
        self::assertSame(['Ocramius', 'changed', 'Danizord'], $arguments, 'By-ref arguments were changed');
395
    }
396
397
    /**
398
     * This test documents a known limitation: `func_get_args()` (and similars) don't work in proxied APIs.
399
     * If you manage to make this test pass, then please do send a patch
400
     *
401
     * @group 265
402
     */
403
    public function testWillNotForwardDynamicArguments() : void
404
    {
405
        $object = (new AccessInterceptorValueHolderFactory())->createProxy(new ClassWithDynamicArgumentsMethod());
406
407
        self::assertSame(['a', 'b'], (new ClassWithDynamicArgumentsMethod())->dynamicArgumentsMethod('a', 'b'));
408
409
        $this->expectException(ExpectationFailedException::class);
410
411
        self::assertSame(['a', 'b'], $object->dynamicArgumentsMethod('a', 'b'));
412
    }
413
414
    /**
415
     * Generates a list of object | invoked method | parameters | expected result
416
     *
417
     * @return string[][]|object[][]|mixed[][]
418
     */
419
    public function getProxyMethods() : array
420
    {
421
        $selfHintParam = new ClassWithSelfHint();
422
        $empty         = new EmptyClass();
423
424
        return [
425
            [
426
                new BaseClass(),
427
                'publicMethod',
428
                [],
429
                'publicMethodDefault',
430
            ],
431
            [
432
                new BaseClass(),
433
                'publicTypeHintedMethod',
434
                ['param' => new stdClass()],
435
                'publicTypeHintedMethodDefault',
436
            ],
437
            [
438
                new BaseClass(),
439
                'publicByReferenceMethod',
440
                [],
441
                'publicByReferenceMethodDefault',
442
            ],
443
            [
444
                new BaseClass(),
445
                'publicMethod',
446
                [],
447
                'publicMethodDefault',
448
            ],
449
            [
450
                new ClassWithSelfHint(),
451
                'selfHintMethod',
452
                ['parameter' => $selfHintParam],
453
                $selfHintParam,
454
            ],
455
            [
456
                new ClassWithParentHint(),
457
                'parentHintMethod',
458
                ['parameter' => $empty],
459
                $empty,
460
            ],
461
        ];
462
    }
463
464
    /**
465
     * Generates proxies and instances with a public property to feed to the property accessor methods
466
     *
467
     * @return array<int, array<int, object|AccessInterceptorValueHolderInterface|string>>
0 ignored issues
show
Documentation introduced by
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
468
     */
469
    public function getPropertyAccessProxies() : array
470
    {
471
        $instance1  = new BaseClass();
472
        $instance2  = new BaseClass();
473
        /** @var AccessInterceptorValueHolderInterface $serialized */
474
        $serialized = unserialize(serialize((new AccessInterceptorValueHolderFactory())->createProxy($instance2)));
475
476
        return [
477
            [
478
                $instance1,
479
                (new AccessInterceptorValueHolderFactory())->createProxy($instance1),
480
                'publicProperty',
481
                'publicPropertyDefault',
482
            ],
483
            [
484
                $instance2,
485
                $serialized,
486
                'publicProperty',
487
                'publicPropertyDefault',
488
            ],
489
        ];
490
    }
491
492
    /**
493
     * @group        276
494
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
495
     */
496
    public function testWillInterceptAccessToPropertiesViaFriendClassAccess(
497
        object $callerObject,
498
        object $realInstance,
499
        string $method,
500
        string $expectedValue,
501
        string $propertyName
502
    ) : void {
503
        $proxy    = (new AccessInterceptorValueHolderFactory())->createProxy($realInstance);
504
        $listener = $this->createMock(CallableInterface::class);
505
506
        $listener
507
            ->expects(self::once())
508
            ->method('__invoke')
509
            ->with($proxy, $realInstance, '__get', ['name' => $propertyName]);
510
511
        $proxy->setMethodPrefixInterceptor(
512
            '__get',
513
            static function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) : void {
514
                $listener->__invoke($proxy, $instance, $method, $params, $returnEarly);
0 ignored issues
show
Bug introduced by
The method __invoke() does not seem to exist on object<PHPUnit\Framework\MockObject\MockObject>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
515
            }
516
        );
517
518
        /** @var callable $accessor */
519
        $accessor = [$callerObject, $method];
520
521
        self::assertSame($expectedValue, $accessor($proxy));
522
    }
523
524
    /**
525
     * @group        276
526
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
527
     */
528
    public function testWillInterceptAccessToPropertiesViaFriendClassAccessEvenIfDeSerialized(
529
        object $callerObject,
530
        object $realInstance,
531
        string $method,
532
        string $expectedValue,
533
        string $propertyName
534
    ) : void {
535
        /** @var AccessInterceptorValueHolderInterface $proxy */
536
        $proxy    = unserialize(serialize((new AccessInterceptorValueHolderFactory())->createProxy($realInstance)));
537
        $listener = $this->createMock(CallableInterface::class);
538
539
        $listener
540
            ->expects(self::once())
541
            ->method('__invoke')
542
            ->with($proxy, $realInstance, '__get', ['name' => $propertyName]);
543
544
        $proxy->setMethodPrefixInterceptor(
545
            '__get',
546
            static function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) : void {
547
                $listener->__invoke($proxy, $instance, $method, $params, $returnEarly);
0 ignored issues
show
Bug introduced by
The method __invoke() does not seem to exist on object<PHPUnit\Framework\MockObject\MockObject>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
548
            }
549
        );
550
551
        /** @var callable $accessor */
552
        $accessor = [$callerObject, $method];
553
554
        self::assertSame($expectedValue, $accessor($proxy));
555
    }
556
557
    /**
558
     * @group        276
559
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
560
     */
561
    public function testWillInterceptAccessToPropertiesViaFriendClassAccessEvenIfCloned(
562
        object $callerObject,
563
        object $realInstance,
564
        string $method,
565
        string $expectedValue,
566
        string $propertyName
567
    ) : void {
568
        $proxy = clone (new AccessInterceptorValueHolderFactory())->createProxy($realInstance);
569
570
        $listener = $this->createMock(CallableInterface::class);
571
572
        $listener
573
            ->expects(self::once())
574
            ->method('__invoke')
575
            ->with($proxy, $realInstance, '__get', ['name' => $propertyName]);
576
577
        $proxy->setMethodPrefixInterceptor(
578
            '__get',
579
            static function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) : void {
580
                $listener->__invoke($proxy, $instance, $method, $params, $returnEarly);
0 ignored issues
show
Bug introduced by
The method __invoke() does not seem to exist on object<PHPUnit\Framework\MockObject\MockObject>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
581
            }
582
        );
583
584
        /** @var callable $accessor */
585
        $accessor = [$callerObject, $method];
586
587
        self::assertSame($expectedValue, $accessor($proxy));
588
    }
589
590
    public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope() : Generator
591
    {
592
        foreach ((new ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) {
593
            $property->setAccessible(true);
594
595
            $propertyName  = $property->getName();
596
            $realInstance  = new OtherObjectAccessClass();
597
            $expectedValue = uniqid('', true);
598
599
            $property->setValue($realInstance, $expectedValue);
600
601
            // callee is an actual object
602
            yield OtherObjectAccessClass::class . '#$' . $propertyName => [
603
                new OtherObjectAccessClass(),
604
                $realInstance,
605
                'get' . ucfirst($propertyName),
606
                $expectedValue,
607
                $propertyName,
608
            ];
609
610
            $realInstance  = new OtherObjectAccessClass();
611
            $expectedValue = uniqid('', true);
612
613
            $property->setValue($realInstance, $expectedValue);
614
615
            // callee is a proxy (not to be lazy-loaded!)
616
            yield '(proxy) ' . OtherObjectAccessClass::class . '#$' . $propertyName => [
617
                (new AccessInterceptorValueHolderFactory())->createProxy(new OtherObjectAccessClass()),
618
                $realInstance,
619
                'get' . ucfirst($propertyName),
620
                $expectedValue,
621
                $propertyName,
622
            ];
623
        }
624
    }
625
626
    /**
627
     * @group 327
628
     */
629
    public function testWillInterceptAndReturnEarlyOnVoidMethod() : void
630
    {
631
        $skip      = random_int(100, 200);
632
        $addMore   = random_int(201, 300);
633
        $increment = random_int(301, 400);
634
635
        $object = (new AccessInterceptorValueHolderFactory())->createProxy(
636
            new VoidCounter(),
637
            [
638
                'increment' => static function (
639
                    AccessInterceptorInterface $proxy,
640
                    VoidCounter $instance,
641
                    string $method,
642
                    array $params,
643
                    ?bool & $returnEarly
644
                ) use ($skip) : void {
645
                    if ($skip !== $params['amount']) {
646
                        return;
647
                    }
648
649
                    $returnEarly = true;
650
                },
651
            ],
652
            [
653
                'increment' => static function (
654
                    AccessInterceptorInterface $proxy,
655
                    VoidCounter $instance,
656
                    string $method,
657
                    array $params,
658
                    ?bool & $returnEarly
0 ignored issues
show
Unused Code introduced by
The parameter $returnEarly is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
659
                ) use ($addMore) : void {
660
                    if ($addMore !== $params['amount']) {
661
                        return;
662
                    }
663
664
                    /** @noinspection IncrementDecrementOperationEquivalentInspection */
665
                    $instance->counter += 1;
666
                },
667
            ]
668
        );
669
670
        $object->increment($skip);
671
        self::assertSame(0, $object->counter);
672
673
        $object->increment($increment);
674
        self::assertSame($increment, $object->counter);
675
676
        $object->increment($addMore);
677
        self::assertSame($increment + $addMore + 1, $object->counter);
678
    }
679
680
    /**
681
     * @param mixed $expected
682
     * @param mixed $actual
683
     */
684
    private static function assertByRefVariableValueSame($expected, & $actual) : void
685
    {
686
        self::assertSame($expected, $actual);
687
    }
688
}
689