programing

침묵 "선언...호환성이 있어야 한다"라는 경고 메시지가 표시됩니다.

projobs 2023. 1. 14. 10:09
반응형

침묵 "선언...호환성이 있어야 한다"라는 경고 메시지가 표시됩니다.

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에서 이러한 오류만 잠재우려면 어떻게 해야 합니까?

  • 7 에는 PHP 7이었습니다.E_STRICT쉽게 처리할 수 있는 경고 유형.이제 그것들은 그저 오래된 경고일 뿐이다.다른 경고에 대해 알고 싶기 때문에 모든 경고를 다 끌 수는 없습니다.

  • 나는 이 레거시 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);
}

는, 「」를 반환합니다.true " "로 시작하는 경우Declaration of기본적으로 경고가 처리되었음을 PHP에 알립니다.그렇기 때문에 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. 적절한 해결책

실제로 다른 사람의 레거시 코드를 수정하는 것에 대해서는, 이것이 용이한 것과 관리하기 쉬운 것 사이에서 행해지는 경우가 많이 있습니다. 클래스의 에서는, 「」를 참조해 주세요.B는 의 입니다.A이러한 예에 따라 LSP 위반을 제거할 필요는 없습니다.

  1. 어떤 경우는 꽤 쉽다.하위 클래스에 누락된 default 인수가 있는 경우 해당 인수를 추가하고 다음으로 넘어갑니다. 예:

    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. 를 완전히 이 더 수 방법은, 「 Method」를 「Superclass Method」로 것이, 「Superclass Method」를 「Superclass Method로 되돌립니다.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()디폴트(통과되지 않은) 인수는 고려되지 않기 때문입니다. 리리오오 we밖에 남지 않았다.func_num_args().

  7. 분기 인터페이스를 가진 클래스의 계층 전체가 있는 경우, 한층 더 분리가 용이할 수 있습니다.모든 클래스에서 정의가 충돌하는 함수의 이름을 바꿉니다.그런 다음 다음 다음 클래스의 단일 중간 부모에 프록시 함수를 추가합니다.

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

    이렇게 하면 경고 없이 LSP를 위반할 수 있지만 모든 유형 체크를 서브클래스로 유지할 수 있습니다.

더 이상 경고가 트리거되지 않도록 코드를 실제로 수정하려는 경우:디폴트값을 지정하면 서브클래스의 오버라이드 메서드에 파라미터를 추가할 수 있다는 것을 알 수 있습니다.예를 들어 다음과 같은 경고가 트리거됩니다.

//"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

// 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($_);

은 PHP 7을 합니다.E_STRICT에러 레벨이에 대한 정보는 PHP7 호환성 노트에서 확인할 수 있습니다.PHP 7 개발 중에 논의된 제안 문서를 읽어보실 수도 있습니다.

간단한 사실은 다음과 같습니다.E_STRICTNotice는 개발자들에게 나쁜 관행을 사용하지만 처음에는 변경을 강요하지 않는다는 것을 알리기 위해 여러 버전 전에 도입되었습니다.그러나 최근 버전, 특히 PHP 7은 이러한 점에 대해 더욱 엄격해지고 있습니다.

발생하고 있는 에러는 전형적인 경우입니다.

부모 클래스에서 같은 이름의 메서드를 재정의하는 메서드를 클래스에 정의했지만 재정의 메서드의 인수 시그니처가 다릅니다.

대부분의 현대 프로그래밍 언어들은 실제로 이것을 전혀 허용하지 않을 것이다.PHP는 개발자들이 이런 것들을 피해갈 수 있게 해 주었지만, 언어는 버전마다 엄격해지고 있습니다. 특히 지금은 PHP 7에서는 하위 호환성을 깨뜨리는 중요한 변경을 정당화할 수 있도록 새로운 메이저 버전 번호를 사용하고 있습니다.

문제는 이미 경고 메시지를 무시하고 있기 때문입니다.질문은 이 솔루션을 계속 사용하려는 것을 의미하지만, "strict" 및 "decommitted"와 같은 메시지는 향후 버전에서 코드가 파손될 수 있음을 나타내는 명시적 경고로 취급해야 합니다.지난 몇 년 동안 그들을 무시함으로써 당신은 현재 상황에 효과적으로 자신을 배치한 것입니다. (그것이 당신이 듣고 싶은 말이 아니라는 것을 알고 있고, 현재 상황에 별로 도움이 되지 않는다는 것을 알지만, 분명히 하는 것이 중요합니다.)

당신이 찾는 종류의 작품은 정말 없어요.PHP 언어는 진화하고 있으며, 만약 당신이 PHP 7을 고수하고 싶다면 당신의 코드도 진화해야 합니다.코드를 수정할 수 없는 경우에는 모든 경고를 억제하거나 이러한 경고로 인해 로그가 엉망이 됩니다.

PHP 7을 계속 사용할 계획이라면 알아야 할 다른 점은 이 버전에는 상당히 미묘한 호환성 문제가 몇 가지 있다는 것입니다.만약 당신이 보고한 것과 같은 에러가 있는 코드의 경우, 그것은 아마도 꽤 오래 전부터 존재해 왔으며 PHP 7에서 문제를 일으킬 수 있는 다른 문제가 있을 것입니다.이런 코드에 대해서는 PHP 7에 커밋하기 전에 코드에 대해 좀 더 철저한 감사를 하는 것이 좋습니다.만약 당신이 그것을 할 준비가 되어 있지 않다면, 혹은 하지 않는 것이 좋습니다.발견된 버그를 수정할 준비가 되어 있습니다(그리고 질문의 의미는 그렇지 않다는 것입니다).그러면 PHP 7은 아마도 당신에게 너무 많은 업그레이드일 것입니다.

PHP 5.6으로 되돌릴 수 있는 옵션이 있습니다.그렇지 않다고 하셨는데, 단기적인 솔루션으로서 좀 더 쉽게 할 수 있을 것 같습니다.솔직히, 그게 최선의 선택일 것 같아.

나는 동의한다: 첫 번째 게시물의 예는 나쁜 관행이다.예를 들어 다음과 같은 예를 제시하겠습니다.

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);
        }
}

이것은 합법적인 코드 구조라고 생각합니다만, 그래도 로그에는 경고가 표시됩니다.displayProperties()동일한 매개 변수가 없습니다.또, 이 옵션을 옵션으로 할 수 없습니다.= null그 다음에...

이 특정 예에서 이 경고가 잘못되었다고 생각하는 것이 맞습니까?

저도 이 문제가 있었어요.부모 클래스의 함수를 재정의하는 클래스가 있지만 재정의 매개 변수 수가 다릅니다.몇 가지 간단한 방법을 생각할 수 있지만 사소한 코드 변경이 필요합니다.

  1. 하위 클래스의 함수 이름을 변경합니다(더 이상 상위 함수를 재정의하지 않도록 함). - 또는-
  2. 상위 함수의 매개 변수를 변경하되 추가 매개 변수(예: function funcsvar1, $var2=functions)를 옵션으로 만듭니다. 이것이 가장 쉽고 코드 변경이 덜 필요할 수 있습니다.그러나 다른 많은 장소에서 사용한다면 부모에게 이것을 바꿀 가치가 없을 수도 있다.그래서 저는 1번으로 했어요.

  3. 가능하면 하위 클래스 함수의 추가 매개 변수를 전달하는 대신 global을 사용하여 추가 매개 변수를 가져옵니다.이것은 이상적인 코딩은 아니지만 어쨌든 가능한 반창고입니다.

부모 클래스 메서드 정의를 모두 제거하고 매직 메서드로 인터셉트할 수 있습니다.

public function __call($name, $args)
{
    if($name == 'do') {
        // do things with the unknown # of args
    } else {
        throw new \Exception("Unknown method $name", 500);
    }
}

나는 이 문제에 부딪혀서 이 길로 갔다.

기본 클래스의 인수가 파생 클래스보다 적은 경우 다음과 같이 파생 클래스에 인수를 추가할 수 있습니다.

    $namespace = 'default';
    if (func_num_args() > 2) {
        $namespace = func_get_arg(2);
    }

이 방법으로 세 번째 "기본" 인수를 추가하지만 시그니처는 변경하지 않습니다.이 코드를 호출하고 변경할 수 없는 대량의 코드가 있어 하위 호환성을 유지하고 싶은 경우에만 제안합니다.

이 상황은 JSession::set이 $namespace 파라미터를 추가했지만 JObject::set에는 이러한 파라미터가 없는 기본 클래스로 JObject가 있는 오래된 Joomla 코드(v1.5)에서 발견되었습니다.

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

반응형