Completed
Pull Request — master (#277)
by Marco
04:04 queued 17s
created

testWillInterceptAccessToPropertiesViaFriendClassAccess()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 26
rs 8.8571
cc 1
eloc 16
nc 1
nop 5
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
namespace ProxyManagerTest\Functional;
20
21
use PHPUnit_Framework_TestCase;
22
use ProxyManager\Factory\RemoteObject\Adapter\JsonRpc as JsonRpcAdapter;
23
use ProxyManager\Factory\RemoteObject\Adapter\XmlRpc as XmlRpcAdapter;
24
use ProxyManager\Factory\RemoteObject\AdapterInterface;
25
use ProxyManager\Generator\ClassGenerator;
26
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
27
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
28
use ProxyManager\Proxy\RemoteObjectInterface;
29
use ProxyManager\ProxyGenerator\RemoteObjectGenerator;
30
use ProxyManagerTestAsset\ClassWithSelfHint;
31
use ProxyManagerTestAsset\OtherObjectAccessClass;
32
use ProxyManagerTestAsset\RemoteProxy\Foo;
33
use ProxyManagerTestAsset\RemoteProxy\FooServiceInterface;
34
use ReflectionClass;
35
use Zend\Server\Client;
36
37
/**
38
 * Tests for {@see \ProxyManager\ProxyGenerator\RemoteObjectGenerator} produced objects
39
 *
40
 * @author Vincent Blanchon <[email protected]>
41
 * @license MIT
42
 *
43
 * @group Functional
44
 * @coversNothing
45
 */
46
class RemoteObjectFunctionalTest extends PHPUnit_Framework_TestCase
47
{
48
    /**
49
     * @param mixed  $expectedValue
50
     * @param string $method
51
     * @param array  $params
52
     *
53
     * @return XmlRpcAdapter
54
     */
55
    protected function getXmlRpcAdapter($expectedValue, $method, array $params)
56
    {
57
        /* @var $client Client|\PHPUnit_Framework_MockObject_MockObject */
58
        $client = $this->getMock(Client::class, ['call']);
59
60
        $client
61
            ->expects($this->any())
62
            ->method('call')
63
            ->with($this->stringEndsWith($method), $params)
64
            ->will($this->returnValue($expectedValue));
65
66
        $adapter = new XmlRpcAdapter(
67
            $client,
68
            [
69
                 'ProxyManagerTestAsset\RemoteProxy\Foo.foo'
70
                     => 'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface.foo'
71
            ]
72
        );
73
74
        return $adapter;
75
    }
76
77
    /**
78
     * @param mixed  $expectedValue
79
     * @param string $method
80
     * @param array  $params
81
     *
82
     * @return JsonRpcAdapter
83
     */
84
    protected function getJsonRpcAdapter($expectedValue, $method, array $params)
85
    {
86
        /* @var $client Client|\PHPUnit_Framework_MockObject_MockObject */
87
        $client = $this->getMock(Client::class, ['call']);
88
89
        $client
90
            ->expects($this->any())
91
            ->method('call')
92
            ->with($this->stringEndsWith($method), $params)
93
            ->will($this->returnValue($expectedValue));
94
95
        $adapter = new JsonRpcAdapter(
96
            $client,
97
            [
98
                 'ProxyManagerTestAsset\RemoteProxy\Foo.foo'
99
                    => 'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface.foo'
100
            ]
101
        );
102
103
        return $adapter;
104
    }
105
106
    /**
107
     * @dataProvider getProxyMethods
108
     *
109
     * @param string|object $instanceOrClassName
110
     * @param string        $method
111
     * @param mixed[]       $params
112
     * @param mixed         $expectedValue
113
     */
114
    public function testXmlRpcMethodCalls($instanceOrClassName, $method, array $params, $expectedValue)
115
    {
116
        $proxyName = $this->generateProxy($instanceOrClassName);
0 ignored issues
show
Bug introduced by
It seems like $instanceOrClassName defined by parameter $instanceOrClassName on line 114 can also be of type object; however, ProxyManagerTest\Functio...alTest::generateProxy() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
117
118
        /* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
119
        $proxy     = $proxyName::staticProxyConstructor($this->getXmlRpcAdapter($expectedValue, $method, $params));
120
121
        $this->assertSame($expectedValue, call_user_func_array([$proxy, $method], $params));
122
    }
123
124
    /**
125
     * @dataProvider getProxyMethods
126
     *
127
     * @param string|object $instanceOrClassName
128
     * @param string        $method
129
     * @param mixed[]       $params
130
     * @param mixed         $expectedValue
131
     */
132
    public function testJsonRpcMethodCalls($instanceOrClassName, $method, array $params, $expectedValue)
133
    {
134
        $proxyName = $this->generateProxy($instanceOrClassName);
0 ignored issues
show
Bug introduced by
It seems like $instanceOrClassName defined by parameter $instanceOrClassName on line 132 can also be of type object; however, ProxyManagerTest\Functio...alTest::generateProxy() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
135
136
        /* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
137
        $proxy     = $proxyName::staticProxyConstructor($this->getJsonRpcAdapter($expectedValue, $method, $params));
138
139
        $this->assertSame($expectedValue, call_user_func_array([$proxy, $method], $params));
140
    }
141
142
    /**
143
     * @dataProvider getPropertyAccessProxies
144
     *
145
     * @param string|object $instanceOrClassName
146
     * @param string        $publicProperty
147
     * @param string        $propertyValue
148
     */
149
    public function testJsonRpcPropertyReadAccess($instanceOrClassName, $publicProperty, $propertyValue)
150
    {
151
        $proxyName = $this->generateProxy($instanceOrClassName);
0 ignored issues
show
Bug introduced by
It seems like $instanceOrClassName defined by parameter $instanceOrClassName on line 149 can also be of type object; however, ProxyManagerTest\Functio...alTest::generateProxy() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
152
153
        /* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
154
        $proxy     = $proxyName::staticProxyConstructor(
155
            $this->getJsonRpcAdapter($propertyValue, '__get', [$publicProperty])
156
        );
157
158
        /* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
159
        $this->assertSame($propertyValue, $proxy->$publicProperty);
160
    }
161
162
    /**
163
     * Generates a proxy for the given class name, and retrieves its class name
164
     *
165
     * @param string $parentClassName
166
     *
167
     * @return string
168
     */
169
    private function generateProxy($parentClassName)
170
    {
171
        $generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
172
        $generator          = new RemoteObjectGenerator();
173
        $generatedClass     = new ClassGenerator($generatedClassName);
174
        $strategy           = new EvaluatingGeneratorStrategy();
175
176
        $generator->generate(new ReflectionClass($parentClassName), $generatedClass);
177
        $strategy->generate($generatedClass);
178
179
        return $generatedClassName;
180
    }
181
182
    /**
183
     * Generates a list of object | invoked method | parameters | expected result
184
     *
185
     * @return array
186
     */
187
    public function getProxyMethods()
188
    {
189
        $selfHintParam = new ClassWithSelfHint();
190
191
        return [
192
            [
193
                'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface',
194
                'foo',
195
                [],
196
                'bar remote'
197
            ],
198
            [
199
                'ProxyManagerTestAsset\RemoteProxy\Foo',
200
                'foo',
201
                [],
202
                'bar remote'
203
            ],
204
            [
205
                new Foo(),
206
                'foo',
207
                [],
208
                'bar remote'
209
            ],
210
            [
211
                'ProxyManagerTestAsset\RemoteProxy\BazServiceInterface',
212
                'baz',
213
                ['baz'],
214
                'baz remote'
215
            ],
216
            [
217
                new ClassWithSelfHint(),
218
                'selfHintMethod',
219
                [$selfHintParam],
220
                $selfHintParam
221
            ],
222
        ];
223
    }
224
225
    /**
226
     * Generates proxies and instances with a public property to feed to the property accessor methods
227
     *
228
     * @return array
229
     */
230
    public function getPropertyAccessProxies()
231
    {
232
        return [
233
            [
234
                FooServiceInterface::class,
235
                'publicProperty',
236
                'publicProperty remote',
237
            ],
238
        ];
239
    }
240
241
    /**
242
     * @group 276
243
     *
244
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
245
     *
246
     * @param object $callerObject
247
     * @param object $realInstance
248
     * @param string $method
249
     * @param string $expectedValue
250
     * @param string $propertyName
251
     */
252
    public function testWillInterceptAccessToPropertiesViaFriendClassAccess(
253
        $callerObject,
254
        $realInstance,
255
        string $method,
256
        string $expectedValue,
257
        string $propertyName
258
    ) {
259
        $proxyName = $this->generateProxy(get_class($realInstance));
260
261
        /* @var $adapter AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */
262
        $adapter = $this->getMock(AdapterInterface::class);
263
264
        $adapter
265
            ->expects(self::once())
266
            ->method('call')
267
            ->with(get_class($realInstance), '__get', [$propertyName])
268
            ->willReturn($expectedValue);
269
270
        /* @var $proxy OtherObjectAccessClass|RemoteObjectInterface */
271
        $proxy = $proxyName::staticProxyConstructor($adapter);
272
273
        /* @var $accessor callable */
274
        $accessor = [$callerObject, $method];
275
276
        self::assertSame($expectedValue, $accessor($proxy));
277
    }
278
279
    /**
280
     * @group 276
281
     *
282
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
283
     *
284
     * @param object $callerObject
285
     * @param object $realInstance
286
     * @param string $method
287
     * @param string $expectedValue
288
     * @param string $propertyName
289
     */
290
    public function testWillInterceptAccessToPropertiesViaFriendClassAccessEvenIfCloned(
291
        $callerObject,
292
        $realInstance,
293
        string $method,
294
        string $expectedValue,
295
        string $propertyName
296
    ) {
297
        $proxyName = $this->generateProxy(get_class($realInstance));
298
299
        /* @var $adapter AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */
300
        $adapter = $this->getMock(AdapterInterface::class);
301
302
        $adapter
303
            ->expects(self::once())
304
            ->method('call')
305
            ->with(get_class($realInstance), '__get', [$propertyName])
306
            ->willReturn($expectedValue);
307
308
        /* @var $proxy OtherObjectAccessClass|RemoteObjectInterface */
309
        $proxy = clone $proxyName::staticProxyConstructor($adapter);
310
311
        /* @var $accessor callable */
312
        $accessor = [$callerObject, $method];
313
314
        self::assertSame($expectedValue, $accessor($proxy));
315
    }
316
317
    public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope() : \Generator
318
    {
319
        foreach ((new \ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) {
320
            $property->setAccessible(true);
321
322
            $propertyName  = $property->getName();
323
            $realInstance  = new OtherObjectAccessClass();
324
            $expectedValue = uniqid('', true);
325
326
            $property->setValue($realInstance, $expectedValue);
327
328
            yield OtherObjectAccessClass::class . '#$' . $propertyName => [
329
                new OtherObjectAccessClass(),
330
                $realInstance,
331
                'get' . ucfirst($propertyName),
332
                $expectedValue,
333
                $propertyName,
334
            ];
335
        }
336
    }
337
}
338