Completed
Push — master ( bf004c...67ba4c )
by Kenji
02:59
created

MonkeyPatchManager::init()   D

Complexity

Conditions 11
Paths 288

Size

Total Lines 71
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 71
rs 4.1584
cc 11
eloc 33
nc 288
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Part of CI PHPUnit Test
4
 *
5
 * @author     Kenji Suzuki <https://github.com/kenjis>
6
 * @license    MIT License
7
 * @copyright  2015 Kenji Suzuki
8
 * @link       https://github.com/kenjis/ci-phpunit-test
9
 */
10
11
namespace Kenjis\MonkeyPatch;
12
13
use LogicException;
14
use RuntimeException;
15
use PhpParser\ParserFactory;
16
use Kenjis\MonkeyPatch\Patcher\FunctionPatcher;
17
18
class MonkeyPatchManager
19
{
20
	public static $debug = false;
21
22
	private static $php_parser = ParserFactory::PREFER_PHP5;
23
24
	private static $log_file;
25
	private static $load_patchers = false;
26
	private static $exit_exception_classname = 
27
		'Kenjis\MonkeyPatch\Exception\ExitException';
28
	/**
29
	 * @var array list of patcher classname
30
	 */
31
	private static $patcher_list = [
32
		'ExitPatcher',
33
		'FunctionPatcher',
34
		'MethodPatcher',
35
	];
36
37
	public static function log($message)
38
	{
39
		if (! self::$debug)
40
		{
41
			return;
42
		}
43
44
		$time = date('Y-m-d H:i:s');
45
		list($usec, $sec) = explode(' ', microtime());
0 ignored issues
show
Unused Code introduced by
The assignment to $sec is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
46
		$usec = substr($usec, 1);
47
		$log = "[$time $usec] $message\n";
48
		file_put_contents(self::$log_file, $log, FILE_APPEND);
49
	}
50
51
	public static function setExitExceptionClassname($name)
52
	{
53
		self::$exit_exception_classname = $name;
54
	}
55
56
	public static function getExitExceptionClassname()
57
	{
58
		return self::$exit_exception_classname;
59
	}
60
61
	public static function getPhpParser()
62
	{
63
		return self::$php_parser;
64
	}
65
66
	public static function init(array $config)
67
	{
68
		if (isset($config['debug']))
69
		{
70
			self::$debug = $config['debug'];
71
		}
72
		if (self::$debug)
73
		{
74
			self::$log_file = __DIR__ . '/debug.log';
75
		}
76
77
		if (isset($config['php_parser']))
78
		{
79
			self::$php_parser = constant('PhpParser\ParserFactory::'.$config['php_parser']);
80
		}
81
82
		if (isset($config['root_dir']))
83
		{
84
			Cache::setProjectRootDir($config['root_dir']);
85
		}
86
		else
87
		{
88
			// APPPATH is constant in CodeIgniter
89
			Cache::setProjectRootDir(APPPATH . '../');
90
		}
91
92
		if (! isset($config['cache_dir']))
93
		{
94
			throw new LogicException('You have to set "cache_dir"');
95
		}
96
		self::setCacheDir($config['cache_dir']);
97
98
		if (! isset($config['include_paths']))
99
		{
100
			throw new LogicException('You have to set "include_paths"');
101
		}
102
		self::setIncludePaths($config['include_paths']);
103
104
		if (isset($config['exclude_paths']))
105
		{
106
			self::setExcludePaths($config['exclude_paths']);
107
		}
108
109
		Cache::createTmpListDir();
110
111
		if (isset($config['patcher_list']))
112
		{
113
			self::setPatcherList($config['patcher_list']);
114
		}
115
		self::checkPatcherListUpdate();
116
		self::checkPathsUpdate();
117
118
		self::loadPatchers();
119
120
		self::addTmpFunctionBlacklist();
121
122
		if (isset($config['functions_to_patch']))
123
		{
124
			FunctionPatcher::addWhitelists($config['functions_to_patch']);
125
		}
126
		self::checkFunctionWhitelistUpdate();
127
		FunctionPatcher::lockFunctionList();
128
129
		if (isset($config['exit_exception_classname']))
130
		{
131
			self::setExitExceptionClassname($config['exit_exception_classname']);
132
		}
133
134
		// Register include stream wrapper for monkey patching
135
		self::wrap();
136
	}
137
138
	protected static function checkPathsUpdate()
139
	{
140
		$cached = Cache::getTmpIncludePaths();
141
		$current = PathChecker::getIncludePaths();
142
143
		// Updated?
144
		if ($cached !== $current)
145
		{
146
			MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__);
147
			Cache::clearSrcCache();
148
			Cache::writeTmpIncludePaths($current);
149
		}
150
151
		$cached = Cache::getTmpExcludePaths();
152
		$current = PathChecker::getExcludePaths();
153
154
		// Updated?
155
		if ($cached !== $current)
156
		{
157
			MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__);
158
			Cache::clearSrcCache();
159
			Cache::writeTmpExcludePaths($current);
160
		}
161
	}
162
163
	protected static function checkPatcherListUpdate()
164
	{
165
		$cached = Cache::getTmpPatcherList();
166
167
		// Updated?
168
		if ($cached !== self::$patcher_list)
169
		{
170
			MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__);
171
			Cache::clearSrcCache();
172
			Cache::writeTmpPatcherList(self::$patcher_list);
173
		}
174
	}
175
176
	protected static function checkFunctionWhitelistUpdate()
177
	{
178
		$cached = Cache::getTmpFunctionWhitelist();
179
		$current = FunctionPatcher::getFunctionWhitelist();
180
181
		// Updated?
182
		if ($cached !== $current)
183
		{
184
			MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__);
185
			Cache::clearSrcCache();
186
			Cache::writeTmpFunctionWhitelist($current);
187
		}
188
	}
189
190
	protected static function addTmpFunctionBlacklist()
191
	{
192
		$list = file(Cache::getTmpFunctionBlacklistFile());
193
		foreach ($list as $function)
194
		{
195
			FunctionPatcher::addBlacklist(trim($function));
196
		}
197
	}
198
199
	public static function isEnabled($patcher)
200
	{
201
		return in_array($patcher, self::$patcher_list);
202
	}
203
204
	public static function setPatcherList(array $list)
205
	{
206
		if (self::$load_patchers)
207
		{
208
			throw new LogicException("Can't change patcher list after initialisation");
209
		}
210
211
		self::$patcher_list = $list;
212
	}
213
214
	public static function setCacheDir($dir)
215
	{
216
		Cache::setCacheDir($dir);
217
	}
218
219
	public static function setIncludePaths(array $dir_list)
220
	{
221
		PathChecker::setIncludePaths($dir_list);
222
	}
223
224
	public static function setExcludePaths(array $dir_list)
225
	{
226
		PathChecker::setExcludePaths($dir_list);
227
	}
228
229
	public static function wrap()
230
	{
231
		IncludeStream::wrap();
232
	}
233
234
	public static function unwrap()
235
	{
236
		IncludeStream::unwrap();
237
	}
238
239
	/**
240
	 * @param string $path original source file path
241
	 * @return resource
242
	 * @throws LogicException
243
	 */
244
	public static function patch($path)
245
	{
246
		if (! is_readable($path))
247
		{
248
			throw new LogicException("Can't read '$path'");
249
		}
250
251
		// Check cache file
252
		if ($cache_file = Cache::getValidSrcCachePath($path))
253
		{
254
			self::log('cache_hit: ' . $path);
255
			return fopen($cache_file, 'r');
256
		}
257
258
		self::log('cache_miss: ' . $path);
259
		$source = file_get_contents($path);
260
261
		list($new_source, $patched) = self::execPatchers($source);
0 ignored issues
show
Unused Code introduced by
The assignment to $patched is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
262
263
		// Write to cache file
264
		self::log('write_cache: ' . $path);
265
		Cache::writeSrcCacheFile($path, $new_source);
266
267
		$resource = fopen('php://memory', 'rb+');
268
		fwrite($resource, $new_source);
269
		rewind($resource);
270
		return $resource;
271
	}
272
273
	protected static function loadPatchers()
274
	{
275
		if (self::$load_patchers)
276
		{
277
			return;
278
		}
279
280
		require __DIR__ . '/Patcher/AbstractPatcher.php';
281
		require __DIR__ . '/Patcher/Backtrace.php';
282
283
		foreach (self::$patcher_list as $classname)
284
		{
285
			require __DIR__ . '/Patcher/' . $classname . '.php';
286
		}
287
288
		self::$load_patchers = true;
289
	}
290
291
	protected static function execPatchers($source)
292
	{
293
		$patched = false;
294
		foreach (self::$patcher_list as $classname)
295
		{
296
			$classname = 'Kenjis\MonkeyPatch\Patcher\\' . $classname;
297
			$patcher = new $classname;
298
			list($source, $patched_this) = $patcher->patch($source);
299
			$patched = $patched || $patched_this;
300
		}
301
302
		return [
303
			$source,
304
			$patched,
305
		];
306
	}
307
}
308