Completed
Push — v1.ns ( a6bb16...dc5273 )
by Timo
03:36
created

ServiceFactory::build()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
c 1
b 1
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
1
<?php
2
3
namespace PEIP\Factory;
4
/* 
5
 * To change this template, choose Tools | Templates
6
 * and open the template in the editor.
7
 */
8
9
/**
10
 * Description of ServiceFactory
11
 *
12
 * @author timo
13
 */
14
use PEIP\Util\Test;
15
use PEIP\Base\GenericBuilder;
16
use PEIP\Context\XMLContext;
17
18
class ServiceFactory {
19
     /**
20
     * Creates and initializes service instance from a given configuration.
21
     *
22
     * @access public
23
     * @param $config configuration of the service
24
     * @return object the initialized service instance
25
     */
26
    public static function createService(array $config){
27
        $args = array();
28
        //build arguments for constructor
29
        if(isset($config['constructor_arg'])){
30
            foreach($config['constructor_arg'] as $arg){
31
                $args[] = self::buildArg($arg);
32
            }
33
        }
34
        return self::buildAndModify($config, $args);
35
    }
36
37
        /**
38
     * Builds an arbitrary service/object instance from a config-obect.
39
     *
40
     * @static
41
     * @access protected
42
     * @param object $config configuration object to build a service instance from.
43
     * @param array $arguments arguments for the service constructor
44
     * @param string $defaultClass class to create instance for if none is set in config
45
     * @return object build and modified srvice instance
46
     */
47
    public static function doBuild($config, $arguments, $defaultClass = false){
48
        $cls = isset($config["class"]) ? trim((string)$config["class"]) : (string)$defaultClass;
49
        if($cls != ''){
50
            try {
51
                $constructor = isset($config["constructor"])?(string)$config["constructor"]:"";
52
                if($constructor != '' && Test::assertMethod($cls, $constructor)){
53
                    $service = call_user_func_array(array($cls, $constructor), $arguments);
54
                }else{
55
                    $service = self::build($cls, $arguments);
0 ignored issues
show
Documentation introduced by
$cls is of type string, but the function expects a object.

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...
56
                }
57
            }catch(\Exception $e){
58
                throw new \RuntimeException('Could not create Service "'.$cls.'" -> '.$e->getMessage());
59
            }
60
        }
61
        if(is_object($service)){
62
            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...
63
        }
64
        throw new \RuntimeException('Could not create Service "'.$cls.'". Class does not exist.');
65
    }
66
67
68
    /**
69
     * Utility function to build an object instance for given class with given constructor-arguments.
70
     *
71
     * @see GenericBuilder
72
     * @static
73
     * @access protected
74
     * @param object $className name of class to build instance for.
75
     * @param array $arguments arguments for the constructor
76
     * @return object build and modified srvice instance
77
     */
78
    public static function build($className, $arguments){
79
        return GenericBuilder::getInstance($className)->build($arguments);
0 ignored issues
show
Documentation introduced by
$className is of type object, but the function expects a string.

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...
80
    }
81
82
        /**
83
     * Builds single argument (to call a method with later) from a config-obect.
84
     *
85
     * @access protected
86
     * @param object $config configuration object to create argument from.
87
     * @return mixed build argument
88
     */
89 View Code Duplication
    protected function buildArg($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...
90
        if(trim((string)$config['value']) != ''){
91
            $arg = (string)$config['value'];
92
        }elseif($config->getName() == 'value'){
93
            $arg = (string)$config;
94
        }elseif($config->getName() == 'list'){
95
            $arg = array();
96
            foreach($config->children() as $entry){
97
                if($entry->getName() == 'value'){
98
                    if($entry['key']){
99
                        $arg[(string)$entry['key']] = (string)$entry;
100
                    }else{
101
                        $arg[] = (string)$entry;
102
                    }
103
                }elseif($entry->getName() == 'service'){
104
                    $arg[] = $this->provideService($entry);
0 ignored issues
show
Bug introduced by
The method provideService() does not seem to exist on object<PEIP\Factory\ServiceFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
105
                }
106
            }
107
        }elseif($config->getName() == 'service'){
108
            $arg = self::provideService($config);
0 ignored issues
show
Bug introduced by
The method provideService() does not seem to exist on object<PEIP\Factory\ServiceFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
109
        }elseif($config->list){
110
            $arg = $this->buildArg($config->list);
111
        }elseif($config->service){
112
            $arg = $this->buildArg($config->service);
113
        }
114
        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...
115
    }
116
117
    /**
118
     * Builds and modifies an arbitrary service/object instance from a config-obect.
119
     *
120
     * @see XMLContext::doBuild
121
     * @see PEIP\Factory\ServiceFactory::modifyService
122
     * @implements \PEIP\INF\Context\Context
123
     * @access public
124
     * @param object $config configuration array to build a service instance from.
125
     * @param array $arguments arguments for the service constructor
126
     * @param string $defaultClass class to create instance for if none is set in config
127
     * @return object build and modified srvice instance
128
     */
129
    public static function buildAndModify(array $config, $arguments, $defaultClass = ""){
130
        if((isset($config["class"]) && "" != (string)$config["class"])  || $defaultClass !== ""){
131
             $service = ServiceFactory::doBuild($config, $arguments, $defaultClass);
0 ignored issues
show
Documentation introduced by
$config is of type array, but the function expects a object.

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...
132
        }else{
133
            throw new \RuntimeException('Could not create Service. no class or reference given.');
134
        }
135
        if(isset($config["ref_property"])){
136
            $service = $service->{(string)$config["ref_property"]};
137
        }elseif(isset($config["ref_method"])){
138
                $args = array();
139
            if(isset($config['argument'])){
140
                        foreach($config['argument'] as $arg){
141
                            $args[] = $this->buildArg($arg);
0 ignored issues
show
Bug introduced by
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
142
                        }
143
                }
144
            $service = call_user_func_array(array($service, (string)$config["ref_method"]), $args);
145
        }
146
        if(!is_object($service)){
147
            throw new \RuntimeException('Could not create Service.');
148
        }
149
        $service = self::modifyService($service, $config);
0 ignored issues
show
Documentation introduced by
$config is of type array<string,?,{"ref_property":"?"}>, but the function expects a object.

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...
150
        
151
        return $service;
152
    }
153
154
155
    /**
156
     * Modifies a service instance from configuration.
157
     *  - Sets properties on the instance.
158
     *  -- Calls a public setter method if exists.
159
     *  -- Else sets a public property if exists.
160
     *  - Calls methods on the instance.
161
     *  - Registers listeners to events on the instance
162
     *
163
     * @access protected
164
     * @param object $service the service instance to modify
165
     * @param object $config configuration to get the modification instructions from.
166
     * @return object the modificated service
167
     */
168
    protected function modifyService($service, $config){        
169
        $config = is_array($config) ? new \ArrayObject($config) : $config;
170
        // set instance properties
171 View Code Duplication
        if(isset($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...
172
            foreach($config->property as $property){
173
                $arg = $this->buildArg($property);
174
                if($arg){
175
                    $setter = self::getSetter($property);
0 ignored issues
show
Bug introduced by
The method getSetter() does not seem to exist on object<PEIP\Factory\ServiceFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
176
                    if($setter &&  self::hasPublicProperty($service, 'Method', $setter)){
0 ignored issues
show
Bug introduced by
The method hasPublicProperty() does not seem to exist on object<PEIP\Factory\ServiceFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
177
                        $service->{$setter}($arg);
178
                    }elseif(in_array($property, self::hasPublicProperty($service, 'Property', $setter))){
0 ignored issues
show
Bug introduced by
The method hasPublicProperty() does not seem to exist on object<PEIP\Factory\ServiceFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
179
                        $service->$setter = $arg;
180
                    }
181
                }
182
            }
183
        }
184
        // call instance methods
185 View Code Duplication
        if(isset($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...
186
            foreach($config->action as $action){
187
                $method = (string)$action['method'] != '' ? (string)$action['method'] : NULL;
188
                if($method && self::hasPublicProperty($service, 'Method', $method)){
0 ignored issues
show
Bug introduced by
The method hasPublicProperty() does not seem to exist on object<PEIP\Factory\ServiceFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
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...
189
                    $args = array();
190
                    foreach($action->children() as $argument){
191
                        $args[] = $this->buildArg($argument);
192
                    }
193
                    call_user_func_array(array($service, (string)$action['method']), $args);
194
                }
195
            }
196
        }
197
        // register instance listeners
198
        if($service instanceof \PEIP\INF\Event\Connectable){
199 View Code Duplication
            if(isset($config->listener)){
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...
200
                foreach($config->listener as $listenerConf){
201
                    $event = (string)$listenerConf['event'];
202
                    $listener = $this->provideService($listenerConf);
0 ignored issues
show
Bug introduced by
The method provideService() does not seem to exist on object<PEIP\Factory\ServiceFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
203
                    $service->connect($event, $listener);
204
                }
205
            }
206
        }
207
        return $service;
208
    } 
209
}
210
211