| @@ 18-330 (lines=313) @@ | ||
| 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 | /** |
|
| 25 | * The path to the log file if `$debug` is true. |
|
| 26 | * Will be set in {@link MonkeyPatchManager::setDebug}. |
|
| 27 | * @var string|null */ |
|
| 28 | public static $log_file = null; |
|
| 29 | private static $load_patchers = false; |
|
| 30 | private static $exit_exception_classname = |
|
| 31 | 'Kenjis\MonkeyPatch\Exception\ExitException'; |
|
| 32 | /** |
|
| 33 | * @var array list of patcher classname |
|
| 34 | */ |
|
| 35 | private static $patcher_list = [ |
|
| 36 | 'ExitPatcher', |
|
| 37 | 'FunctionPatcher', |
|
| 38 | 'MethodPatcher', |
|
| 39 | 'ConstantPatcher', |
|
| 40 | ]; |
|
| 41 | ||
| 42 | public static function log($message) |
|
| 43 | { |
|
| 44 | if (! self::$debug) |
|
| 45 | { |
|
| 46 | return; |
|
| 47 | } |
|
| 48 | ||
| 49 | $time = date('Y-m-d H:i:s'); |
|
| 50 | list($usec, $sec) = explode(' ', microtime()); |
|
| 51 | $usec = substr($usec, 1); |
|
| 52 | $log = "[$time $usec] $message\n"; |
|
| 53 | file_put_contents(self::$log_file, $log, FILE_APPEND); |
|
| 54 | } |
|
| 55 | ||
| 56 | public static function setExitExceptionClassname($name) |
|
| 57 | { |
|
| 58 | self::$exit_exception_classname = $name; |
|
| 59 | } |
|
| 60 | ||
| 61 | public static function getExitExceptionClassname() |
|
| 62 | { |
|
| 63 | return self::$exit_exception_classname; |
|
| 64 | } |
|
| 65 | ||
| 66 | public static function getPhpParser() |
|
| 67 | { |
|
| 68 | return self::$php_parser; |
|
| 69 | } |
|
| 70 | ||
| 71 | protected static function setDebug(array $config) |
|
| 72 | { |
|
| 73 | if (isset($config['debug'])) |
|
| 74 | { |
|
| 75 | self::$debug = $config['debug']; |
|
| 76 | } |
|
| 77 | if (isset($config['log_file'])) |
|
| 78 | { |
|
| 79 | self::$debug = $config['log_file']; |
|
| 80 | } |
|
| 81 | if (is_null(self::$log_file)) |
|
| 82 | { |
|
| 83 | self::$log_file = __DIR__ . '/debug.log'; |
|
| 84 | } |
|
| 85 | } |
|
| 86 | ||
| 87 | protected static function setDir(array $config) |
|
| 88 | { |
|
| 89 | if (isset($config['root_dir'])) |
|
| 90 | { |
|
| 91 | Cache::setProjectRootDir($config['root_dir']); |
|
| 92 | } |
|
| 93 | else |
|
| 94 | { |
|
| 95 | // APPPATH is constant in CodeIgniter |
|
| 96 | Cache::setProjectRootDir(APPPATH . '../'); |
|
| 97 | } |
|
| 98 | ||
| 99 | if (! isset($config['cache_dir'])) |
|
| 100 | { |
|
| 101 | throw new LogicException('You have to set "cache_dir"'); |
|
| 102 | } |
|
| 103 | self::setCacheDir($config['cache_dir']); |
|
| 104 | } |
|
| 105 | ||
| 106 | protected static function setPaths(array $config) |
|
| 107 | { |
|
| 108 | if (! isset($config['include_paths'])) |
|
| 109 | { |
|
| 110 | throw new LogicException('You have to set "include_paths"'); |
|
| 111 | } |
|
| 112 | self::setIncludePaths($config['include_paths']); |
|
| 113 | ||
| 114 | if (isset($config['exclude_paths'])) |
|
| 115 | { |
|
| 116 | self::setExcludePaths($config['exclude_paths']); |
|
| 117 | } |
|
| 118 | } |
|
| 119 | ||
| 120 | public static function init(array $config) |
|
| 121 | { |
|
| 122 | self::setDebug($config); |
|
| 123 | ||
| 124 | if (isset($config['php_parser'])) |
|
| 125 | { |
|
| 126 | self::$php_parser = constant('PhpParser\ParserFactory::'.$config['php_parser']); |
|
| 127 | } |
|
| 128 | ||
| 129 | self::setDir($config); |
|
| 130 | self::setPaths($config); |
|
| 131 | ||
| 132 | Cache::createTmpListDir(); |
|
| 133 | ||
| 134 | if (isset($config['patcher_list'])) |
|
| 135 | { |
|
| 136 | self::setPatcherList($config['patcher_list']); |
|
| 137 | } |
|
| 138 | self::checkPatcherListUpdate(); |
|
| 139 | self::checkPathsUpdate(); |
|
| 140 | ||
| 141 | self::loadPatchers(); |
|
| 142 | ||
| 143 | self::addTmpFunctionBlacklist(); |
|
| 144 | ||
| 145 | if (isset($config['functions_to_patch'])) |
|
| 146 | { |
|
| 147 | FunctionPatcher::addWhitelists($config['functions_to_patch']); |
|
| 148 | } |
|
| 149 | self::checkFunctionWhitelistUpdate(); |
|
| 150 | FunctionPatcher::lockFunctionList(); |
|
| 151 | ||
| 152 | if (isset($config['exit_exception_classname'])) |
|
| 153 | { |
|
| 154 | self::setExitExceptionClassname($config['exit_exception_classname']); |
|
| 155 | } |
|
| 156 | ||
| 157 | // Register include stream wrapper for monkey patching |
|
| 158 | self::wrap(); |
|
| 159 | } |
|
| 160 | ||
| 161 | protected static function checkPathsUpdate() |
|
| 162 | { |
|
| 163 | $cached = Cache::getTmpIncludePaths(); |
|
| 164 | $current = PathChecker::getIncludePaths(); |
|
| 165 | ||
| 166 | // Updated? |
|
| 167 | if ($cached !== $current) |
|
| 168 | { |
|
| 169 | MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); |
|
| 170 | Cache::clearSrcCache(); |
|
| 171 | Cache::writeTmpIncludePaths($current); |
|
| 172 | } |
|
| 173 | ||
| 174 | $cached = Cache::getTmpExcludePaths(); |
|
| 175 | $current = PathChecker::getExcludePaths(); |
|
| 176 | ||
| 177 | // Updated? |
|
| 178 | if ($cached !== $current) |
|
| 179 | { |
|
| 180 | MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); |
|
| 181 | Cache::clearSrcCache(); |
|
| 182 | Cache::writeTmpExcludePaths($current); |
|
| 183 | } |
|
| 184 | } |
|
| 185 | ||
| 186 | protected static function checkPatcherListUpdate() |
|
| 187 | { |
|
| 188 | $cached = Cache::getTmpPatcherList(); |
|
| 189 | ||
| 190 | // Updated? |
|
| 191 | if ($cached !== self::$patcher_list) |
|
| 192 | { |
|
| 193 | MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); |
|
| 194 | Cache::clearSrcCache(); |
|
| 195 | Cache::writeTmpPatcherList(self::$patcher_list); |
|
| 196 | } |
|
| 197 | } |
|
| 198 | ||
| 199 | protected static function checkFunctionWhitelistUpdate() |
|
| 200 | { |
|
| 201 | $cached = Cache::getTmpFunctionWhitelist(); |
|
| 202 | $current = FunctionPatcher::getFunctionWhitelist(); |
|
| 203 | ||
| 204 | // Updated? |
|
| 205 | if ($cached !== $current) |
|
| 206 | { |
|
| 207 | MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); |
|
| 208 | Cache::clearSrcCache(); |
|
| 209 | Cache::writeTmpFunctionWhitelist($current); |
|
| 210 | } |
|
| 211 | } |
|
| 212 | ||
| 213 | protected static function addTmpFunctionBlacklist() |
|
| 214 | { |
|
| 215 | $list = file(Cache::getTmpFunctionBlacklistFile()); |
|
| 216 | foreach ($list as $function) |
|
| 217 | { |
|
| 218 | FunctionPatcher::addBlacklist(trim($function)); |
|
| 219 | } |
|
| 220 | } |
|
| 221 | ||
| 222 | public static function isEnabled($patcher) |
|
| 223 | { |
|
| 224 | return in_array($patcher, self::$patcher_list); |
|
| 225 | } |
|
| 226 | ||
| 227 | public static function setPatcherList(array $list) |
|
| 228 | { |
|
| 229 | if (self::$load_patchers) |
|
| 230 | { |
|
| 231 | throw new LogicException("Can't change patcher list after initialisation"); |
|
| 232 | } |
|
| 233 | ||
| 234 | self::$patcher_list = $list; |
|
| 235 | } |
|
| 236 | ||
| 237 | public static function setCacheDir($dir) |
|
| 238 | { |
|
| 239 | Cache::setCacheDir($dir); |
|
| 240 | } |
|
| 241 | ||
| 242 | public static function setIncludePaths(array $dir_list) |
|
| 243 | { |
|
| 244 | PathChecker::setIncludePaths($dir_list); |
|
| 245 | } |
|
| 246 | ||
| 247 | public static function setExcludePaths(array $dir_list) |
|
| 248 | { |
|
| 249 | PathChecker::setExcludePaths($dir_list); |
|
| 250 | } |
|
| 251 | ||
| 252 | public static function wrap() |
|
| 253 | { |
|
| 254 | IncludeStream::wrap(); |
|
| 255 | } |
|
| 256 | ||
| 257 | public static function unwrap() |
|
| 258 | { |
|
| 259 | IncludeStream::unwrap(); |
|
| 260 | } |
|
| 261 | ||
| 262 | /** |
|
| 263 | * @param string $path original source file path |
|
| 264 | * @return resource |
|
| 265 | * @throws LogicException |
|
| 266 | */ |
|
| 267 | public static function patch($path) |
|
| 268 | { |
|
| 269 | if (! is_readable($path)) |
|
| 270 | { |
|
| 271 | throw new LogicException("Can't read '$path'"); |
|
| 272 | } |
|
| 273 | ||
| 274 | // Check cache file |
|
| 275 | if ($cache_file = Cache::getValidSrcCachePath($path)) |
|
| 276 | { |
|
| 277 | self::log('cache_hit: ' . $path); |
|
| 278 | return fopen($cache_file, 'r'); |
|
| 279 | } |
|
| 280 | ||
| 281 | self::log('cache_miss: ' . $path); |
|
| 282 | $source = file_get_contents($path); |
|
| 283 | ||
| 284 | list($new_source, $patched) = self::execPatchers($source); |
|
| 285 | ||
| 286 | // Write to cache file |
|
| 287 | self::log('write_cache: ' . $path); |
|
| 288 | Cache::writeSrcCacheFile($path, $new_source); |
|
| 289 | ||
| 290 | $resource = fopen('php://memory', 'rb+'); |
|
| 291 | fwrite($resource, $new_source); |
|
| 292 | rewind($resource); |
|
| 293 | return $resource; |
|
| 294 | } |
|
| 295 | ||
| 296 | protected static function loadPatchers() |
|
| 297 | { |
|
| 298 | if (self::$load_patchers) |
|
| 299 | { |
|
| 300 | return; |
|
| 301 | } |
|
| 302 | ||
| 303 | require __DIR__ . '/Patcher/AbstractPatcher.php'; |
|
| 304 | require __DIR__ . '/Patcher/Backtrace.php'; |
|
| 305 | ||
| 306 | foreach (self::$patcher_list as $classname) |
|
| 307 | { |
|
| 308 | require __DIR__ . '/Patcher/' . $classname . '.php'; |
|
| 309 | } |
|
| 310 | ||
| 311 | self::$load_patchers = true; |
|
| 312 | } |
|
| 313 | ||
| 314 | protected static function execPatchers($source) |
|
| 315 | { |
|
| 316 | $patched = false; |
|
| 317 | foreach (self::$patcher_list as $classname) |
|
| 318 | { |
|
| 319 | $classname = 'Kenjis\MonkeyPatch\Patcher\\' . $classname; |
|
| 320 | $patcher = new $classname; |
|
| 321 | list($source, $patched_this) = $patcher->patch($source); |
|
| 322 | $patched = $patched || $patched_this; |
|
| 323 | } |
|
| 324 | ||
| 325 | return [ |
|
| 326 | $source, |
|
| 327 | $patched, |
|
| 328 | ]; |
|
| 329 | } |
|
| 330 | } |
|
| 331 | ||
| @@ 18-330 (lines=313) @@ | ||
| 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 | /** |
|
| 25 | * The path to the log file if `$debug` is true. |
|
| 26 | * Will be set in {@link MonkeyPatchManager::setDebug}. |
|
| 27 | * @var string|null */ |
|
| 28 | public static $log_file = null; |
|
| 29 | private static $load_patchers = false; |
|
| 30 | private static $exit_exception_classname = |
|
| 31 | 'Kenjis\MonkeyPatch\Exception\ExitException'; |
|
| 32 | /** |
|
| 33 | * @var array list of patcher classname |
|
| 34 | */ |
|
| 35 | private static $patcher_list = [ |
|
| 36 | 'ExitPatcher', |
|
| 37 | 'FunctionPatcher', |
|
| 38 | 'MethodPatcher', |
|
| 39 | 'ConstantPatcher', |
|
| 40 | ]; |
|
| 41 | ||
| 42 | public static function log($message) |
|
| 43 | { |
|
| 44 | if (! self::$debug) |
|
| 45 | { |
|
| 46 | return; |
|
| 47 | } |
|
| 48 | ||
| 49 | $time = date('Y-m-d H:i:s'); |
|
| 50 | list($usec, $sec) = explode(' ', microtime()); |
|
| 51 | $usec = substr($usec, 1); |
|
| 52 | $log = "[$time $usec] $message\n"; |
|
| 53 | file_put_contents(self::$log_file, $log, FILE_APPEND); |
|
| 54 | } |
|
| 55 | ||
| 56 | public static function setExitExceptionClassname($name) |
|
| 57 | { |
|
| 58 | self::$exit_exception_classname = $name; |
|
| 59 | } |
|
| 60 | ||
| 61 | public static function getExitExceptionClassname() |
|
| 62 | { |
|
| 63 | return self::$exit_exception_classname; |
|
| 64 | } |
|
| 65 | ||
| 66 | public static function getPhpParser() |
|
| 67 | { |
|
| 68 | return self::$php_parser; |
|
| 69 | } |
|
| 70 | ||
| 71 | protected static function setDebug(array $config) |
|
| 72 | { |
|
| 73 | if (isset($config['debug'])) |
|
| 74 | { |
|
| 75 | self::$debug = $config['debug']; |
|
| 76 | } |
|
| 77 | if (isset($config['log_file'])) |
|
| 78 | { |
|
| 79 | self::$debug = $config['log_file']; |
|
| 80 | } |
|
| 81 | if (self::$debug && is_null(self::$log_file)) |
|
| 82 | { |
|
| 83 | self::$log_file = __DIR__ . '/debug.log'; |
|
| 84 | } |
|
| 85 | } |
|
| 86 | ||
| 87 | protected static function setDir(array $config) |
|
| 88 | { |
|
| 89 | if (isset($config['root_dir'])) |
|
| 90 | { |
|
| 91 | Cache::setProjectRootDir($config['root_dir']); |
|
| 92 | } |
|
| 93 | else |
|
| 94 | { |
|
| 95 | // APPPATH is constant in CodeIgniter |
|
| 96 | Cache::setProjectRootDir(APPPATH . '../'); |
|
| 97 | } |
|
| 98 | ||
| 99 | if (! isset($config['cache_dir'])) |
|
| 100 | { |
|
| 101 | throw new LogicException('You have to set "cache_dir"'); |
|
| 102 | } |
|
| 103 | self::setCacheDir($config['cache_dir']); |
|
| 104 | } |
|
| 105 | ||
| 106 | protected static function setPaths(array $config) |
|
| 107 | { |
|
| 108 | if (! isset($config['include_paths'])) |
|
| 109 | { |
|
| 110 | throw new LogicException('You have to set "include_paths"'); |
|
| 111 | } |
|
| 112 | self::setIncludePaths($config['include_paths']); |
|
| 113 | ||
| 114 | if (isset($config['exclude_paths'])) |
|
| 115 | { |
|
| 116 | self::setExcludePaths($config['exclude_paths']); |
|
| 117 | } |
|
| 118 | } |
|
| 119 | ||
| 120 | public static function init(array $config) |
|
| 121 | { |
|
| 122 | self::setDebug($config); |
|
| 123 | ||
| 124 | if (isset($config['php_parser'])) |
|
| 125 | { |
|
| 126 | self::$php_parser = constant('PhpParser\ParserFactory::'.$config['php_parser']); |
|
| 127 | } |
|
| 128 | ||
| 129 | self::setDir($config); |
|
| 130 | self::setPaths($config); |
|
| 131 | ||
| 132 | Cache::createTmpListDir(); |
|
| 133 | ||
| 134 | if (isset($config['patcher_list'])) |
|
| 135 | { |
|
| 136 | self::setPatcherList($config['patcher_list']); |
|
| 137 | } |
|
| 138 | self::checkPatcherListUpdate(); |
|
| 139 | self::checkPathsUpdate(); |
|
| 140 | ||
| 141 | self::loadPatchers(); |
|
| 142 | ||
| 143 | self::addTmpFunctionBlacklist(); |
|
| 144 | ||
| 145 | if (isset($config['functions_to_patch'])) |
|
| 146 | { |
|
| 147 | FunctionPatcher::addWhitelists($config['functions_to_patch']); |
|
| 148 | } |
|
| 149 | self::checkFunctionWhitelistUpdate(); |
|
| 150 | FunctionPatcher::lockFunctionList(); |
|
| 151 | ||
| 152 | if (isset($config['exit_exception_classname'])) |
|
| 153 | { |
|
| 154 | self::setExitExceptionClassname($config['exit_exception_classname']); |
|
| 155 | } |
|
| 156 | ||
| 157 | // Register include stream wrapper for monkey patching |
|
| 158 | self::wrap(); |
|
| 159 | } |
|
| 160 | ||
| 161 | protected static function checkPathsUpdate() |
|
| 162 | { |
|
| 163 | $cached = Cache::getTmpIncludePaths(); |
|
| 164 | $current = PathChecker::getIncludePaths(); |
|
| 165 | ||
| 166 | // Updated? |
|
| 167 | if ($cached !== $current) |
|
| 168 | { |
|
| 169 | MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); |
|
| 170 | Cache::clearSrcCache(); |
|
| 171 | Cache::writeTmpIncludePaths($current); |
|
| 172 | } |
|
| 173 | ||
| 174 | $cached = Cache::getTmpExcludePaths(); |
|
| 175 | $current = PathChecker::getExcludePaths(); |
|
| 176 | ||
| 177 | // Updated? |
|
| 178 | if ($cached !== $current) |
|
| 179 | { |
|
| 180 | MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); |
|
| 181 | Cache::clearSrcCache(); |
|
| 182 | Cache::writeTmpExcludePaths($current); |
|
| 183 | } |
|
| 184 | } |
|
| 185 | ||
| 186 | protected static function checkPatcherListUpdate() |
|
| 187 | { |
|
| 188 | $cached = Cache::getTmpPatcherList(); |
|
| 189 | ||
| 190 | // Updated? |
|
| 191 | if ($cached !== self::$patcher_list) |
|
| 192 | { |
|
| 193 | MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); |
|
| 194 | Cache::clearSrcCache(); |
|
| 195 | Cache::writeTmpPatcherList(self::$patcher_list); |
|
| 196 | } |
|
| 197 | } |
|
| 198 | ||
| 199 | protected static function checkFunctionWhitelistUpdate() |
|
| 200 | { |
|
| 201 | $cached = Cache::getTmpFunctionWhitelist(); |
|
| 202 | $current = FunctionPatcher::getFunctionWhitelist(); |
|
| 203 | ||
| 204 | // Updated? |
|
| 205 | if ($cached !== $current) |
|
| 206 | { |
|
| 207 | MonkeyPatchManager::log('clear_src_cache: from ' . __METHOD__); |
|
| 208 | Cache::clearSrcCache(); |
|
| 209 | Cache::writeTmpFunctionWhitelist($current); |
|
| 210 | } |
|
| 211 | } |
|
| 212 | ||
| 213 | protected static function addTmpFunctionBlacklist() |
|
| 214 | { |
|
| 215 | $list = file(Cache::getTmpFunctionBlacklistFile()); |
|
| 216 | foreach ($list as $function) |
|
| 217 | { |
|
| 218 | FunctionPatcher::addBlacklist(trim($function)); |
|
| 219 | } |
|
| 220 | } |
|
| 221 | ||
| 222 | public static function isEnabled($patcher) |
|
| 223 | { |
|
| 224 | return in_array($patcher, self::$patcher_list); |
|
| 225 | } |
|
| 226 | ||
| 227 | public static function setPatcherList(array $list) |
|
| 228 | { |
|
| 229 | if (self::$load_patchers) |
|
| 230 | { |
|
| 231 | throw new LogicException("Can't change patcher list after initialisation"); |
|
| 232 | } |
|
| 233 | ||
| 234 | self::$patcher_list = $list; |
|
| 235 | } |
|
| 236 | ||
| 237 | public static function setCacheDir($dir) |
|
| 238 | { |
|
| 239 | Cache::setCacheDir($dir); |
|
| 240 | } |
|
| 241 | ||
| 242 | public static function setIncludePaths(array $dir_list) |
|
| 243 | { |
|
| 244 | PathChecker::setIncludePaths($dir_list); |
|
| 245 | } |
|
| 246 | ||
| 247 | public static function setExcludePaths(array $dir_list) |
|
| 248 | { |
|
| 249 | PathChecker::setExcludePaths($dir_list); |
|
| 250 | } |
|
| 251 | ||
| 252 | public static function wrap() |
|
| 253 | { |
|
| 254 | IncludeStream::wrap(); |
|
| 255 | } |
|
| 256 | ||
| 257 | public static function unwrap() |
|
| 258 | { |
|
| 259 | IncludeStream::unwrap(); |
|
| 260 | } |
|
| 261 | ||
| 262 | /** |
|
| 263 | * @param string $path original source file path |
|
| 264 | * @return resource |
|
| 265 | * @throws LogicException |
|
| 266 | */ |
|
| 267 | public static function patch($path) |
|
| 268 | { |
|
| 269 | if (! is_readable($path)) |
|
| 270 | { |
|
| 271 | throw new LogicException("Can't read '$path'"); |
|
| 272 | } |
|
| 273 | ||
| 274 | // Check cache file |
|
| 275 | if ($cache_file = Cache::getValidSrcCachePath($path)) |
|
| 276 | { |
|
| 277 | self::log('cache_hit: ' . $path); |
|
| 278 | return fopen($cache_file, 'r'); |
|
| 279 | } |
|
| 280 | ||
| 281 | self::log('cache_miss: ' . $path); |
|
| 282 | $source = file_get_contents($path); |
|
| 283 | ||
| 284 | list($new_source, $patched) = self::execPatchers($source); |
|
| 285 | ||
| 286 | // Write to cache file |
|
| 287 | self::log('write_cache: ' . $path); |
|
| 288 | Cache::writeSrcCacheFile($path, $new_source); |
|
| 289 | ||
| 290 | $resource = fopen('php://memory', 'rb+'); |
|
| 291 | fwrite($resource, $new_source); |
|
| 292 | rewind($resource); |
|
| 293 | return $resource; |
|
| 294 | } |
|
| 295 | ||
| 296 | protected static function loadPatchers() |
|
| 297 | { |
|
| 298 | if (self::$load_patchers) |
|
| 299 | { |
|
| 300 | return; |
|
| 301 | } |
|
| 302 | ||
| 303 | require __DIR__ . '/Patcher/AbstractPatcher.php'; |
|
| 304 | require __DIR__ . '/Patcher/Backtrace.php'; |
|
| 305 | ||
| 306 | foreach (self::$patcher_list as $classname) |
|
| 307 | { |
|
| 308 | require __DIR__ . '/Patcher/' . $classname . '.php'; |
|
| 309 | } |
|
| 310 | ||
| 311 | self::$load_patchers = true; |
|
| 312 | } |
|
| 313 | ||
| 314 | protected static function execPatchers($source) |
|
| 315 | { |
|
| 316 | $patched = false; |
|
| 317 | foreach (self::$patcher_list as $classname) |
|
| 318 | { |
|
| 319 | $classname = 'Kenjis\MonkeyPatch\Patcher\\' . $classname; |
|
| 320 | $patcher = new $classname; |
|
| 321 | list($source, $patched_this) = $patcher->patch($source); |
|
| 322 | $patched = $patched || $patched_this; |
|
| 323 | } |
|
| 324 | ||
| 325 | return [ |
|
| 326 | $source, |
|
| 327 | $patched, |
|
| 328 | ]; |
|
| 329 | } |
|
| 330 | } |
|
| 331 | ||