XMLContext::createRouter()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 7
Ratio 77.78 %

Importance

Changes 4
Bugs 2 Features 0
Metric Value
c 4
b 2
f 0
dl 7
loc 9
rs 9.6666
cc 2
eloc 6
nc 2
nop 2
1
<?php
2
3
namespace PEIP\Context;
4
5
namespace PEIP\Context;
6
7
/*
8
 * This file is part of the PEIP package.oBuild
9
 * (c) 2009-2016 Timo Michna <timomichna/yahoo.de>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
/*
16
 * XMLContext
17
 *
18
 * @author Timo Michna <timomichna/yahoo.de>
19
 * @package PEIP
20
 * @subpackage context
21
 * @implements \PEIP\INF\Context\Context, \PEIP\INF\Channel\ChannelResolver
22
 */
23
24
25
use PEIP\Base\GenericBuilder;
26
use PEIP\Channel\ChannelRegistry;
27
use PEIP\Context\XMLContextReader;
28
use PEIP\Factory\ServiceFactory;
29
use PEIP\Plugins\BasePlugin;
30
use PEIP\Service\HeaderServiceActivator;
31
use PEIP\Service\ServiceProvider;
32
33
class XMLContext implements
34
        \PEIP\INF\Context\Context,
35
        \PEIP\INF\Channel\ChannelResolver
36
{
37
    protected $services = [],
38
        $configs = [],
39
        $gateways = [],
40
        $nodeBuilders = [],
41
        $channelRegistry,
42
        $serviceProvider;
43
44
    /**
45
     * constructor.
46
     *
47
     * @param string $string the configuration string
48
     *
49
     * @return
50
     */
51
    public function __construct($string)
52
    {
53
        $this->initNodeBuilders();
54
        $reader = new XMLContextReader($string);
55
56
57
        $serviceActivator = new HeaderServiceActivator([$this, 'addConfig'], 'NODE');
58
59
        $reader->connect('read_node', $serviceActivator);
0 ignored issues
show
Documentation introduced by
$serviceActivator is of type object<PEIP\Service\HeaderServiceActivator>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
60
        $reader->read();
61
    }
62
63
    public function addConfig($config)
64
    {
65
        return $this->getServiceProvider()->addConfig($config);
66
    }
67
68
    public function handleReadConfig(\PEIP\INF\Event\Event $event)
69
    {
70
        $this->addConfig($event->getHeader('NODE'));
71
    }
72
73
    /**
74
     * Creates and returns a XMLContext instance from a given config-string.
75
     *
76
     * @param string $string the configuration string
77
     *
78
     * @throws RuntimeException
79
     *
80
     * @return XMLContext the context instance
81
     */
82
    public static function createFromString($string)
83
    {
84
        return new self($string);
85
    }
86
87
    /**
88
     * Creates and returns a XMLContext instance from a given config-file.
89
     *
90
     * @param string $file the path to the configuration file
91
     *
92
     * @throws RuntimeException
93
     *
94
     * @return XMLContext the context instance
95
     */
96
    public static function createFromFile($file)
97
    {
98
        if (file_exists($file)) {
99
            return self::createFromString(file_get_contents($file));
100
        } else {
101
            throw new \RuntimeException('Cannot open file  "'.$file.'".');
102
        }
103
    }
104
105
    /**
106
     * Initializes the context.
107
     *
108
     * @return void
109
     */
110
    protected function init()
111
    {
112
        $xml = $this->simpleXML;
0 ignored issues
show
Bug introduced by
The property simpleXML does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
113
        $this->channelRegistry = ChannelRegistry::getInstance();
114
        // register this context as a service if id is set.
115
        if ($xml['id']) {
116
            $this->services[(string) $xml['id']] = $this;
117
        }
118
        // build services
119
        foreach ($xml->children() as $entry) {
120
            $this->buildNode($entry);
121
        }
122
    }
123
124
    /**
125
     * Registers a callable as builder for given node-name.
126
     *
127
     * @implements \PEIP\INF\Context\Context
128
     *
129
     * @param string   $nodeName the name of the node
130
     * @param callable $callable a callable which creates instances for node-name
131
     */
132
    public function registerNodeBuilder($nodeName, $callable)
133
    {
134
        return  $this->getServiceProvider()->registerNodeBuilder($nodeName, $callable);
135
    }
136
137
    /**
138
     * Registers a context-plugin instance.
139
     *
140
     * @implements \PEIP\INF\Context\Context
141
     *
142
     * @param \PEIP\INF\Context\ContextPlugin $plugin a plugin instance
143
     */
144
    public function addPlugin(\PEIP\INF\Context\ContextPlugin $plugin)
145
    {
146
        $plugin->init($this);
147
    }
148
149
    /**
150
     * Creates a registers a context-plugin instance from a config object.
151
     *
152
     * @param object $config configuration object for the plugin
153
     *
154
     * @return
155
     */
156
    public function createPlugin($config)
157
    {
158
        $plugin = $this->createService($config);
159
        $this->addPlugin($plugin);
160
    }
161
162
    /**
163
     * Adds a context instance to the services stack.
164
     * Note: Object instances registered with the included context will
165
     * overwrite any instance with the same id on the including context.
166
     * If you need a different behavior, please, make use
167
     * of an include-tag in your configuration before your main
168
     * configuration part.
169
     * eg.:
170
     * <config>
171
     *    <include file="path/to/include/context/config.xml"/>
172
     *    <!-- main configuration -->
173
     * </config>.
174
     */
175
    public function includeContext(XMLContext $context)
176
    {
177
        $this->services = array_merge($this->services, $context->getServices());
178
    }
179
180
    /**
181
     * Creates and adds a context from file.
182
     *
183
     * @see XMLContext::includeContext
184
     *
185
     * @param string $filePath
186
     */
187
    public function includeContextFromFile($filePath)
188
    {
189
        if (file_exists($filePath)) {
190
            $this->includeContextFromString(file_get_contents($filePath));
191
        }
192
    }
193
194
    /**
195
     * Creates and adds a context from string.
196
     *
197
     * @see XMLContext::includeContext
198
     *
199
     * @param string $configString the config to include
200
     */
201
    public function includeContextFromString($configString)
202
    {
203
        $context = new self($configString);
204
        $this->includeContext($context);
205
    }
206
207
    /**
208
     * Creates a context instance from a config object and includes it.
209
     *
210
     * @see XMLContext::includeContext
211
     *
212
     * @param object $config the configuration for the context
213
     */
214
    protected function createContext($config)
215
    {
216
        if ((string) $config['file'] != '') {
217
            $this->includeContextFromFile((string) $config['file']);
218
        }
219
    }
220
221
    public function getServiceProvider()
222
    {
223
        return isset($this->serviceProvider)
224
            ? $this->serviceProvider
225
            : $this->serviceProvider = new ServiceProvider();
226
    }
227
228
    /**
229
     * Registers the build-methods for the main-components with this context.
230
     * Note: This method and subsequent registered methods of this class are
231
     * candidates for refactoring. Because this class has grown much to large
232
     * and for better design and flexibility the core builder-methods should be
233
     * put into a core context-plugin.
234
     *
235
     * @see XMLContext::includeContext
236
     */
237
    protected function initNodeBuilders()
238
    {
239
        $builders = [
240
            'include'                   => 'createContext',
241
            'plugin'                    => 'createPlugin',
242
            'channel'                   => 'createChannel',
243
            'publish_subscribe_channel' => 'createSubscribableChannel',
244
            'service'                   => 'initService',
245
            'service_activator'         => 'createServiceActivator',
246
            'gateway'                   => 'createGateway',
247
            'splitter'                  => 'createSplitter',
248
            'transformer'               => 'createTransformer',
249
            'router'                    => 'createRouter',
250
            'aggregator'                => 'createAggregator',
251
            'wiretap'                   => 'createWiretap',
252
253
        ];
254
        $plugin = new BasePlugin();
255
        $this->addPlugin($plugin);
256
        foreach ($builders as $nodeName => $method) {
257
            $this->registerNodeBuilder($nodeName, [$this, $method]);
258
        }
259
    }
260
261
    /**
262
     * Builds a specific configuration-node. Calls the build-method which
263
     * is registered with the node-name. If none is registered does nothing.
264
     *
265
     * @see XMLContext::doCreateChannel
266
     *
267
     * @param object $node configuration-node
268
     *
269
     * @return void
270
     */
271
    protected function buildNode($node)
272
    {
273
        $nodeName = $node->getName();
274
        // call the builder method registered for the node.
275
        if (array_key_exists($nodeName, $this->nodeBuilders)) {
276
            call_user_func($this->nodeBuilders[$nodeName], $node);
277
        }
278
    }
279
280
    /**
281
     * Resolves a channel-name and returns channel-instace if found.
282
     * Main purpose is to allow the context to act as a channel-resolver for
283
     * mainly routers, hence implement the \PEIP\INF\Channel\ChannelResolver pattern.
284
     * Note: Channels are registerd globally in a registry per thread/process.
285
     * This allows to connect many context instances through channels without
286
     * coupling them by in a include in configuration.
287
     *
288
     * @see \PEIP\INF\Channel\ChannelResolver
289
     * @implements \PEIP\INF\Channel\ChannelResolver
290
     *
291
     * @param string $channelName the name/id of the channel to return
292
     *
293
     * @return \PEIP\INF\Channel\Channel
294
     */
295
    public function resolveChannelName($channelName)
296
    {
297
        return $this->channelRegistry->get($channelName);
298
    }
299
300
    /**
301
     * returns a service for a given id.
302
     *
303
     * @implements \PEIP\INF\Context\Context
304
     *
305
     * @param mixed $id the id for the service
306
     *
307
     * @return object the service instance if found
308
     */
309
    public function getService($id)
310
    {
311
        return $this->getServiceProvider()->provideService($id);
312
    }
313
314
    /**
315
     * returns all registered services.
316
     *
317
     * @return array registered services
318
     */
319
    public function getServices()
320
    {
321
        return $this->getServiceProvider()->getServices();
322
    }
323
324
    /**
325
     * Checks wether a service with a given id is registered.
326
     *
327
     * @param mixed $id the id for the service
328
     *
329
     * @return bool wether service is registered
330
     */
331
    public function hasService($id)
332
    {
333
        return isset($this->services[$id]);
334
    }
335
336
    /**
337
     * Tries to receive a service for a given id.
338
     * Throws RuntimeException if no service is found.
339
     *
340
     * @param mixed $id the id for the service
341
     *
342
     * @throws RuntimeException
343
     *
344
     * @return object the service instance if found
345
     */
346
    protected function requestService($id)
347
    {
348
        $service = $this->getService($id);
349
        if ($service === null) {
350
            throw new \RuntimeException('Service "'.$id.'" not found.');
351
        }
352
353
        return $service;
354
    }
355
356
    /**
357
     * Creates and initializes service instance from a given configuration.
358
     * Registers instance if id is set in config.
359
     *
360
     * @param object $config
361
     *
362
     * @return \PEIP\INF\Context\ContextPlugin|null the initialized service instance
363
     */
364
    protected function initService($config)
365
    {
366
        $id = trim((string) $config['id']);
367
        if ($id != '') {
368
            return $this->services[$id] = $this->createService($config);
369
        }
370
    }
371
372
    /**
373
     * Creates and initializes service instance from a given configuration.
374
     *
375
     * @param $config
376
     *
377
     * @return \PEIP\INF\Context\ContextPlugin the initialized service instance
378
     */
379
    public function createService($config)
380
    {
381
        return ServiceFactory::createService($config);
382
    }
383
384
    /**
385
     * Modifies a service instance from configuration.
386
     *  - Sets properties on the instance.
387
     *  -- Calls a public setter method if exists.
388
     *  -- Else sets a public property if exists.
389
     *  - Calls methods on the instance.
390
     *  - Registers listeners to events on the instance.
391
     *
392
     * @param object $service the service instance to modify
393
     * @param object $config  configuration to get the modification instructions from.
394
     *
395
     * @return object the modificated service
396
     */
397
    protected function modifyService($service, $config)
398
    {
399
        // set instance properties
400 View Code Duplication
        if ($config->property) {
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...
401
            foreach ($config->property as $property) {
402
                $arg = $this->buildArg($property);
403
                if ($arg) {
404
                    $setter = self::getSetter($property);
405
                    if ($setter && self::hasPublicProperty($service, 'Method', $setter)) {
406
                        $service->{$setter}($arg);
407
                    } elseif (in_array($property, self::hasPublicProperty($service, 'Property', $setter))) {
408
                        $service->$setter = $arg;
409
                    }
410
                }
411
            }
412
        }
413
        // call instance methods
414 View Code Duplication
        if ($config->action) {
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...
415
            foreach ($config->action as $action) {
416
                $method = (string) $action['method'] != '' ? (string) $action['method'] : null;
417
                if ($method && self::hasPublicProperty($service, 'Method', $method)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $method of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
418
                    $args = [];
419
                    foreach ($action->children() as $argument) {
420
                        $args[] = $this->buildArg($argument);
421
                    }
422
                    call_user_func_array([$service, (string) $action['method']], $args);
423
                }
424
            }
425
        }
426
        // register instance listeners
427 View Code Duplication
        if ($service instanceof \PEIP\INF\Event\Connectable) {
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...
428
            if ($config->listener) {
429
                foreach ($config->listener as $listenerConf) {
430
                    $event = (string) $listenerConf['event'];
431
                    $listener = $this->provideService($listenerConf);
432
                    $service->connect($event, $listener);
433
                }
434
            }
435
        }
436
437
        return $service;
438
    }
439
440
    /**
441
     * returns gateway instance forgiven id.
442
     * the use of this method is deprecated. Use getService instead.
443
     *
444
     * @deprecated
445
     *
446
     * @param mixed $id the id ofthe gateway
447
     *
448
     * @return object the gateway instance
449
     */
450
    public function getGateway($id)
451
    {
452
        return $this->getServiceProvider()->provideService($id);
453
    }
454
455
    /**
456
     * Creates a pollable channel from a configuration object.
457
     *
458
     * @see XMLContext::doCreateChannel
459
     *
460
     * @param object $config configuration object for the pollable channel.
461
     *
462
     * @return \PEIP\INF\Channel\Channel the created pollable channel instance
463
     */
464
    public function createChannel($config)
465
    {
466
        return $this->doCreateChannel($config, 'PollableChannel');
467
    }
468
469
    /**
470
     * Creates a subscribable channel from a configuration object.
471
     *
472
     * @see XMLContext::doCreateChannel
473
     *
474
     * @param object $config configuration object for the subscribable channel.
475
     *
476
     * @return \PEIP\INF\Channel\Channel the created subscribable channel instance
477
     */
478
    public function createSubscribableChannel($config)
479
    {
480
        return $this->doCreateChannel($config, 'PublishSubscribeChannel');
481
    }
482
483
    /**
484
     * Creates and registers arbitrary channel from a configuration object and additional information.
485
     *
486
     * @param object $config              configuration object for the channel.
487
     * @param string $defaultChannelClass the channel class to use if none is set in config
488
     * @param $additionalArguments additional arguments for the channel constructor (without first arg = id)
489
     *
490
     * @return \PEIP\INF\Channel\Channel the created channel instance
491
     */
492 View Code Duplication
    public function doCreateChannel($config, $defaultChannelClass, array $additionalArguments = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
493
    {
494
        $id = (string) $config['id'];
495
        if ($id != '') {
496
            array_unshift($additionalArguments, $id);
497
            $channel = $this->buildAndModify($config, $additionalArguments, $defaultChannelClass);
498
            $this->channelRegistry->register($channel);
499
500
            return $channel;
501
        }
502
    }
503
504
    /**
505
     * Creates and registers gateway from a configuration object.
506
     *
507
     * @see XMLContext::initNodeBuilders
508
     *
509
     * @param object $config       configuration object for the gateway.
510
     * @param string $defaultClass the class to use if none is set in config.
511
     *
512
     * @return object the gateway instance
513
     */
514 View Code Duplication
    public function createGateway($config, $defaultClass = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
515
    {
516
        $args = [
517
            $this->getRequestChannel($config),
518
            $this->getReplyChannel($config),
519
        ];
520
        $defaultClass = $defaultClass ? $defaultClass : 'SimpleMessagingGateway';
521
        $gateway = $this->buildAndModify($config, $args, $defaultClass);
522
        $id = (string) $config['id'];
523
        $this->gateways[$id] = $gateway;
524
525
        return $gateway;
526
    }
527
528
    /**
529
     * Creates and registers router from a configuration object.
530
     * Adds this context instance as channel-resolver to the router if
531
     * none is set in config.
532
     *
533
     * @see XMLContext::resolveChannelName
534
     * @see XMLContext::initNodeBuilders
535
     *
536
     * @param object $config       configuration object for the gateway.
537
     * @param string $defaultClass the class to use if none is set in config.
538
     *
539
     * @return object the router instance
540
     */
541 View Code Duplication
    public function createRouter($config, $defaultClass = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
542
    {
543
        $resolver = $config['channel_resolver'] ? (string) $config['channel_resolver'] : $this->channelRegistry;
544
545
        return $this->buildAndModify($config, [
546
            $resolver,
547
            $this->doGetChannel('input', $config),
548
        ], $defaultClass);
549
    }
550
551
    /**
552
     * Creates and registers splitter from a configuration object.
553
     *
554
     * @see XMLContext::initNodeBuilders
555
     * @see XMLContext::createReplyMessageHandler
556
     *
557
     * @param object $config configuration object for the splitter.
558
     *
559
     * @return object the splitter instance
560
     */
561
    public function createSplitter($config)
562
    {
563
        return $this->createReplyMessageHandler($config);
564
    }
565
566
    /**
567
     * Creates and registers transformer from a configuration object.
568
     *
569
     * @see XMLContext::initNodeBuilders
570
     * @see XMLContext::createReplyMessageHandler
571
     *
572
     * @param object $config configuration object for the transformer.
573
     *
574
     * @return object the transformer instance
575
     */
576
    public function createTransformer($config)
577
    {
578
        return $this->createReplyMessageHandler($config);
579
    }
580
581
    /**
582
     * Creates aggregator from a configuration object.
583
     *
584
     * @see XMLContext::initNodeBuilders
585
     * @see XMLContext::createReplyMessageHandler
586
     *
587
     * @param object $config configuration object for the aggregator.
588
     *
589
     * @return object the aggregator instance
590
     */
591
    public function createAggregator($config)
592
    {
593
        return $this->createReplyMessageHandler($config);
594
    }
595
596
    /**
597
     * Creates wiretap from a configuration object.
598
     *
599
     * @see XMLContext::initNodeBuilders
600
     * @see XMLContext::createReplyMessageHandler
601
     *
602
     * @param object $config configuration object for the wiretap.
603
     *
604
     * @return object the wiretap instance
605
     */
606
    public function createWiretap($config)
607
    {
608
        return $this->createReplyMessageHandler($config, 'Wiretap');
609
    }
610
611
    /**
612
     * Creates a reply-message-handler from a configuration object.
613
     *
614
     * @see XMLContext::initNodeBuilders
615
     *
616
     * @param object $config       configuration object for the reply-message-handler.
617
     * @param string $defaultClass the class to use if none is set in config.
618
     *
619
     * @return object the reply-message-handler instance
620
     */
621
    public function createReplyMessageHandler($config, $defaultClass = false)
622
    {
623
        return $this->buildAndModify($config, $this->getReplyHandlerArguments($config), $defaultClass);
624
    }
625
626
    /**
627
     * Creates and registers service-activator from a configuration object.
628
     *
629
     * @see XMLContext::initNodeBuilders
630
     *
631
     * @param object $config       configuration object for the service-activator.
632
     * @param string $defaultClass the class to use if none is set in config.
633
     *
634
     * @return object the service-activator instance
635
     */
636 View Code Duplication
    public function createServiceActivator($config, $defaultClass = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
637
    {
638
        $method = (string) $config['method'];
639
        $service = $this->getService((string) $config['ref']);
640
        if ($method && $service) {
641
            $args = $this->getReplyHandlerArguments($config);
642
            array_unshift($args, [
643
                $service,
644
                $method,
645
            ]);
646
            $defaultClass = $defaultClass ? $defaultClass : 'ServiceActivator';
647
648
            return $this->buildAndModify($config, $args, $defaultClass);
649
        }
650
    }
651
652
    /**
653
     * Provides a service for a configuration object.
654
     * Returns reference to a service if configured, otherwise
655
     * creates new service instance.
656
     *
657
     * @see XMLContext::getService
658
     * @see XMLContext::createService
659
     *
660
     * @param object $config configuration object for the service.
661
     *
662
     * @return
663
     */
664
    protected function provideService($config)
665
    {
666
        $ref = trim((string) $config['ref']);
667
        if ($ref != '') {
668
            $service = $this->getService($ref);
669
        } else {
670
            $service = $this->createService($config);
671
        }
672
673
        return $service;
674
    }
675
676
    /**
677
     * Utility method to return a (camel-cased) setter method-name for a property of a config-obect.
678
     * Returns setter-name if set in configuration, otherwise if name of property is set, returns
679
     * camel-cased setter-name build from property-name.
680
     *
681
     * @see XMLContext::getService
682
     * @see XMLContext::createService
683
     *
684
     * @param object $config configuration object for the setter-method.
685
     *
686
     * @return string camel-cased
687
     */
688
    protected static function getSetter($config)
689
    {
690
        if ($config['setter']) {
691
            $setter = (string) $config['setter'];
692
        } elseif ($config['name']) {
693
            $setter = 'set'.ucfirst((string) $config['name']);
694
        }
695
696
        return $setter;
0 ignored issues
show
Bug introduced by
The variable $setter does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
697
    }
698
699
    /**
700
     * Builds single argument (to call a method with later) from a config-obect.
701
     *
702
     * @param object $config configuration object to create argument from.
703
     *
704
     * @return mixed build argument
705
     */
706
    protected function buildArg($config)
707
    {
708
        if (trim((string) $config['value']) != '') {
709
            $arg = (string) $config['value'];
710
        } elseif ($config->getName() == 'value') {
711
            $arg = (string) $config;
712
        } elseif ($config->getName() == 'list') {
713
            $arg = [];
714
            foreach ($config->children() as $entry) {
715
                if ($entry->getName() == 'value') {
716
                    if ($entry['key']) {
717
                        $arg[(string) $entry['key']] = (string) $entry;
718
                    } else {
719
                        $arg[] = (string) $entry;
720
                    }
721
                } elseif ($entry->getName() == 'service') {
722
                    $arg[] = $this->provideService($entry);
723
                }
724
            }
725
        } elseif ($config->getName() == 'service') {
726
            $arg = $this->provideService($config);
727
        } elseif ($config->list) {
728
            $arg = $this->buildArg($config->list);
729
        } elseif ($config->service) {
730
            $arg = $this->buildArg($config->service);
731
        }
732
733
        return $arg;
0 ignored issues
show
Bug introduced by
The variable $arg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
734
    }
735
736
    /**
737
     * Utility method to create arguments for a reply-handler constructor from a config-obect.
738
     *
739
     * @param object $config configuration object to create arguments from.
740
     *
741
     * @return \PEIP\INF\Channel\Channel[] build arguments
742
     */
743 View Code Duplication
    protected function getReplyHandlerArguments($config)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
744
    {
745
        $args = [
746
            $this->doGetChannel('input', $config),
747
            $this->doGetChannel('output', $config),
748
        ];
749
        if ($args[0] == null) {
750
            throw new \RuntimeException('Could not receive input channel.');
751
        }
752
753
        return $args;
754
    }
755
756
    /**
757
     * Utility method to return a request-channel from a config-obect.
758
     *
759
     * @see XMLContext::doGetChannel
760
     *
761
     * @param object $config configuration object to return request-channel from.
762
     *
763
     * @return \PEIP\INF\Channel\Channel request-channel
764
     */
765
    protected function getRequestChannel($config)
766
    {
767
        return $this->doGetChannel('request', $config);
768
    }
769
770
    /**
771
     * Utility method to return a reply-channel from a config-obect.
772
     *
773
     * @see XMLContext::doGetChannel
774
     *
775
     * @param object $config configuration object to return reply-channel from.
776
     *
777
     * @return \PEIP\INF\Channel\Channel reply-channel
778
     */
779
    protected function getReplyChannel($config)
780
    {
781
        return $this->doGetChannel('reply', $config);
782
    }
783
784
    /**
785
     * Utility method to return a certainn channel from a config-obect.
786
     *
787
     * @param string the configuration type ofthe channel (e.g.: 'reply', 'request')
788
     * @param object $config configuration object to return channel from.
789
     * @param string $type
790
     *
791
     * @return \PEIP\INF\Channel\Channel reply-channel
792
     */
793
    public function doGetChannel($type, $config)
794
    {
795
        $channelName = $config[$type.'_channel']
796
            ? $config[$type.'_channel']
797
            : $config['default_'.$type.'_channel'];
798
799
        return $this->serviceProvider->provideService(trim((string) $channelName));
800
    }
801
802
    /**
803
     * Builds and modifies an arbitrary service/object instance from a config-obect.
804
     *
805
     * @see XMLContext::doBuild
806
     * @see XMLContext::modifyService
807
     * @implements \PEIP\INF\Context\Context
808
     *
809
     * @param object $config       configuration object to build a service instance from.
810
     * @param array  $arguments    arguments for the service constructor
811
     * @param string $defaultClass class to create instance for if none is set in config
812
     *
813
     * @return object build and modified srvice instance
814
     */
815
    public function buildAndModify($config, $arguments, $defaultClass = false)
816
    {
817
        return ServiceFactory::buildAndModify($config, $arguments, $defaultClass);
0 ignored issues
show
Documentation introduced by
$config is of type object, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
It seems like $defaultClass defined by parameter $defaultClass on line 815 can also be of type false; however, PEIP\Factory\ServiceFactory::buildAndModify() does only seem to accept string, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
818
    }
819
820
    /**
821
     * Builds an arbitrary service/object instance from a config-obect.
822
     *
823
     * @static
824
     *
825
     * @param object $config       configuration object to build a service instance from.
826
     * @param array  $arguments    arguments for the service constructor
827
     * @param string $defaultClass class to create instance for if none is set in config
828
     *
829
     * @return object build and modified srvice instance
830
     */
831
    protected static function doBuild($config, $arguments, $defaultClass = false)
832
    {
833
        $cls = $config['class'] ? trim((string) $config['class']) : (string) $defaultClass;
834
        if ($cls != '') {
835
            try {
836
                $constructor = (string) $config['constructor'];
837
                if ($constructor != '') {
838
                    $service = call_user_func_array([$cls, $constructor], $arguments);
839
                } else {
840
                    $service = self::build($cls, $arguments);
841
                }
842
            } catch (\Exception $e) {
843
                throw new \RuntimeException('Could not create Service "'.$cls.'" -> '.$e->getMessage());
844
            }
845
        }
846
        if (is_object($service)) {
847
            return $service;
0 ignored issues
show
Bug introduced by
The variable $service does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
848
        }
849
        throw new \RuntimeException('Could not create Service "'.$cls.'". Class does not exist.');
850
    }
851
852
    /**
853
     * Utility function to build an object instance for given class with given constructor-arguments.
854
     *
855
     * @see GenericBuilder
856
     * @static
857
     *
858
     * @param string $className name of class to build instance for.
859
     * @param array  $arguments arguments for the constructor
860
     *
861
     * @return object build and modified srvice instance
862
     */
863
    protected static function build($className, $arguments)
864
    {
865
        return GenericBuilder::getInstance($className)->build($arguments);
866
    }
867
868
    /**
869
     * @param string $type
870
     * @param string $name
871
     */
872
    protected static function hasPublicProperty($service, $type, $name)
873
    {
874
        $reflection = GenericBuilder::getInstance(get_class($service))->getReflectionClass();
875
        if ($reflection->{'has'.$type}($name) && $reflection->{'get'.$type}($name)->isPublic()) {
876
            return true;
877
        }
878
879
        return false;
880
    }
881
}
882