Custom PHP rule to check method signature for multi-layer inheritance

Hi All, My goal is to add a SonarQube rule by extending PHPVisitorCheck class which can be used to check if all the inherited methods have the same signature as the parent class method in a PHP code. How can I access all the super classes tree from the child class? For example, accessing class C and B from class A will help me recursively check if the isTrue method is static from the child class to its super class. If class A get scanned first, the visitMethodDeclaration and visitClassDeclaration methods tree will only know about class B but not class C. Could anyone please provide some documentation regarding multi-layer inheritance?

class C {
     public static function isTrue() {
         return true;
     }
}

class B extends C {
    public static function isTrue() {
         return true;
    }
}

class A extends B {
   public static function isTrue() {
         return true;
    }
}

Hi @dagi and welcome to the community.

This sounds like a very promising rule idea that we would like to implement.

Would you give us some more examples and context? Which situation should the rule assess exactly and under which circumstances should an issue be raised?

Based on this I can then create a specification and implement the rule.

Best,
Nils

Hi @Nils_Werner, Thanks for your reply. I’m trying to add a SonarQube rule for XMLReader.

In PHP 8.0: XMLReader: XMLReader::open() and XMLReader::xml() are now static methods. They can still be called as instance methods, but inheriting classes need to declare them as static if they override these methods.

Test case:

<?php

class XMLReaderTest1 extends XMLReader {
	public static function open(string $uri, ?string $encoding = null, int $flags = 0): bool { // OK
		return false;
	}

    public static function xml(string $uri, ?string $encoding = null, int $flags = 0): bool { // OK
		return false;
	}
}

class XMLReaderTest2 extends XMLReader {

    public function open(string $uri, ?string $encoding = null, int $flags = 0): bool { // NOK
		return false;
    }

    public function xml(string $uri, ?string $encoding = null, int $flags = 0): bool { // NOK
		return false;
	}
}

class XMLReaderTest3 extends XMLReaderTest1 {

    public static function open(string $uri, ?string $encoding = null, int $flags = 0): bool { // OK
		return false;
    }

    public static function xml(string $uri, ?string $encoding = null, int $flags = 0): bool { // OK
		return false;
	}
}

class XMLReaderTest4 extends XMLReaderTest1 {

    public function open(string $uri, ?string $encoding = null, int $flags = 0): bool { // NOK
		return false;
    }

    public function xml(string $uri, ?string $encoding = null, int $flags = 0): bool { // NOK
		return false;
	}
}

class XMLReaderTest5 {

    public function open(string $uri, ?string $encoding = null, int $flags = 0): bool { // OK
		return false;
    }

    public function xml(string $uri, ?string $encoding = null, int $flags = 0): bool { // OK
		return false;
	}
}

One of the test case is if the XMLReaderTest3 and XMLReaderTest4 are before XMLReaderTest1.

Hi @dagi,

thank you for your input. I’ve created a ticket to specify and implement a rule which will address this usage of the “static” keyword. Feel free to take a look at the description and give feedback if the described behavior matches your intention.

Best,
Nils