Completed
Pull Request — master (#5)
by mw
01:35
created

getReturnValueFromSingletonFor()   C

Complexity

Conditions 8
Paths 12

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 16
cts 16
cp 1
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 15
nc 12
nop 2
crap 8
1
<?php
2
3
namespace Onoi\CallbackContainer;
4
5
use Closure;
6
use Onoi\CallbackContainer\Exception\ServiceTypeMismatchException;
7
use Onoi\CallbackContainer\Exception\ServiceCircularReferenceException;
8
use Onoi\CallbackContainer\Exception\InvalidParameterTypeException;
9
use Onoi\CallbackContainer\Exception\FileNotFoundException;
10
use Onoi\CallbackContainer\Exception\ServiceNotFoundException;
11
12
/**
13
 * @license GNU GPL v2+
14
 * @since 2.0
15
 *
16
 * @author mwjames
17
 */
18
class CallbackContainerBuilder implements ContainerBuilder {
19
20
	/**
21
	 * @var array
22
	 */
23
	protected $registry = array();
24
25
	/**
26
	 * @var array
27
	 */
28
	protected $singletons = array();
29
30
	/**
31
	 * @var array
32
	 */
33
	protected $expectedReturnTypeByHandler = array();
34
35
	/**
36
	 * @var array
37
	 */
38
	protected $recursiveMarker = array();
39
40
	/**
41
	 * @since 2.0
42
	 *
43
	 * @param CallbackContainer|null $callbackContainer
44
	 */
45 29
	public function __construct( CallbackContainer $callbackContainer = null ) {
46 29
		if ( $callbackContainer !== null ) {
47 3
			$this->registerCallbackContainer( $callbackContainer );
48 3
		}
49 29
	}
50
51
	/**
52
	 * @since 2.0
53
	 *
54
	 * @param CallbackContainer $callbackContainer
55
	 */
56 4
	public function registerCallbackContainer( CallbackContainer $callbackContainer ) {
57 4
		$callbackContainer->register( $this );
58 4
	}
59
60
	/**
61
	 * @since 2.0
62
	 *
63
	 * @param string $file
64
	 * @throws FileNotFoundException
65
	 */
66 5
	public function registerFromFile( $file ) {
67
68 5
		if ( !is_readable( ( $file = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, $file ) ) ) ) {
69 1
			throw new FileNotFoundException( "Cannot access or read {$file}" );
70
		}
71
72 4
		$defintions = require $file;
73
74 4
		foreach ( $defintions as $serviceName => $callback ) {
75
76 4
			if ( !is_callable( $callback ) ) {
77 4
				continue;
78
			}
79
80 4
			$this->registerCallback( $serviceName, $callback );
0 ignored issues
show
Documentation introduced by
$callback is of type callable, but the function expects a object<Closure>.

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...
81 4
		}
82 4
	}
83
84
	/**
85
	 * @since 2.0
86
	 *
87
	 * {@inheritDoc}
88
	 */
89 19
	public function registerCallback( $serviceName, Closure $callback ) {
90
91 19
		if ( !is_string( $serviceName ) ) {
92 1
			throw new InvalidParameterTypeException( "Expected a string" );
93
		}
94
95 18
		$this->registry[$serviceName] = $callback;
96 18
	}
97
98
	/**
99
	 * If you are not running PHPUnit or for that matter any other testing
100
	 * environment then you are not suppose to use this function.
101
	 *
102
	 * @since 2.0
103
	 *
104
	 * @param string $serviceName
105
	 * @param mixed $instance
106
	 */
107 4
	public function registerObject( $serviceName, $instance ) {
108
109 4
		if ( !is_string( $serviceName ) ) {
110 1
			throw new InvalidParameterTypeException( "Expected a string" );
111
		}
112
113 3
		unset( $this->singletons[$serviceName] );
114
115 3
		$this->registry[$serviceName] = $instance;
116 3
		$this->singletons[$serviceName]['#'] = $instance;
117 3
	}
118
119
	/**
120
	 * @since 2.0
121
	 *
122
	 * {@inheritDoc}
123
	 */
124 14
	public function registerExpectedReturnType( $serviceName, $type ) {
125
126 14
		if ( !is_string( $serviceName ) || !is_string( $type ) ) {
127 1
			throw new InvalidParameterTypeException( "Expected a string" );
128
		}
129
130 13
		$this->expectedReturnTypeByHandler[$serviceName] = $type;
131 13
	}
132
133
	/**
134
	 * @since 2.0
135
	 *
136
	 * {@inheritDoc}
137
	 */
138 1
	public function isRegistered( $serviceName ) {
139 1
		return isset( $this->registry[$serviceName] );
140
	}
141
142
	/**
143
	 * @since 2.0
144
	 *
145
	 * {@inheritDoc}
146
	 */
147 18
	public function create( $serviceName ) {
148 18
		return $this->getReturnValueFromCallbackHandlerFor( $serviceName, func_get_args() );
149
	}
150
151
	/**
152
	 * @since 2.0
153
	 *
154
	 * {@inheritDoc}
155
	 */
156 10
	public function singleton( $serviceName ) {
157 10
		return $this->getReturnValueFromSingletonFor( $serviceName, func_get_args() );
158
	}
159
160
	/**
161
	 * @since 2.0
162
	 *
163
	 * @param string $serviceName
164
	 */
165 1
	public function deregister( $serviceName ) {
166 1
		unset( $this->registry[$serviceName] );
167 1
		unset( $this->singletons[$serviceName] );
168 1
		unset( $this->expectedReturnTypeByHandler[$serviceName] );
169 1
	}
170
171 23
	private function addRecursiveMarkerFor( $serviceName ) {
172
173 23
		if ( !is_string( $serviceName ) ) {
174 2
			throw new InvalidParameterTypeException( "Expected a string" );
175
		}
176
177 21
		if ( !isset( $this->recursiveMarker[$serviceName] ) ) {
178 21
			$this->recursiveMarker[$serviceName] = 0;
179 21
		}
180
181 21
		$this->recursiveMarker[$serviceName]++;
182
183 21
		if ( $this->recursiveMarker[$serviceName] > 1 ) {
184 3
			throw new ServiceCircularReferenceException( $serviceName );
185
		}
186 21
	}
187
188 22
	private function getReturnValueFromCallbackHandlerFor( $serviceName, $parameters ) {
189
190 22
		$instance = null;
0 ignored issues
show
Unused Code introduced by
$instance is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
191 22
		$this->addRecursiveMarkerFor( $serviceName );
192
193 21
		if ( !isset( $this->registry[$serviceName] ) ) {
194 2
			throw new ServiceNotFoundException( "$serviceName is an unknown service." );
195
		}
196
197
		// Remove the ServiceName
198 19
		array_shift( $parameters );
199
200
		// Shift the ContainerBuilder to the first position in the parameter list
201 19
		array_unshift( $parameters, $this );
202 19
		$service = $this->registry[$serviceName];
203
204 19
		$instance = is_callable( $service ) ? call_user_func_array( $service, $parameters ) : $service;
205 16
		$this->recursiveMarker[$serviceName]--;
206
207 16
		if ( !isset( $this->expectedReturnTypeByHandler[$serviceName] ) || is_a( $instance, $this->expectedReturnTypeByHandler[$serviceName] ) ) {
208 15
			return $instance;
209
		}
210
211 1
		throw new ServiceTypeMismatchException( $serviceName, $this->expectedReturnTypeByHandler[$serviceName], ( is_object( $instance ) ? get_class( $instance ) : $instance ) );
212
	}
213
214 10
	private function getReturnValueFromSingletonFor( $serviceName, $parameters ) {
215
216 10
		$instance = null;
217 10
		$fingerprint = $parameters !== array() ? md5( json_encode( $parameters ) ) : '#';
218
219 10
		$this->addRecursiveMarkerFor( $serviceName );
220
221 9
		if ( isset( $this->singletons[$serviceName][$fingerprint] ) ) {
222 3
			$service = $this->singletons[$serviceName][$fingerprint];
223 3
			$instance = is_callable( $service ) ? call_user_func( $service ) : $service;
224 3
		}
225
226 9
		$this->recursiveMarker[$serviceName]--;
227
228 9
		if ( $instance !== null && ( !isset( $this->expectedReturnTypeByHandler[$serviceName] ) || is_a( $instance, $this->expectedReturnTypeByHandler[$serviceName] ) ) ) {
229 3
			return $instance;
230
		}
231
232 9
		$instance = $this->getReturnValueFromCallbackHandlerFor( $serviceName, $parameters );
233
234 3
		$this->singletons[$serviceName][$fingerprint] = function() use ( $instance ) {
235 3
			static $singleton;
236 3
			return $singleton = $singleton === null ? $instance : $singleton;
237
		};
238
239 7
		return $instance;
240
	}
241
242
}
243