Completed
Push — master ( af4b60...a13948 )
by Jefersson
11s
created

testWillExecuteLogicInAVoidMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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