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 Closure;
8
use Generator;
9
use PHPUnit\Framework\ExpectationFailedException;
10
use PHPUnit\Framework\MockObject\MockObject as Mock;
11
use PHPUnit\Framework\TestCase;
12
use ProxyManager\Factory\LazyLoadingValueHolderFactory;
13
use ProxyManager\Proxy\LazyLoadingInterface;
14
use ProxyManager\Proxy\VirtualProxyInterface;
15
use ProxyManagerTestAsset\BaseClass;
16
use ProxyManagerTestAsset\BaseInterface;
17
use ProxyManagerTestAsset\CallableInterface;
18
use ProxyManagerTestAsset\ClassWithCounterConstructor;
19
use ProxyManagerTestAsset\ClassWithDynamicArgumentsMethod;
20
use ProxyManagerTestAsset\ClassWithMagicMethods;
21
use ProxyManagerTestAsset\ClassWithMethodWithByRefVariadicFunction;
22
use ProxyManagerTestAsset\ClassWithMethodWithVariadicFunction;
23
use ProxyManagerTestAsset\ClassWithParentHint;
24
use ProxyManagerTestAsset\ClassWithPublicArrayProperty;
25
use ProxyManagerTestAsset\ClassWithPublicProperties;
26
use ProxyManagerTestAsset\ClassWithSelfHint;
27
use ProxyManagerTestAsset\EmptyClass;
28
use ProxyManagerTestAsset\OtherObjectAccessClass;
29
use ProxyManagerTestAsset\VoidCounter;
30
use ReflectionClass;
31
use ReflectionProperty;
32
use stdClass;
33
use function array_values;
34
use function get_class;
35
use function random_int;
36
use function serialize;
37
use function ucfirst;
38
use function uniqid;
39
use function unserialize;
40
41
/**
42
 * Tests for {@see \ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator} produced objects
43
 *
44
 * @group Functional
45
 * @coversNothing
46
 */
47
final class LazyLoadingValueHolderFunctionalTest extends TestCase
48
{
49
    /**
50
     * @param mixed[] $params
51
     * @param mixed   $expectedValue
52
     *
53
     * @dataProvider getProxyMethods
54
     *
55
     * @psalm-template OriginalClass
56
     * @psalm-param class-string<OriginalClass> $className
57
     * @psalm-param OriginalClass $instance
58
     */
59
    public function testMethodCalls(string $className, object $instance, string $method, array $params, $expectedValue) : void
60
    {
61
        $proxy = (new LazyLoadingValueHolderFactory())->createProxy(
62
            $className,
63
            $this->createInitializer($className, $instance)
64
        );
65
66
        self::assertFalse($proxy->isProxyInitialized());
67
68
        /** @var callable $callProxyMethod */
69
        $callProxyMethod = [$proxy, $method];
70
        $parameterValues = array_values($params);
71
72
        self::assertSame($expectedValue, $callProxyMethod(...$parameterValues));
73
        self::assertTrue($proxy->isProxyInitialized());
74
        self::assertSame($instance, $proxy->getWrappedValueHolderValue());
75
    }
76
77
    /**
78
     * @param mixed[] $params
79
     * @param mixed   $expectedValue
80
     *
81
     * @dataProvider getProxyMethods
82
     *
83
     * @psalm-template OriginalClass
84
     * @psalm-param class-string<OriginalClass> $className
85
     * @psalm-param OriginalClass $instance
86
     */
87
    public function testMethodCallsAfterUnSerialization(
88
        string $className,
89
        object $instance,
90
        string $method,
91
        array $params,
92
        $expectedValue
93
    ) : void {
94
        /** @var VirtualProxyInterface $proxy */
95
        $proxy = unserialize(serialize((new LazyLoadingValueHolderFactory())->createProxy(
96
            $className,
97
            $this->createInitializer($className, $instance)
98
        )));
99
100
        self::assertTrue($proxy->isProxyInitialized());
101
102
        $callProxyMethod = [$proxy, $method];
103
        $parameterValues = array_values($params);
104
105
        self::assertIsCallable($callProxyMethod);
106
107
        self::assertSame($expectedValue, $callProxyMethod(...$parameterValues));
108
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
109
    }
110
111
    /**
112
     * @param mixed[] $params
113
     * @param mixed   $expectedValue
114
     *
115
     * @dataProvider getProxyMethods
116
     *
117
     * @psalm-template OriginalClass
118
     * @psalm-param class-string<OriginalClass> $className
119
     * @psalm-param OriginalClass $instance
120
     */
121
    public function testMethodCallsAfterCloning(
122
        string $className,
123
        object $instance,
124
        string $method,
125
        array $params,
126
        $expectedValue
127
    ) : void {
128
        $proxy = (new LazyLoadingValueHolderFactory())->createProxy(
129
            $className,
130
            $this->createInitializer($className, $instance)
131
        );
132
        $cloned = clone $proxy;
133
134
        self::assertTrue($cloned->isProxyInitialized());
135
        self::assertNotSame($proxy->getWrappedValueHolderValue(), $cloned->getWrappedValueHolderValue());
136
137
        $callProxyMethod = [$cloned, $method];
138
        $parameterValues = array_values($params);
139
140
        self::assertIsCallable($callProxyMethod);
141
142
        self::assertSame($expectedValue, $callProxyMethod(...$parameterValues));
143
        self::assertEquals($instance, $cloned->getWrappedValueHolderValue());
144
    }
145
146
    /**
147
     * @param mixed $propertyValue
148
     *
149
     * @dataProvider getPropertyAccessProxies
150
     */
151
    public function testPropertyReadAccess(
152
        object $instance,
153
        VirtualProxyInterface $proxy,
154
        string $publicProperty,
155
        $propertyValue
156
    ) : void {
157
        self::assertSame($propertyValue, $proxy->$publicProperty);
158
        self::assertTrue($proxy->isProxyInitialized());
159
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
160
    }
161
162
    /**
163
     * @dataProvider getPropertyAccessProxies
164
     */
165
    public function testPropertyWriteAccess(object $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...
166
    {
167
        $newValue               = uniqid('', true);
168
        $proxy->$publicProperty = $newValue;
169
170
        self::assertTrue($proxy->isProxyInitialized());
171
        self::assertSame($newValue, $proxy->$publicProperty);
172
173
        $wrappedValue = $proxy->getWrappedValueHolderValue();
174
175
        self::assertNotNull($wrappedValue);
176
177
        self::assertSame($newValue, $wrappedValue->$publicProperty);
178
    }
179
180
    /**
181
     * @dataProvider getPropertyAccessProxies
182
     */
183
    public function testPropertyExistence(object $instance, VirtualProxyInterface $proxy, string $publicProperty) : void
184
    {
185
        self::assertSame(isset($instance->$publicProperty), isset($proxy->$publicProperty));
186
        self::assertTrue($proxy->isProxyInitialized());
187
        self::assertEquals($instance, $proxy->getWrappedValueHolderValue());
188
    }
189
190
    /**
191
     * @dataProvider getPropertyAccessProxies
192
     */
193
    public function testPropertyAbsence(object $instance, VirtualProxyInterface $proxy, string $publicProperty) : void
194
    {
195
        $instance                  = $proxy->getWrappedValueHolderValue() ?: $instance;
196
        $instance->$publicProperty = null;
197
        self::assertFalse(isset($proxy->$publicProperty));
198
        self::assertTrue($proxy->isProxyInitialized());
199
    }
200
201
    /**
202
     * @dataProvider getPropertyAccessProxies
203
     */
204
    public function testPropertyUnset(object $instance, VirtualProxyInterface $proxy, string $publicProperty) : void
205
    {
206
        $instance = $proxy->getWrappedValueHolderValue() ?: $instance;
207
        unset($proxy->$publicProperty);
208
209
        self::assertTrue($proxy->isProxyInitialized());
210
211
        self::assertFalse(isset($instance->$publicProperty));
212
        self::assertFalse(isset($proxy->$publicProperty));
213
    }
214
215
    /**
216
     * Verifies that accessing a public property containing an array behaves like in a normal context
217
     */
218
    public function testCanWriteToArrayKeysInPublicProperty() : void
219
    {
220
        $proxy = (new LazyLoadingValueHolderFactory())->createProxy(
221
            ClassWithPublicArrayProperty::class,
222
            $this->createInitializer(ClassWithPublicArrayProperty::class, new ClassWithPublicArrayProperty())
0 ignored issues
show
Documentation introduced by
new \ProxyManagerTestAss...thPublicArrayProperty() is of type object<ProxyManagerTestA...ithPublicArrayProperty>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
223
        );
224
225
        $proxy->arrayProperty['foo'] = 'bar';
226
227
        self::assertByRefVariableValueSame('bar', $proxy->arrayProperty['foo']);
228
229
        $proxy->arrayProperty = ['tab' => 'taz'];
230
231
        self::assertByRefVariableValueSame(['tab' => 'taz'], $proxy->arrayProperty);
232
    }
233
234
    /**
235
     * Verifies that public properties retrieved via `__get` don't get modified in the object itself
236
     */
237
    public function testWillNotModifyRetrievedPublicProperties() : void
238
    {
239
        $proxy = (new LazyLoadingValueHolderFactory())->createProxy(
240
            ClassWithPublicProperties::class,
241
            $this->createInitializer(ClassWithPublicProperties::class, new ClassWithPublicProperties())
0 ignored issues
show
Documentation introduced by
new \ProxyManagerTestAss...sWithPublicProperties() is of type object<ProxyManagerTestA...ssWithPublicProperties>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
242
        );
243
        $variable = $proxy->property0;
244
245
        self::assertByRefVariableValueSame('property0', $variable);
246
247
        $variable = 'foo';
248
249
        self::assertByRefVariableValueSame('property0', $proxy->property0);
250
        self::assertByRefVariableValueSame('foo', $variable);
251
    }
252
253
    /**
254
     * Verifies that public properties references retrieved via `__get` modify in the object state
255
     */
256
    public function testWillModifyByRefRetrievedPublicProperties() : void
257
    {
258
        $proxy = (new LazyLoadingValueHolderFactory())->createProxy(
259
            ClassWithPublicProperties::class,
260
            $this->createInitializer(ClassWithPublicProperties::class, new ClassWithPublicProperties())
0 ignored issues
show
Documentation introduced by
new \ProxyManagerTestAss...sWithPublicProperties() is of type object<ProxyManagerTestA...ssWithPublicProperties>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
261
        );
262
        $variable = & $proxy->property0;
263
264
        self::assertByRefVariableValueSame('property0', $variable);
265
266
        $variable = 'foo';
267
268
        self::assertByRefVariableValueSame('foo', $proxy->property0);
269
        self::assertByRefVariableValueSame('foo', $variable);
270
    }
271
272
    /**
273
     * @group 16
274
     *
275
     * Verifies that initialization of a value holder proxy may happen multiple times
276
     */
277
    public function testWillAllowMultipleProxyInitialization() : void
278
    {
279
        $counter    = 0;
280
281
        $proxy = (new LazyLoadingValueHolderFactory())->createProxy(
282
            BaseClass::class,
283
            static function (?object & $wrappedInstance) use (& $counter) : bool {
284
                $wrappedInstance = new BaseClass();
285
286
                /** @var int $counter */
287
                $wrappedInstance->publicProperty = (string) ($counter += 1);
288
289
                return true;
290
            }
291
        );
292
293
        self::assertSame('1', $proxy->publicProperty);
294
        self::assertSame('2', $proxy->publicProperty);
295
        self::assertSame('3', $proxy->publicProperty);
296
    }
297
298
    /**
299
     * @group 115
300
     * @group 175
301
     */
302
    public function testWillBehaveLikeObjectWithNormalConstructor() : void
303
    {
304
        $instance = new ClassWithCounterConstructor(10);
305
306
        self::assertSame(10, $instance->amount, 'Verifying that test asset works as expected');
307
        self::assertSame(10, $instance->getAmount(), 'Verifying that test asset works as expected');
308
        $instance->__construct(3);
309
        self::assertSame(13, $instance->amount, 'Verifying that test asset works as expected');
310
        self::assertSame(13, $instance->getAmount(), 'Verifying that test asset works as expected');
311
312
        $proxyName = get_class(
313
            (new LazyLoadingValueHolderFactory())
314
                ->createProxy(
315
                    ClassWithCounterConstructor::class,
316
                    static function () : bool {
317
                        return true;
318
                    }
319
                )
320
        );
321
322
        $proxy = new $proxyName(15);
323
324
        self::assertSame(15, $proxy->amount, 'Verifying that the proxy constructor works as expected');
325
        self::assertSame(15, $proxy->getAmount(), 'Verifying that the proxy constructor works as expected');
326
        $proxy->__construct(5);
327
        self::assertSame(20, $proxy->amount, 'Verifying that the proxy constructor works as expected');
328
        self::assertSame(20, $proxy->getAmount(), 'Verifying that the proxy constructor works as expected');
329
    }
330
331
    /**
332
     * @group 265
333
     */
334
    public function testWillForwardVariadicByRefArguments() : void
335
    {
336
        $object = (new LazyLoadingValueHolderFactory())->createProxy(
337
            ClassWithMethodWithByRefVariadicFunction::class,
338
            static function (?object & $wrappedInstance) : bool {
339
                $wrappedInstance = new ClassWithMethodWithByRefVariadicFunction();
340
341
                return true;
342
            }
343
        );
344
345
        $parameters = ['a', 'b', 'c'];
346
347
        // first, testing normal variadic behavior (verifying we didn't screw up in the test asset)
348
        self::assertSame(['a', 'changed', 'c'], (new ClassWithMethodWithByRefVariadicFunction())->tuz(...$parameters));
349
        self::assertSame(['a', 'changed', 'c'], $object->tuz(...$parameters));
350
        self::assertSame(['a', 'changed', 'c'], $parameters, 'by-ref variadic parameter was changed');
351
    }
352
353
    /**
354
     * This test documents a known limitation: `func_get_args()` (and similars) don't work in proxied APIs.
355
     * If you manage to make this test pass, then please do send a patch
356
     *
357
     * @group 265
358
     */
359
    public function testWillNotForwardDynamicArguments() : void
360
    {
361
        $object = (new LazyLoadingValueHolderFactory())->createProxy(
362
            ClassWithDynamicArgumentsMethod::class,
363
            static function (?object & $wrappedInstance) : bool {
364
                $wrappedInstance = new ClassWithDynamicArgumentsMethod();
365
366
                return true;
367
            }
368
        );
369
370
        self::assertSame(['a', 'b'], (new ClassWithDynamicArgumentsMethod())->dynamicArgumentsMethod('a', 'b'));
371
372
        $this->expectException(ExpectationFailedException::class);
373
374
        self::assertSame(['a', 'b'], $object->dynamicArgumentsMethod('a', 'b'));
375
    }
376
377
    /**
378
     * @psalm-param (CallableInterface&Mock)|null $initializerMatcher
379
     *
380
     * @return Closure(
0 ignored issues
show
Documentation introduced by
The doc-type Closure( could not be parsed: Expected "|" or "end of type", but got "(" at position 7. (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...
381
     *  object|null,
382
     *  VirtualProxyInterface,
383
     *  string,
384
     *  array,
385
     *  ?Closure
386
     * ) : bool
387
     */
388
    private function createInitializer(string $className, object $realInstance, ?Mock $initializerMatcher = null) : Closure
389
    {
390
        if (! $initializerMatcher) {
391
            $initializerMatcher = $this->createMock(CallableInterface::class);
392
393
            $initializerMatcher
394
                ->expects(self::once())
395
                ->method('__invoke')
396
                ->with(
397
                    self::logicalAnd(
398
                        self::isInstanceOf(VirtualProxyInterface::class),
399
                        self::isInstanceOf($className)
400
                    ),
401
                    $realInstance
402
                );
403
        }
404
405
        return static function (
406
            ?object & $wrappedObject,
407
            VirtualProxyInterface $proxy,
408
            string $method,
409
            array $params,
410
            ?Closure & $initializer
411
        ) use (
412
            $initializerMatcher,
413
            $realInstance
414
        ) : bool {
415
            $initializer = null;
416
417
            $wrappedObject = $realInstance;
418
419
            $initializerMatcher->__invoke($proxy, $wrappedObject, $method, $params);
420
421
            return true;
422
        };
423
    }
424
425
    /**
426
     * Generates a list of object, invoked method, parameters, expected result
427
     *
428
     * @return string[][]|object[][]|bool[][]|mixed[][][]
429
     */
430
    public function getProxyMethods() : array
431
    {
432
        $selfHintParam = new ClassWithSelfHint();
433
        $empty         = new EmptyClass();
434
435
        return [
436
            [
437
                BaseClass::class,
438
                new BaseClass(),
439
                'publicMethod',
440
                [],
441
                'publicMethodDefault',
442
            ],
443
            [
444
                BaseClass::class,
445
                new BaseClass(),
446
                'publicTypeHintedMethod',
447
                [new stdClass()],
448
                'publicTypeHintedMethodDefault',
449
            ],
450
            [
451
                BaseClass::class,
452
                new BaseClass(),
453
                'publicByReferenceMethod',
454
                [],
455
                'publicByReferenceMethodDefault',
456
            ],
457
            [
458
                BaseInterface::class,
459
                new BaseClass(),
460
                'publicMethod',
461
                [],
462
                'publicMethodDefault',
463
            ],
464
            [
465
                ClassWithSelfHint::class,
466
                new ClassWithSelfHint(),
467
                'selfHintMethod',
468
                ['parameter' => $selfHintParam],
469
                $selfHintParam,
470
            ],
471
            [
472
                ClassWithParentHint::class,
473
                new ClassWithParentHint(),
474
                'parentHintMethod',
475
                ['parameter' => $empty],
476
                $empty,
477
            ],
478
            [
479
                ClassWithMethodWithVariadicFunction::class,
480
                new ClassWithMethodWithVariadicFunction(),
481
                'buz',
482
                ['Ocramius', 'Malukenho'],
483
                ['Ocramius', 'Malukenho'],
484
            ],
485
            [
486
                ClassWithMethodWithByRefVariadicFunction::class,
487
                new ClassWithMethodWithByRefVariadicFunction(),
488
                'tuz',
489
                ['Ocramius', 'Malukenho'],
490
                ['Ocramius', 'changed'],
491
            ],
492
            [
493
                ClassWithMagicMethods::class,
494
                new ClassWithMagicMethods(),
495
                '__get',
496
                ['parameterName'],
497
                'parameterName',
498
            ],
499
            [
500
                ClassWithMagicMethods::class,
501
                new ClassWithMagicMethods(),
502
                '__set',
503
                ['foo', 'bar'],
504
                ['foo' => 'bar'],
505
            ],
506
            [
507
                ClassWithMagicMethods::class,
508
                new ClassWithMagicMethods(),
509
                '__isset',
510
                ['example'],
511
                true,
512
            ],
513
            [
514
                ClassWithMagicMethods::class,
515
                new ClassWithMagicMethods(),
516
                '__isset',
517
                [''],
518
                false,
519
            ],
520
            [
521
                ClassWithMagicMethods::class,
522
                new ClassWithMagicMethods(),
523
                '__unset',
524
                ['example'],
525
                true,
526
            ],
527
        ];
528
    }
529
530
    /**
531
     * Generates proxies and instances with a public property to feed to the property accessor methods
532
     *
533
     * @return array<int, array<int, object|VirtualProxyInterface|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...
534
     */
535
    public function getPropertyAccessProxies() : array
536
    {
537
        $instance1  = new BaseClass();
538
        $instance2  = new BaseClass();
539
        $factory    = new LazyLoadingValueHolderFactory();
540
        /** @var VirtualProxyInterface $serialized */
541
        $serialized = unserialize(serialize($factory->createProxy(
542
            BaseClass::class,
543
            $this->createInitializer(BaseClass::class, $instance2)
0 ignored issues
show
Documentation introduced by
$instance2 is of type object<ProxyManagerTestAsset\BaseClass>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
544
        )));
545
546
        return [
547
            [
548
                $instance1,
549
                $factory->createProxy(
550
                    BaseClass::class,
551
                    $this->createInitializer(BaseClass::class, $instance1)
0 ignored issues
show
Documentation introduced by
$instance1 is of type object<ProxyManagerTestAsset\BaseClass>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
552
                ),
553
                'publicProperty',
554
                'publicPropertyDefault',
555
            ],
556
            [
557
                $instance2,
558
                $serialized,
559
                'publicProperty',
560
                'publicPropertyDefault',
561
            ],
562
        ];
563
    }
564
565
    /**
566
     * @group 276
567
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
568
     */
569
    public function testWillLazyLoadMembersOfOtherProxiesWithTheSamePrivateScope(
570
        object $callerObject,
571
        object $realInstance,
572
        string $method,
573
        string $expectedValue
574
    ) : void {
575
        $className = get_class($realInstance);
576
        $proxy     = (new LazyLoadingValueHolderFactory())->createProxy(
577
            $className,
578
            $this->createInitializer($className, $realInstance)
579
        );
580
581
        $accessor = [$callerObject, $method];
582
583
        self::assertIsCallable($accessor);
584
        self::assertFalse($proxy->isProxyInitialized());
585
        self::assertSame($expectedValue, $accessor($proxy));
586
        self::assertTrue($proxy->isProxyInitialized());
587
    }
588
589
    /**
590
     * @group 276
591
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
592
     */
593
    public function testWillFetchMembersOfOtherDeSerializedProxiesWithTheSamePrivateScope(
594
        object $callerObject,
595
        object $realInstance,
596
        string $method,
597
        string $expectedValue
598
    ) : void {
599
        $className = get_class($realInstance);
600
        /** @var LazyLoadingInterface $proxy */
601
        $proxy     = unserialize(serialize((new LazyLoadingValueHolderFactory())->createProxy(
602
            $className,
603
            $this->createInitializer($className, $realInstance)
604
        )));
605
606
        /** @var callable $accessor */
607
        $accessor = [$callerObject, $method];
608
609
        self::assertTrue($proxy->isProxyInitialized());
610
        self::assertSame($expectedValue, $accessor($proxy));
611
    }
612
613
    /**
614
     * @group 276
615
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
616
     */
617
    public function testWillFetchMembersOfOtherClonedProxiesWithTheSamePrivateScope(
618
        object $callerObject,
619
        object $realInstance,
620
        string $method,
621
        string $expectedValue
622
    ) : void {
623
        $className = get_class($realInstance);
624
        $proxy     = clone (new LazyLoadingValueHolderFactory())->createProxy(
625
            $className,
626
            $this->createInitializer($className, $realInstance)
627
        );
628
629
        /** @var callable $accessor */
630
        $accessor = [$callerObject, $method];
631
632
        self::assertTrue($proxy->isProxyInitialized());
633
        self::assertSame($expectedValue, $accessor($proxy));
634
    }
635
636
    /**
637
     * @group 327
638
     */
639
    public function testWillExecuteLogicInAVoidMethod() : void
640
    {
641
        $proxy = (new LazyLoadingValueHolderFactory())->createProxy(
642
            VoidCounter::class,
643
            $this->createInitializer(VoidCounter::class, new VoidCounter())
0 ignored issues
show
Documentation introduced by
new \ProxyManagerTestAsset\VoidCounter() is of type object<ProxyManagerTestAsset\VoidCounter>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
644
        );
645
646
        $increment = random_int(100, 1000);
647
648
        $proxy->increment($increment);
649
650
        self::assertSame($increment, $proxy->counter);
651
    }
652
653
    public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope() : Generator
654
    {
655
        foreach ((new ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) {
656
            $propertyName  = $property->getName();
657
            $expectedValue = uniqid('', true);
658
659
            // callee is an actual object
660
            yield OtherObjectAccessClass::class . '#$' . $propertyName => [
661
                new OtherObjectAccessClass(),
662
                $this->buildInstanceWithValues(new OtherObjectAccessClass(), [$propertyName => $expectedValue]),
0 ignored issues
show
Documentation introduced by
new \ProxyManagerTestAss...therObjectAccessClass() is of type object<ProxyManagerTestA...OtherObjectAccessClass>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
663
                'get' . ucfirst($propertyName),
664
                $expectedValue,
665
            ];
666
667
            $expectedValue = uniqid('', true);
668
669
            // callee is a proxy (not to be lazy-loaded!)
670
            yield '(proxy) ' . OtherObjectAccessClass::class . '#$' . $propertyName => [
671
                (new LazyLoadingValueHolderFactory())->createProxy(
672
                    OtherObjectAccessClass::class,
673
                    $this->createInitializer(
674
                        OtherObjectAccessClass::class,
675
                        new OtherObjectAccessClass()
0 ignored issues
show
Documentation introduced by
new \ProxyManagerTestAss...therObjectAccessClass() is of type object<ProxyManagerTestA...OtherObjectAccessClass>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
676
                    )
677
                ),
678
                $this->buildInstanceWithValues(new OtherObjectAccessClass(), [$propertyName => $expectedValue]),
0 ignored issues
show
Documentation introduced by
new \ProxyManagerTestAss...therObjectAccessClass() is of type object<ProxyManagerTestA...OtherObjectAccessClass>, but the function expects a object<ProxyManagerTest\Functional\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
679
                'get' . ucfirst($propertyName),
680
                $expectedValue,
681
            ];
682
        }
683
    }
684
685
    /** @param array<string, string> $values */
686
    private function buildInstanceWithValues(object $instance, array $values) : object
687
    {
688
        foreach ($values as $property => $value) {
689
            $property = new ReflectionProperty($instance, $property);
690
691
            $property->setAccessible(true);
692
693
            $property->setValue($instance, $value);
694
        }
695
696
        return $instance;
697
    }
698
699
    /**
700
     * @param mixed $expected
701
     * @param mixed $actual
702
     */
703
    private static function assertByRefVariableValueSame($expected, & $actual) : void
704
    {
705
        self::assertSame($expected, $actual);
706
    }
707
}
708