development

부모 클래스 (정적 컨텍스트)에서 자식 클래스의 이름 가져 오기

big-blog 2020. 9. 10. 08:13
반응형

부모 클래스 (정적 컨텍스트)에서 자식 클래스의 이름 가져 오기


저는 재사용과 단순성을 염두에두고 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();

분명히 이것은 내가 기대했던 행동이 아닙니다 (실제 행동도 의미가 있지만). 그래서 내 질문은 부모 클래스에서 자식 클래스의 이름을 얻는 방법을 알고 있는지입니다.


간단히 말해서. 이건 불가능 해. php4에서는 끔찍한 해킹을 구현할 수 debug_backtrace()있지만 (를 검사합니다 )이 방법은 PHP5에서 작동하지 않습니다. 참조 :

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

그리고 자식 클래스의 이름을 문자열로 반환합니다.

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

달콤한 응?


나는이 질문이 정말 오래되었다는 것을 알고 있지만 클래스 이름을 포함하는 모든 클래스에서 속성을 정의하는 것보다 더 실용적인 솔루션을 찾는 사람들을 위해 :

이를 위해 static키워드를 사용할 수 있습니다 .

php 문서의이 기여자 노트에 설명 된대로

static키워드는 메서드가 호출되는 하위 클래스에 액세스하기 위해 수퍼 클래스 내에서 사용할 수 있습니다.

예:

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

이전 게시물을 알고 있지만 찾은 솔루션을 공유하고 싶습니다.

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

위의 예는 다음을 출력합니다.

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

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)

싱글 톤 패턴을 팩토리 패턴으로 사용하려는 것 같습니다. 디자인 결정을 평가하는 것이 좋습니다. 싱글 톤이 실제로 적절하다면 상속이 필요 하지 않은 정적 메서드 만 사용하는 것이 좋습니다 .

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

이것은 실제로 질문에 대한 답이 아닐 수도 있지만, 유형을 지정하는 매개 변수를 get ()에 추가 할 수 있습니다. 그런 다음 전화 할 수 있습니다

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

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


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.

Try something along these lines.

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

Two variations on Preston's answer:

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

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"

참고URL : https://stackoverflow.com/questions/283004/getting-the-name-of-a-child-class-in-the-parent-class-static-context

반응형