PHP soyut özellikleri


126

PHP'de soyut sınıf özelliklerini tanımlamanın herhangi bir yolu var mı?

abstract class Foo_Abstract {
    abstract public $tablename;
}

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

Yanıtlar:


154

Bir özelliği tanımlamak diye bir şey yoktur.

Yalnızca, başlangıçta bellekte ayrılmış veri kapsayıcıları oldukları için özellikleri bildirebilirsiniz.

Öte yandan bir işlev tanımlanmadan (işlev gövdesi eksik) bildirilebilir (türler, adlar, parametreler) ve böylece soyut hale getirilebilir.

"Özet" yalnızca bir şeyin açıklandığını ancak tanımlanmadığını gösterir ve bu nedenle onu kullanmadan önce onu tanımlamanız gerekir, yoksa işe yaramaz hale gelir.


59
'Soyut' kelimesinin statik özellikler üzerinde kullanılmamasının açık bir nedeni yoktur - ancak biraz farklı bir anlamla. Örneğin, bir alt sınıfın özellik için bir değer sağlaması gerektiğini gösterebilir.
frodeborli

2
TypeScript'te soyut özellikler ve erişimciler vardır . PHP'de imkansız olması üzücü.
Ирина Зеленько

52

Hayır, derleyici ile bunu zorlamanın bir yolu yoktur, $tablenamedeğişken için çalışma zamanı kontrolleri (örneğin yapıcıda) kullanmanız gerekir , örneğin:

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

Bunu Foo_Abstract'un tüm türetilmiş sınıfları için zorlamak için final, geçersiz kılmayı önleyerek Foo_Abstract'un kurucusunu yapmanız gerekir .

Bunun yerine soyut bir alıcı ilan edebilirsiniz:

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

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

Güzel özellik, soyut özellikleri nasıl uyguladığınızı seviyorum.
Mathieu Dumoulin

4
Bu, oluşturucuyu soyut temel sınıfta son hale getirmenizi gerektirir.
hakre

3
Bazı açıklama: Yapıcı içinde denetimi yaparsanız ve zorunlu olması gerekiyorsa, her örnek somutlaştırmada yürütüldüğünden emin olmanız gerekir. Bu nedenle, örneğin sınıfı genişleterek ve kurucuyu değiştirerek kaldırılmasını önlemeniz gerekir. Son anahtar kelime bunu yapmak için izin verecek.
hakre

1
"Soyut alıcı" çözümünü beğendim. Bir sınıf özetinde bir işlev bildirdiğinizde, sınıfın kendisini soyut olarak bildirmelisiniz. Bu, sınıfın genişletilmedikçe ve tam olarak uygulanmadıkça kullanılamayacağı anlamına gelir. Bu sınıfı genişletirken, "alıcı" işlevi için bir uygulama sağlamalısınız. Bu, genişleyen sınıf içinde de ilgili bir özellik oluşturmanız gerektiği anlamına gelir, çünkü işlevin döndürülecek bir şeyi olması gerekir. Bu örüntüyü takiben, soyut bir özellik bildirmekle aynı sonucu alırsınız, bu aynı zamanda temiz ve net bir yaklaşımdır. Aslında böyle yapılır.
Salivan

1
Soyut bir alıcı kullanmak , mantıklı olduğunda, sabit bir değer döndürmenin aksine, onu bir değer üreterek uygulamanıza izin verir . Soyut bir özellik, özellikle statik bir özellik bunu yapmanıza izin vermez.
Tobia

27

Özelliğin bağlamına bağlı olarak, bir alt nesnede soyut bir nesne özelliğinin bildirimini zorlamak istersem static, soyut nesne yapıcısı veya ayarlayıcı / alıcı yöntemlerinde özellik için anahtar kelimeyle bir sabit kullanmayı tercih ederim . finalYöntemin genişletilmiş sınıflarda geçersiz kılınmasını önlemek için isteğe bağlı olarak kullanabilirsiniz .

Bunun dışında alt nesne, yeniden tanımlanırsa üst nesne özelliğini ve yöntemlerini geçersiz kılar. Örneğin, bir özellik üstteki gibi bildirilirse protectedve alt öğe olarak yeniden tanımlanırsa public, ortaya çıkan özellik geneldir. Ancak, mülk privateebeveynde beyan edilirse kalır privateve çocuk için mevcut olmaz.

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
Buradaki en zarif çözüm
Jannie Theunissen

24

Yukarıda belirtildiği gibi, böyle kesin bir tanım yoktur. Bununla birlikte, alt sınıfı "soyut" özelliği tanımlamaya zorlamak için bu basit geçici çözümü kullanıyorum:

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

Zarif, ancak staticmülklerle ilgili sorunu çözmüyor .
Robbert

1
Soyut yöntemler için özel olabileceğinizi sanmıyorum.
Zorji

@ Phate01 anladığım kadarıyla, yorumun kendisinde belirtiyor the only "safe" methods to have in a constructor are private and/or final ones, benim geçici çözümüm böyle bir durum değil mi? içinde özel kullanıyorum
ulkas

4
Bu güzel görünüyor, ancak bir çocuk sınıfını gerçekten ayarlamaya zorlamaz $name. setName()İşlevi gerçekten ayarlamadan uygulayabilirsiniz $name.
JohnWE

3
Bunun getNameyerine kullanmanın $namedaha iyi olduğunu düşünüyorum . abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
Hamid

7

Bugün kendime aynı soruyu sordum ve iki sentimi de eklemek istiyorum.

abstractÖzellikleri istememizin nedeni , alt sınıfların onları tanımladığından emin olmak ve olmadıklarında istisnalar atmaktır. Benim özel durumumda, staticmüttefikle çalışabilecek bir şeye ihtiyacım vardı .

İdeal olarak şöyle bir şey isterim:

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.
}

Bu uygulamayı bitirdim

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
{
}

Gördüğünüz gibi içinde Atanımlamıyorum $prop, ama onu bir staticalıcıda kullanıyorum. Bu nedenle, aşağıdaki kod çalışır

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

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

Öte Cyandan, ben tanımlamıyorum $prop, bu yüzden istisnalar alıyorum:

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

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

İstisnayı getProp() elde etmek için yöntemi çağırmalıyım ve bunu sınıf yüklemesinde alamıyorum, ancak en azından benim durumumda istenen davranışa oldukça yakın.

Zeki bir adamın (diğer bir deyişle 6 ay içinde kendimden) bunu yapmaktan kaçınmak için tanımlıyorum getProp().final

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

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

Bu çok zekice bir hack. Umarım bunun gelecekte yapılması gerekmez.
CMCDragonkai

6

Sadece kodunuzu test ederek öğrenebileceğiniz gibi:

Önemli hata: Özellikler 3. satırda ... satırında soyut olarak bildirilemez

Hayır yok. PHP'de özellikler soyut olarak bildirilemez.

Bununla birlikte, bir alıcı / ayarlayıcı işlev özeti uygulayabilirsiniz, aradığınız şey bu olabilir.

Mülkler uygulanmaz (özellikle kamu mülkleri), sadece vardırlar (veya yoktur):

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

6

Soyut özelliklere duyulan ihtiyaç, tasarım sorunlarını gösterebilir. Yanıtların çoğu, bir tür Şablon yöntemi modeli uygularken ve işe yarasa da, her zaman biraz garip görünür.

Orijinal örneğe bir göz atalım:

abstract class Foo_Abstract {
    abstract public $tablename;
}

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

Bir şeyi işaretlemek abstract, ona sahip olunması gereken bir şeyi belirtmektir. Eh, bir olmazsa olmaz değeri (bu durumda) o işlem yaptığı yapıcı iletilmesi gereken böylece gerekli bir bağımlılık vardır :

class Table
{
    private $name;

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

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

O zaman gerçekten daha somut bir adlandırılmış sınıf istiyorsanız, şu şekilde miras alabilirsiniz:

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

DI kapsayıcı kullanıyorsanız ve farklı nesneler için farklı tablolar iletmeniz gerekiyorsa bu yararlı olabilir.


3

PHP 7, soyut "özellikler" oluşturmayı oldukça kolaylaştırır. Yukarıda olduğu gibi, bunları soyut işlevler oluşturarak yapacaksınız, ancak PHP 7 ile bu işlev için dönüş türünü tanımlayabilirsiniz, bu da herkesin genişletebileceği bir temel sınıf oluştururken işleri çok daha kolay hale getirir.

<?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 değeri nesnenin ömrü boyunca asla değişmeyecekse, aşağıdaki basit ama güvenli bir uygulama olacaktır.

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

Buradaki anahtar, 'users' dize değerinin alt sınıf uygulamasında doğrudan getTablename () içinde belirtilmesi ve döndürülmesidir. İşlev "salt okunur" bir özelliği taklit eder.

Bu, daha önce ek bir değişken kullanan bir çözüme oldukça benzer. Biraz daha karmaşık olsa da Marco'nun çözümünü de beğeniyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.