अभिभावक वर्ग (स्थिर संदर्भ) में एक बाल वर्ग का नाम प्राप्त करना


93

मैं एक 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();

जाहिर है, यह वह व्यवहार नहीं है जिसकी मैं उम्मीद कर रहा था (हालांकि वास्तविक व्यवहार भी समझ में आता है) .. तो मेरा सवाल यह है कि क्या आप लोगों को माता-पिता की कक्षा में, बच्चे की कक्षा के नाम का मतलब पता है।

जवाबों:


98

संक्षेप में। यह नहीं हो सकता। php4 में आप एक भयानक हैक को लागू कर सकते हैं (जांच सकते हैं debug_backtrace()) लेकिन यह विधि PHP5 में काम नहीं करती है। संदर्भ:

संपादित करें : 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"

हां, मैं अभी पढ़ता हूं debug_backtrace().. एक संभावित समाधान PHP 5.3 से देर से स्थिर बंधन का उपयोग करना होगा लेकिन मेरे मामले में यह संभावना नहीं है। धन्यवाद।
साला

187

यदि आप एक स्थिर संदर्भ के बाहर ऐसा करने के लिए गर्भ धारण करने में सक्षम हैं, तो आपको 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

मीठा हुह?


11
यदि आप अमूर्त कक्षाओं का उपयोग कर रहे हैं, तो यह अवश्य पता है।
लेवी मॉरिसन

1
मुझे लगता है कि $x = new Parent();होना चाहिए $x = new Child();
जस्टिन सी

यह बात है!
बाल्डिना

बाल वर्ग बिना कंस्ट्रक्टर के हो सकता है
shmnff

20

मुझे पता है कि यह प्रश्न वास्तव में पुराना है, लेकिन उन लोगों के लिए जो वर्ग नाम वाले प्रत्येक वर्ग में एक संपत्ति को परिभाषित करने की तुलना में अधिक व्यावहारिक समाधान की तलाश कर रहे हैं:

आप इसके लिए 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

1
धन्यवाद! ReflectionClass के साथ संघर्ष कर रहा था static(), लेकिन कॉलिंग जाने का रास्ता है!
गोदाम 12

16

मुझे इसकी पुरानी पोस्ट पता है लेकिन मैंने जो समाधान पाया है उसे साझा करना चाहता हूं।

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"

6

यदि आप get_called_class () का उपयोग नहीं करना चाहते हैं तो आप लेट स्टैटिक बाइंडिंग (PHP 5.3+) के अन्य ट्रिक्स का उपयोग कर सकते हैं। लेकिन इस मामले में नकारात्मक पक्ष आपको हर मॉडल में गेटक्लास () पद्धति का होना चाहिए। जो कोई बड़ी बात नहीं है 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)

2

ऐसा प्रतीत होता है कि आप फैक्ट्री पैटर्न के रूप में सिंगलटन पैटर्न का उपयोग करने की कोशिश कर रहे होंगे। मैं आपके डिज़ाइन निर्णयों का मूल्यांकन करने की सलाह दूंगा। यदि एक सिंगलटन वास्तव में उपयुक्त है, तो मैं केवल स्थैतिक तरीकों का उपयोग करने की सिफारिश करूंगा जहां विरासत वांछित नहीं है

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

.. नहीं। आपको समझ में नहीं आया कि मैं क्या उबालने की कोशिश कर रहा था, लेकिन ऐसा इसलिए है क्योंकि मैंने इसे अच्छी तरह समझाया नहीं है :)। वास्तव में, मैं बस दिए गए मॉडल का एक उदाहरण (हो सकता है कि यह उपयोगकर्ता, भूमिका या जो भी हो) पाने के लिए एक स्थिर विधि (बेसमॉडल में लागू) प्रदान करने की कोशिश कर रहा हूं। मैं सवाल अपडेट करूंगा ..
saalaa

ठीक है, अपडेट किया गया। बेशक बेसमॉडल वर्ग में बहुत अधिक विधियां हैं, जिनमें से कुछ को वस्तु में बदले जाने का ट्रैक रखने के लिए शामिल किया गया है और केवल उसी चीज को अद्यतन किया गया है, जिसने चेज किया है, आदि ... लेकिन वैसे भी धन्यवाद :)।
साला

2

शायद यह वास्तव में सवाल का जवाब नहीं दे रहा है, लेकिन आप टाइप करने के लिए () निर्दिष्ट करने के लिए एक पैरामीटर जोड़ सकते हैं। तो आप कॉल कर सकते हैं

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

उपयोगकर्ता को कॉल करने के बजाय :: get ()। आप BaseModel में तर्क जोड़ सकते हैं :: 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'उस स्थिति में


वास्तव में हाँ। इस PHP सीमा का मुझे अपने डिज़ाइन की समीक्षा करने के लिए मजबूर करने का बड़ा फायदा था। यह बहुत अधिक $ कनेक्शन की तरह दिखेगा-> ('उपयोगकर्ता', 24); चूँकि यह एक ही समय में कई कनेक्शन की अनुमति देता है और शब्दार्थ रूप से अधिक सही भी है। लेकिन आपको एक बिंदु मिला :)।
साला

array_shift धीमा लगता है, क्योंकि इसे सरणी की प्रत्येक कुंजी को बदलना होगा ... इसके बजाय बस $ args [0];
Xesau

0

समस्या कोई भाषा सीमा नहीं है, यह आपका डिज़ाइन है। कभी भी यह मत सोचिए कि आपके पास कक्षाएं हैं; स्टैटिक विधियाँ ऑब्जेक्ट-ओरिएंटेड डिज़ाइन के बजाय एक प्रक्रियात्मक होती हैं। आप किसी न किसी रूप में वैश्विक स्थिति का उपयोग कर रहे हैं। ( get_row_from_db_as_array()डेटाबेस को कैसे पता करें?) और आखिरकार यूनिट टेस्ट करना बहुत मुश्किल है।

इन लाइनों के साथ कुछ प्रयास करें।

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

0

प्रेस्टन के उत्तर पर दो भिन्नताएँ:

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

नोट: _ के साथ एक संपत्ति का नाम शुरू करना एक सम्मेलन है जिसका मूल अर्थ है "मुझे पता है कि मैंने इसे सार्वजनिक किया है, लेकिन इसे वास्तव में संरक्षित किया जाना चाहिए था, लेकिन मैं ऐसा नहीं कर सका और अपने लक्ष्य को प्राप्त कर सका"

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.