[php] PHP 5.2 이상이 추상 정적 클래스 메서드를 허용하지 않는 이유는 무엇입니까?

PHP 5.2에서 엄격한 경고를 활성화 한 후, 원래 엄격한 경고없이 작성된 프로젝트에서 엄격한 표준 경고를 보았습니다.

엄격한 표준 : 정적 함수 Program :: getSelectSQL () Program.class.inc에서 추상이 아니어야합니다.

문제의 함수는 추상 부모 클래스 Program에 속하며 TVProgram과 같은 자식 클래스에서 구현되어야하므로 추상 정적으로 선언됩니다.

여기 에서이 변경 사항에 대한 참조를 찾았 습니다 .

추상 정적 클래스 함수를 삭제했습니다. 감독으로 인해 PHP 5.0.x 및 5.1.x는 클래스에서 추상 정적 함수를 허용했습니다. PHP 5.2.x부터는 인터페이스 만 가질 수 있습니다.

내 질문은 : 누군가 PHP에 추상 정적 함수가 없어야하는 이유를 분명하게 설명 할 수 있습니까?



답변

정적 메서드는이를 선언 한 클래스에 속합니다. 클래스를 확장 할 때 동일한 이름의 정적 메서드를 만들 수 있지만 실제로는 정적 추상 메서드를 구현하지 않습니다.

정적 메서드로 모든 클래스를 확장 할 때도 마찬가지입니다. 해당 클래스를 확장하고 동일한 시그니처의 정적 메서드를 생성하는 경우 실제로 슈퍼 클래스의 정적 메서드를 재정의하지 않습니다.

편집 (2009 년 9 월 16 일) 이에 대한
업데이트입니다. PHP 5.3을 실행하면 추상 정적이 좋든 나쁘 든 돌아 왔습니다. (자세한 내용은 http://php.net/lsb 참조 )

수정 (philfreo에 의한)
abstract static은 PHP 5.3에서 여전히 허용되지 않으며 LSB 는 관련이 있지만 다릅니다.


답변

길고 슬픈 이야기입니다.

PHP 5.2가이 경고를 처음 도입했을 때 늦은 정적 바인딩 은 아직 언어로 제공되지 않았습니다. 후기 정적 바인딩에 익숙하지 않은 경우 다음과 같은 코드가 예상대로 작동하지 않습니다.

<?php

abstract class ParentClass {
    static function foo() {
        echo "I'm gonna do bar()";
        self::bar();
    }

    abstract static function bar();
}

class ChildClass extends ParentClass {
    static function bar() {
        echo "Hello, World!";
    }
}

ChildClass::foo();

엄격 모드 경고를 제외하고 위의 코드는 작동하지 않습니다. self::bar()에서 통화 foo()명확가 지칭 bar()방법 ParentClass때에도 foo()방법으로 불린다 ChildClass. 엄격 모드를 끈 상태에서이 코드를 실행하려고하면 ” PHP 치명적 오류 : 추상 메소드 ParentClass :: bar ()를 호출 할 수 없습니다. “가 표시됩니다.

이 점을 감안할 때 PHP 5.2의 추상 정적 메서드는 쓸모가 없었습니다. 전체를 점 하고 다른 자식 클래스에 다른 구현을 제공 – 추상적 인 방법을 사용하여 당신이 그것을 호출 될 것 무엇을 구현 모른 채 메서드를 호출하는 코드를 작성할 수 있다는 것입니다. 그러나 PHP 5.2는 호출되는 자식 클래스의 정적 메서드를 호출하는 부모 클래스의 메서드를 작성하는 명확한 방법을 제공하지 않기 때문에 이러한 추상 정적 메서드 사용은 불가능합니다. 따라서 abstract staticPHP 5.2에서 사용하는 것은 잘못된 코드입니다.self 키워드가 작동 . 이것에 대해 경고를 던지는 것은 전적으로 합리적이었습니다.

그러나 PHP 5.3은 static키워드 를 통해 메서드가 호출 된 클래스를 참조 할 수있는 기능이 추가되었습니다 ( self항상 메서드가 정의 된 클래스를 참조 하는 키워드 와 달리 ). 위의 예제에서로 변경 self::bar()하면 static::bar()PHP 5.3 이상에서 제대로 작동합니다. 당신은 더 약 읽을 수 selfstatic새 정적 대 새로운 자기 .

static 키워드를 추가하면 abstract static 경고 던지는 가 사라졌습니다. 후기 정적 바인딩의 주요 목적은 부모 클래스에 정의 된 메서드가 자식 클래스에 정의 된 정적 메서드를 호출 할 수 있도록하는 것이 었습니다. 추상 정적 메서드를 허용하는 것은 늦은 정적 바인딩이 존재하는 경우 합리적이고 일관된 것처럼 보입니다.

당신은 여전히 ​​경고를 유지하기 위해 소송을 제기 할 수 있습니다. 예를 들어, PHP를 사용하면 추상 클래스의 정적 메서드를 호출 할 수 있기 때문에 위의 예에서 (으로 대체 self하여 수정 한 후에도 static) 깨져서 실제로 원하지 않는 공용 메서드 ParentClass::foo()를 노출 한다고 주장 할 수 있습니다. 폭로. 비 정적 클래스 사용-즉, 모든 메서드 인스턴스 메서드를 만들고 자식을 만드는ParentClass 모든 싱글 톤 또는 무언가로 이 문제를 해결할 수 있습니다. 왜냐하면 ParentClass, 추상이 될 수없고 인스턴스 메서드가 인스턴스화 할 수 없기 때문입니다. 불리다. 이 주장은 약하다고 생각합니다.ParentClass::foo() 큰 문제가 아니며 정적 클래스 대신 싱글 톤을 사용하는 것은 종종 불필요하게 장황하고 추악합니다.) 그러나 합리적으로 동의하지 않을 수 있습니다. 다소 주관적인 호출입니다.

그래서이 주장을 바탕으로 PHP 개발자는 경고를 언어로 유지했습니다.

어, 정확히는 아닙니다 .

위에 링크 된 PHP 버그 보고서 53081은 static::foo()구조 추가로 인해 추상 정적 메서드가 합리적이고 유용하게 되었기 때문에 경고를 삭제하도록 요청했습니다 . Rasmus Lerdorf (PHP 창시자)는 요청을 가짜로 표시하는 것으로 시작하여 경고를 정당화하기 위해 긴 잘못된 추론을 거칩니다. 그런 다음 마지막으로이 교환이 발생합니다.

조르지오

알지만:

abstract class cA
{
      //static function A(){self::B();} error, undefined method
      static function A(){static::B();} // good
      abstract static function B();
}

class cB extends cA
{
    static function B(){echo "ok";}
}

cB::A();

라스무스

맞습니다, 그것이 정확히 작동하는 방식입니다.

조르지오

그러나 그것은 허용되지 않습니다 🙁

라스무스

허용되지 않는 것은 무엇입니까?

abstract class cA {
      static function A(){static::B();}
      abstract static function B();
}

class cB extends cA {
    static function B(){echo "ok";}
}

cB::A();

이것은 잘 작동합니다. 분명히 self :: B ()를 호출 할 수 없지만 static :: B ()는 괜찮습니다.

그의 예제에있는 코드가 “잘 작동한다”는 Rasmus의 주장은 거짓입니다. 아시다시피 엄격 모드 경고가 발생합니다. Strict 모드가 켜지지 않은 상태에서 테스트하고 있었던 것 같습니다. 그럼에도 불구하고 혼란스러운 Rasmus는 요청을 “가짜”로 잘못 종료했습니다.

그것이 경고가 여전히 언어로 된 이유입니다. 이것은 완전히 만족스러운 설명이 아닐 수 있습니다. 경고의 합리적 정당성이 있기를 바라면서 여기에 왔을 것입니다. 불행히도 현실 세계에서 때때로 선택은 합리적인 의사 결정이 아닌 일상적인 실수와 잘못된 추론에서 비롯됩니다. 이것은 단순히 그 시간 중 하나입니다.

운 좋게도 추정 가능한 Nikita Popov는 PHP RFC : Reclassify E_STRICT notices의 일부로 PHP 7의 언어에서 경고를 제거했습니다 . 궁극적으로 온전한 상태가 유지되었으며 PHP 7이 출시되면 abstract static이 어리석은 경고를받지 않고 모두 즐겁게 사용할 수 있습니다 .


답변

이 문제에 대한 매우 간단한 해결 방법이 있으며 이는 실제로 디자인 관점에서 의미가 있습니다. Jonathan은 다음과 같이 썼습니다.

정적 메서드로 모든 클래스를 확장 할 때도 마찬가지입니다. 해당 클래스를 확장하고 동일한 시그니처의 정적 메서드를 생성하는 경우 실제로 슈퍼 클래스의 정적 메서드를 재정의하지 않습니다.

따라서 해결 방법으로 다음과 같이 할 수 있습니다.

<?php
abstract class MyFoo implements iMyFoo {

    public static final function factory($type, $someData) {
        // don't forget checking and do whatever else you would
        // like to do inside a factory method
        $class = get_called_class()."_".$type;
        $inst = $class::getInstance($someData);
        return $inst;
    }
}


interface iMyFoo {
    static function factory($type, $someData);
    static function getInstance();
    function getSomeData();
}
?>

이제 MyFoo를 서브 클래 싱하는 모든 클래스가 getInstance 정적 메서드와 공용 getSomeData 메서드를 구현하도록 강제합니다. MyFoo를 하위 클래스로 지정하지 않아도 iMyFoo를 구현하여 유사한 기능을 가진 클래스를 만들 수 있습니다.


답변

낡은 건 알지만 ….

그 부모 클래스의 정적 메서드에 예외를 던지지 않는 이유는, 재정의하지 않으면 예외가 발생합니다.


답변

추상 클래스 / 인터페이스가 프로그래머 간의 계약으로 볼 수 있다고 주장합니다. 실제 기능을 구현하지 않고 어떻게 보이는지 / 동작해야 하는지를 더 많이 다룹니다. php5.0 및 5.1.x에서 볼 수 있듯이 PHP 개발자가이를 수행하는 것을 방지하는 것은 자연법이 아니라 다른 언어로 된 다른 OO 디자인 패턴과 함께 가고 싶은 충동입니다. 기본적으로 이러한 아이디어는 이미 다른 언어에 익숙한 경우 예기치 않은 동작을 방지하려고합니다.


답변

정적 추상 함수를 금지 할 이유가 없습니다. 금지 할 이유가 없다는 가장 좋은 주장은 Java에서 허용된다는 것입니다. 질문은 다음과 같습니다.-기술적으로 실행 가능합니까? -예, PHP 5.2에 존재하고 Java에 존재하기 때문입니다. 그래서 그는 그것을 할 수 있습니다. 우리가해야합니까? -말이 되나요? 예. 클래스의 일부를 구현하고 클래스의 다른 부분을 사용자에게 맡기는 것이 합리적입니다. 비 정적 함수에서 이치에 맞습니다. 왜 정적 함수에서는 이치에 맞지 않나요? 정적 함수의 한 가지 용도는 둘 이상의 인스턴스 (싱글 톤)가 없어야하는 클래스입니다. 예를 들어 암호화 엔진. 여러 인스턴스에 존재할 필요는 없으며이를 방지 할 이유가 있습니다. 예를 들어, 침입자로부터 메모리의 한 부분 만 보호해야합니다. 따라서 엔진의 한 부분을 구현하고 암호화 알고리즘을 사용자에게 맡기는 것이 완벽합니다. 이것은 하나의 예일뿐입니다. 정적 함수를 사용하는 데 익숙하다면 더 많은 것을 찾을 수 있습니다.


답변

PHP 5.4 이상에서는 특성을 사용하십시오.

trait StaticExample {
    public static function instance () {
    return new self;
    }
}

그리고 당신의 수업에서 시작하십시오 :

use StaticExample;