Failed Conditions
Pull Request — feature/symfony-extension (#20)
by Yo
01:50
created

loadJsonRpcMethodsFromTag()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 3
nop 1
dl 0
loc 21
ccs 0
cts 19
cp 0
crap 20
rs 9.0534
c 0
b 0
f 0
1
<?php
2
namespace Yoanm\JsonRpcServer\Infra\Symfony\DependencyInjection;
3
4
use Symfony\Component\DependencyInjection\ContainerBuilder;
5
use Symfony\Component\DependencyInjection\Definition;
6
use Symfony\Component\DependencyInjection\Exception\LogicException;
7
use Symfony\Component\DependencyInjection\Extension\Extension;
8
use Symfony\Component\DependencyInjection\Reference;
9
use Yoanm\JsonRpcServer\App\Creator\CustomExceptionCreator;
10
use Yoanm\JsonRpcServer\App\Creator\ResponseCreator;
11
use Yoanm\JsonRpcServer\App\Manager\MethodManager;
12
use Yoanm\JsonRpcServer\App\RequestHandler;
13
use Yoanm\JsonRpcServer\App\Serialization\RequestDenormalizer;
14
use Yoanm\JsonRpcServer\App\Serialization\ResponseNormalizer;
15
use Yoanm\JsonRpcServer\Infra\Endpoint\JsonRpcEndpoint;
16
use Yoanm\JsonRpcServer\Infra\Resolver\ArrayMethodResolver;
17
use Yoanm\JsonRpcServer\Infra\Serialization\RawRequestSerializer;
18
use Yoanm\JsonRpcServer\Infra\Serialization\RawResponseSerializer;
19
20
/**
21
 * Class JsonRpcServerExtension
22
 */
23
class JsonRpcServerExtension extends Extension
24
{
25
    // Use this service to inject string request
26
    const ENDPOINT_SERVICE_NAME = 'yoanm.jsonrpc_server_sdk.endpoint';
27
    // Use this tag to inject your own resolver
28
    const METHOD_RESOLVER_TAG = 'yoanm.jsonrpc_server_sdk.method_resolver';
29
    // Use this tag to inject your JSON-RPC methods into the default method resolver
30
    const JSONRPC_METHOD_TAG = 'yoanm.jsonrpc_server_sdk.jsonrpc_method';
31
    // In case you use the ArrayMethodResolver, use this service to manually inject your JSON-RPC methods
32
    const DEFAULT_METHOD_RESOLVER_SERVICE_NAME   = 'yoanm.jsonrpc_server_sdk.infra.resolver.array_method_resolver';
33
34
35
    const JSONRPC_METHOD_TAG_METHOD_NAME_KEY = 'method';
36
37
38
    private $appResponseCreatorServiceId        = 'yoanm.jsonrpc_server_sdk.app.creator.response';
39
    private $appCustomExceptionCreatorServiceId = 'yoanm.jsonrpc_server_sdk.app.creator.custom_exception';
40
    private $appRequestDenormalizerServiceId    = 'yoanm.jsonrpc_server_sdk.app.serialization.request_denormalizer';
41
    private $appResponseNormalizerServiceId     = 'yoanm.jsonrpc_server_sdk.app.serialization.response_normalizer';
42
    private $appMethodManagerServiceId          = 'yoanm.jsonrpc_server_sdk.app.manager.method';
43
    private $appRequestHandlerServiceId         = 'yoanm.jsonrpc_server_sdk.app.handler.request';
44
45
    private $infraRawReqSerializerServiceId  = 'yoanm.jsonrpc_server_sdk.infra.serialization.raw_request_serializer';
46
    private $infraRawRespSerializerServiceId = 'yoanm.jsonrpc_server_sdk.infra.serialization.raw_response_serializer';
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    public function load(array $configs, ContainerBuilder $container)
52
    {
53
        // Use only references to avoid class instantiation
54
        // And don't use file configuration in order to not add Symfony\Component\Config as dependency
55
        $this->createPublicServiceDefinitions($container);
56
        $this->createInfraServiceDefinitions($container);
57
        $this->createAppServiceDefinitions($container);
58
    }
59
60
    /**
61
     * @param ContainerBuilder $container
62
     */
63
    protected function createAppServiceDefinitions(ContainerBuilder $container)
64
    {
65
        // RequestDenormalizer
66
        $container->setDefinition($this->appRequestDenormalizerServiceId, new Definition(RequestDenormalizer::class));
67
        // ResponseNormalizer
68
        $container->setDefinition($this->appResponseNormalizerServiceId, new Definition(ResponseNormalizer::class));
69
        // ResponseCreator
70
        $container->setDefinition($this->appResponseCreatorServiceId, new Definition(ResponseCreator::class));
71
        // CustomExceptionCreator
72
        $container->setDefinition(
73
            $this->appCustomExceptionCreatorServiceId,
74
            new Definition(CustomExceptionCreator::class)
75
        );
76
77
        // MethodManager
78
        $container->setDefinition(
79
            $this->appMethodManagerServiceId,
80
            new Definition(
81
                MethodManager::class,
82
                [
83
                    new Reference($this->getMethodResolverServiceId($container)),
84
                    new Reference($this->appCustomExceptionCreatorServiceId)
85
                ]
86
            )
87
        );
88
        // RequestHandler
89
        $container->setDefinition(
90
            $this->appRequestHandlerServiceId,
91
            new Definition(
92
                RequestHandler::class,
93
                [
94
                    new Reference($this->appMethodManagerServiceId),
95
                    new Reference($this->appResponseCreatorServiceId)
96
                ]
97
            )
98
        );
99
    }
100
101
    /**
102
     * @param ContainerBuilder $container
103
     */
104
    protected function createInfraServiceDefinitions(ContainerBuilder $container)
105
    {
106
        // RawRequestSerializer
107
        $container->setDefinition(
108
            $this->infraRawReqSerializerServiceId,
109
            new Definition(
110
                RawRequestSerializer::class,
111
                [new Reference($this->appRequestDenormalizerServiceId)]
112
            )
113
        );
114
115
        // RawResponseSerializer
116
        $container->setDefinition(
117
            $this->infraRawRespSerializerServiceId,
118
            new Definition(
119
                RawResponseSerializer::class,
120
                [new Reference($this->appResponseNormalizerServiceId)]
121
            )
122
        );
123
    }
124
125
    /**
126
     * @param ContainerBuilder $container
127
     */
128
    protected function createPublicServiceDefinitions(ContainerBuilder $container)
129
    {
130
        $container->setDefinition(
131
            self::ENDPOINT_SERVICE_NAME,
132
            (new Definition(
133
                JsonRpcEndpoint::class,
134
                [
135
                    new Reference($this->infraRawReqSerializerServiceId),
136
                    new Reference($this->appRequestHandlerServiceId),
137
                    new Reference($this->infraRawRespSerializerServiceId),
138
                    new Reference($this->appResponseCreatorServiceId)
139
                ]
140
            ))->setPrivate(false)
141
        );
142
143
        $container->setDefinition(
144
            self::DEFAULT_METHOD_RESOLVER_SERVICE_NAME,
145
            (new Definition(ArrayMethodResolver::class))->setPrivate(false)
146
        );
147
    }
148
149
    /**
150
     * @param ContainerBuilder $container
151
     */
152
    private function getMethodResolverServiceId(ContainerBuilder $container)
153
    {
154
        $serviceIdList = array_keys($container->findTaggedServiceIds(self::METHOD_RESOLVER_TAG));
155
        $serviceCount = count($serviceIdList);
156
        if ($serviceCount > 0) {
157
            if ($serviceCount > 1) {
158
                throw new LogicException(
159
                    sprintf(
160
                        'Only one method resolver could be defined, found following services : %s',
161
                        implode(', ', $serviceIdList)
162
                    )
163
                );
164
            }
165
            // Use the first result
166
            $resolverServiceId = array_shift($serviceIdList);
167
        } else {
168
            // Use ArrayMethodResolver as default resolver
169
            $resolverServiceId = self::DEFAULT_METHOD_RESOLVER_SERVICE_NAME;
170
            $this->loadJsonRpcMethodsFromTag($container);
171
        }
172
173
        return $resolverServiceId;
174
    }
175
176
    /**
177
     * @param ContainerBuilder $container
178
     */
179
    private function loadJsonRpcMethodsFromTag(ContainerBuilder $container)
180
    {
181
        // Check if methods have been defined by tags
182
        $methodServiceList = $container->findTaggedServiceIds(self::JSONRPC_METHOD_TAG);
183
        $defaultResolverDefinition = $container->getDefinition(self::DEFAULT_METHOD_RESOLVER_SERVICE_NAME);
184
185
        foreach ($methodServiceList as $serviceId => $tags) {
186
            $firstTag = array_shift($tags);
187
            if (!is_array($firstTag) || !array_key_exists(self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY, $firstTag)) {
188
                throw new LogicException(sprintf(
189
                    'Service %s is taggued as JSON-RPC method but does not have'
190
                        .'method name defined under "%s" tag attribute key',
191
                    $serviceId,
192
                    self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY
193
                ));
194
            }
195
            $defaultResolverDefinition->addMethodCall(
196
                'addMethod',
197
                [
198
                    new Reference($serviceId),
199
                    $firstTag[self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY]
200
                ]
201
            );
202
        }
203
    }
204
}
205