Giter Site home page Giter Site logo

phpstan / phpstan-strict-rules Goto Github PK

View Code? Open in Web Editor NEW
563.0 18.0 46.0 347 KB

Extra strict and opinionated rules for PHPStan

License: MIT License

PHP 99.46% Makefile 0.54%
phpstan static-code-analysis static-analysis safety strongly-typed php php7

phpstan-strict-rules's Introduction

Extra strict and opinionated rules for PHPStan

Build Latest Stable Version License

PHPStan focuses on finding bugs in your code. But in PHP there's a lot of leeway in how stuff can be written. This repository contains additional rules that revolve around strictly and strongly typed code with no loose casting for those who want additional safety in extremely defensive programming:

  • Require booleans in if, elseif, ternary operator, after !, and on both sides of && and ||.
  • Require numeric operands or arrays in + and numeric operands in -/*///**/%.
  • Require numeric operand in $var++, $var--, ++$varand --$var.
  • These functions contain a $strict parameter for better type safety, it must be set to true:
    • in_array (3rd parameter)
    • array_search (3rd parameter)
    • array_keys (3rd parameter; only if the 2nd parameter $search_value is provided)
    • base64_decode (2nd parameter)
  • Variables assigned in while loop condition and for loop initial assignment cannot be used after the loop.
  • Variables set in foreach that's always looped thanks to non-empty arrays cannot be used after the loop.
  • Types in switch condition and case value must match. PHP compares them loosely by default and that can lead to unexpected results.
  • Check that statically declared methods are called statically.
  • Disallow empty() - it's a very loose comparison (see manual), it's recommended to use more strict one.
  • Disallow short ternary operator (?:) - implies weak comparison, it's recommended to use null coalesce operator (??) or ternary operator with strict condition.
  • Disallow variable variables ($$foo, $this->$method() etc.)
  • Disallow overwriting variables with foreach key and value variables
  • Always true instanceof, type-checking is_* functions and strict comparisons ===/!==. These checks can be turned off by setting checkAlwaysTrueInstanceof/checkAlwaysTrueCheckTypeFunctionCall/checkAlwaysTrueStrictComparison to false.
  • Correct case for referenced and called function names.
  • Correct case for inherited and implemented method names.
  • Contravariance for parameter types and covariance for return types in inherited methods (also known as Liskov substitution principle - LSP)
  • Check LSP even for static methods
  • Require calling parent constructor
  • Disallow usage of backtick operator ($ls = `ls -la`)
  • Closure should use $this directly instead of using $this variable indirectly

Additional rules are coming in subsequent releases!

Installation

To use this extension, require it in Composer:

composer require --dev phpstan/phpstan-strict-rules

If you also install phpstan/extension-installer then you're all set!

Manual installation

If you don't want to use phpstan/extension-installer, include rules.neon in your project's PHPStan config:

includes:
    - vendor/phpstan/phpstan-strict-rules/rules.neon

Disabling rules

You can disable rules using configuration parameters:

parameters:
	strictRules:
		disallowedLooseComparison: false
		booleansInConditions: false
		uselessCast: false
		requireParentConstructorCall: false
		disallowedConstructs: false
		overwriteVariablesWithLoop: false
		closureUsesThis: false
		matchingInheritedMethodNames: false
		numericOperandsInArithmeticOperators: false
		strictCalls: false
		switchConditionsMatchingType: false
		noVariableVariables: false
		strictArrayFilter: false

Aside from introducing new custom rules, phpstan-strict-rules also change the default values of some configuration parameters that are present in PHPStan itself. These parameters are documented on phpstan.org.

Enabling rules one-by-one

If you don't want to start using all the available strict rules at once but only one or two, you can!

You can disable all rules from the included rules.neon with:

parameters:
	strictRules:
		allRules: false

Then you can re-enable individual rules with configuration parameters:

parameters:
	strictRules:
		allRules: false
		booleansInConditions: true

Even with strictRules.allRules set to false, part of this package is still in effect. That's because phpstan-strict-rules also change the default values of some configuration parameters that are present in PHPStan itself. These parameters are documented on phpstan.org.

phpstan-strict-rules's People

Contributors

adaamz avatar carusogabriel avatar dependabot[bot] avatar dereuromark avatar dktapps avatar dmytro-dymarchuk avatar grahamcampbell avatar herndlm avatar ikvasnica avatar iluuu1994 avatar kamil-zacek avatar kocal avatar kukulich avatar localheinz avatar lookyman avatar mad-briller avatar majkl578 avatar martinmystikjonas avatar muno92 avatar nagmat84 avatar nelson6e65 avatar ondrejmirtes avatar renovate-bot avatar renovate[bot] avatar ruudk avatar staabm avatar tomasvotruba avatar villfa avatar vincentlanglet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phpstan-strict-rules's Issues

Instanceof between Doctrine collections and array of entities

Hi,

I might have found a bug when using the instanceof with Doctrine collections and entities.

Code example:

    use Doctrine\Common\Collections\ArrayCollection;
    use App\Entity\TextEntity;

    /**
     * @param TextEntityList|TextEntity[]|ArrayCollection $text
     */
    public function setText($text): void
    {
        if ($text instanceof TextEntityList) { // just a regular object
            $this->text = new ArrayCollection($text->getArrayValues());
        } elseif ($text instanceof ArrayCollection) {
            $this->text = $text;
        } else { // this should happen only when $text is an array of TextEntity (which is a standard Doctrine entity object)
            $this->text = new ArrayCollection($text);
        }
    }

PHPStan Strict Rules evaluates this code with the following error:
Instanceof between Doctrine\\Common\\Collections\\ArrayCollection&iterable<App\Entity\TextEntity> and Doctrine\\Common\\Collections\\ArrayCollection will always evaluate to true

In reality, PHP does not evaluate this condition as always true and the else branch is, in fact, reachable.

Is there any solution I am missing or is it a bug?

Thank you.

Booleans on both side

What is wrong with shortcut $doSomething && $something()?

Full snippet:

<?php

declare(strict_types = 1);

namespace Foo;

class Configurator
{
    public function loadConfig()
    {
        return $this;
    }
}

$configurator = new Configurator();

$devMode = true;
$devMode && $configurator->loadConfig();

Error:

------ -----------------------------------------------------------------------
  Line   bootstrap.php
 ------ -----------------------------------------------------------------------
  18     Only booleans are allowed in &&, Foo\Configurator given on
         the right side.
 ------ -----------------------------------------------------------------------

Only allow loose comparison with numeric operands

Similar to arithmetic operators (+/-/*///**/%), loose comparison operators should only be used for numeric values. This is true for ==, !=, <, >, <=, >= and <=>.

Using any of these operators for non-numeric values may lead to unexpected results.

The option to disallow == and != completely doesn't cover the full problem as other comparison operators are still allowed and might give unexpected result. It also disallows cases where you do want to use == because you're comparing numbers.

Good

42 == 42.0 // true
42 == "42" // true
42 == "4.2e1" // true
42 < "0xf1" // true

Bad

// convert to int, I guess
1000 >= "42 bytes" // true
(int)"42 bytes" // 42

// but not always :-(
(int)"4.2e1 bytes" // 42
1000 >= "4.2e1 bytes" // false ??

Universal rules of logic state that if a > b and b > c than a > c

$a = '42';
$b = 10;
$c = '9 dwarfs';

$a > $b // true
$b > $c // true
$a > $c // false ??

The spaceship operator should also not be used for strings.

function cmp1(string $x, string $y) {
    return $x <=> $y;
}

function cmp2(string $x, string $y) {
    return ($x . 'Foo') <=> ($y . 'Foo');
}

// Both functions should do the same, but...
cmp1("55", "5.5e1"); // 0
cmp2("55", "5.5e1"); // 1

The logic behind wether or not a string is converted to a number is just to complex. It's party explained in the manual, but that's not conclusive.

For strings you SHOULD always use === or strcmp.

While == could be useful to compare objects, the strange behaviour on strings makes this too dangerous.

$one = (object)['a' => 0];
$two = (object)['a' => '0.0'];
$three = (object)['a' => 'bar'];

$one == $two; // true
$one == $three; // true
$two == $three; // false

MissingPropertyTypehintRule does not see typehints of trait properties when in the context of a class

With the following code:

// T.php
trait T
{
    /** @var int */
    private $foo;
}
// C.php
class C
{
    use T;
}

And the MissingPropertyTypehintRule added like this:

services:
	-	
		class: PHPStan\Rules\Properties\MissingPropertyTypehintRule
		tags:
			- phpstan.rules.rule

When running phpstan with T.php and C.php as input, I get the following error:

 ------ -----------------------------------------------------
  Line   T.php (in context of class C)
 ------ -----------------------------------------------------
  12     Property C::$foo has no typehint specified.
 ------ -----------------------------------------------------

MissingPropertyTypehintRule: false positive with cache

Steps to reproduce:

  1. Create trait with property, but with missing @var annotation.
  2. Use it in some class
  3. Run phpstan - it should warn that the property has no typehint specified
  4. Add @var annotation
  5. Run phpstan again
  6. It still shows, that property has no typehint specified
  7. Clear cache

Dynamic call to static method

Hi. Would there be interest in a new rule that checks that static methods are called with :: and not with ->? I see that fairly regulary in the wild (mainly with PHPUnit's asserts) and it's driving me crazy.

Feature request: forced strict_types=1 declaration

Can such a check be made? I would like to force all files in a project to implement

<?php
declare(strict_types=1);

...

I tried looking but the closest I found were style guide rules for other tools.

Closure: report missing function parameter typehint and missing return type

Hi, I would like to enable for Closures the two rules already present for normal functions, MissingFunctionParameterTypehintRule and MissingFunctionReturnTypehintRule.

I found that master branch has this commit 491540d which already enabled PHPStan\Rules\Missing\MissingClosureNativeReturnTypehintRule, so I guess only the parameter typehint is still missing.

May I propose a PR for a MissingClosureParameterTypehintRule?

Strict comparison check on readdir maybe wrong

Running the following code with phpstan-strict-rules results in an unexpected error 9 Strict comparison using !== between false and string will always evaluate to true.

After a short analysis it seems like readdir has been typehinted (in functionMap.php) with string only instead of string|false (see http://php.net/manual/de/function.readdir.php). Is this a bug in the php docs, phpstan or am i missing something here?

<?php declare(strict_types = 1);

function getFiles(string $dir)
{
	$files = [];

	$handle = opendir($dir);
	if (false !== $handle) {
		while (false !== ($file = readdir($handle))) {
			if ('.' !== $file && '..' !== $file) {
				$files[] = $file;
			}
		}
		closedir($handle);
	}
	return $files;
}

Running

"phpstan/phpstan": "^0.10.1",
"phpstan/phpstan-strict-rules": "^0.10.0",

(Optionally) reduce strictness of "booleans everywhere" rule

Require booleans in if, elseif, ternary operator, after !, and on both sides of && and ||.

I get what this rule is trying to accomplish, but imho truthiness is pretty intuitive and unsurprising for most types. For example, I don't see anything wrong with doing an if ($something) where $something is known to be Something|null.

The obvious and afaik only exception would be strings, where e.g. "0" is falsey... So I suggest to change this rule to:

Forbid strings in if, elseif, ternary operator, after !, and on both sides of && and ||.

Either by default or opt-in via a parameter.

Support @inheritDoc without curly braces

For the following code, PHPStan will issue an error: Return type (Traversable) of method Bar1::convert() should be covariant with return type (iterable<string>&Traversable) of method Foo::convert().

The most common way to inherit PHPDoc explicitly is to use {@inheritDoc} (as in Bar2), but using @inheritDoc (as in Bar1) is the correct way, according to the spec draft.

interface Foo
{
    /**
     * @return Traversable<string>
     */
    public function convert(): Traversable;
}

abstract class Bar1 implements Foo
{
    /**
     * @inheritDoc
     */
    abstract public function convert(): Traversable;
}

abstract class Bar2 implements Foo
{
    /**
     * {@inheritDoc}
     */
    abstract public function convert(): Traversable;
}

abstract class Bar3 implements Foo
{
    abstract public function convert(): Traversable;
}

MissingMethodParameterTypehintRule does not see typehints of method parameters when in the context of a class

Similar to #30.

trait T
{
    /**
     * @param mixed  $bar
     * @param string $baz
     */
    public function Foo($bar, string $baz): void
    {

    }
}
class C
{
    use T;
}
parameters:
    bootstrap: %rootDir%/../../../phpstan-bootstrap.php
includes:
    - vendor/phpstan/phpstan-strict-rules/rules.neon
"phpstan/phpstan": "^0.10.1",
"phpstan/phpstan-strict-rules": "^0.10.0",

Getting error:

------ ----------------------------------------------------------------------------
  Line   stan/T.php (in context of class common\stan\C)
 ------ ----------------------------------------------------------------------------
  18     Method common\stan\C::Foo() has parameter $bar with no typehint specified.
 ------ ----------------------------------------------------------------------------

Error: "Property has no typehint specified" although the type is specified in constructor

Hello,
Strict-rules require all class properties to have typehint, although the ones that are specified in the constructor are perfectly inferred by the IDE.
Maybe we could make the property typehint optional if type of the property is specified in the constructor?

The code:

<?php declare(strict_types = 1);

class Foo
{
    private $bar;

    public function __construct(Bar $bar)
    {
        $this->bar = $bar;
    }
}

class Bar {}

Output:

+------------------------------------------------------+
| Line | test.php                                      |
+------------------------------------------------------+
| 5    | Property Foo::$bar has no typehint specified. |
+------------------------------------------------------+

 [ERROR] Found 1 error 

Expected output:
No errors

Determining type based on previous condition

See the following code. When evaluating $bar+1 in the second condition, we know that 0 === $foo. (If not, the condition would have short-circuited.) Since 0 === $foo, $bar must be an integer. (If it was null, the function would have returned in the first condition.) Hence evaluating $bar+1 should be fine. However, PHPStan gives the following error: Only numeric types are allowed in +, int|null given on the left side..

function foo(int $foo, ?int $bar): bool
{
    if (0 === $foo && null === $bar) {
        return true;
    }

    if (0 === $foo && 0 <= $bar+1) {
        return true;
    }

    return false;
}

False positive when using interface + trait

Testing code:

interface FooInterface
{
	/**
	 * @param mixed $value
	 *
	 * @return mixed
	 */
	public function test($value);
}

trait FooTrait
{
	/**
	 * @param mixed $value
	 *
	 * @return mixed
	 */
	public function test($value)
	{
	}
}

class Bar implements FooInterface
{
	use FooTrait;
}

Actual output:

 ------ ------------------------------------------------------------------------- 
  Line   Bar.php (in context of class App\Bar)                                 
 ------ ------------------------------------------------------------------------- 
  204    Method App\Bar::test() has no return typehint specified.                 
  204    Method App\Bar::test() has parameter $value with no typehint specified.  
 ------ ------------------------------------------------------------------------- 

                                                                                                                        
 [ERROR] Found 2 errors

Expected output: no error, both the parameter and the return type are correctly annotated.

Checking "If condition" error.

Exampe:

<?php declare(strict_types=1);

$arr = [];

if ($arr) {
    echo 'some';
}

if (!empty($arr)) {
    echo 'some';
}

If I have some array in return and check it in "if condition" I got next errors:
5 Only booleans are allowed in an if condition, array given.
9 Construct empty() is not allowed. Use more strict comparison.

So my question is: How it should be fixed?

False positive on the type of the result of an arithmetic operation

The check checkAlwaysTrueCheckTypeFunctionCall incorrectly assumes that the result of adding two integers is always an integer:

function addExact(int $augend, int $addend) : int
{
    $result = $augend + $addend;

    // Call to function is_int() with int will always evaluate to true.  
    if (!\is_int($result)) {
        throw new OverflowException('The result overflows the range of an integer.');
    }

    return $result;
}

addExact(\PHP_INT_MAX, 1);

Proposal: Forbidding constant "overriding"

Const overrides violate const invariants and are not LSP-compliant (types can be changed randomly).

There may be dragons when constants are used with static:: (sniff request to forbid that: slevomat/coding-standard#473) or $object::.

Any occurence of a constant override should be (abstract) method instead.

Edge case: constants like NAME on each member of a tree?

Weak comparison detection

Related: phpstan/phpstan#791

A weak comparison error should be thrown for such case:

<?php declare(strict_type=1);

function getCount(): ?int
{
	return 0 === rand(1, 5) % 5 ? 42 : null;
}

if (!getCount()) {
	echo 'test';
}

Because 0 and null will go to the same result.

But this case must not rise an error:

 <?php declare(strict_type=1);

function getUpdatedAt(): ?\DateTime
{
	return 0 === rand(1, 5) % 5 ? new \DateTime('now') : null;
}

if (!getUpdatedAt()) {
	echo 'test';
}

Because it's currently fine, no confusable value can be return here.

Allow empty for array arguments

There are some cases where using empty improves code readability and at no risk.

Consider the following example:

class Foo {
     // Always an array
     private $values = [];

    public function isEmpty() : bool
    {
        // This is a way better than \count($this->values) in terms of readability
        return empty($this->values);
    }
}

In that sense, I'd like to propose to now warn about the use of empty in cases where the subject can only be an array.

Cannot recognize property type when using traits inside traits

I'm not sure if this is an issue with PHPStan or strict rules, but it kicks in only when using strict rules.

trait Foo
{
    /**
     * @var string
     */
    protected $foo;
}

trait Bar
{
    use Foo;
}

class Baz
{
    use Bar;
}

results in:

 ------ ---------------------------------------------- 
  Line   src/Foo.php (in context of class Baz)         
 ------ ---------------------------------------------- 
  8      Property Baz::$foo has no typehint specified  
 ------ ---------------------------------------------- 

Implementing method with mixed type and without {@inheritdoc} fails

I'm not sure if this is an issue with PHPStan or the strict rules, but it kicks in only when using strict rules.

As far as I understand, {@inheritdoc} should not be required to inherit the types from the parent.

<?php

interface Foo
{
    /**
     * @param int|string $foo
     *
     * @return mixed
     */
    public function foo($foo);
}

class Bar implements Foo
{
    public function foo($foo)
    {
        return null;
    }
}

This results in:

 ------ ----------------------------------------------------------------- 
  Line   src/Bar.php                                                      
 ------ ----------------------------------------------------------------- 
  5      Method Bar::foo() has no return typehint specified               
  5      Method Bar::foo() has parameter $foo with no typehint specified  
 ------ ----------------------------------------------------------------- 

Once I add {@inheritdoc} to Bar::foo it works as expected.

Exclude PHPUnit asserts etc. from DynamicCallOnStaticMethodsRule?

When using PHPUnit, assertions and some other methods are normally called dynamically (e.g. $this->assertSame() or $this->once()), although they are declared as static methods. This of course triggers the DynamicCallOnStaticMethodsRule for all these calls.

According to the author of PHPUnit, it is encouraged to use $this-> instead of self::.

Keeping that in mind the question is: Should the PHPUnit classes (e.g. PHPUnit\Framework\Assert and PHPUnit\Framework\TestCase) be excluded from the DynamicCallOnStaticMethodsRule? (Maybe through a parameter to enable/disable this?)

Typecasting magic methods

Hello,

I have several magic methods in my code that I am unable to typehint correctly with phpstan-strict-rules.

Example:

public function __get(string $name) : mixed
{
    if (array_key_exists($name, $this->data)) {
        return $this->data[$name];
    }

    $trace = debug_backtrace();
    trigger_error(
        'Undefined property via __get(): ' . $name .
        ' in ' . $trace[0]['file'] .
        ' on line ' . $trace[0]['line'],
        E_USER_NOTICE
    );

    return null;
}

The method can return arrays, boolean, strings or null, which I thought was called mixed. When I add mixed as typecast though I get two errors:

58     Return typehint of method Boka10\Skeleton::__get() has invalid type    
     Boka10\Traits\mixed.                                                   
72     Method Boka10\Skeleton::__get() should return Boka10\Traits\mixed but  
     returns null.       

(Boka10\Traits is the namespace that the trait defining __get is in)

What would be the correct way to say "well, this method can return anything at this point"?

Using phpstan dev-master@c6a8cd1 with PHP 7.2.10 and declare(strict_types=1); is set

Idea: disallow protected members in final classes

Originally reported as slevomat/coding-standard#563 but impossible to implement there.

The idea is to ensure that final classes don't contain any new protected members. Such members can be made private because there are no descendants that can use them. Special care has to be taken to avoid changing protected methods from parent classes to private, such as in this example:

abstract class MyAbstractClass
{
    protected function protectedFunction()
    {
        echo 'foo';
    }
}

final class MyClass extends MyAbstractClass
{
    protected function protectedFunction()
    {
        echo 'bar';
    }
}

In this case, protectedFunction may not cause an error because its visibility can't be changed to private.

Detect using deprecated elements

Continuing work from phpstan/phpstan#690... /cc @iluuu1994

  • Extending deprecated class
  • Implementing deprecated interface
  • Using deprecated trait
  • newing deprecated class
  • Accessing deprecated property
  • Accessing deprecated static property
  • Accessing static property on a deprecated class
  • Accessing deprecated constant
  • Accessing constant on a deprecated class
  • Calling deprecated method
  • Calling deprecated static method
  • Calling static method on a deprecated class
  • Calling deprecated function
  • Overriding deprecated method (?)
  • Typehinting deprecated class/interface (?) - in properties, method and function parameters, return typehints, in @method and @property annotations

Some of them are a little bit more obscure so I don't require all of them to exist right away, this is just a list of what I could think of.

request for optional strict rule requiring packages using phpstan/phpstan to also use phpstan/phpstan-strict-rules

Not sure if rules that analyse composer.lock are within the scope of this project, but I'd find it useful (from a housekeeping stand point) to be able to configure phpstan to check that any package that has phpstan/phpstan listed in it's dependencies, then report those that don't also require phpstan/phpstan-strict-rules

i.e.

  • package A/A includes A/B, A/C, A/D, B/Foo C/Bar
  • packages A/* use phpstan/phpstan in require-dev
  • package B/Foo uses phpstan/phpstan, package C/Bar does not use phpstan
  • package A/A, A/B, A/D use phpstan/phpstan-strict-rules
  • running phpstan on A/A drops an error saying A/C & B/Foo do not use phpstan/phpstan-strict-rules

Trait explicitly said to be evaluated in context of class complains of invalid return type hint

I get a notice, say,
"RPC/Behavior/AttributeSerializerTrait.php (in context of class RPC\Controller\BaseController)"

Now regardless that it is explicitly states that it is "in context of" blah, phpstan complains that "return typehint of method" "has invalid type RPC\Behavior\BaseAttributes".

Which is of course missing, given that it is aliased/defined in BaseController.

Now I surely know that I could declare an abstract grandparent for the abstract parent attribute classes, so that I could put it fully qualified in the type hint of the method in the trait.

But it is not a question whether or not I should do this anyway (I did it at times, when I had any other use for this, and sometimes even when not; however, at other times, especially in the "Not Invented Here spirit", this is just infeasible.)

The question is that is it intentional that the trait's aliases are not resolved regardless of the context they are said to be evaluated in, or am I missing something?
After all, if this is indeed intentional, this seems to behave different from what PHP itself seems to do, which may be misleading.

Missing typehints reports when suppressed

So far we have had disabled missing typehints rule:

- "~^(Method|Property) .+::.+(\\(\\))? (has parameter \\$\\w+ with no|has no return|has no) typehint specified$~"

Because it reported errors also for classes where we disabled the same reporting with @phpssSuppress used for CodeSniffer. And that was disabled for a reason.

For example, we have overriden PHPUnit method

    /**
     * Returns a test double for the specified class.
     *
     * @param string|string[] $originalClassName
     *
     * @throws Exception
     * @throws \InvalidArgumentException
     */
    protected function createMock($originalClassName): MockObject
    {
        return $this->getMockBuilder($originalClassName)
                    ->disableOriginalConstructor()
                    ->disableOriginalClone()
                    ->disableArgumentCloning()
                    ->disallowMockingUnknownTypes()
                    ->getMock();
    }

with

    /**
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
     */
    protected function createMock($originalClassName) : MockObject
    {
        throw new \BadMethodCallException('Use Mockery instead.');
    }

We suppresed type hint checks because we have to follow parent signature from external library so types cannot be added.

However, this PHPStan strict check is very handy but also it's very exhausting to manually enumarete all places to ignoreErrors in config.

As example I have modified the MissingMethodParameterTypehintRule rule in a following way

    private function checkMethodParameter(MethodReflection $methodReflection, ParameterReflection $parameterReflection, ?Doc $doc): ?string
    {
        $parameterType = $parameterReflection->getType();

        if (
            $parameterType instanceof MixedType &&
            !$parameterType->isExplicitMixed() &&
            (
                $doc === null ||
                preg_match(
                    '~(@phpcsSuppress SlevomatCodingStandard\.TypeHints\.TypeHintDeclaration\.MissingParameterTypeHint|@inheritdoc)~',
                    $doc->getText()
                ) === 0
            )
        ) {
            return sprintf(
                'Method %s::%s() has parameter $%s with no typehint specified.',
                $methodReflection->getDeclaringClass()->getDisplayName(),
                $methodReflection->getName(),
                $parameterReflection->getName()
            );
        }

        return null;
    }

But I don't see how it would fit in the existing codebase when PR is sent. So I came to ask for ideas/share this snippet for anyone who would like to use it. Feel free to close this, I don't see a way how this could be integrated here in a clean way myself. IMO combining with PHPCS rules is exceeding the responsibility of this library.

Property has no typhint when using trait and abstract class

I have this trait:

<?php

namespace App\Traits;

use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;

trait EntityCreatedModified
{
    /**
     * @var DateTimeInterface|null
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $created;

    /**
     * @var DateTimeInterface|null
     * @ORM\Column(type="datetime", nullable=true)
     */
    private $updated;

    public function getCreated(): ?DateTimeInterface
    {
        return $this->created;
    }

    public function setCreated(?DateTimeInterface $created): self
    {
        $this->created = $created;

        return $this;
    }

    public function getUpdated(): ?DateTimeInterface
    {
        return $this->updated;
    }

    public function setUpdated(?DateTimeInterface $updated): self
    {
        $this->updated = $updated;

        return $this;
    }

    /**
     * @ORM\PrePersist()
     */
    public function onEntityCreated(): void
    {
        $this->setCreated(new DateTime());
        $this->setUpdated(new DateTime());
    }

    /**
     * @ORM\PreUpdate()
     */
    public function onEntityUpdated(): void
    {
        if (!$this->created) {
            $this->setCreated(new DateTime());
        }
        $this->setUpdated(new DateTime());
    }
}

Then I have this abstract class:

<?php

namespace App\Entity;

use App\Traits\EntityCreatedModified;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\MappedSuperclass()
 */
abstract class AbstractBaseEntity
{
    use EntityCreatedModified;
}

I get this error:

 ------ -------------------------------------------------------------------------------------- 
  Line   Traits/EntityCreatedModified.php (in context of class App\Entity\AbstractBaseEntity)  
 ------ -------------------------------------------------------------------------------------- 
  15     Property App\Entity\AbstractBaseEntity::$created has no typehint                      
         specified.                                                                            
  21     Property App\Entity\AbstractBaseEntity::$updated has no typehint                      
         specified.                                                                            
 ------ -------------------------------------------------------------------------------------- 

When I extend the abstract class or use the trait directly in a concrete class, there is no error.

Comparison operators should not allow string or array in the left side

Inspired by a discussing with @Ocramius, @alcaeus, and @Majkl578 today, some comparison operators should not allow an array or string types on its left side, or it will always result in false. Examples:

<?php

var_dump('foo' > 4); //bool(false)
var_dump([1, 2, 3] < 2); //bool(false)

I'd like to discuss it before I propose an implementation for it.

Edit: Now PHPStan does detect comparisons between arrays and numbers: https://phpstan.org/r/eaa2bf97-ddb6-4118-a596-49333c7acf7a, which leave us just with the discussion of strings and numbers.

@param annotation not inherited for renamed parameters in overridden methods

When a child class overrides a method and renames one if its parameter, the @param annotation of the parameter on the parent method is ignored. I think it should still be inherited.

In the following example:

class Foo
{
    /**
     * @param string $foo
     */
    public function test($foo) { }
}

class Bar extends Foo
{
    public function test($bar) { }
}

PHPStan will report the following error:

Method Bar::test() has parameter $bar with no typehint specified.

If I rename $bar to $foo, the error isn't reported anymore.

Disallow usage of ISO8601 constants

I have created a custom rule that disallows use of \DateTimeInterface::ISO8601 and \DATE_ISO8601 constants because they are not actually ISO 8601 compatible (ATOM is). It's a common pitfall and I wanted to prevent it from happening again in my team.

I could cook up a PR for this repository in a matter of minutes. Would that be appreciated or does the rule not really fit in with this repository's idealogy?

https://www.php.net/manual/en/class.datetimeinterface.php#datetime.constants.iso8601

Feature request: Disallow uninitialized properties

With PHP 7.4 typed properties, following is possible:

class Foo
{
    public Bar $bar;
}

$foo = new Foo();
var_dump($foo->bar);

Which produces an error.

Would it be useful to have a check that properties may be uninitialized only during __constructor, and if the constructor does not set a value it is reported as an error?

Idea: Disallow Negative Operator

PHP's negative operator can return true for a number of values: https://3v4l.org/OPBti.

Instead of using it, an explicit condition ($foo === false, for example) should be used instead.

The rule itself should be easy to create, but I'd like to discuss it first :)

Check whether function has return typehint

Hi,
I would like to report methods which return type is not known - no php typehint/phpdoc.

Summary of a problem or a feature request

I know that there is rule that checks whether function returns which it typehints - eg.

function a(): string
{
    return new stdClass; 
}

this is correctly reported as should return string but returns stdClass.
But when no typehint is defined I would expected that function "returns" void.

Code snippet that reproduces the problem

https://phpstan.org/r/10e7cc35381dcada1f8b1f54095fb0b1

Expected output

should return void but returns stdClass

Feature request: Disallow usage of backtick operator

Hi,

I'm opening issue here since this is the strict ruleset and I want to suggest to disallow usage of backtick operator.

Basically disallow to do something like this

<?php

$ls = `ls -la`;

echo "<pre>$ls</pre>";

Currently there is a RFC to deprecate them and internals are currently having a debate regarding the RFC.

The point is that you can replace backtick operators with shell_exec() which much more explicit.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.