Completed
Push — master ( 9df8e6...eec8c7 )
by Tobias
04:43
created

HttplugExtension   C

Complexity

Total Complexity 63

Size/Duplication

Total Lines 522
Duplicated Lines 10.34 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 87.39%

Importance

Changes 0
Metric Value
wmc 63
lcom 1
cbo 9
dl 54
loc 522
ccs 104
cts 119
cp 0.8739
rs 5.8893
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
B load() 0 45 6
B configureClients() 0 24 5
A configurePlugins() 0 18 4
C configurePluginByName() 0 62 15
B configureAuthentication() 0 37 6
C configureClient() 20 74 9
A createUri() 0 9 1
B configureAutoDiscoveryClients() 34 54 7
B registerAutoDiscoverableClient() 0 27 2
A getConfiguration() 0 4 1
A configurePlugin() 0 13 2
A decoratePluginWithProfilePlugin() 0 11 1
A configureStackPlugin() 0 13 2
A configureAutoDiscoveryFactory() 0 18 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like HttplugExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HttplugExtension, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Http\HttplugBundle\DependencyInjection;
4
5
use Http\Client\Common\BatchClient;
6
use Http\Client\Common\FlexibleHttpClient;
7
use Http\Client\Common\HttpMethodsClient;
8
use Http\Client\Common\Plugin\AuthenticationPlugin;
9
use Http\Discovery\HttpAsyncClientDiscovery;
10
use Http\Discovery\HttpClientDiscovery;
11
use Http\HttplugBundle\ClientFactory\DummyClient;
12
use Http\HttplugBundle\ClientFactory\PluginClientFactory;
13
use Http\HttplugBundle\Collector\ProfileClientFactory;
14
use Http\HttplugBundle\Collector\ProfilePlugin;
15
use Http\Message\Authentication\BasicAuth;
16
use Http\Message\Authentication\Bearer;
17
use Http\Message\Authentication\Wsse;
18
use Psr\Http\Message\UriInterface;
19
use Symfony\Component\Config\FileLocator;
20
use Symfony\Component\DependencyInjection\ChildDefinition;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Definition;
23
use Symfony\Component\DependencyInjection\DefinitionDecorator;
24
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
25
use Symfony\Component\DependencyInjection\Reference;
26
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
27
28
/**
29
 * @author David Buchmann <[email protected]>
30
 * @author Tobias Nyholm <[email protected]>
31
 */
32
class HttplugExtension extends Extension
33
{
34
    /**
35
     * {@inheritdoc}
36
     */
37 13
    public function load(array $configs, ContainerBuilder $container)
38
    {
39 13
        $configuration = $this->getConfiguration($configs, $container);
40 13
        $config = $this->processConfiguration($configuration, $configs);
41
42 13
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
43
44 13
        $loader->load('services.xml');
45 13
        $loader->load('plugins.xml');
46
47
        // Register default services
48 13
        foreach ($config['classes'] as $service => $class) {
49 13
            if (!empty($class)) {
50 1
                $container->register(sprintf('httplug.%s.default', $service), $class);
51 1
            }
52 13
        }
53
54
        // Set main aliases
55 13
        foreach ($config['main_alias'] as $type => $id) {
56 13
            $container->setAlias(sprintf('httplug.%s', $type), $id);
57 13
        }
58
59
        // Configure toolbar
60 13
        $profilingEnabled = $this->isConfigEnabled($container, $config['profiling']);
61 13
        if ($profilingEnabled) {
62 10
            $loader->load('data-collector.xml');
63
64 10
            if (!empty($config['profiling']['formatter'])) {
65
                // Add custom formatter
66
                $container
67 1
                    ->getDefinition('httplug.collector.formatter')
68 1
                    ->replaceArgument(0, new Reference($config['profiling']['formatter']))
69
                ;
70 1
            }
71
72
            $container
73 10
                ->getDefinition('httplug.formatter.full_http_message')
74 10
                ->addArgument($config['profiling']['captured_body_length'])
75
            ;
76 10
        }
77
78 13
        $this->configureClients($container, $config, $profilingEnabled);
79 13
        $this->configurePlugins($container, $config['plugins']); // must be after clients, as clients.X.plugins might use plugins as templates that will be removed
80 13
        $this->configureAutoDiscoveryClients($container, $config);
81 13
    }
82
83
    /**
84
     * Configure client services.
85
     *
86
     * @param ContainerBuilder $container
87
     * @param array            $config
88
     * @param bool             $profiling
89
     */
90 13
    private function configureClients(ContainerBuilder $container, array $config, $profiling)
0 ignored issues
show
Unused Code introduced by
The parameter $profiling 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...
91
    {
92 13
        $first = null;
93 13
        $clients = [];
94
95 13
        foreach ($config['clients'] as $name => $arguments) {
96 6
            if ($first === null) {
97
                // Save the name of the first configured client.
98 6
                $first = $name;
99 6
            }
100
101 6
            $this->configureClient($container, $name, $arguments, $this->isConfigEnabled($container, $config['profiling']));
102 6
            $clients[] = $name;
103 13
        }
104
105
        // If we have clients configured
106 13
        if ($first !== null) {
107
            // If we do not have a client named 'default'
108 6
            if (!isset($config['clients']['default'])) {
109
                // Alias the first client to httplug.client.default
110 6
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
111 6
            }
112 6
        }
113 13
    }
114
115
    /**
116
     * Configure all Httplug plugins or remove their service definition.
117
     *
118
     * @param ContainerBuilder $container
119
     * @param array            $config
120
     */
121 13
    private function configurePlugins(ContainerBuilder $container, array $config)
122
    {
123 13
        if (!empty($config['authentication'])) {
124
            $this->configureAuthentication($container, $config['authentication']);
125
        }
126 13
        unset($config['authentication']);
127
128 13
        foreach ($config as $name => $pluginConfig) {
129 13
            $pluginId = 'httplug.plugin.'.$name;
130
131 13
            if ($this->isConfigEnabled($container, $pluginConfig)) {
132 13
                $def = $container->getDefinition($pluginId);
133 13
                $this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId);
134 13
            } else {
135 13
                $container->removeDefinition($pluginId);
136
            }
137 13
        }
138 13
    }
139
140
    /**
141
     * @param string           $name
142
     * @param Definition       $definition
143
     * @param array            $config
144
     * @param ContainerBuilder $container  In case we need to add additional services for this plugin
145
     * @param string           $serviceId  Service id of the plugin, in case we need to add additional services for this plugin.
146
     */
147 13
    private function configurePluginByName($name, Definition $definition, array $config, ContainerBuilder $container, $serviceId)
148
    {
149
        switch ($name) {
150 13
            case 'cache':
151
                $definition
152
                    ->replaceArgument(0, new Reference($config['cache_pool']))
153
                    ->replaceArgument(1, new Reference($config['stream_factory']))
154
                    ->replaceArgument(2, $config['config']);
155
                break;
156 13
            case 'cookie':
157
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
158
                break;
159 13
            case 'decoder':
160 13
                $definition->addArgument([
161 13
                    'use_content_encoding' => $config['use_content_encoding'],
162 13
                ]);
163 13
                break;
164 13
            case 'history':
165
                $definition->replaceArgument(0, new Reference($config['journal']));
166
                break;
167 13
            case 'logger':
168 13
                $definition->replaceArgument(0, new Reference($config['logger']));
169 13
                if (!empty($config['formatter'])) {
170
                    $definition->replaceArgument(1, new Reference($config['formatter']));
171
                }
172 13
                break;
173 13
            case 'redirect':
174 13
                $definition->addArgument([
175 13
                    'preserve_header' => $config['preserve_header'],
176 13
                    'use_default_for_multiple' => $config['use_default_for_multiple'],
177 13
                ]);
178 13
                break;
179 13
            case 'retry':
180 13
                $definition->addArgument([
181 13
                    'retries' => $config['retry'],
182 13
                ]);
183 13
                break;
184 13
            case 'stopwatch':
185 13
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
186 13
                break;
187
188
            /* client specific plugins */
189
190 3
            case 'add_host':
191 3
                $uriService = $serviceId.'.host_uri';
192 3
                $this->createUri($container, $uriService, $config['host']);
193 3
                $definition->replaceArgument(0, new Reference($uriService));
194 3
                $definition->replaceArgument(1, [
195 3
                    'replace' => $config['replace'],
196 3
                ]);
197 3
                break;
198 1
            case 'header_append':
199 1
            case 'header_defaults':
200 1
            case 'header_set':
201 1
            case 'header_remove':
202 1
                $definition->replaceArgument(0, $config['headers']);
203 1
                break;
204
205
            default:
206
                throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name));
207
        }
208 13
    }
209
210
    /**
211
     * @param ContainerBuilder $container
212
     * @param array            $config
213
     *
214
     * @return array List of service ids for the authentication plugins.
215
     */
216 3
    private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication')
217
    {
218 3
        $pluginServices = [];
219
220 3
        foreach ($config as $name => $values) {
221 3
            $authServiceKey = sprintf($servicePrefix.'.%s.auth', $name);
222 3
            switch ($values['type']) {
223 3
                case 'bearer':
224
                    $container->register($authServiceKey, Bearer::class)
225
                        ->addArgument($values['token']);
226
                    break;
227
                case 'basic':
228
                    $container->register($authServiceKey, BasicAuth::class)
229
                        ->addArgument($values['username'])
230
                        ->addArgument($values['password']);
231
                    break;
232
                case 'wsse':
233
                    $container->register($authServiceKey, Wsse::class)
234
                        ->addArgument($values['username'])
235
                        ->addArgument($values['password']);
236
                    break;
237
                case 'service':
238
                    $authServiceKey = $values['service'];
239
                    break;
240
                default:
241
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
242
            }
243
244
            $pluginServiceKey = $servicePrefix.'.'.$name;
245
            $container->register($pluginServiceKey, AuthenticationPlugin::class)
246
                ->addArgument(new Reference($authServiceKey))
247
            ;
248
            $pluginServices[] = $pluginServiceKey;
249
        }
250
251
        return $pluginServices;
252
    }
253
254
    /**
255
     * @param ContainerBuilder $container
256
     * @param string           $clientName
257
     * @param array            $arguments
258
     * @param bool             $profiling
259
     */
260
    private function configureClient(ContainerBuilder $container, $clientName, array $arguments, $profiling)
261
    {
262
        $serviceId = 'httplug.client.'.$clientName;
263
264
        $plugins = [];
265
        foreach ($arguments['plugins'] as $plugin) {
266
            list($pluginName, $pluginConfig) = each($plugin);
267
            if ('reference' === $pluginName) {
268
                $plugins[] = $pluginConfig['id'];
269
            } elseif ('authentication' === $pluginName) {
270
                $plugins = array_merge($plugins, $this->configureAuthentication($container, $pluginConfig, $serviceId.'.authentication'));
271
            } else {
272
                $plugins[] = $this->configurePlugin($container, $serviceId, $pluginName, $pluginConfig);
273
            }
274
        }
275
276
        $pluginClientOptions = [];
277
        if ($profiling) {
278
            //Decorate each plugin with a ProfilePlugin instance.
279
            foreach ($plugins as $pluginServiceId) {
280
                $this->decoratePluginWithProfilePlugin($container, $pluginServiceId);
281
            }
282
283
            // To profile the requests, add a StackPlugin as first plugin in the chain.
284
            $stackPluginId = $this->configureStackPlugin($container, $clientName, $serviceId);
285
            array_unshift($plugins, $stackPluginId);
286
        }
287
288
        $container
289
            ->register($serviceId, DummyClient::class)
290
            ->setFactory([PluginClientFactory::class, 'createPluginClient'])
291
            ->addArgument(
292
                array_map(
293
                    function ($id) {
294
                        return new Reference($id);
295
                    },
296
                    $plugins
297
                )
298
            )
299
            ->addArgument(new Reference($arguments['factory']))
300
            ->addArgument($arguments['config'])
301
            ->addArgument($pluginClientOptions)
302
        ;
303
304
        /*
305
         * Decorate the client with clients from client-common
306
         */
307 View Code Duplication
        if ($arguments['flexible_client']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
308
            $container
309
                ->register($serviceId.'.flexible', FlexibleHttpClient::class)
310
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
311
                ->setPublic(false)
312
                ->setDecoratedService($serviceId)
313
            ;
314
        }
315
316 View Code Duplication
        if ($arguments['http_methods_client']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
317
            $container
318
                ->register($serviceId.'.http_methods', HttpMethodsClient::class)
319
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
320
                ->setPublic(false)
321
                ->setDecoratedService($serviceId)
322
            ;
323
        }
324
325 View Code Duplication
        if ($arguments['batch_client']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
326
            $container
327
                ->register($serviceId.'.batch_client', BatchClient::class)
328
                ->setArguments([new Reference($serviceId.'.batch_client.inner')])
329
                ->setPublic(false)
330
                ->setDecoratedService($serviceId)
331
            ;
332
        }
333
    }
334
335
    /**
336
     * Create a URI object with the default URI factory.
337
     *
338
     * @param ContainerBuilder $container
339
     * @param string           $serviceId Name of the private service to create
340
     * @param string           $uri       String representation of the URI
341
     */
342
    private function createUri(ContainerBuilder $container, $serviceId, $uri)
343
    {
344
        $container
345
            ->register($serviceId, UriInterface::class)
346
            ->setPublic(false)
347
            ->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
348
            ->addArgument($uri)
349
        ;
350
    }
351
352
    /**
353
     * Make the user can select what client is used for auto discovery. If none is provided, a service will be created
354
     * by finding a client using auto discovery.
355
     *
356
     * @param ContainerBuilder $container
357
     * @param array            $config
358
     */
359
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
360
    {
361
        $httpClient = $config['discovery']['client'];
362
363 View Code Duplication
        if (!empty($httpClient)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
364
            if ($httpClient === 'auto') {
365
                $httpClient = $this->registerAutoDiscoverableClient(
366
                    $container,
367
                    'auto_discovered_client',
368
                    $this->configureAutoDiscoveryFactory(
369
                        $container,
370
                        HttpClientDiscovery::class,
371
                        'auto_discovered_client',
372
                        $config
373
                    ),
374
                    $this->isConfigEnabled($container, $config['profiling'])
375
                );
376
            }
377
378
            $httpClient = new Reference($httpClient);
379
        }
380
381
        $asyncHttpClient = $config['discovery']['async_client'];
382
383 View Code Duplication
        if (!empty($asyncHttpClient)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
384
            if ($asyncHttpClient === 'auto') {
385
                $asyncHttpClient = $this->registerAutoDiscoverableClient(
386
                    $container,
387
                    'auto_discovered_async',
388
                    $this->configureAutoDiscoveryFactory(
389
                        $container,
390
                        HttpAsyncClientDiscovery::class,
391
                        'auto_discovered_async',
392
                        $config
393
                    ),
394
                    $this->isConfigEnabled($container, $config['profiling'])
395
                );
396
            }
397
398
            $asyncHttpClient = new Reference($asyncHttpClient);
399
        }
400
401
        if (null === $httpClient && null === $asyncHttpClient) {
402
            $container->removeDefinition('httplug.strategy');
403
404
            return;
405
        }
406
407
        $container
408
            ->getDefinition('httplug.strategy')
409
            ->addArgument($httpClient)
410
            ->addArgument($asyncHttpClient)
411
        ;
412
    }
413
414
    /**
415
     * Find a client with auto discovery and return a service Reference to it.
416
     *
417
     * @param ContainerBuilder   $container
418
     * @param string             $name
419
     * @param Reference|callable $factory
420
     * @param bool               $profiling
421
     *
422
     * @return string service id
423
     */
424
    private function registerAutoDiscoverableClient(ContainerBuilder $container, $name, $factory, $profiling)
425
    {
426
        $serviceId = 'httplug.auto_discovery.'.$name;
427
428
        $plugins = [];
429
        if ($profiling) {
430
            // To profile the requests, add a StackPlugin as first plugin in the chain.
431
            $plugins[] = $this->configureStackPlugin($container, $name, $serviceId);
432
        }
433
434
        $container
435
            ->register($serviceId, DummyClient::class)
436
            ->setFactory([PluginClientFactory::class, 'createPluginClient'])
437
            ->setArguments([
438
                array_map(
439
                    function ($id) {
440
                        return new Reference($id);
441
                    },
442
                    $plugins
443
                ),
444
                $factory,
445
                [],
446
            ])
447
        ;
448
449
        return $serviceId;
450
    }
451
452
    /**
453
     * {@inheritdoc}
454
     */
455
    public function getConfiguration(array $config, ContainerBuilder $container)
456
    {
457
        return new Configuration($container->getParameter('kernel.debug'));
458
    }
459
460
    /**
461
     * Configure a plugin using the parent definition from plugins.xml.
462
     *
463
     * @param ContainerBuilder $container
464
     * @param string           $serviceId
465
     * @param string           $pluginName
466
     * @param array            $pluginConfig
467
     *
468
     * @return string configured service id
469
     */
470
    private function configurePlugin(ContainerBuilder $container, $serviceId, $pluginName, array $pluginConfig)
471
    {
472
        $pluginServiceId = $serviceId.'.plugin.'.$pluginName;
473
474
        $definition = class_exists(ChildDefinition::class)
475
            ? new ChildDefinition('httplug.plugin.'.$pluginName)
476
            : new DefinitionDecorator('httplug.plugin.'.$pluginName);
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
477
478
        $this->configurePluginByName($pluginName, $definition, $pluginConfig, $container, $pluginServiceId);
479
        $container->setDefinition($pluginServiceId, $definition);
480
481
        return $pluginServiceId;
482
    }
483
484
    /**
485
     * Decorate the plugin service with a ProfilePlugin service.
486
     *
487
     * @param ContainerBuilder $container
488
     * @param string           $pluginServiceId
489
     */
490
    private function decoratePluginWithProfilePlugin(ContainerBuilder $container, $pluginServiceId)
491
    {
492
        $container->register($pluginServiceId.'.debug', ProfilePlugin::class)
493
            ->setDecoratedService($pluginServiceId)
494
            ->setArguments([
495
                new Reference($pluginServiceId.'.debug.inner'),
496
                new Reference('httplug.collector.collector'),
497
                new Reference('httplug.collector.formatter'),
498
            ])
499
            ->setPublic(false);
500
    }
501
502
    /**
503
     * Configure a StackPlugin for a client.
504
     *
505
     * @param ContainerBuilder $container
506
     * @param string           $clientName Client name to display in the profiler.
507
     * @param string           $serviceId  Client service id. Used as base for the StackPlugin service id.
508
     *
509
     * @return string configured StackPlugin service id
510
     */
511
    private function configureStackPlugin(ContainerBuilder $container, $clientName, $serviceId)
512
    {
513
        $pluginServiceId = $serviceId.'.plugin.stack';
514
515
        $definition = class_exists(ChildDefinition::class)
516
            ? new ChildDefinition('httplug.plugin.stack')
517
            : new DefinitionDecorator('httplug.plugin.stack');
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
518
519
        $definition->addArgument($clientName);
520
        $container->setDefinition($pluginServiceId, $definition);
521
522
        return $pluginServiceId;
523
    }
524
525
    /**
526
     * Configure the discovery factory when profiling is enabled to get client decorated with a ProfileClient.
527
     *
528
     * @param ContainerBuilder $container
529
     * @param string           $discovery
530
     * @param string           $name
531
     * @param array            $config
532
     *
533
     * @return callable|Reference
534
     */
535
    private function configureAutoDiscoveryFactory(ContainerBuilder $container, $discovery, $name, array $config)
536
    {
537
        $factory = [$discovery, 'find'];
538
        if ($this->isConfigEnabled($container, $config['profiling'])) {
539
            $factoryServiceId = 'httplug.auto_discovery.'.$name.'.factory';
540
            $container->register($factoryServiceId, ProfileClientFactory::class)
541
                ->setPublic(false)
542
                ->setArguments([
543
                    $factory,
544
                    new Reference('httplug.collector.collector'),
545
                    new Reference('httplug.collector.formatter'),
546
                    new Reference('debug.stopwatch'),
547
                ]);
548
            $factory = new Reference($factoryServiceId);
549
        }
550
551
        return $factory;
552
    }
553
}
554