Dinamik bir sınıf uzatmak mümkün mü?

5 Cevap php

Ben kriterlere bağlı olarak farklı sınıflar (yüzlerce kadar) uzatmak için kullanmanız gereken bir dersim var. Bir dinamik sınıf adında bir sınıf genişletmek için PHP bir yolu var mı?

Ben örnekleme ile uzantısı belirtmek için bir yöntem gerektirir varsayıyorum.

Fikirler?

5 Cevap

Ben dinamik (yanılıyorsam ancak ben bunun nasıl yapıldığını görmek isterim) bir sınıf uzatmak mümkün olduğunu sanmıyorum. Eğer Kompozit desen (http://en.wikipedia.org/wiki/Composite%5Fpattern, http://devzone.zend.com/article/7) kullanarak düşündünüz mü? Başka bir sınıf dinamik kompozit olabilir - çocuk sınıfa 'inject' ebeveyn sınıfının yöntemleri / özellikleri ile (hatta birden çok sınıfları bu sık etrafında çoklu miras için bir çalışma olarak kullanılır).

Tam olarak cevap değil hala mümkün değil ve süre ben aynı şeyi gerekli ve So i koşullarda uzanarak varsayılan bir sınıf kullanmış eval, maymun-yama vb kullanmak istedim vermedi.

Eğer başka uzanan operasyonda 100 condtions eklemek gerekir uzatmak için 100 sınıfları var ama benim için bu doğru bir yol gibi görünüyordu, o elbette demektir.

<?php
if(class_exists('SolutionClass')) {
    class DynamicParent extends SolutionClass {}
} else {
    class DynamicParent extends DefaultSolutionClass {}
}

class ProblemChild extends DynamicParent {}
?>

Bir başkası aynı şeyi merak ve kısmi bir çözüm ile geldi yerde, this bir göz atın.

Bu sihirli __ çağrısı fonksiyonunun gücünü kullanarak PHP dinamik miras oluşturmak mümkündür. O iş için altyapı kod biraz sürer ama çok zor değil.

Disclaimer

Gerçekten aslında yapmak tür kötü şey gibi bu tekniği kullanmadan önce en az iki kere düşünmek gerek.

Ben bir site şablonları oluşturarak zaman arayüz tanımları veya kurulum bağımlılık enjeksiyonları oluşturmak için ya var istemiyorum çünkü bu tekniği kullanıyorum tek nedeni budur. Ben sadece bir şablon işlevi 'taşları' bir çift tanımlamak ve sonra devralma otomatik olarak doğru 'block' kullanmak var edebilmek istiyorum.

Implementation

Gerekli adımlar şunlardır:

  • Çocuk sınıfı şimdi bir 'DynamicExtender' sınıfını genişletir. Bu sınıf alt sınıf var olmayan yöntemleri, çocuk sınıf tarafından yapılan çağrıları karşılar ve üst örneğine onları yönlendirir.

  • Her 'EbeveynSınıf', bir 'ProxyParentClass' genişletilir. Üst sınıf her erişilebilir yöntem için, 'ProxyParentClass' eşdeğer bir yöntem mevcuttur. Yöntem çocuğu olarak var ve eğer varsa fonksiyonu oğul sürümünü çağırır, aksi takdirde EbeveynSınıf gelen sürümü çağırırsa 'ProxyParentClass çekleri bu yöntemlerin her görmek için

  • DynamicExtender sınıfı oluşturulur zaman DynamicExtender bu sınıfın yeni bir örneğini oluşturur ve EbeveynSınıf çocuğu olarak kendini ayarlar, ne gerek üst sınıf geçmek.

Biz alt nesne oluşturduğunuzda Yani, şimdi biz gereken ana sınıfını belirtmek ve DynamicExtender bizim için yaratacak ve çocuk sınıf biz zor olmaktan ziyade çalışma zamanında istenen sınıftan uzatılır sanki görünecektir kodlu.

Bu görüntülerin bir çift olarak anlamak daha kolay olabilir:

Fixed inheritance is fixed

enter görüntü açıklaması here

Dynamic inheritance with proxies

enter görüntü açıklaması here

Demo implementation

Bu çözüm için kod available on Github ve nasıl bu can be used here biraz daha dolgun bir açıklama, ama yukarıdaki görüntü için kodu:

//An interface that defines the method that must be implemented by any renderer.
interface Render {
    public function render();
}


/**
 * Class DynamicExtender
 */
class DynamicExtender implements Render {

    var $parentInstance = null;

    /**
     * Construct a class with it's parent class chosen dynamically.
     *
     * @param $parentClassName The parent class to extend.
     */
    public function __construct($parentClassName) {
        $parentClassName = "Proxied".$parentClassName;

        //Check that the requested parent class implements the interface 'Render'
        //to prevent surprises later.
        if (is_subclass_of($parentClassName, 'Render') == false) {
            throw new Exception("Requested parent class $parentClassName does not implement Render, so cannot extend it.");
        }

        $this->parentInstance = new $parentClassName($this);
    }

    /**
     * Magic __call method is triggered whenever the child class tries to call a method that doesn't
     * exist in the child class. This is the case whenever the child class tries to call a method of
     * the parent class. We then redirect the method call to the parentInstance.
     *
     * @param $name
     * @param array $arguments
     * @return mixed
     * @throws PHPTemplateException
     */
    public function __call($name, array $arguments) {
        if ($this->parentInstance == null) {
            throw new Exception("parentInstance is null in Proxied class in renderInternal.");
        }

        return call_user_func_array([$this->parentInstance, $name], $arguments);
    }

    /**
     * Render method needs to be defined to satisfy the 'implements Render' but it
     * also just delegates the function to the parentInstance.
     * @throws Exception
     */
    function render() {
        $this->parentInstance->render();
    }
}



/**
 * Class PageLayout
 *
 * Implements render with a full HTML layout.
 */
class PageLayout implements Render {

    //renders the whole page.
    public function render() {
        $this->renderHeader();
        $this->renderMainContent();
        $this->renderFooter();
    }

    //Start HTML page
    function renderHeader() {
        echo "<html><head></head><body>";
        echo "<h2>Welcome to a test server!</h2>";

        echo "<span id='mainContent'>";
    }

    //Renders the main page content. This method should be overridden for each page
    function renderMainContent(){
        echo "Main content goes here.";
    }

    //End the HTML page, including Javascript
    function renderFooter(){
        echo "</span>";
        echo "<div style='margin-top: 20px'>Dynamic Extension Danack@basereality.com</div>";
        echo "</body>";
        echo "<script type='text/javascript' src='jquery-1.9.1.js' ></script>";
        echo "<script type='text/javascript' src='content.js' ></script>";
        echo "</html>";
    }

    //Just to prove we're extending dynamically.
    function getLayoutType() {
        return get_class($this);
    }
}

/**
 * Class ProxiedPageLayout
 *
 * Implements render for rendering some content surrounded by the opening and closing HTML
 * tags, along with the Javascript required for a page.
 */
class ProxiedPageLayout extends PageLayout {

    /**
     * The child instance which has extended this class.
     */
    var $childInstance = null;

    /**
     * Construct a ProxiedPageLayout. The child class must be passed in so that any methods
     * implemented by the child class can override the same method in this class.
     * @param $childInstance
     */
    function __construct($childInstance){
        $this->childInstance = $childInstance;
    }

    /**
     * Check if method exists in child class or just call the version in PageLayout
     */
    function renderHeader() {
        if (method_exists ($this->childInstance, 'renderHeader') == true) {
            return $this->childInstance->renderHeader();
        }
        parent::renderHeader();
    }

    /**
     * Check if method exists in child class or just call the version in PageLayout
     */
    function renderMainContent(){
        if (method_exists ($this->childInstance, 'renderMainContent') == true) {
            return $this->childInstance->renderMainContent();
        }
        parent::renderMainContent();
    }

    /**
     * Check if method exists in child class or just call the version in PageLayout
     */
    function renderFooter(){
        if (method_exists ($this->childInstance, 'renderFooter') == true) {
            return $this->childInstance->renderFooter();
        }
        parent::renderFooter();
    }
}


/**
 * Class AjaxLayout
 *
 * Implements render for just rendering a panel to replace the existing content.
 */
class AjaxLayout implements Render {

    //Render the Ajax request.
    public function render() {
        $this->renderMainContent();
    }

    //Renders the main page content. This method should be overridden for each page
    function renderMainContent(){
        echo "Main content goes here.";
    }

    //Just to prove we're extending dynamically.
    function getLayoutType() {
        return get_class($this);
    }
}

/**
 * Class ProxiedAjaxLayout
 *
 * Proxied version of AjaxLayout. All public functions must be overridden with a version that tests
 * whether the method exists in the child class.
 */
class ProxiedAjaxLayout extends AjaxLayout {

    /**
     * The child instance which has extended this class.
     */
    var $childInstance = null;

    /**
     * Construct a ProxiedAjaxLayout. The child class must be passed in so that any methods
     * implemented by the child class can override the same method in this class.
     * @param $childInstance
     */
    function __construct($childInstance){
        $this->childInstance = $childInstance;
    }

    /**
     * Check if method exists in child class or just call the version in AjaxLayout
     */
    function renderMainContent() {
        if (method_exists ($this->childInstance, 'renderMainContent') == true) {
            return $this->childInstance->renderMainContent();
        }
        parent::renderMainContent();
    }
}



/**
 * Class ImageDisplay
 *
 * Renders some images on a page or Ajax request.
 */
class ImageDisplay extends DynamicExtender {

    private $images = array(
        "6E6F0115.jpg",
        "6E6F0294.jpg",
        "6E6F0327.jpg",
        "6E6F0416.jpg",
        "6E6F0926.jpg",
        "6E6F1061.jpg",
        "6E6F1151.jpg",
        "IMG_4353_4_5_6_7_8.jpg",
        "IMG_4509.jpg",
        "IMG_4785.jpg",
        "IMG_4888.jpg",
        "MK3L5774.jpg",
        "MK3L5858.jpg",
        "MK3L5899.jpg",
        "MK3L5913.jpg",
        "MK3L7764.jpg",
        "MK3L8562.jpg",
    );

    //Renders the images on a page, along with a refresh button
    function renderMainContent() {
        $totalImages = count($this->images);
        $imagesToShow = 4;
        $startImage = rand(0, $totalImages - $imagesToShow);

        //Code inspection will not be available for 'getLayoutType' as it
        //doesn't exist statically in the class hierarchy
        echo "Parent class is of type: ".$this->getLayoutType()."<br/>";

        for($x=0 ; $x<$imagesToShow ; $x++) {
            echo "<img src='images/".$this->images[$startImage + $x]."'/>";
        }

        echo "<br/>&nbsp;<br/>";
        echo "<span onclick='loadImagesDynamic();' style='border: 2px solid #000000; padding: 4px:'>Click to refresh images</span>";
    }
}


$parentClassName = 'PageLayout';

if (isset($_REQUEST['panel']) && $_REQUEST['panel']) {
    //YAY! Dynamically set the parent class.
    $parentClassName = 'AjaxLayout';
}

$page = new ImageDisplay($parentClassName);

$page->render();

Sadece bir eval kullanmak değil misiniz?

<?php
function dynamic_class_name() {
    if(time() % 60)
        return "Class_A";
    if(time() % 60 == 0)
        return "Class_B";
}
eval(
    "class MyRealClass extends " . dynamic_class_name() . " {" . 
    # some code string here, possibly read from a file
    . "}"
);
?>