├── .gitignore
├── Resources
├── config
│ ├── routing.yml
│ └── services.yml
├── meta
│ └── LICENSE
└── doc
│ └── index.rst
├── Wa72JsonRpcBundle.php
├── Tests
├── Fixtures
│ ├── app
│ │ ├── config
│ │ │ ├── config2.yml
│ │ │ └── config1.yml
│ │ ├── Wa72JsonRpcBundleTestKernel1.php
│ │ └── Wa72JsonRpcBundleTestKernel2.php
│ └── Testparameter.php
├── bootstrap.php
├── Testservice.php
├── JsonRpcController2Test.php
└── JsonRpcController1Test.php
├── phpunit.xml.dist
├── README.md
├── LICENSE
├── DependencyInjection
├── Wa72JsonRpcExtension.php
├── Compiler
│ └── JsonRpcExposablePass.php
└── Configuration.php
├── composer.json
├── .github
└── workflows
│ └── tests.yml
├── CHANGELOG.md
└── Controller
└── JsonRpcController.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 | composer.phar
4 |
--------------------------------------------------------------------------------
/Resources/config/routing.yml:
--------------------------------------------------------------------------------
1 | wa72_json_rpc:
2 | path: /
3 | defaults: { _controller: 'wa72_jsonrpc.jsonrpccontroller::execute', optionalTrailingSlash : "/" }
4 | requirements: { optionalTrailingSlash : "[/]{0,1}" }
5 |
--------------------------------------------------------------------------------
/Resources/config/services.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | wa72_jsonrpc.jsonrpccontroller.class: Wa72\JsonRpcBundle\Controller\JsonRpcController
3 |
4 | services:
5 | wa72_jsonrpc.jsonrpccontroller:
6 | public: true
7 | class: '%wa72_jsonrpc.jsonrpccontroller.class%'
8 | arguments: ['@service_container', '%wa72.jsonrpc%']
9 |
10 |
--------------------------------------------------------------------------------
/Wa72JsonRpcBundle.php:
--------------------------------------------------------------------------------
1 | addCompilerPass(new JsonRpcExposablePass());
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Tests/Fixtures/app/config/config2.yml:
--------------------------------------------------------------------------------
1 | framework:
2 | secret: dummy
3 | serializer:
4 | enabled: true
5 | property_access:
6 | enabled: true
7 |
8 | wa72_json_rpc:
9 | functions:
10 | testhello:
11 | service: wa72_jsonrpc.testservice
12 | method: hello
13 |
14 | services:
15 | wa72_jsonrpc.testservice:
16 | class: 'Wa72\JsonRpcBundle\Tests\Testservice'
17 | public: true
18 | tags:
19 | - {name: wa72_jsonrpc.exposable}
20 |
21 |
--------------------------------------------------------------------------------
/Tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./
6 |
7 |
8 | ./Resources
9 | ./Tests
10 | ./vendor
11 |
12 |
13 |
14 |
15 | ./Tests/
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | JsonRpcBundle
2 | =============
3 |
4 | 
5 | [](https://packagist.org/packages/wa72/jsonrpc-bundle)
6 | [](https://packagist.org/packages/wa72/jsonrpc-bundle)
7 |
8 |
9 | JsonRpcBundle is a bundle for Symfony that allows to easily build a JSON-RPC server for web services using
10 | [JSON-RPC 2.0](http://www.jsonrpc.org/specification).
11 |
12 | The bundle contains a controller that is able to expose methods of any public service registered in the Symfony service
13 | container as a JSON-RPC web service.
14 |
15 | Documentation
16 | -------------
17 |
18 | Documentation is found in [Resources/doc/index.rst](Resources/doc/index.rst).
19 |
20 |
21 | © 2013-2023 Christoph Singer. Licensed under the MIT license.
22 |
--------------------------------------------------------------------------------
/Tests/Fixtures/Testparameter.php:
--------------------------------------------------------------------------------
1 | a = $a;
27 | }
28 |
29 |
30 | public function getA(): string
31 | {
32 | return $this->a;
33 | }
34 |
35 |
36 | public function getB(): string
37 | {
38 | return $this->b;
39 | }
40 |
41 |
42 | public function setB(string $b)
43 | {
44 | $this->b = $b;
45 | }
46 |
47 |
48 | public function getC(): string
49 | {
50 | return $this->c;
51 | }
52 |
53 |
54 | public function setC(string $c)
55 | {
56 | $this->c = $c;
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/Tests/Testservice.php:
--------------------------------------------------------------------------------
1 | getA() . $arg3->getB() . $arg3->getC();
29 | }
30 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Resources/meta/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Christoph Singer, Web-Agentur 72
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | The software is provided "as is", without warranty of any kind, express or
14 | implied, including but not limited to the warranties of merchantability,
15 | fitness for a particular purpose and noninfringement. In no event shall the
16 | authors or copyright holders be liable for any claim, damages or other
17 | liability, whether in an action of contract, tort or otherwise, arising from,
18 | out of or in connection with the software or the use or other dealings in
19 | the software.
20 |
--------------------------------------------------------------------------------
/Tests/Fixtures/app/Wa72JsonRpcBundleTestKernel1.php:
--------------------------------------------------------------------------------
1 | load(__DIR__ . '/config/config1.yml');
23 | }
24 |
25 | /**
26 | * @return string
27 | */
28 | public function getCacheDir(): string
29 | {
30 | return sys_get_temp_dir().'/Wa72JsonRpcBundle/cache';
31 | }
32 |
33 | /**
34 | * @return string
35 | */
36 | public function getLogDir(): string
37 | {
38 | return sys_get_temp_dir().'/Wa72JsonRpcBundle/logs';
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/DependencyInjection/Wa72JsonRpcExtension.php:
--------------------------------------------------------------------------------
1 | processConfiguration($configuration, $configs);
26 |
27 | $container->setParameter('wa72.jsonrpc', $config);
28 |
29 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
30 | $loader->load('services.yml');
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Tests/Fixtures/app/Wa72JsonRpcBundleTestKernel2.php:
--------------------------------------------------------------------------------
1 | load(__DIR__ . '/config/config2.yml');
24 | }
25 |
26 |
27 | /**
28 | * @return string
29 | */
30 | public function getCacheDir(): string
31 | {
32 | return sys_get_temp_dir() . '/Wa72JsonRpcBundle/cache';
33 | }
34 |
35 | /**
36 | * @return string
37 | */
38 | public function getLogDir(): string
39 | {
40 | return sys_get_temp_dir() . '/Wa72JsonRpcBundle/logs';
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"wa72/jsonrpc-bundle",
3 | "description":"JSON-RPC server for Symfony: exposes services registered in the service container as JSON-RPC webservices",
4 | "keywords":["json", "rpc", "jsonrpc", "json-rpc", "webservice", "symfony", "symfony-bundle" ],
5 | "homepage":"https://github.com/wasinger/jsonrpc-bundle",
6 | "license":"MIT",
7 | "type": "symfony-bundle",
8 | "authors":[
9 | {
10 | "name":"Christoph Singer",
11 | "email":"dev@ccss.de"
12 | }
13 | ],
14 | "require":{
15 | "php":">=8.0",
16 | "ext-json": "*",
17 | "symfony/framework-bundle":"^5.4|^6|^7",
18 | "symfony/yaml": "^5.4|^6|^7"
19 | },
20 | "require-dev": {
21 | "phpunit/phpunit": "^9",
22 | "jms/serializer-bundle": "^5.4",
23 | "symfony/property-access": "^5.4|^6.0|^7.0",
24 | "symfony/property-info": "^5.4|^6.0|^7.0",
25 | "symfony/serializer": "^5.4|^6.0|^7.0"
26 | },
27 | "suggest": {
28 | "jms/serializer-bundle": "If you want to use JMS Serializer for serialization/deserialization",
29 | "symfony/serializer": "For using Symfony's Serializer component"
30 | },
31 | "autoload":{
32 | "psr-4":{
33 | "Wa72\\JsonRpcBundle\\":""
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/JsonRpcExposablePass.php:
--------------------------------------------------------------------------------
1 | getDefinition('wa72_jsonrpc.jsonrpccontroller');
20 | $services = $container->findTaggedServiceIds('wa72_jsonrpc.exposable');
21 | foreach ($services as $service => $attributes) {
22 | $definition->addMethodCall('addService', [$service]);
23 | }
24 |
25 | // Add an alias for the serializer service to make it public
26 | try {
27 | $serializer = $container->getDefinition('serializer');
28 | } catch (\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException $e) {
29 | $serializer = null;
30 | }
31 | if ($serializer) {
32 | $a = new Alias('serializer', true);
33 | $container->setAlias('wa72_jsonrpc.serializer', $a);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 | workflow_dispatch:
11 |
12 | jobs:
13 | php:
14 | runs-on: ubuntu-latest
15 |
16 | strategy:
17 | matrix:
18 | php: [8.0, 8.1, 8.2, 8.3, 8.4]
19 | dependency-version: [prefer-lowest, prefer-stable]
20 | optional-deps: [without-jms, without-serializer, with-both]
21 | name: PHP ${{ matrix.php }} - ${{ matrix.dependency-version }} - ${{ matrix.optional-deps }}
22 |
23 | steps:
24 | - name: checkout code
25 | uses: actions/checkout@v4
26 |
27 | - name: setup PHP
28 | uses: shivammathur/setup-php@v2
29 | with:
30 | php-version: ${{ matrix.php }}
31 | coverage: xdebug
32 |
33 | - name: get composer cache directory
34 | id: composer-cache
35 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
36 |
37 | - name: cache composer dependencies
38 | uses: actions/cache@v4
39 | with:
40 | path: ${{ steps.composer-cache.outputs.dir }}
41 | key: ${{ runner.os }}-composer-${{ matrix.php }}-${{ matrix.dependency-version }}-${{ hashFiles('**/composer.lock') }}
42 | restore-keys: |
43 | ${{ runner.os }}-composer-${{ matrix.php }}-${{ matrix.dependency-version }}-
44 | ${{ runner.os }}-composer-${{ matrix.php }}-
45 | ${{ runner.os }}-composer-
46 |
47 | - name: install dependencies
48 | run: composer update --${{ matrix.dependency-version }} --no-progress --no-interaction
49 |
50 | - name: remove optional dependency (jms/serializer-bundle)
51 | if: matrix.optional-deps == 'without-jms'
52 | run: composer remove jms/serializer-bundle --dev --no-progress --no-interaction
53 |
54 | - name: remove optional dependency (symfony/serializer)
55 | if: matrix.optional-deps == 'without-serializer'
56 | run: composer remove symfony/serializer --dev --no-progress --no-interaction
57 |
58 | - name: run tests
59 | run: php vendor/bin/phpunit
60 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### v0.8.0 2025-07-26 ###
2 | - Support for Symfony 7, minimum required Symfony version is now 5.4.
3 | - JMS\SerializerBundle is no longer a required dependency, symfony/serializer can be used instead.
4 | - Configuration changes:
5 | - Use `serialization_context` instead of `jms_serialization_context`.
6 | - Use `enable_max_depth' instead of `max_depth_checks`.
7 |
8 | ### v0.7.0-beta 2023-10-24 ###
9 | - Updated for Symfony 5 and 6 and PHP 8.
10 |
11 | ### v0.6.1 2018-03-10 ###
12 | - compatibility with Symfony 4, thanks to @shreypuranik
13 | - make unit tests compatible with newer phpunit versions
14 |
15 | ### v0.6.0 2017-02-09 ###
16 | - added compatibility with Symfony 3, thanks to @JasonMiesionczek
17 | - minimum required Symfony version is now 2.7
18 |
19 | ### v0.5.0 2014-12-17 ###
20 | - It is now possible to call methods that require objects as parameters (in previous versions only methods with scalar
21 | and array parameters could be called). That's why JMSSerializerBundle is now a required dependency.
22 | For this to work, the following conditions must be met:
23 | - The method parameters must be correctly type hinted
24 | - The parameter classes must contain [jms_serializer `@Type` annotations for its properties](http://jmsyst.com/libs/serializer/master/reference/annotations#type)
25 |
26 | ### v0.4.4 2014-11-28 ###
27 | - The test service is no longer registered automatically. If you want to use it,
28 | you must define it in the config.yml of your application, see updated [Resources/doc/index.rst](Resources/doc/index.rst).
29 |
30 | ### v0.4.2 2014-11-28 ###
31 | - fixed bug introduced in v0.2.0: named parameters given in associative arrays were always passed to the method in
32 | the order they were given, not by their names. Thanks to @teet.
33 |
34 | ### v0.4.1 2014-11-19 ###
35 | - raised required symfony version to 2.3
36 | - Added mandatory LICENSE and index.rst files
37 | - only metadata, no code changes
38 |
39 | ### v0.4.0 2014-10-23 ###
40 | - All public methods of services tagged with 'wa72_jsonrpc.exposable' can be called via JSON-RPC. The method name
41 | to be used in the RPC call is "service:method", i.e. the name of the service and the method separated by colon.
--------------------------------------------------------------------------------
/DependencyInjection/Configuration.php:
--------------------------------------------------------------------------------
1 | getRootNode();
17 |
18 | $rootNode
19 | ->children()
20 | ->arrayNode('functions')
21 | ->useAttributeAsKey('function')
22 | ->prototype('array')
23 | ->children()
24 | ->scalarNode('service')->end()
25 | ->scalarNode('method')->end()
26 | ->arrayNode('serialization_context')
27 | ->children()
28 | ->arrayNode('groups')
29 | ->beforeNormalization()
30 | ->ifTrue(function ($v) { return is_string($v); })
31 | ->then(function ($v) { return array($v); })
32 | ->end()
33 | ->prototype('scalar')->end()
34 | ->end()
35 | ->scalarNode('version')->end()
36 | ->booleanNode('max_depth_checks')->setDeprecated(
37 | 'wa72/json-rpc-bundle', '0.8.0',
38 | 'The "%node%" option is deprecated. Use "enable_max_depth" instead.'
39 | )->end()
40 | ->booleanNode('enable_max_depth')->end()
41 | ->end()
42 | ->end()
43 | ->arrayNode('jms_serialization_context')
44 | ->setDeprecated(
45 | 'wa72/json-rpc-bundle', '0.8.0',
46 | 'The "%node%" option is deprecated. Use "serialization_context" instead.'
47 | )
48 | ->children()
49 | ->arrayNode('groups')
50 | ->beforeNormalization()
51 | ->ifTrue(function ($v) { return is_string($v); })
52 | ->then(function ($v) { return array($v); })
53 | ->end()
54 | ->prototype('scalar')->end()
55 | ->end()
56 | ->scalarNode('version')->end()
57 | ->booleanNode('max_depth_checks')->end()
58 | ->end()
59 | ->end()
60 | ->end()
61 | ->end()
62 | ->end()
63 | ->end();
64 |
65 | return $treeBuilder;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Resources/doc/index.rst:
--------------------------------------------------------------------------------
1 | =============
2 | JsonRpcBundle
3 | =============
4 |
5 | JsonRpcBundle is a bundle for Symfony 2.7 and up that allows to easily build a JSON-RPC server for web services using `JSON-RPC 2.0`_.
6 |
7 | The bundle contains a controller that is able to expose methods of any public service registered in the Symfony service container as a JSON-RPC web service. The return value of the service method is converted to JSON using `jms\_serializer`_.
8 |
9 | Of course, it doesn't simply expose all your services' methods to the public, but only those explicitely mentioned in the configuration. And service methods cannot be called by it's original name but by an alias to be defined in the configuration.
10 |
11 |
12 | Installation
13 | ============
14 |
15 | 1. Add "wa72/jsonrpc-bundle" as requirement to your composer.json
16 |
17 | 2. Add "new Wa72\\JsonRpcBundle\\Wa72JsonRpcBundle()" in your AppKernel::registerBundles() function
18 |
19 | 3. Import the bundle's route in your routing.yml:
20 |
21 | .. code-block:: yaml
22 |
23 | # app/config/routing.yml
24 | wa72_json_rpc:
25 | resource: "@Wa72JsonRpcBundle/Resources/config/routing.yml"
26 | prefix: /jsonrpc
27 |
28 | Your JSON-RPC web service will then be available in your project calling the /jsonrpc/ URL.
29 |
30 | Configuration
31 | =============
32 |
33 | You must configure which functions of the services registered in the Service Container will be available as web services.
34 | There are two methods how to do this:
35 |
36 | Method 1: Expose single methods from any service via yaml configuration
37 | -----------------------------------------------------------------------
38 |
39 | Configuration is done under the "wa72\_json\_rpc" key of your configuration (usually defined in your app/config/config.yml).
40 | To enable a Symfony2 service method to be called as a JSON-RPC web service, add it to the "functions" array of the configuration.
41 | The key of an entry of the "functions" array is the alias name for the method to be called over RPC and it needs two sub keys:
42 | "service" specifies the name of the service and "method" the name of the method to call. Example:
43 |
44 | .. code-block:: yaml
45 |
46 | # app/config/config.yml
47 | wa72_json_rpc:
48 | functions:
49 | myfunction1:
50 | service: "mybundle.servicename"
51 | method: "methodofservice"
52 | anotherfunction:
53 | service: "bundlename.foo"
54 | method: "bar"
55 |
56 | In this example, "myfunction1" and "anotherfunction" are aliases for service methods that are used as JSON-RPC method names.
57 | A method name "myfunction1" in the JSON-RPC call will then call the method "methodofservice" of service "mybundle.servicename".
58 |
59 | If you use `jms\_serializer`_ you can also configure exclusion strategies (groups, version, or max depth checks) :
60 |
61 | .. code-block:: yaml
62 |
63 | # app/config/config.yml
64 | wa72_json_rpc:
65 | functions:
66 | myfunction1:
67 | service: "mybundle.servicename"
68 | method: "methodofservice"
69 | jms_serialization_context:
70 | group: "my_group"
71 | version: "1"
72 | max_depth_checks: true
73 |
74 |
75 | Method 2: tag a sercive to make all it's public methods callable by JSON-RPC
76 | ----------------------------------------------------------------------------
77 |
78 | Starting with v0.4.0, it is also possible to fully expose all methods of a service by tagging it with ``wa72\_jsonrpc.exposable``.
79 | All public methods of services tagged with 'wa72\_jsonrpc.exposable' can be called via JSON-RPC. The method name
80 | to be used in the RPC call is "service\:method", i.e. the name of the service and the method separated by colon.
81 |
82 |
83 | Notes
84 | =====
85 |
86 | **New in Version 0.5.0**: It is now possible to call methods that require objects as parameters (in previous versions
87 | only methods with scalar and array parameters could be called).
88 | That's why *JMSSerializerBundle is now a required dependency*.
89 | For this to work, the following conditions must be met:
90 |
91 | - The method parameters must be correctly type hinted
92 | - The classes used as parameters must contain `jms\_serializer @Type annotations for their properties `_
93 |
94 |
95 | Testing
96 | =======
97 |
98 | The bundle comes with a simple test service. If you have imported the bundle's routing to ``/jsonrpc`` (see above)
99 | register the test service in your DI container:
100 |
101 | .. code-block:: yaml
102 |
103 | # app/config/config_dev.yml
104 | services:
105 | wa72_jsonrpc.testservice:
106 | class: Wa72\JsonRpcBundle\Tests\Testservice
107 | tags:
108 | - {name: wa72_jsonrpc.exposable}
109 |
110 | You should then be able to test your service by sending a JSON-RPC request using curl:
111 |
112 | .. code-block:: bash
113 |
114 | curl -XPOST http://your-symfony-project/app_dev.php/jsonrpc/ -d '{"jsonrpc":"2.0","method":"wa72_jsonrpc.testservice:hello","id":"foo","params":{"name":"Joe"}}'
115 |
116 | and you should get the following answer:
117 |
118 | .. code-block:: json
119 |
120 | {"jsonrpc":"2.0","result":"Hello Joe!","id":"foo"}
121 |
122 | There are also unit tests for phpunit. Just install the required development dependencies using ``composer install`` and run
123 | ``vendor/bin/phpunit`` in the root directory of the project.
124 |
125 | © 2013-2018 Christoph Singer, Web-Agentur 72. Licensed under the MIT license.
126 |
127 |
128 | .. _`JSON-RPC 2.0`: http://www.jsonrpc.org/specification
129 | .. _`jms\_serializer`: https://github.com/schmittjoh/JMSSerializerBundle
--------------------------------------------------------------------------------
/Tests/JsonRpcController2Test.php:
--------------------------------------------------------------------------------
1 | markTestSkipped('Symfony Serializer not available.');
27 | } else {
28 | $this->fail('No serializer available. Please install JMS Serializer Bundle or Symfony Serializer.');
29 | }
30 | } else {
31 | $this->kernel = new \Wa72JsonRpcBundleTestKernel2('test', true);
32 | $this->kernel->boot();
33 | $container = $this->kernel->getContainer();
34 | $this->serializer = $container->get('wa72_jsonrpc.serializer');
35 | $this->controller = $container->get('wa72_jsonrpc.jsonrpccontroller');
36 | }
37 | }
38 |
39 | public function testHello()
40 | {
41 | $requestdata = array(
42 | 'jsonrpc' => '2.0',
43 | 'id' => 'test',
44 | 'method' => 'testhello',
45 | 'params' => array('name' => 'Joe')
46 | );
47 | $response = $this->makeRequest($this->controller, $requestdata);
48 | $this->assertEquals('2.0', $response['jsonrpc']);
49 | $this->assertEquals('test', $response['id']);
50 | $this->assertArrayHasKey('result', $response);
51 | $this->assertArrayNotHasKey('error', $response);
52 | $this->assertEquals('Hello Joe!', $response['result']);
53 |
54 | // Test: missing parameter
55 | $requestdata = array(
56 | 'jsonrpc' => '2.0',
57 | 'id' => 'test',
58 | 'method' => 'testhello'
59 | );
60 | $response = $this->makeRequest($this->controller, $requestdata);
61 | $this->assertArrayHasKey('error', $response);
62 | $this->assertArrayNotHasKey('result', $response);
63 | $this->assertEquals(-32602, $response['error']['code']);
64 | }
65 |
66 | public function testService()
67 | {
68 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
69 | $requestdata = array(
70 | 'jsonrpc' => '2.0',
71 | 'id' => 'testservice',
72 | 'method' => 'wa72_jsonrpc.testservice:hello',
73 | 'params' => array('name' => 'Max')
74 | );
75 |
76 | $response = $this->makeRequest($controller, $requestdata);
77 | $this->assertEquals('2.0', $response['jsonrpc']);
78 | $this->assertEquals('testservice', $response['id']);
79 | $this->assertArrayNotHasKey('error', $response);
80 | $this->assertArrayHasKey('result', $response);
81 | $this->assertEquals('Hello Max!', $response['result']);
82 |
83 | // Test: non-existing service should return "Method not found" error
84 | $requestdata = array(
85 | 'jsonrpc' => '2.0',
86 | 'id' => 'testservice',
87 | 'method' => 'someservice:somemethod',
88 | 'params' => array('name' => 'Max')
89 | );
90 |
91 | $response = $this->makeRequest($controller, $requestdata);
92 | $this->assertEquals('2.0', $response['jsonrpc']);
93 | $this->assertEquals('testservice', $response['id']);
94 | $this->assertArrayNotHasKey('result', $response);
95 | $this->assertArrayHasKey('error', $response);
96 | $this->assertEquals(-32601, $response['error']['code']);
97 | }
98 |
99 | public function testParameters()
100 | {
101 | // params as associative array in right order
102 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
103 | $requestdata = array(
104 | 'jsonrpc' => '2.0',
105 | 'id' => 'parametertest',
106 | 'method' => 'wa72_jsonrpc.testservice:parametertest',
107 | 'params' => array('arg1' => 'abc', 'arg2' => 'def', 'arg_array' => array())
108 | );
109 |
110 | $response = $this->makeRequest($controller, $requestdata);
111 | $this->assertArrayNotHasKey('error', $response);
112 | $this->assertArrayHasKey('result', $response);
113 | $this->assertEquals('abcdef', $response['result']);
114 |
115 | // params as simple array in right order
116 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
117 | $requestdata = array(
118 | 'jsonrpc' => '2.0',
119 | 'id' => 'parametertest',
120 | 'method' => 'wa72_jsonrpc.testservice:parametertest',
121 | 'params' => array('abc', 'def', array())
122 | );
123 |
124 | $response = $this->makeRequest($controller, $requestdata);
125 | $this->assertArrayNotHasKey('error', $response);
126 | $this->assertArrayHasKey('result', $response);
127 | $this->assertEquals('abcdef', $response['result']);
128 |
129 | // params as associative array in mixed order
130 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
131 | $requestdata = array(
132 | 'jsonrpc' => '2.0',
133 | 'id' => 'parametertest',
134 | 'method' => 'wa72_jsonrpc.testservice:parametertest',
135 | 'params' => array('arg_array' => array(), 'arg2' => 'def', 'arg1' => 'abc')
136 | );
137 |
138 | $response = $this->makeRequest($controller, $requestdata);
139 | $this->assertArrayNotHasKey('error', $response);
140 | $this->assertArrayHasKey('result', $response);
141 | $this->assertEquals('abcdef', $response['result']);
142 |
143 | // params with objects
144 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
145 | $arg3 = new Testparameter('abc');
146 | $arg3->setB('def');
147 | $arg3->setC('ghi');
148 | $requestdata = array(
149 | 'jsonrpc' => '2.0',
150 | 'id' => 'testParameterTypes',
151 | 'method' => 'wa72_jsonrpc.testservice:testParameterTypes',
152 | 'params' => array('arg1' => array(), 'arg2' => new \stdClass(), 'arg3' => $arg3)
153 | );
154 |
155 | $response = $this->makeRequest($controller, $requestdata);
156 | $this->assertArrayNotHasKey('error', $response);
157 | $this->assertArrayHasKey('result', $response);
158 | $this->assertEquals('abcdefghi', $response['result']);
159 | }
160 |
161 | public function testAddMethod()
162 | {
163 | $requestdata = array(
164 | 'jsonrpc' => '2.0',
165 | 'id' => 'test',
166 | 'method' => 'testhi',
167 | 'params' => array('name' => 'Tom')
168 | );
169 | // this request will fail because there is no such method "testhi"
170 | $response = $this->makeRequest($this->controller, $requestdata);
171 | $this->assertArrayHasKey('error', $response);
172 | $this->assertArrayNotHasKey('result', $response);
173 | $this->assertEquals(-32601, $response['error']['code']);
174 |
175 | // add the method definition for "testhi"
176 | $this->controller->addMethod('testhi', 'wa72_jsonrpc.testservice', 'hi');
177 |
178 | // now the request should succeed
179 | $response = $this->makeRequest($this->controller, $requestdata);
180 | $this->assertArrayHasKey('result', $response);
181 | $this->assertArrayNotHasKey('error', $response);
182 | $this->assertEquals('Hi Tom!', $response['result']);
183 | }
184 |
185 | private function makeRequest($controller, $requestdata)
186 | {
187 | return json_decode($controller->execute(
188 | new Request([], [], [], [], [], [], $this->serializer->serialize($requestdata, 'json'))
189 | )->getContent(), true);
190 | }
191 |
192 | /**
193 | * Shuts the kernel down if it was used in the test.
194 | */
195 | protected function tearDown(): void
196 | {
197 | $this->kernel->shutdown();
198 | }
199 |
200 | }
201 |
--------------------------------------------------------------------------------
/Tests/JsonRpcController1Test.php:
--------------------------------------------------------------------------------
1 | markTestSkipped('JMS Serializer not available.');
28 | } else {
29 | $this->fail('No serializer available. Please install JMS Serializer Bundle or Symfony Serializer.');
30 | }
31 | } else {
32 | $this->kernel = new \Wa72JsonRpcBundleTestKernel1('test', false);
33 | $this->kernel->boot();
34 | $container = $this->kernel->getContainer();
35 | $this->serializer = $container->get('jms_serializer');
36 | $this->controller = $container->get('wa72_jsonrpc.jsonrpccontroller');
37 | }
38 | }
39 |
40 | public function testHello()
41 | {
42 | $requestdata = array(
43 | 'jsonrpc' => '2.0',
44 | 'id' => 'test',
45 | 'method' => 'testhello',
46 | 'params' => array('name' => 'Joe')
47 | );
48 | $response = $this->makeRequest($this->controller, $requestdata);
49 | $this->assertEquals('2.0', $response['jsonrpc']);
50 | $this->assertEquals('test', $response['id']);
51 | $this->assertArrayHasKey('result', $response);
52 | $this->assertArrayNotHasKey('error', $response);
53 | $this->assertEquals('Hello Joe!', $response['result']);
54 |
55 | // Test: missing parameter
56 | $requestdata = array(
57 | 'jsonrpc' => '2.0',
58 | 'id' => 'test',
59 | 'method' => 'testhello'
60 | );
61 | $response = $this->makeRequest($this->controller, $requestdata);
62 | $this->assertArrayHasKey('error', $response);
63 | $this->assertArrayNotHasKey('result', $response);
64 | $this->assertEquals(-32602, $response['error']['code']);
65 | }
66 |
67 | public function testService()
68 | {
69 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
70 | $requestdata = array(
71 | 'jsonrpc' => '2.0',
72 | 'id' => 'testservice',
73 | 'method' => 'wa72_jsonrpc.testservice:hello',
74 | 'params' => array('name' => 'Max')
75 | );
76 |
77 | $response = $this->makeRequest($controller, $requestdata);
78 | $this->assertEquals('2.0', $response['jsonrpc']);
79 | $this->assertEquals('testservice', $response['id']);
80 | $this->assertArrayNotHasKey('error', $response);
81 | $this->assertArrayHasKey('result', $response);
82 | $this->assertEquals('Hello Max!', $response['result']);
83 |
84 | // Test: non-existing service should return "Method not found" error
85 | $requestdata = array(
86 | 'jsonrpc' => '2.0',
87 | 'id' => 'testservice',
88 | 'method' => 'someservice:somemethod',
89 | 'params' => array('name' => 'Max')
90 | );
91 |
92 | $response = $this->makeRequest($controller, $requestdata);
93 | $this->assertEquals('2.0', $response['jsonrpc']);
94 | $this->assertEquals('testservice', $response['id']);
95 | $this->assertArrayNotHasKey('result', $response);
96 | $this->assertArrayHasKey('error', $response);
97 | $this->assertEquals(-32601, $response['error']['code']);
98 | }
99 |
100 | public function testParameters()
101 | {
102 | // params as associative array in right order
103 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
104 | $requestdata = array(
105 | 'jsonrpc' => '2.0',
106 | 'id' => 'parametertest',
107 | 'method' => 'wa72_jsonrpc.testservice:parametertest',
108 | 'params' => array('arg1' => 'abc', 'arg2' => 'def', 'arg_array' => array())
109 | );
110 |
111 | $response = $this->makeRequest($controller, $requestdata);
112 | $this->assertArrayNotHasKey('error', $response);
113 | $this->assertArrayHasKey('result', $response);
114 | $this->assertEquals('abcdef', $response['result']);
115 |
116 | // params as simple array in right order
117 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
118 | $requestdata = array(
119 | 'jsonrpc' => '2.0',
120 | 'id' => 'parametertest',
121 | 'method' => 'wa72_jsonrpc.testservice:parametertest',
122 | 'params' => array('abc', 'def', array())
123 | );
124 |
125 | $response = $this->makeRequest($controller, $requestdata);
126 | $this->assertArrayNotHasKey('error', $response);
127 | $this->assertArrayHasKey('result', $response);
128 | $this->assertEquals('abcdef', $response['result']);
129 |
130 | // params as associative array in mixed order
131 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
132 | $requestdata = array(
133 | 'jsonrpc' => '2.0',
134 | 'id' => 'parametertest',
135 | 'method' => 'wa72_jsonrpc.testservice:parametertest',
136 | 'params' => array('arg_array' => array(), 'arg2' => 'def', 'arg1' => 'abc')
137 | );
138 |
139 | $response = $this->makeRequest($controller, $requestdata);
140 | $this->assertArrayNotHasKey('error', $response);
141 | $this->assertArrayHasKey('result', $response);
142 | $this->assertEquals('abcdef', $response['result']);
143 |
144 | // params with objects
145 | $controller = $this->kernel->getContainer()->get('wa72_jsonrpc.jsonrpccontroller');
146 | $arg3 = new Testparameter('abc');
147 | $arg3->setB('def');
148 | $arg3->setC('ghi');
149 | $requestdata = array(
150 | 'jsonrpc' => '2.0',
151 | 'id' => 'testParameterTypes',
152 | 'method' => 'wa72_jsonrpc.testservice:testParameterTypes',
153 | 'params' => array('arg1' => array(), 'arg2' => new \stdClass(), 'arg3' => $arg3)
154 | );
155 |
156 | $response = $this->makeRequest($controller, $requestdata);
157 | $this->assertArrayNotHasKey('error', $response);
158 | $this->assertArrayHasKey('result', $response);
159 | $this->assertEquals('abcdefghi', $response['result']);
160 | }
161 |
162 | public function testAddMethod()
163 | {
164 | $requestdata = array(
165 | 'jsonrpc' => '2.0',
166 | 'id' => 'test',
167 | 'method' => 'testhi',
168 | 'params' => array('name' => 'Tom')
169 | );
170 | // this request will fail because there is no such method "testhi"
171 | $response = $this->makeRequest($this->controller, $requestdata);
172 | $this->assertArrayHasKey('error', $response);
173 | $this->assertArrayNotHasKey('result', $response);
174 | $this->assertEquals(-32601, $response['error']['code']);
175 |
176 | // add the method definition for "testhi"
177 | $this->controller->addMethod('testhi', 'wa72_jsonrpc.testservice', 'hi');
178 |
179 | // now the request should succeed
180 | $response = $this->makeRequest($this->controller, $requestdata);
181 | $this->assertArrayHasKey('result', $response);
182 | $this->assertArrayNotHasKey('error', $response);
183 | $this->assertEquals('Hi Tom!', $response['result']);
184 | }
185 |
186 | private function makeRequest($controller, $requestdata)
187 | {
188 | $container = $this->kernel->getContainer();
189 | if ($container->has('jms_serializer')) {
190 | $serializer = $container->get('jms_serializer');
191 | } elseif ($container->has('serializer')) {
192 | $serializer = $container->get('serializer');
193 | } else {
194 | $this->fail('No serializer service found in container.');
195 | }
196 | return json_decode($controller->execute(
197 | new Request([], [], [], [], [], [], $serializer->serialize($requestdata, 'json'))
198 | )->getContent(), true);
199 | }
200 |
201 | /**
202 | * Shuts the kernel down if it was used in the test.
203 | */
204 | protected function tearDown(): void
205 | {
206 | $this->kernel->shutdown();
207 | }
208 |
209 | }
210 |
--------------------------------------------------------------------------------
/Controller/JsonRpcController.php:
--------------------------------------------------------------------------------
1 | array(
25 | * 'myfunction1' => array(
26 | * 'service' => 'mybundle.servicename',
27 | * 'method' => 'methodofservice'
28 | * ),
29 | * 'anotherfunction' => array(
30 | * 'service' => 'mybundle.foo',
31 | * 'method' => 'bar'
32 | * )
33 | * )
34 | * );
35 | *
36 | * A method name "myfunction1" in the RPC request will then call
37 | * $this->container->get('mybundle.servicename')->methodofservice()
38 | *
39 | * If you want to add a service completely so that all public methods of
40 | * this service may be called, use the addService($servicename) method.
41 | * Methods of the services added this way can be called remotely using
42 | * "servicename:method" as RPC method name.
43 | *
44 | * @license MIT
45 | * @author Christoph Singer
46 | *
47 | */
48 | class JsonRpcController
49 | {
50 | private ContainerInterface $container;
51 |
52 | const PARSE_ERROR = -32700;
53 | const INVALID_REQUEST = -32600;
54 | const METHOD_NOT_FOUND = -32601;
55 | const INVALID_PARAMS = -32602;
56 | const INTERNAL_ERROR = -32603;
57 |
58 | /**
59 | * Functions that are allowed to be called
60 | */
61 | private array $functions = [];
62 |
63 | /**
64 | * Array of names of fully exposed services (all methods of this services are allowed to be called)
65 | */
66 | private array $services = [];
67 |
68 |
69 | private JMS_SerializerInterface|SerializerInterface $serializer;
70 |
71 |
72 | private JMS_SerializationContext|array $serializationContext = [];
73 |
74 | /**
75 | * @param ContainerInterface $container
76 | * @param array $config Associative array for configuration, expects at least a key "functions"
77 | * @throws \InvalidArgumentException
78 | */
79 | public function __construct(ContainerInterface $container, array $config)
80 | {
81 | if (isset($config['functions'])) {
82 | if (!is_array($config['functions'])) throw new \InvalidArgumentException('Configuration parameter "functions" must be array');
83 | $this->functions = $config['functions'];
84 | }
85 | $this->container = $container;
86 | if ($this->container->has('jms_serializer')) {
87 | $this->serializer = $this->container->get('jms_serializer');
88 | } elseif ($this->container->has('wa72_jsonrpc.serializer')) {
89 | $this->serializer = $this->container->get('wa72_jsonrpc.serializer');
90 | } else {
91 | throw new \InvalidArgumentException('No serializer service found in container. Please install jms/serializer-bundle or symfony/serializer.');
92 | }
93 | }
94 |
95 | /**
96 | * @param Request $httprequest
97 | * @return Response
98 | */
99 | public function execute(Request $httprequest): Response
100 | {
101 | $json = $httprequest->getContent();
102 | $request = json_decode($json, true);
103 | $requestId = ($request['id'] ?? null);
104 |
105 | if ($request === null) {
106 | return $this->getErrorResponse(self::PARSE_ERROR, null);
107 | } elseif (!(isset($request['jsonrpc']) && isset($request['method']) && $request['jsonrpc'] == '2.0')) {
108 | return $this->getErrorResponse(self::INVALID_REQUEST, $requestId);
109 | }
110 |
111 | if (in_array($request['method'], array_keys($this->functions))) {
112 | $servicename = $this->functions[$request['method']]['service'];
113 | $method = $this->functions[$request['method']]['method'];
114 | } else {
115 | if (count($this->services) && strpos($request['method'], ':') > 0) {
116 | list($servicename, $method) = explode(':', $request['method']);
117 | if (!in_array($servicename, $this->services)) {
118 | return $this->getErrorResponse(self::METHOD_NOT_FOUND, $requestId);
119 | }
120 | } else {
121 | return $this->getErrorResponse(self::METHOD_NOT_FOUND, $requestId);
122 | }
123 | }
124 | try {
125 | $service = $this->container->get($servicename);
126 | } catch (ServiceNotFoundException $e) {
127 | return $this->getErrorResponse(self::METHOD_NOT_FOUND, $requestId);
128 | }
129 | $params = ($request['params'] ?? []);
130 |
131 | if (is_callable([$service, $method])) {
132 | $r = new \ReflectionMethod($service, $method);
133 | $rps = $r->getParameters();
134 |
135 | if (is_array($params)) {
136 | if (!(count($params) >= $r->getNumberOfRequiredParameters()
137 | && count($params) <= $r->getNumberOfParameters())
138 | ) {
139 | return $this->getErrorResponse(self::INVALID_PARAMS, $requestId,
140 | sprintf('Number of given parameters (%d) does not match the number of expected parameters (%d required, %d total)',
141 | count($params), $r->getNumberOfRequiredParameters(), $r->getNumberOfParameters()));
142 | }
143 |
144 | }
145 | if ($this->isAssoc($params)) {
146 | $newparams = [];
147 | foreach ($rps as $i => $rp) {
148 | $name = $rp->name;
149 | if (!isset($params[$rp->name]) && !$rp->isOptional()) {
150 | return $this->getErrorResponse(self::INVALID_PARAMS, $requestId,
151 | sprintf('Parameter %s is missing', $name));
152 | }
153 | if (isset($params[$rp->name])) {
154 | $newparams[] = $params[$rp->name];
155 | } else {
156 | $newparams[] = null;
157 | }
158 | }
159 | $params = $newparams;
160 | }
161 |
162 | // correctly deserialize object parameters
163 | foreach ($params as $index => $param) {
164 | // if the json_decode'd param value is an array but an object is expected as method parameter,
165 | // re-encode the array value to json and correctly decode it using the serializer.
166 | //
167 | // TODO: since PHP 8, the method type hints can include union types, so we need to handle those as well.
168 | if (is_array($param) && !$rps[$index]->isArray() && $rps[$index]->getClass() != null) {
169 | $class = $rps[$index]->getClass()->getName();
170 | $params[$index] = $this->deserialize(json_encode($param), $class);
171 | }
172 | }
173 |
174 | try {
175 | $result = call_user_func_array([$service, $method], $params);
176 | } catch (\Exception $e) {
177 | return $this->getErrorResponse(self::INTERNAL_ERROR, $requestId, $this->convertExceptionToErrorData($e));
178 | }
179 | $response = ['jsonrpc' => '2.0'];
180 | $response['result'] = $result;
181 | $response['id'] = $requestId;
182 | $response = $this->serialize($response, $request['method']);
183 | return JsonResponse::fromJsonString($response);
184 | } else {
185 | return $this->getErrorResponse(self::METHOD_NOT_FOUND, $requestId);
186 | }
187 | }
188 |
189 | /**
190 | * Add a new function that can be called by RPC
191 | *
192 | * @param string $alias The function name used in the RPC call
193 | * @param string $service The service name of the method to call
194 | * @param string $method The method of $service
195 | * @param bool $overwrite Whether to overwrite an existing function
196 | * @throws \InvalidArgumentException
197 | */
198 | public function addMethod($alias, $service, $method, $overwrite = false)
199 | {
200 | if (isset($this->functions[$alias]) && !$overwrite) {
201 | throw new \InvalidArgumentException('JsonRpcController: The function "' . $alias . '" already exists.');
202 | }
203 | $this->functions[$alias] = [
204 | 'service' => $service,
205 | 'method' => $method
206 | ];
207 | }
208 |
209 | /**
210 | * Add a new service that is fully exposed by json-rpc
211 | *
212 | * @param string $service The id of a service
213 | */
214 | public function addService($service)
215 | {
216 | $this->services[] = $service;
217 | }
218 |
219 | /**
220 | * Remove a method definition
221 | *
222 | * @param string $alias
223 | */
224 | public function removeMethod($alias)
225 | {
226 | if (isset($this->functions[$alias])) {
227 | unset($this->functions[$alias]);
228 | }
229 | }
230 |
231 | protected function convertExceptionToErrorData(\Exception $e): string
232 | {
233 | return $e->getMessage();
234 | }
235 |
236 | protected function getError($code): array
237 | {
238 | $message = '';
239 | switch ($code) {
240 | case self::PARSE_ERROR:
241 | $message = 'Parse error';
242 | break;
243 | case self::INVALID_REQUEST:
244 | $message = 'Invalid request';
245 | break;
246 | case self::METHOD_NOT_FOUND:
247 | $message = 'Method not found';
248 | break;
249 | case self::INVALID_PARAMS:
250 | $message = 'Invalid params';
251 | break;
252 | case self::INTERNAL_ERROR:
253 | $message = 'Internal error';
254 | break;
255 | }
256 |
257 | return array('code' => $code, 'message' => $message);
258 | }
259 |
260 | protected function getErrorResponse($code, $id, $data = null): JsonResponse
261 | {
262 | $response = array('jsonrpc' => '2.0');
263 | $response['error'] = $this->getError($code);
264 |
265 | if ($data != null) {
266 | $response['error']['data'] = $data;
267 | }
268 |
269 | $response['id'] = $id;
270 |
271 | return new JsonResponse($response);
272 | }
273 |
274 | /**
275 | * Serialize the return value of a method call to JSON.
276 | */
277 | protected function serialize(mixed $data, string $rpc_method): string
278 | {
279 | return $this->serializer->serialize($data, 'json', $this->getSerializationContext($rpc_method));
280 | }
281 |
282 | /**
283 | * Deserialize parameter values coming with the RPC request to the expected type.
284 | */
285 | protected function deserialize(string $json, string $class): mixed
286 | {
287 | return $this->serializer->deserialize($json, $class, 'json');
288 | }
289 |
290 | /**
291 | * Set SerializationContext
292 | *
293 | */
294 | public function setSerializationContext(array|JMS_SerializationContext $context): void
295 | {
296 | if ($this->serializer instanceof JMS_SerializerInterface && !($context instanceof JMS_SerializationContext)) {
297 | throw new \InvalidArgumentException('If jms_serializer is used, the SerializationContext must be an instance of JMS_SerializationContext');
298 | }
299 | if ($this->serializer instanceof SerializerInterface && !is_array($context)) {
300 | throw new \InvalidArgumentException('If symfony/serializer is used, the SerializationContext must be an array');
301 | }
302 | $this->serializationContext = $context;
303 | }
304 |
305 | /**
306 | * Get SerializationContext for a given rpc_method.
307 | *
308 | * The context will be created from the configuration array for this method if available,
309 | * otherwise the default serialization context (set by $this->setSerializationContext()) will be used.
310 | */
311 | protected function getSerializationContext(string $rpc_method): JMS_SerializationContext|array
312 | {
313 | $functionConfig = $this->functions[$rpc_method] ?? [];
314 | if ($this->serializer instanceof JMS_SerializerInterface) {
315 | // legacy support for jms_serialization_context
316 | if (isset($functionConfig['jms_serialization_context'])) {
317 | $functionConfig['serialization_context'] = $functionConfig['jms_serialization_context'];
318 | }
319 | if (isset($functionConfig['serialization_context'])) {
320 | $context = JMS_SerializationContext::create();
321 | if (isset($functionConfig['serialization_context']['groups'])) {
322 | $context->setGroups($functionConfig['jms_serialization_context']['groups']);
323 | }
324 | if (isset($functionConfig['serialization_context']['version'])) {
325 | $context->setVersion($functionConfig['jms_serialization_context']['version']);
326 | }
327 | if (!empty($functionConfig['serialization_context']['max_depth_checks']) || !empty($functionConfig['serialization_context']['enable_max_depth'])) {
328 | $context->enableMaxDepthChecks();
329 | }
330 | } else {
331 | if ($this->serializationContext instanceof JMS_SerializationContext) {
332 | $context = $this->serializationContext;
333 | } else {
334 | $context = JMS_SerializationContext::create();
335 | }
336 | }
337 | } elseif ($this->serializer instanceof SerializerInterface) {
338 | $context = $functionConfig['serialization_context'] ?? $this->serializationContext;
339 | if (!empty($context['max_depth_checks'])) { // legacy support for max_depth_checks
340 | $context['enable_max_depth'] = true;
341 | }
342 | } else {
343 | throw new \LogicException('No serializer service found in container. Please install jms/serializer-bundle or symfony/serializer.');
344 | }
345 |
346 | return $context;
347 | }
348 |
349 | /**
350 | * Finds whether a variable is an associative array
351 | *
352 | * @param $var
353 | * @return bool
354 | */
355 | protected function isAssoc($var)
356 | {
357 | return array_keys($var) !== range(0, count($var) - 1);
358 | }
359 | }
360 |
--------------------------------------------------------------------------------