php 获取父类中子类的名称(静态上下文)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/283004/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 22:14:44  来源:igfitidea点击:

Getting the name of a child class in the parent class (static context)

phpinheritancestatic-methods

提问by saalaa

I'm building an ORM library with reuse and simplicity in mind; everything goes fine except that I got stuck by a stupid inheritance limitation. Please consider the code below:

我正在构建一个具有重用性和简单性的 ORM 库;一切都很好,只是我被一个愚蠢的继承限制卡住了。请考虑以下代码:

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

Obviously, this is not the behavior I was expecting (although the actual behavior also makes sense).. So my question is if you guys know of a mean to get, in the parent class, the name of child class.

显然,这不是我期望的行为(尽管实际行为也有道理)。所以我的问题是你们是否知道在父类中获取子类名称的方法。

回答by

You don't need to wait for PHP 5.3 if you're able to conceive of a way to do this outside of a static context. In php 5.2.9, in a non-static method of the parent class, you can do:

如果您能够想出一种在静态上下文之外执行此操作的方法,则无需等待 PHP 5.3。在 php 5.2.9 中,在父类的非静态方法中,您可以执行以下操作:

get_class($this);

and it will return the name of the child class as a string.

它将以字符串形式返回子类的名称。

i.e.

IE

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

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

$x = new Child();

this will output:

这将输出:

Parent class: Parent
Child class: Child

sweet huh?

甜吧?

回答by Owen

in short. this is not possible. in php4 you could implement a terrible hack (examine the debug_backtrace()) but that method does not work in PHP5. references:

简而言之。这不可能。在 php4 中,您可以实现一个可怕的 hack(检查debug_backtrace()),但该方法在 PHP5 中不起作用。参考:

edit: an example of late static binding in PHP 5.3 (mentioned in comments). note there are potential problems in it's current implementation (src).

编辑: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"

回答by Joas

I know this question is really old, but for those looking for a more practical solution than defining a property in every class containing the class name:

我知道这个问题真的很老,但是对于那些寻找比在每个包含类名的类中定义属性更实用的解决方案的人来说:

You can use the statickeyword for this.

您可以static为此使用关键字。

As explained in this contributor note in the php documentation

php 文档中的贡献者注释中所述

the statickeyword can be used inside a super class to access the sub class from which a method is called.

static关键字可以超类中被用来访问该子类从该方法被调用。

Example:

例子:

class Base
{
    public static function init() // Initializes a new instance of the static class
    {
        return new static();
    }

    public static function getClass() // Get static class
    {
        return static::class;
    }

    public function getStaticClass() // Non-static function to get static class
    {
        return static::class;
    }
}

class Child extends Base
{

}

$child = Child::init();         // Initializes a new instance of the Child class

                                // Output:
var_dump($child);               // object(Child)#1 (0) {}
echo $child->getStaticClass();  // Child
echo Child::getClass();         // Child

回答by Engr Syed Rowshan Ali

I know its old post but want to share the solution I have found.

我知道它的旧帖子,但想分享我找到的解决方案。

Tested with PHP 7+ Use the function get_class()link

用 PHP 7+ 测试使用函数get_class()链接

<?php
abstract class bar {
    public function __construct()
    {
        var_dump(get_class($this));
        var_dump(get_class());
    }
}

class foo extends bar {
}

new foo;
?>

The above example will output:

上面的例子将输出:

string(3) "foo"
string(3) "bar"

回答by Engr Syed Rowshan Ali

In case you don't want to use get_called_class() you can use other tricks of late static binding (PHP 5.3+). But the downside in this case you need to have getClass() method in every model. Which is not a big deal IMO.

如果您不想使用 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)

回答by Engr Syed Rowshan Ali

It appears you might be trying to use a singleton pattern as a factory pattern. I would recommend evaluating your design decisions. If a singleton really is appropriate, I would also recommend only using static methods where inheritance is notdesired.

看来您可能正在尝试使用单例模式作为工厂模式。我建议评估您的设计决策。如果一个单身真的是合适的,我也只是建议使用,其中继承静态方法希望的。

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

回答by Tom Haigh

Maybe this isn't actually answering the question, but you could add a parameter to get() specifing the type. then you can call

也许这实际上并没有回答这个问题,但是您可以向 get() 添加一个参数来指定类型。然后你可以打电话

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

instead of calling User::get(). You could add logic in BaseModel::get() to check whether a get method exists in the subclass and then call that if you want to allow the subclass to override it.

而不是调用 User::get()。您可以在 BaseModel::get() 中添加逻辑以检查子类中是否存在 get 方法,然后如果您希望允许子类覆盖它,则调用该方法。

Otherwise the only way I can think of obviously is by adding stuff to each subclass, which is stupid:

否则我能想到的唯一方法显然是向每个子类添加东西,这很愚蠢:

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

This would probably break if you then subclassed User, but I suppose you could replace get_parent_class(__CLASS__)with 'BaseModel'in that case

如果您随后将 User 子类化,这可能会中断,但我想在这种情况下您可以替换get_parent_class(__CLASS__)'BaseModel'

回答by Preston

The problem is not a language limitation, it is your design. Never mind that you have classes; the static methods belie a procedural rather than object-oriented design. You're also using global state in some form. (How does get_row_from_db_as_array()know where to find the database?) And finally it looks very difficult to unit test.

问题不是语言限制,而是您的设计。别介意你有课;静态方法掩盖了程序设计而不是面向对象设计。您还以某种形式使用全局状态。(如何get_row_from_db_as_array()知道在哪里可以找到数据库?)最后看起来很难进行单元测试。

Try something along these lines.

沿着这些方向尝试一些东西。

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

回答by lo_fye

Two variations on Preston's answer:

普雷斯顿答案的两个变体:

1)

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)

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

Note: starting a property name with _ is a convention that basically means "i know i made this public, but it really should have been protected, but i couldn't do that and achieve my goal"

注意:以 _ 开头的属性名称是一个约定,基本上意味着“我知道我公开了它,但它确实应该受到保护,但我无法这样做并实现我的目标”