Completed
Pull Request — master (#345)
by Jefersson
04:59
created

getProxyMethods()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 91
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 91
rs 8.518
c 0
b 0
f 0
cc 1
eloc 60
nc 1
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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