1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-16 03:17:02 +01:00
psalm/src/Psalm/Diff/ClassStatementsDiffer.php

149 lines
5.2 KiB
PHP
Raw Normal View History

<?php
namespace Psalm\Diff;
use PhpParser;
/**
* @internal
*/
class ClassStatementsDiffer extends Differ
{
/** @var PhpParser\PrettyPrinter\Standard|null */
private static $pretty_printer;
/**
* Calculate diff (edit script) from $a to $b.
*
* @param PhpParser\Node\Stmt[] $a
* @param PhpParser\Node\Stmt[] $b New array
*
* @return array{0:array<int, string>, 1:array<int, string>}
*/
public static function diff(string $name, array $a, array $b, string $a_code, string $b_code)
{
list($trace, $x, $y, $bc) = self::calculateTrace(
function (
PhpParser\Node\Stmt $a,
PhpParser\Node\Stmt $b,
string $a_code,
string $b_code,
bool &$body_change = false
) : bool {
if (get_class($a) !== get_class($b)) {
return false;
}
$a_start = (int)$a->getAttribute('startFilePos');
$a_end = (int)$a->getAttribute('endFilePos');
$b_start = (int)$b->getAttribute('startFilePos');
$b_end = (int)$b->getAttribute('endFilePos');
$a_comments_end = $a_start;
$b_comments_end = $b_start;
$a_comments = $a->getComments();
$b_comments = $b->getComments();
$signature_change = false;
$body_change = false;
if ($a_comments) {
if (!$b_comments) {
$signature_change = true;
}
$a_start = $a_comments[0]->getFilePos();
}
if ($b_comments) {
if (!$a_comments) {
$signature_change = true;
}
$b_start = $b_comments[0]->getFilePos();
}
$a_size = $a_end - $a_start;
$b_size = $b_end - $b_start;
if (substr($a_code, $a_start, $a_size) === substr($b_code, $b_start, $b_size)) {
return true;
}
if (!$signature_change
&& substr($a_code, $a_start, $a_comments_end - $a_start)
!== substr($b_code, $b_start, $b_comments_end - $b_start)
) {
$signature_change = true;
}
if (!self::$pretty_printer) {
self::$pretty_printer = new PhpParser\PrettyPrinter\Standard;
}
if ($a instanceof PhpParser\Node\Stmt\ClassMethod && $b instanceof PhpParser\Node\Stmt\ClassMethod) {
if ((string) $a->name !== (string) $b->name) {
return false;
}
$a_stmts = $a->stmts;
$a->stmts = [];
$b_stmts = $b->stmts;
$b->stmts = [];
$body_change = $a_stmts !== $b_stmts
&& self::$pretty_printer->prettyPrint($a_stmts ?: [])
!== self::$pretty_printer->prettyPrint($b_stmts ?: []);
$signature_change = $signature_change
|| self::$pretty_printer->prettyPrint([$a]) !== self::$pretty_printer->prettyPrint([$b]);
$a->stmts = $a_stmts;
$b->stmts = $b_stmts;
} else {
$signature_change = $signature_change
|| self::$pretty_printer->prettyPrint([$a]) !== self::$pretty_printer->prettyPrint([$b]);
}
return !$signature_change;
},
$a,
$b,
$a_code,
$b_code
);
$diff = self::extractDiff($trace, $x, $y, $a, $b, $bc);
$keep = [];
$keep_signature = [];
foreach ($diff as $diff_elem) {
if ($diff_elem->type === DiffElem::TYPE_KEEP) {
if ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassMethod) {
$keep[] = strtolower($name) . '::' . strtolower((string) $diff_elem->old->name);
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\Property) {
foreach ($diff_elem->old->props as $prop) {
$keep[] = strtolower($name) . '::$' . $prop->name;
}
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassConst) {
foreach ($diff_elem->old->consts as $const) {
$keep[] = strtolower($name) . '::' . $const->name;
}
}
} elseif ($diff_elem->type === DiffElem::TYPE_KEEP_SIGNATURE) {
if ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassMethod) {
$keep_signature[] = strtolower($name) . '::' . strtolower((string) $diff_elem->old->name);
} elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\Property) {
foreach ($diff_elem->old->props as $prop) {
$keep_signature[] = strtolower($name) . '::$' . $prop->name;
}
}
}
};
return [$keep, $keep_signature];
}
}