PHP 추상 속성


126

PHP에서 추상 클래스 속성을 정의하는 방법이 있습니까?

abstract class Foo_Abstract {
    abstract public $tablename;
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' $property
    public $tablename = 'users';   
}

답변:


154

속성을 정의하는 것과 같은 것은 없습니다.

속성은 초기화시 메모리에 예약 된 데이터 컨테이너이므로 선언 만 할 수 있습니다.

반면에 함수는 정의되지 않고 (형, 이름, 매개 변수) 선언 될 수 있으며 (함수 본문 누락), 따라서 추상이 될 수 있습니다.

"Abstract"는 선언되었지만 정의되지 않았 음을 나타낼 뿐이므로 사용하기 전에 정의해야합니다. 그렇지 않으면 쓸모 없게됩니다.


59
'추상'이라는 단어가 정적 속성에 사용될 수없는 분명한 이유는 없지만 약간 다른 의미로 사용됩니다. 예를 들어 하위 클래스가 속성 값을 제공해야 함을 나타낼 수 있습니다.
frodeborli

2
TypeScript에는 추상 속성과 접근자가 있습니다. PHP에서는 불가능하다는 것이 슬프다.
Илья Зеленько

52

아니요, 컴파일러로이를 강제 할 방법이 없습니다. $tablename변수에 대해 런타임 검사 ( 예 : 생성자에서)를 사용해야 합니다. 예 :

class Foo_Abstract {
  public final function __construct(/*whatever*/) {
    if(!isset($this->tablename))
      throw new LogicException(get_class($this) . ' must have a $tablename');
  }
}

Foo_Abstract의 모든 파생 클래스에 대해이를 적용하려면 Foo_Abstract의 constructor를 만들어 final재정의를 방지해야합니다.

대신 추상 getter를 선언 할 수 있습니다.

abstract class Foo_Abstract {
  abstract public function get_tablename();
}

class Foo extends Foo_Abstract {
  protected $tablename = 'tablename';
  public function get_tablename() {
    return $this->tablename;
  }
}

멋진 기능, 추상 속성을 구현하는 방법이 마음에 듭니다.
Mathieu Dumoulin 2011 년

4
이렇게하려면 추상 기본 클래스에서 생성자를 최종적으로 만들어야합니다.
hakre

3
몇 가지 설명 : 생성자 내부에서 확인을 수행하고 필수 항목인지 확인하는 경우 모든 인스턴스 인스턴스화에서 실행되는지 확인해야합니다. 따라서 클래스를 확장하고 생성자를 대체하여 제거되는 것을 방지해야합니다. 마지막 키워드는 당신이 그렇게하도록 허용합니다.
hakre

1
저는 "추상 게터"솔루션을 좋아합니다. 클래스 추상에서 함수를 선언 할 때 클래스 자체를 추상으로 선언해야합니다. 이것은 확장되고 완전히 구현되지 않으면 클래스를 사용할 수 없음을 의미합니다. 해당 클래스를 확장 할 때 "getter"함수에 대한 구현을 제공해야합니다. 즉, 함수에 반환 할 항목이 있어야하므로 확장 클래스 내에 관련 속성도 만들어야합니다. 이 패턴을 따르면 추상 속성을 선언하는 것과 동일한 결과를 얻을 수 있으며 이는 또한 깔끔하고 명확한 접근 방식입니다. 그것이 실제로 수행되는 방법입니다.
Salivan 2015-08-02

1
추상 getter를 사용하면 값 을 생성 하여 구현할 수도 있습니다 . 상수 값을 반환하는 것이 합리적 일 때 반환하는 것이 아닙니다. 추상 속성, 특히 정적 속성은 그렇게 할 수 없습니다.
Tobia

27

자식 개체에서 추상 개체 속성을 강제로 선언하려는 경우 속성의 컨텍스트에 따라 static추상 개체 생성자 또는 setter / getter 메서드에서 속성에 대한 키워드 와 함께 상수를 사용하고 싶습니다 . 선택적으로를 사용 final하여 확장 클래스에서 메서드가 재정의되는 것을 방지 할 수 있습니다 .

그 외에는 하위 개체가 재정의 된 경우 상위 개체 속성 및 메서드를 재정의합니다. 예를 들어 속성이 protected부모 에서처럼 선언되고 public자식 에서처럼 재정 의 된 경우 결과 속성은 공용입니다. 그러나 속성이 private부모에서 선언 된 경우 해당 속성은 그대로 유지 private되며 자식이 사용할 수 없습니다.

http://www.php.net//manual/en/language.oop5.static.php

abstract class AbstractFoo
{
    public $bar;

    final public function __construct()
    {
       $this->bar = static::BAR;
    }
}

class Foo extends AbstractFoo
{
    //const BAR = 'foobar';
}

$foo = new Foo; //Fatal Error: Undefined class constant 'BAR' (uncomment const BAR = 'foobar';)
echo $foo->bar;

4
여기에 대부분의 우아한 솔루션
쟈니 Theunissen

24

위에서 언급했듯이 정확한 정의는 없습니다. 그러나이 간단한 해결 방법을 사용하여 자식 클래스가 "abstract"속성을 정의하도록 강제합니다.

abstract class Father 
{
  public $name;
  abstract protected function setName(); // now every child class must declare this 
                                      // function and thus declare the property

  public function __construct() 
  {
    $this->setName();
  }
}

class Son extends Father
{
  protected function setName()
  {
    $this->name = "son";
  }

  function __construct(){
    parent::__construct();
  }
}

우아하지만 static속성 문제를 해결하지 못합니다 .
Robbert

1
나는 당신이 추상적 인 방법을 위해 개인을 가질 수 있다고 생각하지 않습니다.
Zorji 2015 년

@ Phate01 내가 그것을 이해하는대로, 그것이 말하는 주석 자체에서 the only "safe" methods to have in a constructor are private and/or final ones내 해결 방법이 그러한 경우가 아닙니까? 메신저 거기에 음부를 사용하여
ulkas

4
이것은 멋져 보이지만 실제로 자식 클래스를 설정하도록 강제하지는 않습니다 $name. setName()실제로 설정하지 않고도 기능을 구현할 수 $name있습니다.
JohnWE 2016 년

3
getName대신 사용하면 $name더 좋다고 생각합니다 . abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
Hamid

7

오늘도 같은 질문을했는데 2 센트를 더하고 싶습니다.

abstract속성 을 원하는 이유는 하위 클래스가 속성을 정의하고 그렇지 않은 경우 예외를 던지도록하기 위해서입니다. 내 특정한 경우에는 static동료 와 함께 일할 수있는 무언가가 필요했습니다 .

이상적으로 나는 다음과 같은 것을 원합니다.

abstract class A {
    abstract protected static $prop;
}

class B extends A {
    protected static $prop = 'B prop'; // $prop defined, B loads successfully
}

class C extends A {
    // throws an exception when loading C for the first time because $prop
    // is not defined.
}

이 구현으로 끝났습니다.

abstract class A
{
    // no $prop definition in A!

    public static final function getProp()
    {
        return static::$prop;
    }
}

class B extends A
{
    protected static $prop = 'B prop';
}

class C extends A
{
}

보시다시피, A나는 정의하지 않지만 getter $prop에서 사용합니다 static. 따라서 다음 코드가 작동합니다.

B::getProp();
// => 'B prop'

$b = new B();
$b->getProp();
// => 'B prop'

에서 C, 다른 한편으로는, 나는 정의하지 $prop내가 예외를 얻을 수 있도록 :

C::getProp();
// => Exception!

$c = new C();
$c->getProp();
// => Exception!

getProp() 예외를 얻으려면 메서드를 호출해야하는데 클래스 로딩시 가져올 수 없지만 적어도 제 경우에는 원하는 동작에 매우 가깝습니다.

나는 정의 getProp()final어떤 것을 방지하기 위해 스마트 사람 (6 개월에서 자신 일명)을 할 유혹

class D extends A {
    public static function getProp() {
        // really smart
    }
}

D::getProp();
// => no exception...

이것은 매우 독창적 인 해킹입니다. 앞으로이 작업을 수행 할 필요가 없기를 바랍니다.
CMCDragonkai

6

코드를 테스트하여 알 수 있듯이 :

치명적인 오류 : 속성은 ...에서 추상으로 선언 할 수 없습니다.

아니 없어. 속성은 PHP에서 추상으로 선언 될 수 없습니다.

그러나 getter / setter 함수 추상을 구현할 수 있습니다. 이것이 여러분이 찾고있는 것일 수 있습니다.

속성은 구현되지 않고 (특히 공용 속성) 존재하거나 존재하지 않습니다.

$foo = new Foo;
$foo->publicProperty = 'Bar';

6

추상 속성의 필요성은 디자인 문제를 나타낼 수 있습니다. 많은 답변이 일종의 Template 메서드 패턴을 구현 하고 작동하지만 항상 이상하게 보입니다.

원래 예를 살펴 보겠습니다.

abstract class Foo_Abstract {
    abstract public $tablename;
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' $property
    public $tablename = 'users';   
}

무언가 abstract를 표시하는 것은 필수품임을 나타내는 것입니다. 음, 필수 값 (이 경우) 은 필수 종속성이므로 인스턴스화 중에 생성자에 전달되어야합니다 .

class Table
{
    private $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function name(): string
    {
        return $this->name;
    }
}

그런 다음 실제로 더 구체적인 명명 된 클래스를 원한다면 다음과 같이 상속 할 수 있습니다.

final class UsersTable extends Table
{
    public function __construct()
    {
        parent::__construct('users');
    }
}

이것은 DI 컨테이너를 사용하고 다른 객체에 대해 다른 테이블을 전달해야하는 경우 유용 할 수 있습니다.


3

PHP 7을 사용하면 추상적 인 "속성"을 훨씬 쉽게 만들 수 있습니다. 위와 같이 추상 함수를 만들어서 만들 겠지만, PHP 7을 사용하면 해당 함수의 반환 유형을 정의 할 수 있으므로 누구나 확장 할 수있는 기본 클래스를 빌드 할 때 작업이 훨씬 쉬워집니다.

<?php

abstract class FooBase {

  abstract public function FooProp(): string;
  abstract public function BarProp(): BarClass;

  public function foo() {
    return $this->FooProp();
  }

  public function bar() {
    return $this->BarProp()->name();
  }

}

class BarClass {

  public function name() {
    return 'Bar!';
  }

}

class FooClass extends FooBase {

  public function FooProp(): string {
    return 'Foo!';
  }

  public function BarProp(): BarClass {
    // This would not work:
    // return 'not working';
    // But this will!
    return new BarClass();
  }

}

$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;

1

객체의 수명 동안 tablename 값이 변경되지 않으면 다음은 간단하면서도 안전한 구현입니다.

abstract class Foo_Abstract {
    abstract protected function getTablename();

    public function showTableName()
    {
        echo 'my table name is '.$this->getTablename();
    }
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' getTablename()
    protected function getTablename()
    {
        return 'users';
    }
}

여기서 핵심은 문자열 값 'users'가 지정되고 자식 클래스 구현의 getTablename ()에 직접 반환된다는 것입니다. 이 함수는 "읽기 전용"속성을 모방합니다.

이것은 추가 변수를 사용하는 이전에 게시 된 솔루션과 상당히 유사합니다. 조금 더 복잡 할 수 있지만 Marco의 솔루션도 마음에 듭니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.