3 abstract class Suppressor
{
6 * @return bool do errors remain
8 abstract public function suppress( $input );
11 * @param string[] $source
16 protected function isSuppressed( array $source, $type, $lineno ) {
17 return $lineno > 0 && preg_match(
18 "|/\*\* @suppress {$type} |",
24 class TextSuppressor
extends Suppressor
{
26 * @param string $input
27 * @return bool do errors remain
29 public function suppress( $input ) {
32 foreach ( explode( "\n", $input ) as $error ) {
33 if ( empty( $error ) ) {
36 if ( !preg_match( '/^(.*):(\d+) (Phan\w+) (.*)$/', $error, $matches ) ) {
37 echo "Failed to parse line: $error\n";
40 list( $source, $file, $lineno, $type, $message ) = $matches;
43 // convert from 1 indexed to 0 indexed
44 'lineno' => $lineno - 1,
48 foreach ( $errors as $file => $fileErrors ) {
49 $source = file( $file );
50 foreach ( $fileErrors as $error ) {
51 if ( !$this->isSuppressed( $source, $error['type'], $error['lineno'] ) ) {
52 echo $error['orig'], "\n";
62 class CheckStyleSuppressor
extends Suppressor
{
64 * @param string $input
65 * @return bool True do errors remain
67 public function suppress( $input ) {
68 $dom = new DOMDocument();
69 $dom->loadXML( $input );
71 // DOMNodeList's are "live", convert to an array so it works as expected
73 foreach ( $dom->getElementsByTagName( 'file' ) as $file ) {
76 foreach ( $files as $file ) {
78 foreach ( $file->getElementsByTagName( 'error' ) as $error ) {
81 $source = file( $file->getAttribute( 'name' ) );
82 $fileHasErrors = false;
83 foreach ( $errors as $error ) {
84 $lineno = $error->getAttribute( 'line' ) - 1;
85 $type = $error->getAttribute( 'source' );
86 if ( $this->isSuppressed( $source, $type, $lineno ) ) {
87 $error->parentNode
->removeChild( $error );
89 $fileHasErrors = true;
93 if ( !$fileHasErrors ) {
94 $file->parentNode
->removeChild( $file );
103 class NoopSuppressor
extends Suppressor
{
106 public function __construct( $mode ) {
109 public function suppress( $input ) {
110 echo "Unsupported output mode: {$this->mode}\n$input";
115 $opt = getopt( "m:", [ "output-mode:" ] );
116 // if provided multiple times getopt returns an array
117 if ( isset( $opt['m'] ) ) {
119 } elseif ( isset( $mode['output-mode'] ) ) {
120 $mode = $opt['output-mode'];
124 if ( is_array( $mode ) ) {
125 // If an option is passed multiple times getopt returns an
126 // array. Just take the last one.
127 $mode = end( $mode );
132 $suppressor = new TextSuppressor();
135 $suppressor = new CheckStyleSuppressor();
138 $suppressor = new NoopSuppressor( $mode );
141 $input = file_get_contents( 'php://stdin' );
142 $hasErrors = $suppressor->suppress( $input );