PHP Magic Instantiation gerçekleştirin miyim?

10 Cevap

PHP kütüphaneleri oldukça kötü bir dil ve ad-hoc set olmasına rağmen ... hangi işlevleri ve nesnelerinin rastgele argüman emir ve genellikle dışarı kötü düşünülmüş anlambilimin karışımı sürekli WTF anları demek ....

... Ben programlamak için oldukça eğlenceli ve oldukça her yerde olduğunu itiraf edecektir. (waiting for Server-side JavaScript to flesh out though)

question: Given a class class RandomName extends CommonAppBase {} is there any way to automatically create an instance of any class extending CommonAppBase without explicitly using new?

Bir kural olarak, sadece PHP dosyası başına bir sınıf tanımı olacaktır. Ve tüm dosyaların sonuna new RandomName() ekleme Ben ortadan kaldırmak istiyoruz şeydir. Uzanan sınıfı yok yapıcısı vardır; sadece CommonAppBase 'nin kurucu denir. CommonAppBase->__construct() apps yürütme kalanını kickstarts.

Herkes bir çözüm bilen varsa garip bir soru, ama güzel olurdu.

Edit

Aşağıdaki yorum daha fazla. Örnekleme yapar kod sınıf dosyası olmayacaktır. Class dosyası sadece ben include('random.class.php') diğer bazı kod istiyorum olmak ve CommonAppBase orada ne olursa olsun sınıf uzanan örneğini.

Herkes my hackish answer Ben ne istediğidir sonra, ama sanest şekilde duyuyorum emin ne için.

Thanks in advance, Aiden

(Btw, benim PHP version 5.3.2 olduğu) herhangi bir cevap ile bir sürümü kısıtlamaları belirtiniz.

Answers

Aşağıdaki tüm auto (php.ini üzerinden veya Apache ile) bir dosyaya eklenebilir, belirli bir ana sınıfının bir sınıf başlatmak.

İlk (teşekkürler dnagirl)

$ca = get_declared_classes();
foreach($ca as $c){
    if(is_subclass_of($c, 'MyBaseClass')){
        $inst = new $c();
    }
}

ve (kabul edilen yanıt, en yakın cevap olarak)

auto_loader();
function auto_loader() {
    // Get classes with parent MyBaseClass
    $classes = array_filter(get_declared_classes(), function($class){
        return get_parent_class($class) === 'MyBaseClass';
    });
    // Instantiate the first one
    if (isset($classes[0])) {
        $inst = new $classes[0];
    }
}

10 Cevap

Ben aşağıdaki sizin için ne arıyorsanız tam olup olmadığından emin değilim, ama bu bir fikir. Kod oldukça kendini açıklayıcı olmalıdır.

auto_loader();
function auto_loader() {
    // Get classes with parent MyBaseClass
    $classes = array_filter(get_declared_classes(), function($class){
        return get_parent_class($class) === 'MyBaseClass';
    });
    // Instantiate the first one
    if (isset($classes[0])) {
        $inst = new $classes[0];
    }
}

Not: fonksiyonlar erken PHP 4 hayatında ama array_filter sadece PHP 5.3.0 'da tanıtıldı ile kullanılan anonim işlev sözdizimi beri mevcuttur.

Ben sorununuzu misinterpreting olabilir ama (bir veritabanı sınıfı gibi) uygulama boyunca kullanmak için bir sınıfın tek bir örnekleme arıyorsanız ben size bir fabrika deseni kullanmak öneririz. Sonra, her zaman sizin gibi bir şey yapabilirsiniz sınıf gerekir ....

$obj = &MyFactory::getClass('mysql_database_class');

Ben kesinlikle sınıf dosyalarının sonunda sınıfları başlatmasını ekarte olacak. Standart OOP ilkeleri takip çalışıyorsanız o başka kodunuzda çarpışmalar neden olabilir o zaman, her ne pahasına bu kaçınmalısınız.

call_user_func_array (array ($ SınıfAdı, '__construct'), $ args);

Bu ne istediğinizi yapar?

class CommonAppBase {}
class RandomName extends CommonAppBase {}
$klass = 'RandomName';
$instance = new $klass();

Ben her zaman new gerekir düşünüyorum.

Eğer bir sınıf dosyası yüklemek ve orada ne olursa olsun sınıf örneğini isterseniz, ben sadece gerçekten güvenilir yolu get_defined_classes() öncesi ve sonrası dahil kullanılarak olduğunu düşünüyorum. Eğer örneğini verecek bu iki liste arasındaki fark, (array_diff()). Ben geçmiş projelerde bu yapmış ama şıklık ve performans için kaygılar dışında, bir daha bugün bunu yapmazdı.

En iyi yolu, IMO adlandırma ile gerçekten sıkı olmaktır. Böylece random.class.php Orada random adında bir sınıf olduğu kongre tanımlamak ve instanciate yüklediğinizde otomatik olarak:

$classname = "random";

require "$classname.class.php";
$$classname = new $classname();  // Produces an object instance `$random`

Var sayarsak bir __autoload function, what is the problem with new?

Bir daha dolaylı örnekleme yöntemi istiyorsanız Ancak, new, statik bir yöntem içinde gizleyebilirsiniz. Eğer baz sınıfın bu iki yöntem vardı, yani:

static function make(array $args=NULL){
  $class=static::who(); //note: static, NOT self
  $obj=new $class($args);

  //some test for whether or not $obj is acceptable
  return ($test) ? $obj : false;  
}

abstract static function who(){
  return __CLASS__;
}

Sonra RandomClass Nesne Örneğini bu şekilde deneyebilirsiniz:

$classname= 'RandomClass';
$someargs=array(1,2,3);

if(!$newobj= $classname::make($someargs)) die('cannot make new object');

My Attempt, which does what i'm after (dubiously)

function auto_loader()
{
    $file = $_SERVER['SCRIPT_FILENAME']; // Some class file
    $cont = file_get_contents($file);
    $tokens = token_get_all($cont);
    for($i=0; $i<sizeof($tokens); $i++) {
        // Token = $token[$i][0]
        // Lexeme is $token[$i][1]
        if($tokens[$i][0]==T_EXTENDS && $tokens[$i+2][1]=="MyBaseClass"){
            // Get the lexeme of the class
            $class = $tokens[$i-2][1];
            break;
        }
    }
    $inst = new $class();
}
auto_loader();

Apache ya da her neyse yoluyla ekteki otomobil hangisi. Iş gibi görünüyor, ancak dikkate hayır / değişen boşluk ($i-2) extends ve BaseAppClass arasına almaz.

Bir kesmek gibi, biraz daha kod çalışır ve bir cache onu bağlayan ve bir rakip olabilir gibi görünüyor. Biraz üzerinde mühendislik.

http://www.foo.com/some_class_file.php istendiğinde Yani, yukarıdaki Apache / php.ini tarafından eklenir ve some_class_file.php olarak sınıf örneğini ve ne olursa olsun sınıf adının yürütme başlatabilirsiniz. Benim durumumda, URL sınıfa ilgili değildir çünkü bu. Bu bir sınıf MyDogBenjiClass örneğinin olabilir.

Attempt 2, thanks dnagirl The following is auto appended like the above to launch the app.

$ca = get_declared_classes();
foreach($ca as $c){
    if(is_subclass_of($c, 'MyBaseClass')){
        $inst = new $c();
    }
}

Sadece bir inek deney olarak, size include işlevi geçersiz APD fonksiyonunu override_function kullanabilirsiniz.

Yeni işlev, sen dahil dosyayı tokenise (sınıf tanımlarını almak için) ve bir sınıf bulunduğunda örneğini olabilir.

Dinamik sınıfları örneğini için Reflection kullanarak düşündünüz mü?

$cls = new ReflectionClass('ClassName');
$obj = $cls->newInstance();

Sen de retrieve its parent class.

  1. index.php her URL'yi haritası
  2. $_SERVER['REQUEST_URI'] (ya da sizin için çalışan ne olursa olsun) gelen dosyayı bulmak
  3. init değişken classname kullanabilirsiniz ($main = new $file())

MVC çerçeveler genellikle bu gibi, ama daha yapısal şekilde bir şey yapın:

  1. istek index.php mod_rewrite'ı, yükleri üzerinden yönlendirilir ve dağıtıcı çeşit çalışır
  2. memuru url ayrıştırır: http://example.com/foo/bar/baz $controller = 'foo', $action = 'bar', $param = 'baz' dönüştü
  3. memuru içerir controllers/foo.php
  4. sonra örnekler: $controllerInstance = new $controller()
  5. sonra eylem çağırır ve ona parametre geçirir: call_user_func_array(array($controllerInstance, 'do'.ucfirst($action)), array($param))