Completed
Push — master ( bfa4db...ac38d0 )
by Marco
12s
created

testPropertyReadAccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProxyManagerTest\Functional;
6
7
use PHPUnit_Framework_TestCase;
8
use ProxyManager\Factory\AccessInterceptorValueHolderFactory;
9
use ProxyManager\Generator\ClassGenerator;
10
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
11
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
12
use ProxyManager\Proxy\AccessInterceptorValueHolderInterface;
13
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolderGenerator;
14
use ProxyManagerTestAsset\BaseClass;
15
use ProxyManagerTestAsset\BaseInterface;
16
use ProxyManagerTestAsset\ClassWithCounterConstructor;
17
use ProxyManagerTestAsset\ClassWithDynamicArgumentsMethod;
18
use ProxyManagerTestAsset\ClassWithMethodWithByRefVariadicFunction;
19
use ProxyManagerTestAsset\ClassWithMethodWithVariadicFunction;
20
use ProxyManagerTestAsset\ClassWithParentHint;
21
use ProxyManagerTestAsset\ClassWithPublicArrayProperty;
22
use ProxyManagerTestAsset\ClassWithPublicProperties;
23
use ProxyManagerTestAsset\ClassWithSelfHint;
24
use ProxyManagerTestAsset\EmptyClass;
25
use ProxyManagerTestAsset\OtherObjectAccessClass;
26
use ProxyManagerTestAsset\VoidCounter;
27
use ReflectionClass;
28
use stdClass;
29
30
/**
31
 * Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator} produced objects
32
 *
33
 * @author Marco Pivetta <[email protected]>
34
 * @license MIT
35
 *
36
 * @group Functional
37
 * @coversNothing
38
 */
39
class AccessInterceptorValueHolderFunctionalTest extends PHPUnit_Framework_TestCase
40
{
41
    /**
42
     * @dataProvider getProxyMethods
43
     *
44
     * @param string  $className
45
     * @param object  $instance
46
     * @param string  $method
47
     * @param mixed[] $params
48
     * @param mixed   $expectedValue
49
     */
50
    public function testMethodCalls(string $className, $instance, string $method, $params, $expectedValue) : void
51
    {
52
        $proxyName = $this->generateProxy($className);
53
54
        /* @var $proxy \ProxyManager\Proxy\AccessInterceptorValueHolderInterface */
55
        $proxy     = $proxyName::staticProxyConstructor($instance);
56
57
        self::assertSame($instance, $proxy->getWrappedValueHolderValue());
58
        self::assertSame($expectedValue, call_user_func_array([$proxy, $method], $params));
59
60
        /* @var $listener callable|\PHPUnit_Framework_MockObject_MockObject */
61
        $listener = $this->getMockBuilder(stdClass::class)->setMethods(['__invoke'])->getMock();
62
        $listener
63
            ->expects(self::once())
64
            ->method('__invoke')
65
            ->with($proxy, $instance, $method, $params, false);
66
67
        $proxy->setMethodPrefixInterceptor(
68
            $method,
69
            function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) {
70
                $listener($proxy, $instance, $method, $params, $returnEarly);
71
            }
72
        );
73
74
        self::assertSame($expectedValue, call_user_func_array([$proxy, $method], $params));
75
76
        $random = uniqid('', true);
77
78
        $proxy->setMethodPrefixInterceptor(
79
            $method,
80
            function ($proxy, $instance, string $method, $params, & $returnEarly) use ($random) : string {
81
                $returnEarly = true;
82
83
                return $random;
84
            }
85
        );
86
87
        self::assertSame($random, call_user_func_array([$proxy, $method], $params));
88
    }
89
90
    /**
91
     * @dataProvider getProxyMethods
92
     *
93
     * @param string  $className
94
     * @param object  $instance
95
     * @param string  $method
96
     * @param mixed[] $params
97
     * @param mixed   $expectedValue
98
     */
99
    public function testMethodCallsWithSuffixListener(
100
        string $className,
101
        $instance,
102
        string $method,
103
        $params,
104
        $expectedValue
105
    ) : void {
106
        $proxyName = $this->generateProxy($className);
107
108
        /* @var $proxy \ProxyManager\Proxy\AccessInterceptorValueHolderInterface */
109
        $proxy    = $proxyName::staticProxyConstructor($instance);
110
        /* @var $listener callable|\PHPUnit_Framework_MockObject_MockObject */
111
        $listener = $this->getMockBuilder(stdClass::class)->setMethods(['__invoke'])->getMock();
112
        $listener
113
            ->expects(self::once())
114
            ->method('__invoke')
115
            ->with($proxy, $instance, $method, $params, $expectedValue, false);
116
117
        $proxy->setMethodSuffixInterceptor(
118
            $method,
119
            function ($proxy, $instance, $method, $params, $returnValue, & $returnEarly) use ($listener) {
120
                $listener($proxy, $instance, $method, $params, $returnValue, $returnEarly);
121
            }
122
        );
123
124
        self::assertSame($expectedValue, call_user_func_array([$proxy, $method], $params));
125
126
        $random = uniqid();
127
128
        $proxy->setMethodSuffixInterceptor(
129
            $method,
130
            function ($proxy, $instance, string $method, $params, $returnValue, & $returnEarly) use ($random) : string {
131
                $returnEarly = true;
132
133
                return $random;
134
            }
135
        );
136
137
        self::assertSame($random, call_user_func_array([$proxy, $method], $params));
138
    }
139
140
    /**
141
     * @dataProvider getProxyMethods
142
     *
143
     * @param string  $className
144
     * @param object  $instance
145
     * @param string  $method
146
     * @param mixed[] $params
147
     * @param mixed   $expectedValue
148
     */
149
    public function testMethodCallsAfterUnSerialization(
150
        string $className,
151
        $instance,
152
        string $method,
153
        $params,
154
        $expectedValue
155
    ) : void {
156
        $proxyName = $this->generateProxy($className);
157
        /* @var $proxy \ProxyManager\Proxy\AccessInterceptorValueHolderInterface */
158
        $proxy     = unserialize(serialize($proxyName::staticProxyConstructor($instance)));
159
160
        self::assertSame($expectedValue, call_user_func_array([$proxy, $method], $params));
161
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
162
    }
163
164
    /**
165
     * @dataProvider getProxyMethods
166
     *
167
     * @param string  $className
168
     * @param object  $instance
169
     * @param string  $method
170
     * @param mixed[] $params
171
     * @param mixed   $expectedValue
172
     */
173
    public function testMethodCallsAfterCloning(
174
        string $className,
175
        $instance,
176
        string $method,
177
        $params,
178
        $expectedValue
179
    ) : void {
180
        $proxyName = $this->generateProxy($className);
181
182
        /* @var $proxy \ProxyManager\Proxy\AccessInterceptorValueHolderInterface */
183
        $proxy     = $proxyName::staticProxyConstructor($instance);
184
        $cloned    = clone $proxy;
185
186
        self::assertNotSame($proxy->getWrappedValueHolderValue(), $cloned->getWrappedValueHolderValue());
187
        self::assertSame($expectedValue, call_user_func_array([$cloned, $method], $params));
188
        self::assertEquals($instance, $cloned->getWrappedValueHolderValue());
189
    }
190
191
    /**
192
     * @dataProvider getPropertyAccessProxies
193
     *
194
     * @param object                                $instance
195
     * @param AccessInterceptorValueHolderInterface $proxy
196
     * @param string                                $publicProperty
197
     * @param mixed                                 $propertyValue
198
     */
199
    public function testPropertyReadAccess(
200
        $instance,
201
        AccessInterceptorValueHolderInterface $proxy,
202
        string $publicProperty,
203
        $propertyValue
204
    ) : void {
205
        self::assertSame($propertyValue, $proxy->$publicProperty);
206
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
207
    }
208
209
    /**
210
     * @dataProvider getPropertyAccessProxies
211
     *
212
     * @param object                                $instance
213
     * @param AccessInterceptorValueHolderInterface $proxy
214
     * @param string                                $publicProperty
215
     */
216
    public function testPropertyWriteAccess(
217
        $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...
218
        AccessInterceptorValueHolderInterface $proxy,
219
        string $publicProperty
220
    ) : void {
221
        $newValue               = uniqid();
222
        $proxy->$publicProperty = $newValue;
223
224
        self::assertSame($newValue, $proxy->$publicProperty);
225
        self::assertSame($newValue, $proxy->getWrappedValueHolderValue()->$publicProperty);
226
    }
227
228
    /**
229
     * @dataProvider getPropertyAccessProxies
230
     *
231
     * @param object                                $instance
232
     * @param AccessInterceptorValueHolderInterface $proxy
233
     * @param string                                $publicProperty
234
     */
235
    public function testPropertyExistence(
236
        $instance,
237
        AccessInterceptorValueHolderInterface $proxy,
238
        string $publicProperty
239
    ) : void {
240
        self::assertSame(isset($instance->$publicProperty), isset($proxy->$publicProperty));
241
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
242
243
        $proxy->getWrappedValueHolderValue()->$publicProperty = null;
244
        self::assertFalse(isset($proxy->$publicProperty));
245
    }
246
247
    /**
248
     * @dataProvider getPropertyAccessProxies
249
     *
250
     * @param object                                $instance
251
     * @param AccessInterceptorValueHolderInterface $proxy
252
     * @param string                                $publicProperty
253
     */
254
    public function testPropertyUnset(
255
        $instance,
256
        AccessInterceptorValueHolderInterface $proxy,
257
        string $publicProperty
258
    ) : void {
259
        $instance = $proxy->getWrappedValueHolderValue() ?: $instance;
260
        unset($proxy->$publicProperty);
261
262
        self::assertFalse(isset($instance->$publicProperty));
263
        self::assertFalse(isset($proxy->$publicProperty));
264
    }
265
266
    /**
267
     * Verifies that accessing a public property containing an array behaves like in a normal context
268
     */
269
    public function testCanWriteToArrayKeysInPublicProperty() : void
270
    {
271
        $instance    = new ClassWithPublicArrayProperty();
272
        $className   = get_class($instance);
273
        $proxyName   = $this->generateProxy($className);
274
        /* @var $proxy ClassWithPublicArrayProperty */
275
        $proxy       = $proxyName::staticProxyConstructor($instance);
276
277
        $proxy->arrayProperty['foo'] = 'bar';
278
279
        self::assertSame('bar', $proxy->arrayProperty['foo']);
280
281
        $proxy->arrayProperty = ['tab' => 'taz'];
282
283
        self::assertSame(['tab' => 'taz'], $proxy->arrayProperty);
284
    }
285
286
    /**
287
     * Verifies that public properties retrieved via `__get` don't get modified in the object state
288
     */
289
    public function testWillNotModifyRetrievedPublicProperties() : void
290
    {
291
        $instance    = new ClassWithPublicProperties();
292
        $className   = get_class($instance);
293
        $proxyName   = $this->generateProxy($className);
294
        /* @var $proxy ClassWithPublicProperties */
295
        $proxy       = $proxyName::staticProxyConstructor($instance);
296
        $variable    = $proxy->property0;
297
298
        self::assertSame('property0', $variable);
299
300
        $variable = 'foo';
301
302
        self::assertSame('property0', $proxy->property0);
303
        self::assertSame('foo', $variable);
304
    }
305
306
    /**
307
     * Verifies that public properties references retrieved via `__get` modify in the object state
308
     */
309
    public function testWillModifyByRefRetrievedPublicProperties() : void
310
    {
311
        $instance    = new ClassWithPublicProperties();
312
        $className   = get_class($instance);
313
        $proxyName   = $this->generateProxy($className);
314
        /* @var $proxy ClassWithPublicProperties */
315
        $proxy       = $proxyName::staticProxyConstructor($instance);
316
        $variable    = & $proxy->property0;
317
318
        self::assertSame('property0', $variable);
319
320
        $variable = 'foo';
321
322
        self::assertSame('foo', $proxy->property0);
323
        self::assertSame('foo', $variable);
324
    }
325
326
    /**
327
     * @group 115
328
     * @group 175
329
     */
330
    public function testWillBehaveLikeObjectWithNormalConstructor() : void
331
    {
332
        $instance = new ClassWithCounterConstructor(10);
333
334
        self::assertSame(10, $instance->amount, 'Verifying that test asset works as expected');
335
        self::assertSame(10, $instance->getAmount(), 'Verifying that test asset works as expected');
336
        $instance->__construct(3);
337
        self::assertSame(13, $instance->amount, 'Verifying that test asset works as expected');
338
        self::assertSame(13, $instance->getAmount(), 'Verifying that test asset works as expected');
339
340
        $proxyName = $this->generateProxy(get_class($instance));
341
342
        /* @var $proxy ClassWithCounterConstructor */
343
        $proxy = new $proxyName(15);
344
345
        self::assertSame(15, $proxy->amount, 'Verifying that the proxy constructor works as expected');
346
        self::assertSame(15, $proxy->getAmount(), 'Verifying that the proxy constructor works as expected');
347
        $proxy->__construct(5);
348
        self::assertSame(20, $proxy->amount, 'Verifying that the proxy constructor works as expected');
349
        self::assertSame(20, $proxy->getAmount(), 'Verifying that the proxy constructor works as expected');
350
    }
351
352
    public function testWillForwardVariadicArguments() : void
353
    {
354
        $factory       = new AccessInterceptorValueHolderFactory();
355
        $targetObject  = new ClassWithMethodWithVariadicFunction();
356
357
        /* @var $object ClassWithMethodWithVariadicFunction */
358
        $object = $factory->createProxy(
359
            $targetObject,
360
            [
361
                function () : string {
362
                    return 'Foo Baz';
363
                },
364
            ]
365
        );
366
367
        self::assertNull($object->bar);
368
        self::assertNull($object->baz);
369
370
        $object->foo('Ocramius', 'Malukenho', 'Danizord');
371
        self::assertSame('Ocramius', $object->bar);
372
        self::assertSame(['Malukenho', 'Danizord'], $object->baz);
373
    }
374
375
    /**
376
     * @group 265
377
     */
378
    public function testWillForwardVariadicByRefArguments() : void
379
    {
380
        $factory       = new AccessInterceptorValueHolderFactory();
381
        $targetObject  = new ClassWithMethodWithByRefVariadicFunction();
382
383
        /* @var $object ClassWithMethodWithByRefVariadicFunction */
384
        $object = $factory->createProxy(
385
            $targetObject,
386
            [
387
                function () : string {
388
                    return 'Foo Baz';
389
                },
390
            ]
391
        );
392
393
        $arguments = ['Ocramius', 'Malukenho', 'Danizord'];
394
395
        self::assertSame(
396
            ['Ocramius', 'changed', 'Danizord'],
397
            (new ClassWithMethodWithByRefVariadicFunction())->tuz(...$arguments),
398
            'Verifying that the implementation of the test asset is correct before proceeding'
399
        );
400
        self::assertSame(['Ocramius', 'changed', 'Danizord'], $object->tuz(...$arguments));
401
        self::assertSame(['Ocramius', 'changed', 'Danizord'], $arguments, 'By-ref arguments were changed');
402
    }
403
404
    /**
405
     * This test documents a known limitation: `func_get_args()` (and similars) don't work in proxied APIs.
406
     * If you manage to make this test pass, then please do send a patch
407
     *
408
     * @group 265
409
     */
410
    public function testWillNotForwardDynamicArguments() : void
411
    {
412
        $proxyName = $this->generateProxy(ClassWithDynamicArgumentsMethod::class);
413
414
        /* @var $object ClassWithDynamicArgumentsMethod */
415
        $object = $proxyName::staticProxyConstructor(new ClassWithDynamicArgumentsMethod());
416
417
        self::assertSame(['a', 'b'], (new ClassWithDynamicArgumentsMethod())->dynamicArgumentsMethod('a', 'b'));
418
419
        $this->expectException(\PHPUnit_Framework_ExpectationFailedException::class);
420
421
        self::assertSame(['a', 'b'], $object->dynamicArgumentsMethod('a', 'b'));
422
    }
423
424
    /**
425
     * Generates a proxy for the given class name, and retrieves its class name
426
     */
427
    private function generateProxy(string $parentClassName) : string
428
    {
429
        $generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
430
        $generator          = new AccessInterceptorValueHolderGenerator();
431
        $generatedClass     = new ClassGenerator($generatedClassName);
432
        $strategy           = new EvaluatingGeneratorStrategy();
433
434
        $generator->generate(new ReflectionClass($parentClassName), $generatedClass);
435
        $strategy->generate($generatedClass);
436
437
        return $generatedClassName;
438
    }
439
440
    /**
441
     * Generates a list of object | invoked method | parameters | expected result
442
     *
443
     * @return array
444
     */
445
    public function getProxyMethods() : array
446
    {
447
        $selfHintParam = new ClassWithSelfHint();
448
        $empty         = new EmptyClass();
449
450
        return [
451
            [
452
                BaseClass::class,
453
                new BaseClass(),
454
                'publicMethod',
455
                [],
456
                'publicMethodDefault'
457
            ],
458
            [
459
                BaseClass::class,
460
                new BaseClass(),
461
                'publicTypeHintedMethod',
462
                ['param' => new stdClass()],
463
                'publicTypeHintedMethodDefault'
464
            ],
465
            [
466
                BaseClass::class,
467
                new BaseClass(),
468
                'publicByReferenceMethod',
469
                [],
470
                'publicByReferenceMethodDefault'
471
            ],
472
            [
473
                BaseInterface::class,
474
                new BaseClass(),
475
                'publicMethod',
476
                [],
477
                'publicMethodDefault'
478
            ],
479
            [
480
                ClassWithSelfHint::class,
481
                new ClassWithSelfHint(),
482
                'selfHintMethod',
483
                ['parameter' => $selfHintParam],
484
                $selfHintParam
485
            ],
486
            [
487
                ClassWithParentHint::class,
488
                new ClassWithParentHint(),
489
                'parentHintMethod',
490
                ['parameter' => $empty],
491
                $empty
492
            ],
493
        ];
494
    }
495
496
    /**
497
     * Generates proxies and instances with a public property to feed to the property accessor methods
498
     */
499
    public function getPropertyAccessProxies() : array
500
    {
501
        $instance1  = new BaseClass();
502
        $proxyName1 = $this->generateProxy(get_class($instance1));
503
        $instance2  = new BaseClass();
504
        $proxyName2 = $this->generateProxy(get_class($instance2));
505
506
        return [
507
            [
508
                $instance1,
509
                $proxyName1::staticProxyConstructor($instance1),
510
                'publicProperty',
511
                'publicPropertyDefault',
512
            ],
513
            [
514
                $instance2,
515
                unserialize(serialize($proxyName2::staticProxyConstructor($instance2))),
516
                'publicProperty',
517
                'publicPropertyDefault',
518
            ],
519
        ];
520
    }
521
522
    /**
523
     * @group 276
524
     *
525
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
526
     *
527
     * @param object $callerObject
528
     * @param object $realInstance
529
     * @param string $method
530
     * @param string $expectedValue
531
     * @param string $propertyName
532
     */
533
    public function testWillInterceptAccessToPropertiesViaFriendClassAccess(
534
        $callerObject,
535
        $realInstance,
536
        string $method,
537
        string $expectedValue,
538
        string $propertyName
539
    ) : void {
540
        $proxyName = $this->generateProxy(get_class($realInstance));
541
        /* @var $proxy OtherObjectAccessClass|AccessInterceptorValueHolderInterface */
542
        $proxy = $proxyName::staticProxyConstructor($realInstance);
543
544
        /* @var $listener callable|\PHPUnit_Framework_MockObject_MockObject */
545
        $listener = $this->getMockBuilder(stdClass::class)->setMethods(['__invoke'])->getMock();
546
547
        $listener
548
            ->expects(self::once())
549
            ->method('__invoke')
550
            ->with($proxy, $realInstance, '__get', ['name' => $propertyName]);
551
552
        $proxy->setMethodPrefixInterceptor(
553
            '__get',
554
            function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) {
555
                $listener($proxy, $instance, $method, $params, $returnEarly);
556
            }
557
        );
558
559
        /* @var $accessor callable */
560
        $accessor = [$callerObject, $method];
561
562
        self::assertSame($expectedValue, $accessor($proxy));
563
    }
564
565
    /**
566
     * @group 276
567
     *
568
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
569
     *
570
     * @param object $callerObject
571
     * @param object $realInstance
572
     * @param string $method
573
     * @param string $expectedValue
574
     * @param string $propertyName
575
     */
576
    public function testWillInterceptAccessToPropertiesViaFriendClassAccessEvenIfDeSerialized(
577
        $callerObject,
578
        $realInstance,
579
        string $method,
580
        string $expectedValue,
581
        string $propertyName
582
    ) : void {
583
        $proxyName = $this->generateProxy(get_class($realInstance));
584
        /* @var $proxy OtherObjectAccessClass|AccessInterceptorValueHolderInterface */
585
        $proxy = unserialize(serialize($proxyName::staticProxyConstructor($realInstance)));
586
587
        /* @var $listener callable|\PHPUnit_Framework_MockObject_MockObject */
588
        $listener = $this->getMockBuilder(stdClass::class)->setMethods(['__invoke'])->getMock();
589
590
        $listener
591
            ->expects(self::once())
592
            ->method('__invoke')
593
            ->with($proxy, $realInstance, '__get', ['name' => $propertyName]);
594
595
        $proxy->setMethodPrefixInterceptor(
596
            '__get',
597
            function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) {
598
                $listener($proxy, $instance, $method, $params, $returnEarly);
599
            }
600
        );
601
602
        /* @var $accessor callable */
603
        $accessor = [$callerObject, $method];
604
605
        self::assertSame($expectedValue, $accessor($proxy));
606
    }
607
608
609
    /**
610
     * @group 276
611
     *
612
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
613
     *
614
     * @param object $callerObject
615
     * @param object $realInstance
616
     * @param string $method
617
     * @param string $expectedValue
618
     * @param string $propertyName
619
     */
620
    public function testWillInterceptAccessToPropertiesViaFriendClassAccessEvenIfCloned(
621
        $callerObject,
622
        $realInstance,
623
        string $method,
624
        string $expectedValue,
625
        string $propertyName
626
    ) : void {
627
        $proxyName = $this->generateProxy(get_class($realInstance));
628
        /* @var $proxy OtherObjectAccessClass|AccessInterceptorValueHolderInterface */
629
        $proxy = clone $proxyName::staticProxyConstructor($realInstance);
630
631
        /* @var $listener callable|\PHPUnit_Framework_MockObject_MockObject */
632
        $listener = $this->getMockBuilder(stdClass::class)->setMethods(['__invoke'])->getMock();
633
634
        $listener
635
            ->expects(self::once())
636
            ->method('__invoke')
637
            ->with($proxy, $realInstance, '__get', ['name' => $propertyName]);
638
639
        $proxy->setMethodPrefixInterceptor(
640
            '__get',
641
            function ($proxy, $instance, $method, $params, & $returnEarly) use ($listener) {
642
                $listener($proxy, $instance, $method, $params, $returnEarly);
643
            }
644
        );
645
646
        /* @var $accessor callable */
647
        $accessor = [$callerObject, $method];
648
649
        self::assertSame($expectedValue, $accessor($proxy));
650
    }
651
652
    public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope() : \Generator
653
    {
654
        $proxyClass = $this->generateProxy(OtherObjectAccessClass::class);
655
656
        foreach ((new \ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) {
657
            $property->setAccessible(true);
658
659
            $propertyName  = $property->getName();
660
            $realInstance  = new OtherObjectAccessClass();
661
            $expectedValue = uniqid('', true);
662
663
            $property->setValue($realInstance, $expectedValue);
664
665
            // callee is an actual object
666
            yield OtherObjectAccessClass::class . '#$' . $propertyName => [
667
                new OtherObjectAccessClass(),
668
                $realInstance,
669
                'get' . ucfirst($propertyName),
670
                $expectedValue,
671
                $propertyName,
672
            ];
673
674
            $realInstance  = new OtherObjectAccessClass();
675
            $expectedValue = uniqid('', true);
676
677
            $property->setValue($realInstance, $expectedValue);
678
679
            // callee is a proxy (not to be lazy-loaded!)
680
            yield '(proxy) ' . OtherObjectAccessClass::class . '#$' . $propertyName => [
681
                $proxyClass::staticProxyConstructor(new OtherObjectAccessClass()),
682
                $realInstance,
683
                'get' . ucfirst($propertyName),
684
                $expectedValue,
685
                $propertyName,
686
            ];
687
        }
688
    }
689
690
    /**
691
     * @group 327
692
     */
693
    public function testWillInterceptAndReturnEarlyOnVoidMethod() : void
694
    {
695
        $skip      = random_int(100, 200);
696
        $addMore   = random_int(201, 300);
697
        $increment = random_int(301, 400);
698
699
        $proxyName = $this->generateProxy(VoidCounter::class);
700
701
        /* @var $object VoidCounter */
702
        $object = $proxyName::staticProxyConstructor(
703
            new VoidCounter(),
704
            [
705
                'increment' => function (
706
                    VoidCounter $proxy,
707
                    VoidCounter $instance,
708
                    string $method,
709
                    array $params,
710
                    ?bool & $returnEarly
711
                ) use ($skip) : void {
712
                    if ($skip === $params['amount']) {
713
                        $returnEarly = true;
714
                    }
715
                },
716
            ],
717
            [
718
                'increment' => function (
719
                    VoidCounter $proxy,
720
                    VoidCounter $instance,
721
                    string $method,
722
                    array $params,
723
                    ?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...
724
                ) use ($addMore) : void {
725
                    if ($addMore === $params['amount']) {
726
                        $instance->counter += 1;
727
                    }
728
                },
729
            ]
730
        );
731
732
        $object->increment($skip);
733
        self::assertSame(0, $object->counter);
734
735
        $object->increment($increment);
736
        self::assertSame($increment, $object->counter);
737
738
        $object->increment($addMore);
739
        self::assertSame($increment + $addMore + 1, $object->counter);
740
    }
741
}
742