在父类中获取子类的名称(静态上下文)
我正在构build一个重用和简单的ORM库; 除了被一个愚蠢的inheritance限制所困住之外,一切都很好。 请考虑下面的代码:
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();
显然,这不是我期待的行为(虽然实际行为也是有道理的)。所以我的问题是,如果你们知道在父类中获得子类的名字的意思。
简而言之。 这不可能。 在php4中,你可以实现一个可怕的黑客(检查debug_backtrace()
),但是这个方法在PHP5中不起作用。 引用:
-
30423
-
37684
-
34421
编辑 :在PHP 5.3中的后期静态绑定的例子(在评论中提到)。 请注意,目前的实施( 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"
如果你能够想象在静态环境之外做到这一点,你不需要等待PHP 5.3。 在PHP 5.2.9中,在父类的非静态方法中,可以这样做:
get_class($this);
它会以string的forms返回子类的名字。
即
class Parent() { function __construct() { echo 'Parent class: ' . get_class() . "\n" . 'Child class: ' . get_class($this); } } class Child() { function __construct() { parent::construct(); } } $x = new Child();
这将输出:
Parent class: Parent Child class: Child
甜心吧?
如果你不想使用get_called_class(),你可以使用后期静态绑定(PHP 5.3+)的其他技巧。 但在这种情况下,你需要在每个模型中都有getClass()方法。 这不是什么大不了的IMO。
<?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)
看起来你可能正在尝试使用单例模式作为工厂模式。 我会build议评估你的devise决定。 如果一个单例真的是合适的,我也build议只使用不需要inheritance的静态方法。
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
也许这实际上并没有回答这个问题,但是你可以添加一个参数来指定types的get()。 那么你可以打电话
BaseModel::get('User', 1);
而不是调用User :: get()。 您可以在BaseModel :: get()中添加逻辑,以检查子类中是否存在get方法,然后调用该方法是否允许子类重写它。
否则我唯一能想到的方法就是把东西添加到每个子类中,这很愚蠢:
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);
这可能会破坏,如果你然后subclassed用户,但我想你可以取代get_parent_class(__CLASS__)
与'BaseModel'
在这种情况下
问题不是语言限制,而是您的devise。 不要紧,你有课; 静态方法是程序而不是面向对象的devise。 你也以某种forms使用全局状态。 ( get_row_from_db_as_array()
如何知道在哪里查找数据库?)最后,unit testing看起来非常困难。
尝试沿着这些线路。
$db = new DatabaseConnection('dsn to database...'); $userTable = new UserTable($db); $user = $userTable->get(24);
普雷斯顿的回答有两个变化:
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); } }
注意:以_开始一个属性名称是一个惯例,基本上意味着“我知道我做了这个公开,但它真的应该受到保护,但我不能这样做,实现我的目标”