Completed
Push — master ( cf3a23...901aad )
by mw
8s
created

DeferredCallbackLoader::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Onoi\CallbackContainer;
4
5
use Closure;
6
use RuntimeException;
7
use InvalidArgumentException;
8
9
/**
10
 * @license GNU GPL v2+
11
 * @since 1.0
12
 *
13
 * @author mwjames
14
 */
15
class DeferredCallbackLoader implements CallbackLoader {
16
17
	/**
18
	 * @var array
19
	 */
20
	private $registry = array();
21
22
	/**
23
	 * @var array
24
	 */
25
	private $singletons = array();
26
27
	/**
28
	 * @var array
29
	 */
30
	private $expectedReturnTypeByHandler = array();
31
32
	/**
33
	 * @var array
34
	 */
35
	private $recursiveMarker = array();
36
37
	/**
38
	 * @since 1.0
39
	 *
40
	 * @param CallbackContainer|null $callbackContainer
41
	 */
42 24
	public function __construct( CallbackContainer $callbackContainer = null ) {
43 24
		if ( $callbackContainer !== null ) {
44 3
			$this->registerCallbackContainer( $callbackContainer );
45 3
		}
46 24
	}
47
48
	/**
49
	 * @since 1.0
50
	 *
51
	 * @param CallbackContainer $callbackContainer
52
	 */
53 4
	public function registerCallbackContainer( CallbackContainer $callbackContainer ) {
54 4
		$callbackContainer->register( $this );
55 4
	}
56
57
	/**
58
	 * @since 1.0
59
	 *
60
	 * {@inheritDoc}
61
	 */
62 15
	public function registerCallback( $handlerName, Closure $callback ) {
63
64 15
		if ( !is_string( $handlerName ) ) {
65 1
			throw new InvalidArgumentException( "Expected a string" );
66
		}
67
68 14
		$this->registry[$handlerName] = $callback;
69 14
	}
70
71
	/**
72
	 * If you are not running PHPUnit or for that matter any other testing
73
	 * environment then you are not suppose to use this function.
74
	 *
75
	 * @since  1.0
76
	 *
77
	 * @param string $handlerName
78
	 * @param mixed $instance
79
	 */
80 4
	public function registerObject( $handlerName, $instance ) {
81
82 4
		if ( !is_string( $handlerName ) ) {
83 1
			throw new InvalidArgumentException( "Expected a string" );
84
		}
85
86 3
		unset( $this->singletons[$handlerName] );
87
88 3
		$this->registry[$handlerName] = $instance;
89 3
		$this->singletons[$handlerName]['#'] = $instance;
90 3
	}
91
92
	/**
93
	 * @since 1.0
94
	 *
95
	 * {@inheritDoc}
96
	 */
97 13
	public function registerExpectedReturnType( $handlerName, $type ) {
98
99 13
		if ( !is_string( $handlerName ) || !is_string( $type ) ) {
100 1
			throw new InvalidArgumentException( "Expected a string" );
101
		}
102
103 12
		$this->expectedReturnTypeByHandler[$handlerName] = $type;
104 12
	}
105
106
	/**
107
	 * @since  1.0
108
	 *
109
	 * {@inheritDoc}
110
	 */
111 13
	public function create( $handlerName ) {
112
113 13
		$parameters = func_get_args();
114 13
		array_shift( $parameters );
115
116 13
		return $this->getReturnValueFromCallbackHandlerFor( $handlerName, $parameters );
117
	}
118
119
	/**
120
	 * @since  1.0
121
	 * @deprecated since 1.1
122
	 *
123
	 * {@inheritDoc}
124
	 */
125 1
	public function load( $handlerName ) {
126
127 1
		$parameters = func_get_args();
128 1
		array_shift( $parameters );
129
130 1
		return $this->getReturnValueFromCallbackHandlerFor( $handlerName, $parameters );
131
	}
132
133
	/**
134
	 * @since  1.0
135
	 *
136
	 * {@inheritDoc}
137
	 */
138 11
	public function singleton( $handlerName ) {
139
140 11
		$parameters = func_get_args();
141 11
		array_shift( $parameters );
142
143 11
		$fingerprint = $parameters !== array() ? md5( json_encode( $parameters ) ) : '#';
144
145 11
		$instance = $this->getReturnValueFromSingletonFor( $handlerName, $fingerprint );
146
147 10
		if ( $instance !== null && ( !isset( $this->expectedReturnTypeByHandler[$handlerName] ) || is_a( $instance, $this->expectedReturnTypeByHandler[$handlerName] ) ) ) {
148 5
			return $instance;
149
		}
150
151 9
		$instance = $this->getReturnValueFromCallbackHandlerFor( $handlerName, $parameters );
152
153 3
		$this->singletons[$handlerName][$fingerprint] = function() use ( $instance ) {
154 3
			static $singleton;
155 3
			return $singleton = $singleton === null ? $instance : $singleton;
156
		};
157
158 8
		return $instance;
159
	}
160
161
	/**
162
	 * @since  1.0
163
	 *
164
	 * @param string $handlerName
165
	 */
166 1
	public function deregister( $handlerName ) {
167 1
		unset( $this->registry[$handlerName] );
168 1
		unset( $this->singletons[$handlerName] );
169 1
		unset( $this->expectedReturnTypeByHandler[$handlerName] );
170 1
	}
171
172 19
	private function addRecursiveMarkerFor( $handlerName ) {
173
174 19
		if ( !is_string( $handlerName ) ) {
175 2
			throw new InvalidArgumentException( "Expected a string" );
176
		}
177
178 17
		if ( !isset( $this->recursiveMarker[$handlerName] ) ) {
179 17
			$this->recursiveMarker[$handlerName] = 0;
180 17
		}
181
182 17
		$this->recursiveMarker[$handlerName]++;
183
184 17
		if ( $this->recursiveMarker[$handlerName] > 1 ) {
185 1
			throw new RuntimeException( "Oh boy, your execution chain for $handlerName caused a circular reference." );
186
		}
187 17
	}
188
189 18
	private function getReturnValueFromCallbackHandlerFor( $handlerName, $parameters ) {
190
191 18
		$instance = null;
192
193 18
		$this->addRecursiveMarkerFor( $handlerName );
194
195 17
		if ( isset( $this->registry[$handlerName] ) ) {
196 14
			$instance = is_callable( $this->registry[$handlerName] ) ? call_user_func_array( $this->registry[$handlerName], $parameters ) : $this->registry[$handlerName];
197 13
		}
198
199 16
		$this->recursiveMarker[$handlerName]--;
200
201 16
		if ( !isset( $this->expectedReturnTypeByHandler[$handlerName] ) || is_a( $instance, $this->expectedReturnTypeByHandler[$handlerName] ) ) {
202 14
			return $instance;
203
		}
204
205 2
		throw new RuntimeException( "Expected " . $this->expectedReturnTypeByHandler[$handlerName] . " type for {$handlerName} could not be match to " . get_class( $instance ) );
206
	}
207
208 11
	private function getReturnValueFromSingletonFor( $handlerName, $fingerprint ) {
209
210 11
		$instance = null;
211
212 11
		$this->addRecursiveMarkerFor( $handlerName );
213
214 10
		if ( isset( $this->singletons[$handlerName][$fingerprint] ) ) {
215 5
			$instance = is_callable( $this->singletons[$handlerName][$fingerprint] ) ? call_user_func( $this->singletons[$handlerName][$fingerprint] ) : $this->singletons[$handlerName][$fingerprint];
216 5
		}
217
218 10
		$this->recursiveMarker[$handlerName]--;
219
220 10
		return $instance;
221
	}
222
223
}
224