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


121

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

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

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

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

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

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


12
새로운 독자는이 불합리한 제한 PHP 7에서 제거 된 것을주의해야한다
마크 Amery

답변:


76

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

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

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

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


3
좋아, 내 추상 클래스를 확장하는 모든 자식에서 getSelectSQL () 함수에 대한 필요성을 강요하려면 어떻게해야할까요? 부모 클래스의 getSelectSQL ()에는 유효한 이유가 없습니다. 최선의 행동 계획은 무엇입니까? 추상 정적을 선택한 이유는 모든 자식에서 getSelectSQL ()을 구현할 때까지 코드가 컴파일되지 않기 때문입니다.
Artem Russakovskii

1
대부분의 경우 getSelectSQL ()이 추상적 인 / instance / 메소드가되도록 재 설계해야합니다. 그렇게하면 각 어린이의 / instances /는 그러한 방법을 갖게됩니다.
Matthew Flaschen

7
추상 정적은 PHP 5.3에서 여전히 허용되지 않습니다. 늦은 정적 바인딩은 이와 관련이 없습니다. 참조 stackoverflow.com/questions/2859633
Artefacto

40
제 생각에이 엄격한 경고는 PHP가 "지연 정적 바인딩"을 가지고 있기 때문에 어리석은 것입니다. 이것은 자연스럽게 클래스 자체가 객체 (루비에서와 같이) 인 것처럼 정적 메서드를 사용하는 아이디어를 제공합니다. 이는 정적 메서드 오버로딩으로 이어지며이 abstract static경우에 유용 할 수 있습니다.
dmitry 2013 년

4
이 대답은 모호하게 잘못되었습니다. "여전히 허용되지 않음"은 E_STRICT 수준 경고를 받게된다는 의미입니다. 최소한 5.3+에서는 추상 정적 함수를 만들고 확장 클래스에서 구현 한 다음 static :: 키워드를 통해 참조 할 수 있습니다. 분명히 부모 클래스의 정적 버전은 여전히 ​​거기에 있고 (self :: 또는 static ::을 통해 해당 클래스 내부를 통해) 추상적이기 때문에 직접 호출 할 수 없으며 일반 비 정적 추상 함수를 호출 한 것처럼 치명적인 오류가 발생합니다. 기능적으로 이것은 유용합니다. 저는 그 효과에 대한 @dmitry 감정에 동의합니다.
ahoffner

79

길고 슬픈 이야기입니다.

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이 어리석은 경고를받지 않고 모두 즐겁게 사용할 수 있습니다 .


70

이 문제에 대한 매우 간단한 해결 방법이 있으며 이는 실제로 디자인 관점에서 의미가 있습니다. 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를 구현하여 유사한 기능을 가진 클래스를 만들 수 있습니다.


2
이 기능하려면이 패턴 가능 보호를 . 내가 할 때, 그것은 MyFoo를 확장하는 클래스가 getInstance가 public이어야한다는 경고를 던진다는 것을 의미합니다. 그리고 인터페이스 정의에 protected를 넣을 수 없습니다.
artfulrobot 2013

3
어떤 때는 static::유용 할 수 있습니다.
세예드

3
특성과 함께 작동하지 않습니다. 유일한 특색이있을 수 있다면, abstract staticPHP는 .... 불평없이 방법
Rudie

1
인터페이스를 사용하는 것이 아마도 여기에서 가장 좋은 해결책 일 것입니다. +1.
Juan Carlos Coto 2015 년

이것은 순전히 단순하기 때문에 실제로 매우 우아합니다. +1
G. Stewart

12

낡은 건 알지만 ....

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


1
그것은 도움이되지 않습니다. 정적 메서드 호출시 예외가 발생합니다. 동시에 재정의하지 않으면 'method does not exist'오류가 발생합니다.
BT

3
@BT 내 말은, 메서드 추상을 선언하지 않고 구현하지만 호출 될 때 예외를 throw합니다. 즉, 재정의되면 throw되지 않습니다.
Petah

이것은 가장 우아한 해결책 인 것 같습니다.
Alex S

런타임보다 컴파일 타임에 이와 같은 것을 보는 것이 좋습니다. 파일을로드하기
만하면

4

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


여기에 PHP와 관련이 없지만 또 다른 좋은 설명이 있습니다. stackoverflow.com/questions/3284/…
merkuro

3
세상에! 당신을 비판 한 두 사람은 완전히 정신이 나갔어요! 이것은이 스레드에서 가장 통찰력있는 답변입니다.
Theodore R. Smith

5
@ TheodoreR.Smith 통찰력? 오류가 있으며 간신히 일관됩니다. 추상 클래스가 "실제 기능을 구현 하지 않는다" 는 주장이 반드시 사실은 아니며 인터페이스 외에도 존재 하는 전체 요점 입니다. "그것은 PHP 개발자가 그것을하는 것을 막는 자연적인 법칙이 아니다" 라는 주장 에서 나는 "그것" 이 무엇인지 전혀 모른다 . 그리고 "기본적으로 이러한 아이디어는 예상치 못한 행동을 방지하기 위해 노력 한다 " 는 주장에서 "이 아이디어"나 잠재적 인 "예기치 않은 행동"이 무엇인지 전혀 알 수 없습니다. 당신이 이것에서 어떤 통찰력을 추출 했든, 그것은 나를 잃었습니다.
Mark Amery

2

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


3
5.2에 존재하는 추상 정적 메서드에 대한 귀하의 요점은 매우 오해의 소지가 있습니다. 첫째,이를 금지하는 엄격 모드 경고가 5.2에서 도입되었습니다. 5.2에서 허용 된 것처럼 들리게합니다. 둘째, 5.2에서는 Late Static Bindings가 아직 존재하지 않았기 때문에 "클래스의 일부를 구현하고 클래스의 다른 부분을 사용자에게 맡기기 위해 " 쉽게 사용할 수 없었습니다 .
Mark Amery

0

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

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

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

use StaticExample;

1
abstract public static function get_table_name();더 이상 E_STRICT 경고없이 특성을 입력하고 추상 클래스 내에서 해당 특성을 사용할 수있었습니다 ! 이것은 내가 바랬던 것처럼 여전히 아이들의 정적 방법을 정의하도록 강요했습니다. 환상적입니다!
Programster

-1

PHP의 'Late Static Binding'문제를 살펴보십시오. 추상 클래스에 정적 메서드를 배치하는 경우 나중에보다 빨리 실행할 것입니다. 엄격한 경고가 잘못된 언어 기능을 사용하지 않도록 지시하는 것은 이치에 맞습니다.


4
나는 그가 "엄격한 표준"경고를 의미한다고 생각한다.
Jacob Hume 2012 년

2
후기 정적 바인딩에 어떤 "문제"가 있다고 주장합니까? 당신은 그들이 증거 나 설명없이 여기에서 만들어진 대담한 주장 인 "깨졌다"고 주장합니다. 이 기능은 항상 잘 작동했으며이 게시물은 말도 안되는 것 같습니다.
마크 Amery
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.