left); $right = formatting_ast($value->right); if (($left['type'] ?? '') === 'Scalar_String' && ($right['type'] ?? '') === 'Scalar_String') { return ['type' => 'Scalar_String', 'value' => $left['value'] . $right['value']]; } return ['type' => $value->getType(), 'left' => $left, 'right' => $right]; } if ($value instanceof PhpParser\Node) { $result = ['type' => $value->getType()]; foreach ($value->getSubNodeNames() as $name) { $result[$name] = formatting_ast($value->$name); } return $result; } if (is_array($value)) { return array_map('formatting_ast', $value); } return $value; } function formatting_git(array $arguments) : string { $process = proc_open(array_merge(['git'], $arguments), [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes); if (!is_resource($process)) { throw new RuntimeException('Could not start Git'); } $output = stream_get_contents($pipes[1]); $error = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); if (proc_close($process) !== 0) { throw new RuntimeException($error); } return $output; } $parser = (new PhpParser\ParserFactory())->createForNewestSupportedVersion(); $options = getopt('', ['base:', 'self-test']); if (isset($options['self-test'])) { $cases = [ ['guard($a, $b);', "/* comment */ guard(\n\t\$a, \$b);", true], ['echo "one two";', 'echo "one " . "two";', true], ['echo "one two";', 'echo "one " . "t" . "wo";', true], ['echo "one two";', 'echo "one two";', false], ['guard($a, $b);', 'guard($b, $a);', false], ['if ($ok) save();', 'if (!$ok) save();', false], ['echo $a . $b . "end";', 'echo $a . ($b . "end");', false], ]; foreach ($cases as [$before, $after, $equal]) { $actual = formatting_ast($parser->parse('parse('parse(formatting_git(['show', $base . ':' . $file]))); $after = formatting_ast($parser->parse(file_get_contents($file))); if ($before !== $after) { fwrite(STDERR, $file . ": parsed behavior differs; add behavior tests and measure coverage.\n"); $failed = true; } } print count($files) . " production PHP files checked for preserved behavior.\n"; exit($failed ? 1 : 0);