class PhpFileCleaner
@author Jordi Boggiano <j.boggiano@seld.be> @internal
Hierarchy
- class \Composer\ClassMapGenerator\PhpFileCleaner
Expanded class hierarchy of PhpFileCleaner
File
-
vendor/
composer/ class-map-generator/ src/ PhpFileCleaner.php, line 21
Namespace
Composer\ClassMapGeneratorView source
class PhpFileCleaner {
/** @var array<array{name: string, length: int, pattern: non-empty-string}> */
private static $typeConfig;
/** @var non-empty-string */
private static $restPattern;
/**
* @readonly
* @var string
*/
private $contents;
/**
* @readonly
* @var int
*/
private $len;
/**
* @readonly
* @var int
*/
private $maxMatches;
/** @var int */
private $index = 0;
/**
* @param string[] $types
*/
public static function setTypeConfig(array $types) : void {
foreach ($types as $type) {
self::$typeConfig[$type[0]] = array(
'name' => $type,
'length' => \strlen($type),
'pattern' => '{.\\b(?<![\\$:>])' . $type . '\\s++[a-zA-Z_\\x7f-\\xff:][a-zA-Z0-9_\\x7f-\\xff:\\-]*+}Ais',
);
}
self::$restPattern = '{[^?"\'</' . implode('', array_keys(self::$typeConfig)) . ']+}A';
}
public function __construct(string $contents, int $maxMatches) {
$this->contents = $contents;
$this->len = \strlen($this->contents);
$this->maxMatches = $maxMatches;
}
public function clean() : string {
$clean = '';
while ($this->index < $this->len) {
$this->skipToPhp();
$clean .= '<?';
while ($this->index < $this->len) {
$char = $this->contents[$this->index];
if ($char === '?' && $this->peek('>')) {
$clean .= '?>';
$this->index += 2;
continue 2;
}
if ($char === '"') {
$this->skipString('"');
$clean .= 'null';
continue;
}
if ($char === "'") {
$this->skipString("'");
$clean .= 'null';
continue;
}
if ($char === "<" && $this->peek('<') && $this->match('{<<<[ \\t]*+([\'"]?)([a-zA-Z_\\x80-\\xff][a-zA-Z0-9_\\x80-\\xff]*+)\\1(?:\\r\\n|\\n|\\r)}A', $match)) {
$this->index += \strlen($match[0]);
$this->skipHeredoc($match[2]);
$clean .= 'null';
continue;
}
if ($char === '/') {
if ($this->peek('/')) {
$this->skipToNewline();
continue;
}
if ($this->peek('*')) {
$this->skipComment();
continue;
}
}
if ($this->maxMatches === 1 && isset(self::$typeConfig[$char])) {
$type = self::$typeConfig[$char];
if (\substr($this->contents, $this->index, $type['length']) === $type['name'] && Preg::isMatch($type['pattern'], $this->contents, $match, 0, $this->index - 1)) {
$clean .= $match[0];
return $clean;
}
}
$this->index += 1;
if ($this->match(self::$restPattern, $match)) {
$clean .= $char . $match[0];
$this->index += \strlen($match[0]);
}
else {
$clean .= $char;
}
}
}
return $clean;
}
private function skipToPhp() : void {
while ($this->index < $this->len) {
if ($this->contents[$this->index] === '<' && $this->peek('?')) {
$this->index += 2;
break;
}
$this->index += 1;
}
}
private function skipString(string $delimiter) : void {
$this->index += 1;
while ($this->index < $this->len) {
if ($this->contents[$this->index] === '\\' && ($this->peek('\\') || $this->peek($delimiter))) {
$this->index += 2;
continue;
}
if ($this->contents[$this->index] === $delimiter) {
$this->index += 1;
break;
}
$this->index += 1;
}
}
private function skipComment() : void {
$this->index += 2;
while ($this->index < $this->len) {
if ($this->contents[$this->index] === '*' && $this->peek('/')) {
$this->index += 2;
break;
}
$this->index += 1;
}
}
private function skipToNewline() : void {
while ($this->index < $this->len) {
if ($this->contents[$this->index] === "\r" || $this->contents[$this->index] === "\n") {
return;
}
$this->index += 1;
}
}
private function skipHeredoc(string $delimiter) : void {
$firstDelimiterChar = $delimiter[0];
$delimiterLength = \strlen($delimiter);
$delimiterPattern = '{' . preg_quote($delimiter) . '(?![a-zA-Z0-9_\\x80-\\xff])}A';
while ($this->index < $this->len) {
// check if we find the delimiter after some spaces/tabs
switch ($this->contents[$this->index]) {
case "\t":
case " ":
$this->index += 1;
continue 2;
case $firstDelimiterChar:
if (\substr($this->contents, $this->index, $delimiterLength) === $delimiter && $this->match($delimiterPattern)) {
$this->index += $delimiterLength;
return;
}
break;
}
// skip the rest of the line
while ($this->index < $this->len) {
$this->skipToNewline();
// skip newlines
while ($this->index < $this->len && ($this->contents[$this->index] === "\r" || $this->contents[$this->index] === "\n")) {
$this->index += 1;
}
break;
}
}
}
private function peek(string $char) : bool {
return $this->index + 1 < $this->len && $this->contents[$this->index + 1] === $char;
}
/**
* @param non-empty-string $regex
* @param null|array<mixed> $match
* @param-out array<int|string, string> $match
*/
private function match(string $regex, ?array &$match = null) : bool {
return Preg::isMatchStrictGroups($regex, $this->contents, $match, 0, $this->index);
}
}
Members
Title Sort descending | Modifiers | Object type | Summary |
---|---|---|---|
PhpFileCleaner::$contents | private | property | @readonly |
PhpFileCleaner::$index | private | property | @var int |
PhpFileCleaner::$len | private | property | @readonly |
PhpFileCleaner::$maxMatches | private | property | @readonly |
PhpFileCleaner::$restPattern | private static | property | @var non-empty-string |
PhpFileCleaner::$typeConfig | private static | property | @var array<array{name: string, length: int, pattern: non-empty-string}> |
PhpFileCleaner::clean | public | function | |
PhpFileCleaner::match | private | function | @param-out array<int|string, string> $match |
PhpFileCleaner::peek | private | function | |
PhpFileCleaner::setTypeConfig | public static | function | |
PhpFileCleaner::skipComment | private | function | |
PhpFileCleaner::skipHeredoc | private | function | |
PhpFileCleaner::skipString | private | function | |
PhpFileCleaner::skipToNewline | private | function | |
PhpFileCleaner::skipToPhp | private | function | |
PhpFileCleaner::__construct | public | function |