Completed
Pull Request — master (#4)
by mw
22:32 queued 09:07
created

LoggableContainerBuilder::initLog()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 18
nc 5
nop 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
	public function __construct( ContainerBuilder $containerBuilder, BacktraceSniffer $backtraceSniffer = null, CallFuncMemorySniffer $callFuncMemorySniffer = null ) {
50
		$this->containerBuilder = $containerBuilder;
51
		$this->backtraceSniffer = $backtraceSniffer;
52
		$this->callFuncMemorySniffer = $callFuncMemorySniffer;
53
	}
54
55
	/**
56
	 * @see LoggerAwareInterface::setLogger
57
	 *
58
	 * @since 2.5
59
	 *
60
	 * @param LoggerInterface $logger
61
	 */
62
	public function setLogger( LoggerInterface $logger ) {
63
		$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
	}
65
66
	/**
67
	 * @since 2.0
68
	 *
69
	 * {@inheritDoc}
70
	 */
71
	public function registerCallbackContainer( CallbackContainer $callbackContainer ) {
72
		$this->containerBuilder->registerCallbackContainer( $callbackContainer );
73
	}
74
75
	/**
76
	 * @since 2.0
77
	 *
78
	 * {@inheritDoc}
79
	 */
80
	public function registerCallback( $serviceName, Closure $callback ) {
81
		$this->containerBuilder->registerCallback( $serviceName, $callback );
82
	}
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
	public function registerFromFile( $file ) {
108
		$this->containerBuilder->registerFromFile( $file );
109
	}
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
	public function create( $serviceName ) {
126
127
		$this->initLog( $serviceName );
128
		$this->logs[$serviceName]['prototype']++;
129
130
		if ( $this->backtraceSniffer !== null ) {
131
			$this->logs[$serviceName]['prototype-backtrace'][] = $this->backtraceSniffer->getCallers();
132
		}
133
134
		if ( $this->callFuncMemorySniffer !== null ) {
135
			$instance = $this->callFuncMemorySniffer->call( array( $this->containerBuilder, 'create' ), func_get_args() );
0 ignored issues
show
Documentation introduced by
array($this->containerBuilder, 'create') is of type array<integer,object<Ono...uilder>","1":"string"}>, 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...
136
			$this->logs[$serviceName]['prototype-memory'][] = $this->callFuncMemorySniffer->getMemoryUsed();
137
			$this->logs[$serviceName]['prototype-time'][] = $this->callFuncMemorySniffer->getTimeUsed();
138
		} else {
139
			$instance = call_user_func_array( array( $this->containerBuilder, 'create' ), func_get_args() );
140
		}
141
142
		return $instance;
143
	}
144
145
	/**
146
	 * @since  2.0
147
	 *
148
	 * {@inheritDoc}
149
	 */
150
	public function singleton( $serviceName ) {
151
152
		$this->initLog( $serviceName );
153
		$this->logs[$serviceName]['singleton']++;
154
155
		if ( $this->callFuncMemorySniffer !== null ) {
156
			$instance = $this->callFuncMemorySniffer->call( array( $this->containerBuilder, 'singleton' ), func_get_args() );
0 ignored issues
show
Documentation introduced by
array($this->containerBuilder, 'singleton') is of type array<integer,object<Ono...uilder>","1":"string"}>, 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...
157
			$this->logs[$serviceName]['singleton-memory'][] = $this->callFuncMemorySniffer->getMemoryUsed();
158
			$this->logs[$serviceName]['singleton-time'][] = $this->callFuncMemorySniffer->getTimeUsed();
159
		} else {
160
			$instance = call_user_func_array( array( $this->containerBuilder, 'singleton' ), func_get_args() );
161
		}
162
163
		return $instance;
164
	}
165
166
	private function initLog( $serviceName ) {
167
168
		if ( isset( $this->logs[$serviceName] ) ) {
169
			return;
170
		}
171
172
		$this->logs[$serviceName] = array(
173
			'prototype' => 0,
174
			'prototype-backtrace' => array(),
175
			'prototype-memory' => array(),
176
			'prototype-time' => array(),
177
			'singleton' => 0,
178
			'singleton-memory' => array(),
179
			'singleton-time' => array(),
180
		);
181
182
		if ( $this->backtraceSniffer === null ) {
183
			unset( $this->logs[$serviceName]['prototype-backtrace'] );
184
		}
185
186
		if ( $this->callFuncMemorySniffer === null ) {
187
			unset( $this->logs[$serviceName]['prototype-memory'] );
188
			unset( $this->logs[$serviceName]['singleton-memory'] );
189
			unset( $this->logs[$serviceName]['prototype-time'] );
190
			unset( $this->logs[$serviceName]['singleton-time'] );
191
		}
192
	}
193
194
	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
196
		// Build median
197
		foreach ( $this->logs as $serviceName => $record ) {
198
199
			$count = $this->logs[$serviceName]['singleton'];
200
			$this->calcMedian( 'singleton-memory', $serviceName, $record,$count );
201
			$this->calcMedian( 'singleton-time', $serviceName, $record, $count );
202
203
			$count = $this->logs[$serviceName]['prototype'];
204
			$this->calcMedian( 'prototype-memory', $serviceName, $record, $count );
205
			$this->calcMedian( 'prototype-time', $serviceName, $record, $count );
206
		}
207
208
		$this->log( json_encode( $this->logs, JSON_PRETTY_PRINT ) );
209
	}
210
211
	private function calcMedian( $type, $serviceName, $record, $count ) {
212
		if ( isset( $record[$type] ) && $count > 0 ) {
213
			$this->logs[$serviceName][$type . '-median'] = array_sum( $record[$type] ) / $count;
214
			unset( $this->logs[$serviceName][$type] );
215
		}
216
	}
217
218
	private function log( $message, $context = array() ) {
219
220
		if ( $this->logger === null ) {
221
			return;
222
		}
223
224
		$this->logger->info( $message, $context );
225
	}
226
227
}
228