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