Failed Conditions
Pull Request — feature/symfony-extension (#20)
by Yo
03:59 queued 02:08
created

createAppServiceDefinitions()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 1
dl 0
loc 33
ccs 0
cts 26
cp 0
crap 2
rs 8.8571
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
    public function load(array $configs, ContainerBuilder $container)
49
    {
50
        // Use only references to avoid class instantiation
51
        // And don't use file configuration in order to not add Symfony\Component\Config as dependency
52
        $this->createPublicServiceDefinitions($container);
53
        $this->createInfraServiceDefinitions($container);
54
        $this->createAppServiceDefinitions($container);
55
    }
56
57
    /**
58
     * @param ContainerBuilder $container
59
     */
60
    protected function createAppServiceDefinitions(ContainerBuilder $container)
61
    {
62
        // RequestDenormalizer
63
        $container->setDefinition($this->appRequestDenormalizerServiceId, new Definition(RequestDenormalizer::class));
64
        // ResponseNormalizer
65
        $container->setDefinition($this->appResponseNormalizerServiceId, new Definition(ResponseNormalizer::class));
66
        // ResponseCreator
67
        $container->setDefinition($this->appResponseCreatorServiceId, new Definition(ResponseCreator::class));
68
        // CustomExceptionCreator
69
        $container->setDefinition(
70
            $this->appCustomExceptionCreatorServiceId,
71
            new Definition(CustomExceptionCreator::class)
72
        );
73
74
        // MethodManager
75
        $container->setDefinition(
76
            $this->appMethodManagerServiceId,
77
            new Definition(
78
                MethodManager::class,
79
                [
80
                    new Reference($this->getMethodResolverServiceId($container)),
81
                    new Reference($this->appCustomExceptionCreatorServiceId)
82
                ]
83
            )
84
        );
85
        // RequestHandler
86
        $container->setDefinition(
87
            $this->appRequestHandlerServiceId,
88
            new Definition(
89
                RequestHandler::class,
90
                [
91
                    new Reference($this->appMethodManagerServiceId),
92
                    new Reference($this->appResponseCreatorServiceId)
93
                ]
94
            )
95
        );
96
    }
97
98
    /**
99
     * @param ContainerBuilder $container
100
     */
101
    protected function createInfraServiceDefinitions(ContainerBuilder $container)
102
    {
103
        // RawRequestSerializer
104
        $container->setDefinition(
105
            $this->infraRawReqSerializerServiceId,
106
            new Definition(
107
                RawRequestSerializer::class,
108
                [new Reference($this->appRequestDenormalizerServiceId)]
109
            )
110
        );
111
112
        // RawResponseSerializer
113
        $container->setDefinition(
114
            $this->infraRawRespSerializerServiceId,
115
            new Definition(
116
                RawResponseSerializer::class,
117
                [new Reference($this->appResponseNormalizerServiceId)]
118
            )
119
        );
120
    }
121
122
    /**
123
     * @param ContainerBuilder $container
124
     */
125
    protected function createPublicServiceDefinitions(ContainerBuilder $container)
126
    {
127
        $container->setDefinition(
128
            self::ENDPOINT_SERVICE_NAME,
129
            (new Definition(
130
                JsonRpcEndpoint::class,
131
                [
132
                    new Reference($this->infraRawReqSerializerServiceId),
133
                    new Reference($this->appRequestHandlerServiceId),
134
                    new Reference($this->infraRawRespSerializerServiceId),
135
                    new Reference($this->appResponseCreatorServiceId)
136
                ]
137
            ))->setPrivate(false)
138
        );
139
140
        $container->setDefinition(
141
            self::DEFAULT_METHOD_RESOLVER_SERVICE_NAME,
142
            (new Definition(ArrayMethodResolver::class))->setPrivate(false)
143
        );
144
    }
145
146
    /**
147
     * @param ContainerBuilder $container
148
     */
149
    private function getMethodResolverServiceId(ContainerBuilder $container)
150
    {
151
        $serviceIdList = array_keys($container->findTaggedServiceIds(self::METHOD_RESOLVER_TAG));
152
        $serviceCount = count($serviceIdList);
153
        if ($serviceCount > 0) {
154
            if ($serviceCount > 1) {
155
                throw new LogicException(
156
                    sprintf(
157
                        'Only one method resolver could be defined, found following services : %s',
158
                        implode(', ', $serviceIdList)
159
                    )
160
                );
161
            }
162
            // Use the first result
163
            $resolverServiceId = array_shift($serviceIdList);
164
        } else {
165
            // Use ArrayMethodResolver as default resolver
166
            $resolverServiceId = self::DEFAULT_METHOD_RESOLVER_SERVICE_NAME;
167
            $this->loadJsonRpcMethodsFromTag($container);
168
        }
169
170
        return $resolverServiceId;
171
    }
172
173
    private function loadJsonRpcMethodsFromTag(ContainerBuilder $container)
174
    {
175
        // Check if methods have been defined by tags
176
        $methodServiceList = $container->findTaggedServiceIds(self::JSONRPC_METHOD_TAG);
177
        $defaultResolverDefinition = $container->getDefinition(self::DEFAULT_METHOD_RESOLVER_SERVICE_NAME);
178
179
        foreach ($methodServiceList as $serviceId => $tags) {
180
            $firstTag = array_shift($tags);
181
            if (!is_array($firstTag) || !array_key_exists(self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY, $firstTag)) {
182
                throw new LogicException(sprintf(
183
                    'Service %s is taggued as JSON-RPC method but does not have'
184
                        .'method name defined under "%s" tag attribute key',
185
                    $serviceId,
186
                    self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY
187
                ));
188
            }
189
            $defaultResolverDefinition->addMethodCall(
190
                'addMethod',
191
                [
192
                    new Reference($serviceId),
193
                    $firstTag[self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY]
194
                ]
195
            );
196
        }
197
    }
198
}
199