Üst sınıf bir alt sınıf adını (statik bağlam) Başlarken

7 Cevap php

Aklımda yeniden kullanım ve sadeliği ile bir ORM kütüphane inşa ediyorum; her şey aptal bir miras sınırlama saplanıp dışında iyi gidiyor. Aşağıdaki kodu göz önünde bulundurun:

class BaseModel {
    /*
     * Return an instance of a Model from the database.
     */
    static public function get (/* varargs */) {
        // 1. Notice we want an instance of User
        $class = get_class(parent); // value: bool(false)
        $class = get_class(self);   // value: bool(false)
        $class = get_class();       // value: string(9) "BaseModel"
        $class =  __CLASS__;        // value: string(9) "BaseModel"

        // 2. Query the database with id
        $row = get_row_from_db_as_array(func_get_args());

        // 3. Return the filled instance
        $obj = new $class();
        $obj->data = $row;
        return $obj;
    }
}

class User extends BaseModel {
    protected $table = 'users';
    protected $fields = array('id', 'name');
    protected $primary_keys = array('id');
}
class Section extends BaseModel {
    // [...]
}

$my_user = User::get(3);
$my_user->name = 'Jean';

$other_user = User::get(24);
$other_user->name = 'Paul';

$my_user->save();
$other_user->save();

$my_section = Section::get('apropos');
$my_section->delete();

Açıkçası, bu .. (fiili davranış da mantıklı rağmen) ben bekliyordum davranış değildir Yani siz almak için ortalama biliyorsanız eğer benim soru ana sınıfında olduğunu, çocuk sınıfın adı.

7 Cevap

kısa. bu mümkün değildir. PHP4 size (debug_backtrace() incelemek) korkunç kesmek uygulamak olabilir ama bu yöntem PHP5'ta çalışmaz. referanslar:

edit: (Açıklamalarda belirtildiği) PHP 5.3 bağlayıcı geç statik bir örnek. Bu mevcut uygulamada potansiyel sorunlar var unutmayın (src).

class Base {
    public static function whoAmI() {
        return get_called_class();
    }
}

class User extends Base {}

print Base::whoAmI(); // prints "Base"
print User::whoAmI(); // prints "User"

Eğer statik bir bağlamda bu dışında bir yol düşünmek mümkün eğer PHP 5.3 için beklemek gerekmez. Php 5.2.9, üst sınıf bir statik olmayan yönteminde, bunu yapabilirsiniz:

get_class($this);

ve bir dize olarak çocuğun sınıf adını döndürür.

yani

class Parent() {
    function __construct() {
        echo 'Parent class: ' . get_class() . "\n" . 'Child class: ' . get_class($this);
    }
}

class Child() {
    function __construct() {
        parent::construct();
    }
}

$x = new Parent();

bu irade çıktı:

Parent class: Parent
Child class: Child

tatlı değil mi?

Eğer () siz (PHP 5.3 +) bağlayıcı geç statik diğer hileler kullanabilirsiniz get_called_class kullanmak istemiyorum. Ancak bu durumda olumsuz her modelde getClass () yöntemi olması gerekir. Hangi IMO büyük bir anlaşma değildir.

<?php

class Base 
{
    public static function find($id)
    {
        $table = static::$_table;
        $class = static::getClass();
        // $data = find_row_data_somehow($table, $id);
        $data = array('table' => $table, 'id' => $id);
        return new $class($data);
    }

    public function __construct($data)
    {
        echo get_class($this) . ': ' . print_r($data, true) . PHP_EOL;
    }
}

class User extends Base
{
    protected static $_table = 'users';

    public static function getClass()
    {
        return __CLASS__;
    }
}

class Image extends Base
{
    protected static $_table = 'images';

    public static function getClass()
    {
        return __CLASS__;
    }
}

$user = User::find(1); // User: Array ([table] => users [id] => 1)  
$image = Image::find(5); // Image: Array ([table] => images [id] => 5)

Bu bir fabrika desen olarak bir singleton deseni kullanmaya çalışıyor olabilir görünür. Ben tasarım kararlarını değerlendirirken öneriyoruz. Bir tek gerçekten uygun değilse, ben de miras not istenen statik yöntemler kullanarak sadece tavsiye ederim.

class BaseModel
{

	public function get () {
		echo get_class($this);

	}

	public static function instance () {
		static $Instance;
		if ($Instance === null) {
			$Instance = new self;

		}
		return $Instance;
	}
}

class User
extends BaseModel
{
	public static function instance () {
		static $Instance;
		if ($Instance === null) {
			$Instance = new self;

		}
		return $Instance;
	}
}

class SpecialUser
extends User
{
	public static function instance () {
		static $Instance;
		if ($Instance === null) {
			$Instance = new self;

		}
		return $Instance;
	}
}


BaseModel::instance()->get();   // value: BaseModel
User::instance()->get();        // value: User
SpecialUser::instance()->get(); // value: SpecialUser

Belki de bu aslında soruyu yanıtlayan değil, ancak () türünü specifing almak için bir parametre ekleyebilirsiniz. o zaman arayabilirsiniz

BaseModel::get('User', 1);

yerine Kullanıcı çağıran :: (olsun). Siz Temel Model mantığı ekleyebilirsiniz :: (olsun) Bir alma yönteminin sınıfta olup olmadığını kontrol edin ve o aramak için size alt sınıf geçersiz kılmak için izin istiyorum.

Aksi takdirde ben açıkçası aklınıza tek yolu aptalca her alt sınıfı, işlerim eklemektir:

class BaseModel {
    public static function get() {
        $args = func_get_args();
        $className = array_shift($args);

        //do stuff
        echo $className;
        print_r($args);
    }
}

class User extends BaseModel {
    public static function get() { 
        $params = func_get_args();
        array_unshift($params, __CLASS__);
        return call_user_func_array( array(get_parent_class(__CLASS__), 'get'), $params); 
    }
}


User::get(1);

Eğer Kullanıcı sınıflandırma ise bu muhtemelen kıracak, ama bu durumda get_parent_class(__CLASS__) 'BaseModel' ile değiştirin olabilir varsayalım

Sorun bu tasarım, bir dil sınırlama değildir. Eğer sınıfları var boşver; Statik yöntemler yerine nesne yönelimli tasarım daha bir usul yanıltmak. Ayrıca bazı form küresel devlet kullanıyor demektir. (Nasıl get_row_from_db_as_array(), nerede? Veritabanını bulmak için biliyor) Ve nihayet birim test etmek çok zor görünüyor.

Bu satırlar boyunca bir şey deneyin.

$db = new DatabaseConnection('dsn to database...');
$userTable = new UserTable($db);
$user = $userTable->get(24);

Preston cevabı iki varyasyonları:

1)

class Base 
{
    public static function find($id)
    {
        $table = static::$_table;
        $class = static::$_class;
        $data = array('table' => $table, 'id' => $id);
        return new $class($data);
    }
}

class User extends Base
{
    public static $_class = 'User';
}

2)

class Base 
{
    public static function _find($class, $id)
    {
        $table = static::$_table;
        $data = array('table' => $table, 'id' => $id);
        return new $class($data);
    }
}

class User extends Base
{
    public static function find($id)
    {
        return self::_find(get_class($this), $id);
    }
}

Not: _ temelde "i bu kamuoyuna biliyorum, ama gerçekten korunmuş olması gerekirdi, ama ben bunu ve benim hedefe ulaşamadık" anlamına gelen bir kuralıdır ile bir özellik adı başlangıç