Completed
Pull Request — master (#4)
by mw
01:48
created

LoggableContainerBuilder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 3
crap 1
1
<?php
2
3
namespace Onoi\CallbackContainer;
4
5
use Closure;
6
use Psr\Log\LoggerInterface;
7
use Psr\Log\LoggerAwareInterface;
8
9
/**
10
 * @license GNU GPL v2+
11
 * @since 2.0
12
 *
13
 * @author mwjames
14
 */
15
class LoggableContainerBuilder implements ContainerBuilder, LoggerAwareInterface {
16
17
	/**
18
	 * @var ContainerBuilder
19
	 */
20
	private $containerBuilder;
21
22
	/**
23
	 * @var BacktraceSniffer|null
24
	 */
25
	private $backtraceSniffer;
26
27
	/**
28
	 * @var CallFuncMemorySniffer|null
29
	 */
30
	private $callFuncMemorySniffer;
31
32
	/**
33
	 * @var array
34
	 */
35
	private $logs = array();
36
37
	/**
38
	 * @var loggerInterface
39
	 */
40
	private $logger;
41
42
	/**
43
	 * @since 2.0
44
	 *
45
	 * @param ContainerBuilder $containerBuilder
46
	 * @param BacktraceSniffer|null $backtraceSniffer
47
	 * @param CallFuncMemorySniffer|null $backtraceSniffer
48
	 */
49 3
	public function __construct( ContainerBuilder $containerBuilder, BacktraceSniffer $backtraceSniffer = null, CallFuncMemorySniffer $callFuncMemorySniffer = null ) {
50 3
		$this->containerBuilder = $containerBuilder;
51 3
		$this->backtraceSniffer = $backtraceSniffer;
52 3
		$this->callFuncMemorySniffer = $callFuncMemorySniffer;
53 3
	}
54
55
	/**
56
	 * @see LoggerAwareInterface::setLogger
57
	 *
58
	 * @since 2.5
59
	 *
60
	 * @param LoggerInterface $logger
61
	 */
62 2
	public function setLogger( LoggerInterface $logger ) {
63 2
		$this->logger = $logger;
0 ignored issues
show
Documentation Bug introduced by
It seems like $logger of type object<Psr\Log\LoggerInterface> is incompatible with the declared type object<Onoi\CallbackContainer\loggerInterface> of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
64 2
	}
65
66
	/**
67
	 * @since 2.0
68
	 *
69
	 * {@inheritDoc}
70
	 */
71 1
	public function registerCallbackContainer( CallbackContainer $callbackContainer ) {
72 1
		$this->containerBuilder->registerCallbackContainer( $callbackContainer );
73
	}
74
75
	/**
76
	 * @since 2.0
77
	 *
78
	 * {@inheritDoc}
79
	 */
80 1
	public function registerCallback( $serviceName, Closure $callback ) {
81 1
		$this->containerBuilder->registerCallback( $serviceName, $callback );
82 1
	}
83
84
	/**
85
	 * @since 2.0
86
	 *
87
	 * {@inheritDoc}
88
	 */
89
	public function registerExpectedReturnType( $serviceName, $type ) {
90
		$this->containerBuilder->registerExpectedReturnType( $serviceName, $type );
91
	}
92
93
	/**
94
	 * @since 2.0
95
	 *
96
	 * {@inheritDoc}
97
	 */
98
	public function registerObject( $serviceName, $instance ) {
99
		$this->containerBuilder->registerObject( $serviceName, $instance );
100
	}
101
102
	/**
103
	 * @since 2.0
104
	 *
105
	 * {@inheritDoc}
106
	 */
107 1
	public function registerFromFile( $file ) {
108 1
		$this->containerBuilder->registerFromFile( $file );
109 1
	}
110
111
	/**
112
	 * @since 2.0
113
	 *
114
	 * {@inheritDoc}
115
	 */
116
	public function isRegistered( $serviceName ) {
117
		return $this->containerBuilder->isRegistered( $serviceName );
118
	}
119
120
	/**
121
	 * @since  2.0
122
	 *
123
	 * {@inheritDoc}
124
	 */
125 2
	public function create( $serviceName ) {
126
127 2
		$this->initLog( $serviceName );
128 2
		$this->logs[$serviceName]['prototype']++;
129
130 2
		if ( $this->backtraceSniffer !== null ) {
131 1
			$this->logs[$serviceName]['prototype-backtrace'][] = $this->backtraceSniffer->getCallers();
132 1
		}
133
134 2
		if ( $this->callFuncMemorySniffer !== null ) {
135 1
			$instance = $this->callFuncMemorySniffer->call( array( $this->containerBuilder, 'create' ), func_get_args() );
136 1
			$this->logs[$serviceName]['prototype-memory'][] = $this->callFuncMemorySniffer->getMemoryUsed();
137 1
			$this->logs[$serviceName]['prototype-time'][] = $this->callFuncMemorySniffer->getTimeUsed();
138 1
		} else {
139 1
			$instance = call_user_func_array( array( $this->containerBuilder, 'create' ), func_get_args() );
140
		}
141
142 2
		return $instance;
143
	}
144
145
	/**
146
	 * @since  2.0
147
	 *
148
	 * {@inheritDoc}
149
	 */
150 2
	public function singleton( $serviceName ) {
151
152 2
		$this->initLog( $serviceName );
153 2
		$this->logs[$serviceName]['singleton']++;
154
155 2
		if ( $this->callFuncMemorySniffer !== null ) {
156 1
			$instance = $this->callFuncMemorySniffer->call( array( $this->containerBuilder, 'singleton' ), func_get_args() );
157 1
			$this->logs[$serviceName]['singleton-memory'][] = $this->callFuncMemorySniffer->getMemoryUsed();
158 1
			$this->logs[$serviceName]['singleton-time'][] = $this->callFuncMemorySniffer->getTimeUsed();
159 1
		} else {
160 1
			$instance = call_user_func_array( array( $this->containerBuilder, 'singleton' ), func_get_args() );
161
		}
162
163 2
		return $instance;
164
	}
165
166 2
	private function initLog( $serviceName ) {
167
168 2
		if ( isset( $this->logs[$serviceName] ) ) {
169 2
			return;
170
		}
171
172 2
		$this->logs[$serviceName] = array(
173 2
			'prototype' => 0,
174 2
			'prototype-backtrace' => array(),
175 2
			'prototype-memory' => array(),
176 2
			'prototype-time' => array(),
177 2
			'singleton' => 0,
178 2
			'singleton-memory' => array(),
179 2
			'singleton-time' => array(),
180
		);
181
182 2
		if ( $this->backtraceSniffer === null ) {
183 1
			unset( $this->logs[$serviceName]['prototype-backtrace'] );
184 1
		}
185
186 2
		if ( $this->callFuncMemorySniffer === null ) {
187 1
			unset( $this->logs[$serviceName]['prototype-memory'] );
188 1
			unset( $this->logs[$serviceName]['singleton-memory'] );
189 1
			unset( $this->logs[$serviceName]['prototype-time'] );
190 1
			unset( $this->logs[$serviceName]['singleton-time'] );
191 1
		}
192 2
	}
193
194 3
	function __destruct() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for __destruct.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
195 3
		call_user_func_array( $this->logWithCallback(), array( $this->logger ) );
196 3
	}
197
198 2
	private function buildLogs() {
199
200 2
		foreach ( $this->logs as $serviceName => $record ) {
201
202 2
			$count = $this->logs[$serviceName]['singleton'];
203 2
			$this->calcMedian( 'singleton-memory', $serviceName, $record,$count );
204 2
			$this->calcMedian( 'singleton-time', $serviceName, $record, $count );
205
206 2
			$count = $this->logs[$serviceName]['prototype'];
207 2
			$this->calcMedian( 'prototype-memory', $serviceName, $record, $count );
208 2
			$this->calcMedian( 'prototype-time', $serviceName, $record, $count );
209 2
		}
210
211
		// PHP 5.4+
212 2
		$flag = defined( 'JSON_PRETTY_PRINT' ) ? JSON_PRETTY_PRINT : 0;
213
214 2
		return json_encode( $this->logs, $flag );
215
	}
216
217 2
	private function calcMedian( $type, $serviceName, $record, $count ) {
218 2
		if ( isset( $record[$type] ) && $count > 0 ) {
219 1
			$this->logs[$serviceName][$type . '-median'] = array_sum( $record[$type] ) / $count;
220 1
			unset( $this->logs[$serviceName][$type] );
221 1
		}
222 2
	}
223
224
	private function logWithCallback( $logger = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $logger 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...
225 3
		return function( $logger = null ) {
226
227 3
			if ( $logger === null ) {
228 1
				return;
229
			}
230
231 2
			$context = array();
232 2
			$logger->info( $this->buildLogs(), $context );
233 3
		};
234
	}
235
236
}
237