program story

PHP 7에서“선언은… 호환되어야합니다”경고가 표시되지 않습니다.

inputbox 2020. 11. 26. 08:15
반응형

PHP 7에서“선언은… 호환되어야합니다”경고가 표시되지 않습니다.


PHP 7로 업그레이드 한 후 이러한 종류의 오류로 인해 로그가 거의 질식했습니다.

PHP Warning: Declaration of Example::do($a, $b, $c) should be compatible with ParentOfExample::do($c = null) in Example.php on line 22548

PHP 7에서이 오류와이 오류 만 어떻게 침묵합니까?

  • PHP 7 이전에는 쉽게 다룰 수있는E_STRICT 경고 유형이었습니다 . 이제 그들은 단지 평범한 오래된 경고입니다. 나는 이후 않는 다른 경고에 대해 알고 싶은, 그냥 아예 모든 경고를 해제 할 수 없습니다.

  • 이러한 레거시 API를 사용하는 모든 소프트웨어를 언급하지 않고도 이러한 레거시 API를 다시 작성할 수있는 정신적 능력이 없습니다. 누구도 그 비용을 지불하지 않을 것입니다. 저도 애초에 그것들을 개발하지 않기 때문에 비난받을 사람이 아닙니다. (단위 테스트? 10 년 전에는 유행하지 않았습니다.)

  • 나는 피하고자하는 어떤 속임수func_get_args가능한 한 같은과 유사한.

  • PHP 5로 다운 그레이드하고 싶지는 않습니다.

  • 여전히 다른 오류 및 경고에 대해 알고 싶습니다.

이를 수행하는 깨끗하고 좋은 방법이 있습니까?


1. 해결 방법

작성하지 않은 모든 코드 , 특히 레거시 코드 항상 수정할 수있는 것은 아니기 때문에 ...

if (PHP_MAJOR_VERSION >= 7) {
    set_error_handler(function ($errno, $errstr) {
       return strpos($errstr, 'Declaration of') === 0;
    }, E_WARNING);
}

이 오류 처리기는 기본적으로 PHP에 경고가 처리되었음을 알리는 true경고를 반환 합니다 Declaration of. 이것이 PHP가 다른 곳에서이 경고를보고하지 않는 이유입니다.

또한이 코드는 PHP 7 이상에서만 실행됩니다.


특정 코드베이스에 대해서만 이런 일이 발생하도록하려면 오류가있는 파일이 해당 코드베이스 또는 관심 라이브러리에 속하는지 확인할 수 있습니다.

if (PHP_MAJOR_VERSION >= 7) {
    set_error_handler(function ($errno, $errstr, $file) {
        return strpos($file, 'path/to/legacy/library') !== false &&
            strpos($errstr, 'Declaration of') === 0;
    }, E_WARNING);
}

2. 적절한 솔루션

실제로 다른 사람의 레거시 코드를 수정하는 것과 관련하여 쉽고 관리하기 쉬운 것 사이에서이 작업을 수행 할 수있는 경우가 많이 있습니다. 아래 예제에서 class B는의 하위 클래스입니다 A. 이러한 예를 따르면 LSP 위반을 반드시 제거 할 필요는 없습니다.

  1. 어떤 경우는 매우 쉽습니다. 하위 클래스에 누락 된 기본 인수가있는 경우 추가하고 계속 진행하십시오. 예 :이 경우 :

    Declaration of B::foo() should be compatible with A::foo($bar = null)
    

    당신은 할 것입니다 :

    - public function foo()
    + public function foo($bar = null)
    
  2. 하위 클래스에 추가 제한이있는 경우 함수 본문 내부로 이동하면서 정의에서 제한을 제거합니다.

    Declaration of B::add(Baz $baz) should be compatible with A::add($n)
    

    심각도에 따라 어설 션을 사용하거나 예외를 발생시킬 수 있습니다.

    - public function add(Baz $baz)
    + public function add($baz)
      {
    +     assert($baz instanceof Baz);
    

    제약 조건이 순전히 문서화 목적으로 사용되는 경우 해당 제약 조건을 속한 위치로 이동하십시오.

    - protected function setValue(Baz $baz)
    + /**
    +  * @param Baz $baz
    +  */
    + protected function setValue($baz)
      {
    +     /** @var $baz Baz */
    
  3. 서브 클래스에 슈퍼 클래스보다 인수가 적고 슈퍼 클래스에서 선택적으로 만들 수있는 경우 하위 클래스에 자리 표시자를 추가하기 만하면됩니다. 주어진 오류 문자열 :

    Declaration of B::foo($param = '') should be compatible with A::foo($x = 40, $y = '')
    

    당신은 할 것입니다 :

    - public function foo($param = '')
    + public function foo($param = '', $_ = null)
    
  4. 서브 클래스에서 요구되는 몇 가지 주장이 보이면 문제를 해결하십시오.

    - protected function foo($bar)
    + protected function foo($bar = null)
      {
    +     if (empty($bar['key'])) {
    +         throw new Exception("Invalid argument");
    +     }
    
  5. 때로는 선택적 인수를 완전히 제외하고 func_get_args마법으로 돌아가도록 수퍼 클래스 메서드를 변경하는 것이 더 쉬울 수 있습니다 . 누락 된 주장을 문서화하는 것을 잊지 마십시오.

      /**
    +  * @param callable $bar
       */
    - public function getFoo($bar = false)
    + public function getFoo()
      {
    +     if (func_num_args() && $bar = func_get_arg(0)) {
    +         // go on with $bar
    

    둘 이상의 인수를 제거해야하는 경우 이는 매우 지루할 수 있습니다.

  6. 대체 원칙을 심각하게 위반하면 상황이 훨씬 더 흥미로워집니다. 입력 된 인수가 없으면 쉽습니다. 모든 추가 인수를 선택 사항으로 설정 한 다음 해당 인수가 있는지 확인하십시오. 주어진 오류 :

    Declaration of B::save($key, $value) should be compatible with A::save($foo = NULL)
    

    당신은 할 것입니다 :

    - public function save($key, $value)
    + public function save($key = null, $value = null)
      {
    +     if (func_num_args() < 2) {
    +         throw new Exception("Required argument missing");
    +     }
    

    func_get_args()기본 (전달되지 않은) 인수를 고려하지 않기 때문에 여기서 사용할 수 없습니다 . 우리는 func_num_args().

  7. 분기 인터페이스가있는 전체 계층 구조가있는 경우 더 쉽게 분기 할 수 있습니다. 모든 클래스에서 정의가 충돌하는 함수의 이름을 바꿉니다. 그런 다음 다음 클래스에 대해 단일 중간 상위에 프록시 함수를 추가합니다.

    function save($arg = null) // conforms to the parent
    {
        $args = func_get_args();
        return $this->saveExtra(...$args); // diverged interface
    }
    

    이렇게하면 경고없이 LSP가 여전히 위반되지만 하위 클래스에있는 모든 유형 검사를 유지하게됩니다.


오류 무음으로 처리 해야하는 경우 즉시 호출되는 무음 함수 식 내에서 클래스를 선언 할 수 있습니다.

<?php

// unsilenced
class Fooable {
    public function foo($a, $b, $c) {}
}

// silenced
@(function () {
    class ExtendedFooable extends Fooable {
        public function foo($d) {}
    }
})();

그러나 나는 이것에 대해 강력히 권장합니다. 코드가 어떻게 손상되었는지에 대한 경고를 끄는 것보다 코드를 수정하는 것이 좋습니다.


PHP 5 호환성을 유지해야하는 경우 위 코드는 PHP 7에서만 작동합니다. PHP 5에는 표현식에 대한 통일 된 구문이 없기 때문 입니다. PHP 5에서 작동하게하려면 호출하기 전에 함수를 변수에 할당해야합니다 (또는 이름이 지정된 함수로 만들어야합니다).

$_ = function () {
    class ExtendedFooable extends Fooable {
        public function foo($d) {}
    }
};
@$_();
unset($_);

더 이상 경고를 트리거하지 않도록 코드를 실제로 수정하려는 사람들을 위해 : 기본값을 제공하는 한 하위 클래스에서 재정의 된 메서드에 추가 매개 변수를 추가 할 수 있다는 것을 배우는 것이 유용하다는 것을 알았습니다. 예를 들어 이렇게하면 경고가 트리거됩니다.

//"Warning: Declaration of B::foo($arg1) should be compatible with A::foo()"
class B extends A {
    function foo($arg1) {}
}

class A {
    function foo() {}
}

이것은 다음을 수행하지 않습니다.

class B extends A {
    function foo($arg1 = null) {}
}

class A {
    function foo() {}
}

PHP 7 removes the E_STRICT error level. Info about this can be found in the PHP7 compatibility notes. You might also want to read the proposal document where it was discussed while PHP 7 was being developed.

The simple fact is this: The E_STRICT notices were introduced a number of versions ago, in an attempt to notify developers that they were using bad practice, but initially without trying to force any changes. However recent versions, and PHP 7 in particular, have become more strict about these things.

The error you're experiencing is a classic case:

You have defined a method in your class that overrides a method of the same name in the parent class, but your override method has a different argument signature.

Most modern programming languages would not actually allow this at all. PHP used to allow developers to get away with stuff like this, but the language is becoming more strict with every version, especially now with PHP 7 -- they went with a new major version number specifically so that they could justify making significant changes that break backward compatibility.

The problem you have is because you've already been ignoring the warning messages. Your question implies that this is the solution you want to continue with, but messages like "strict" and "deprecated" should be treated as an explicit warning that your code is likely to break in future versions. By ignoring them for the past number of years, you have effectively placed yourself in the situation you have now. (I know that's not what you want to hear, and doesn't really help the situation now, but it's important to make it clear)

There really isn't a work around of the kind you're looking for. The PHP language is evolving, and if you want to stick with PHP 7 your code will need to evolve too. If you really can't fix the code, then you will either have to suppress all warnings or else live with these warnings cluttering up your logs.

The other thing you need to know if you plan to stick with PHP 7 is that there are a number of other compatibility breaks with this version, including some that are quite subtle. If your code is in a state where it has errors like the one you're reporting, it means that it's probably been around for quite a while, and likely has other issues that will cause you problems in PHP 7. For code like this, I would suggest doing a more thorough audit of the code before committing to PHP 7. If you're not prepared to do that, or not prepared to fix the bugs that are found (and the implication from your question is that you are not), then I'd suggest that PHP 7 is probably an upgrade too far for you.

You do have the option of reverting to PHP 5.6. I know you said you don't want to do that, but as a short-to-medium term solution it will make things easier for you. Frankly, I think it might be your best option.


I agree: the example in the first post is bad practice. Now what if you have that example :

class AnimalData {
        public $shout;
}

class BirdData extends AnimalData {
        public $wingNumber;
}

class DogData extends AnimalData {
        public $legNumber;
}

class AnimalManager {
        public static function displayProperties(AnimalData $animal) {
                var_dump($animal->shout);
        }
}

class BirdManager extends AnimalManager {
        public static function displayProperties(BirdData $bird) {
                self::displayProperties($bird);
                var_dump($bird->wingNumber);
        }
}

class DogManager extends AnimalManager {
        public static function displayProperties(DogData $dog) {
                self::displayProperties($dog);
                var_dump($dog->legNumber);
        }
}

I believe this is a legitimate code structure, nevertheless this will raise a warning in my logs because the displayProperties() do not have the same parameters. Moreover I can't make them optional by adding a = null after them...

Am I right thinking this warning is wrong in this specific example please?


I had this issue as well. I have a class that overrides a function of the parent class, but the override has different num of parameters. I can think of a few easy work arounds - but do require minor code change.

  1. change the name of the function in the subclass (so it no longer overrides parent function) -or-
  2. change the parameters of the parent function, but make the extra parameters optional (e.g., function func($var1, $var2=null) - this may be easiest and require less code changes. But it may not be worth to change this in the parent if its used so many other places. So I went with #1 in my case.

  3. If possible, instead of passing the extra params in the subclass function, use global to pull in the extra params. This is not ideal coding; but a possible band-aid anyway.

참고URL : https://stackoverflow.com/questions/36079651/silence-declaration-should-be-compatible-warnings-in-php-7

반응형