1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275:
<?php
declare(strict_types=1);
namespace ArrayIterator\Extension;
class Parser implements ParserInterface
{
public function parse(
string $directory,
bool $strict = false,
array $existingClass = [],
array &$duplication = []
) {
$parser = $this->scanDirectory(
$directory,
$strict,
$existingClass,
$duplication
);
if ($parser) {
$parser = new ExtensionInfo($parser, $strict);
}
return $parser;
}
protected function scanDirectory(
string $directory,
bool $strict,
array $existingClass = [],
array &$duplication = []
) {
$directory = rtrim(
Utility::normalizeDirectorySeparator($directory),
DIRECTORY_SEPARATOR
);
$baseName = basename($directory);
$ref = null;
$className = Utility::parseClassName($baseName);
if ($className) {
$className = $directory . DIRECTORY_SEPARATOR . $className . '.php';
if (is_file($className) && is_readable($className)) {
$ref = $this->parseFile($className);
}
clearstatcache(true, $className);
if ($strict && ($ref === null || strtolower($ref->getShortName()) !== strtolower($className))) {
unset($ref);
return null;
}
} elseif ($strict) {
return null;
}
if ($ref) {
if (in_array($ref->getName(), $existingClass)) {
$duplication[$baseName] = [$ref->getName()];
$ref = null;
}
return $ref;
}
foreach (new \FilesystemIterator(
$directory,
\FilesystemIterator::CURRENT_AS_FILEINFO
| \FilesystemIterator::KEY_AS_FILENAME
| \FilesystemIterator::SKIP_DOTS
) as $fileName => $item) {
if ($fileName === $className
|| $item->isFile() === false
|| $item->getExtension() !== 'php'
|| $item->isReadable() === false
) {
continue;
}
if (($ref = $this->parseFile($item->getRealPath()))) {
if (!in_array($ref->getName(), $existingClass)) {
break;
}
if (!isset($duplication[$baseName])) {
$duplication[$baseName] = [];
}
$duplication[$baseName][] = $ref->getName();
$ref = null;
}
}
return $ref;
}
protected function parseFile(string $target)
{
if (substr($target, -4) !== '.php' || !is_file($target)) {
return null;
}
$data = php_strip_whitespace($target);
if (strtolower(substr($data, 0, 5)) !== '<?php'
|| preg_replace('~\<\?php\s*|[\s]|\s*\?\>~i', '', $data) === ''
) {
return null;
}
$data = preg_replace(
'/^\<\?php\s+declare[^;]+\;\s*/smi',
"<?php\n",
$data
);
preg_match(
'~^\<\?php
\s+namespace\s+
(
[a-z\_][a-z0-9\_]{0,}
(?:[\\\]?[a-z\_][a-z0-9\_]{0,}){0,}
)\s*[;]+
~smix',
$data,
$namespace
);
$namespace = empty($namespace[1]) ? '' : $namespace[1].'\\';
if ($namespace === '' && preg_match('~^\<\?php\s*namespace\s+~i', $data)) {
unset($data);
return null;
}
preg_match(
'~
\s+
class\s+([a-z\_][a-z0-9\_]{0,})
(?:
\s+
(?:
implements\s+
\\\?[a-z\_][a-z0-9\_]{0,}
(?:\s*[\,]\s*[\\\]?[a-z\_][a-z0-9\_]{0,}){0,}
| extends\s+\\\?[a-z\_][a-z0-9\_]{0,}
(?:[\\\][a-z\_][a-z0-9\_]{0,}){0,}
)
)
~xi',
$data,
$class
);
unset($data);
if (empty($class[1])) {
return null;
}
$class = $namespace . $class[1];
try {
$ref = new \ReflectionClass($class);
if (! $ref->isAnonymous()
&& $ref->isInstantiable()
&& $ref->isSubclassOf(ExtensionInterface::class)
) {
return $ref;
}
} catch (\Exception $e) {
}
set_error_handler(function () {
error_clear_last();
throw new \Exception();
});
$ref = null;
try {
include_once $target;
$ref = new \ReflectionClass($class);
if ($ref->isAnonymous()
|| ! $ref->isInstantiable()
|| ! $ref->isSubclassOf(ExtensionInterface::class)
) {
$ref = null;
}
} catch (\Exception $e) {
} catch (\Throwable $e) {
} finally {
unset($e);
restore_error_handler();
}
return $ref;
}
public function serialize() : string
{
return serialize([]);
}
public function unserialize($serialized)
{
return;
}
}