PHP Scrutinizer Checks

Call Checks

Scrutinizer performs various checks on function and method calls:

Calls on Invalid Types

Scrutinizer checks if the method can be called on the given type:

$a = 1;
$a->foo(); // Raises foo() is not callable on integer type.

Function/Method Existence

Scrutinizer checks that functions and methods exist on the called types.

If a method does not exist, Scrutinizer tries to determine the cause of the error. This could be:

Typo in the Method Name

It could be that there is a typo and you meant to call another method. Scrutinizer will suggest the alternative name.

Coding against a Super-Type

It could be that you get an interface, but the method does not exist on the interface, but only in a specific implementation of said interface.

This could be resolved by adding the method to the super-type, for example as an abstract method, or you might want to add an assertion that you are sure to get the specific sub-type which has the method.

Undocumented Magic Calls

It could be that you are using a magic __call method, but have not documented the method.

/**
 * @method void foo()
 */
class A {
    public function __call(/*...*/) { /*... */ }
}

$a = new A();
$a->foo(); // Ok, it's documented
$a->bar(); // Raises an issue suggesting to document bar()

Argument Checks

Scrutinizer checks if a method can be called with the given arguments. It also considers variadic arguments and also supports function overloading for example in the case of built-in PHP functions like strtr that have different signatures.

Argument checks include:

  • Checking that a compatible number of arguments was passed (not too few, not too many)
  • Checking that the right types have been passed
  • Checking that reference arguments can be passed by reference

Statically Calling Non-Static Methods

Scrutinizer checks if a non-static method is called statically:

class A {
    public function foo() { return 'foo'; }
}

A::foo(); // Raises an issue

Property Access Checks

Scrutinizer checks accesses to properties:

Property Existence

Scrutinizer checks if called properties exist. If a property could not be found, Scrutinizer attempts to determine the cause. This could be:

  • There is a typo, and you meant to call another property. Scrutinizer will suggest the alternative name instead.
  • The property exists only in a sub-type, but you code against an interface. Scrutinizer will suggest to code against the expected implementation instead.
  • You are using magic __get or __set methods, but have not documented the properties.

Type Compatibility

Scrutinizer checks assignments to properties for compatibility with their documented type. Scrutinizer will for example raise an issue if a property is documented as an object, but you assign a string to it.

Unreachable/Infeasible Code

Scrutinizer checks for code that is unreachable:

function foo() {
    return 'foo';

    echo 'foo'; // Not reachable
}

It also checks conditions for feasibility:

$foo = 'foo';
if ($foo === true) { // Raises an issue that $foo can never be true.
}

Variable Checks

Scrutinizer checks your code for various variable errors:

  • non-existent variables
  • typos in variable names
  • dead assignments which are not used in the code
  • un-initialized array variables
  • unintentionally overwriting variables in foreach loops

Method/Function Return Checks

Scrutinizer checks the implementation of methods/functions to make sure they are compatible with their signature.

If Scrutinizer detects an incompatibility, it tries to determine the cause for the error. This could be:

  • the documented type is simply invalid and needs to be updated
  • the returned expression could contain additional types that need to be ruled out. For example, adding assertions for certain error conditions.
  • some code branches are missing a return statement and thus implicitly return null
  • ? and more

In total, Scrutinizer checks for over 15 different causes for incompatibility and provides targeted issue messages for each of them to aid you in understanding and fixing the issue faster.

Doc Comment Checks

Scrutinizer checks for invalid doc-comments and suggests fixes.

Deprecated Code

Scrutinizer flags usage of deprecated code elements. Code elements include functions, methods, constants, or entire classes/interfaces/traits.

Suspicious Code

Scrutinizer flags various forms of suspicious code:

Loose Comparisons

Scrutinizer flags various loose comparisons (==, !=) when Scrutinizer deems them to be ambiguous and a strict comparison (!==, ===) would produce a different outcome.

In total, Scrutinizer checks for more than 10 different causes and produces targeted issue messages and suggested fixes for each cause.

Switch Fallthrough

Scrutinizer checks switch statements and their corresponding case statements for fall-throughs.

If a fallthrough has no explicit explanatory comment in the source code, it will be flagged by Scrutinizer.

Empty Catch Statements

Scrutinizer will flag empty catch statements unless they have an explicit explanatory comment in the source code.

Binary Operations

Scrutinizer checks binary operations for common errors like:

  • using & (bitwise-and) instead of && (boolean-and).
  • wrong placement or missing () to ensure correct precedence like in $a & $b === 0

Unused Code

Scrutinizer flags unused code such as:

  • unused private method
  • unused parameters
  • unused imports
  • unused private properties

and also provides automated fixes for some of them.

Best Practices

No Closing Tag

Scrutinizer flags usage of PHP's closing tag ?> in most files. A notable exception where we do not flag it are template files that are meant to produce output.

No Short PHP Opening Tags

Scrutinizer flags usage of PHP's short opening tag <? and suggest to use <?php instead.

Left-over Debug Code

Scrutinizer will check for left-over debug code such as var_dump or similar.

Usage of exit should be avoided in most cases

Scrutinizer will flag usages of exit in many cases as it prevents code from being properly tested and also makes it harder to integrate.

In some cases, like in CLI scripts, the usage is generally fine and Scrutinizer will not flag it.

Usage of eval

Scrutinizer generally flags the usage of eval. There are some rare cases where it is fine, but that is typically in a framework context and not in userland code.

Usage of goto

Scrutinizer generally flags usage of goto as it is a generally agreed bad practice and makes code hard to read.

There are some specific situations when its usage can make sense like the optimization of very hot code paths, or optimizing generated code, or similar scenarios. These usually do not happen in userland, but rather in a framework context.

Usage of Superglobals

Depending on your application, you should try to avoid superglobals like $_GET, but instead use the abstraction that your framework or application provides.

This makes your code easier to test. Furthermore, frameworks usually provide additional features along with their request abstractions.

Readable Variable/Method Names

Scrutinizer can check if your variables are within certain length restrictions. The general goal is to ensure your code is optimized for easy reading by humans.

Casing of Properties/Parameters

Scrutinizer can check if your properties and parameters are using camel-casing which is used by most PHP projects.

Duplicate Code

Scrutinizer checks for similar code fragments in your code.

Verbatim Duplications

function foo($a, $b) {
    if ($a === $b) {
        do_something();
    } else {
        do_someting_else();
    }
}

// bar() is exactly a duplicate of foo()
function bar($a, $b) {
    if ($a === $b) {
        do_something();
    } else {
        do_someting_else();
    }
}

Structural Duplications

Scrutinizer also finds structural duplications where the structure could be abstracted:

function findUsersByName(array $users, string $name) {
    $filteredUsers = array();
    foreach ($users as $user) {
        if ($user->getName() === $name) {
            $filteredUsers[] = $user;
        }
    }

    return $filteredUsers;
}

function findUsersById(array $users, int $id) {
    $filteredUsers = array();

    foreach ($users as $user) {
        if ($user->getId() === $id) {
            $filteredUsers[] = $user;
        }
    }

    return $filteredUsers;
}

Scrutinizer will flag this code as a duplicate. You could for example refactor it like this:

function findMatchingUsers(array $users, callable $predicate) {
    $filteredUsers = array();

    foreach ($users as $user) {
        if ($predicate($user)) {
            $filteredUsers[] = $user;
        }
    }

    return $filteredUsers;
}

$usersWithName = findMatchingUsers($user, function(User $user) { return $user->getId() === 'Foo'; });

Laravel-Specific Checks

Scrutinizer provides some Laravel specific checks.

Eloquent Models

Scrutinizer is able to understand Eloquent database migrations and builds a representation of the database before it starts its analysis.

This improves checks for Eloquent model classes because Scrutinizer is able to infer the types and names of the various properties available. It will also raise issues if you are accessing attributes for which no migration was found.

Routes

Scrutinizer also checks your route definitions to make sure action methods are present in the targeted controller class.