2009-12-14 11 views
11

Proszę zobaczyć kod poniżej:PHP [OOP] - Jak ręcznie wywołać konstruktor klas?

01. class Test { 
02.  public function __construct($param1, $param2, $param3) { 
03.   echo $param1.$param2.$param3; 
04.  } 
05. } 
06. 
07. $params = array('p1','p2','p3'); 
08. 
09. $ob = new Test; 
10. 
11. if(method_exists($ob,'__construct')) { 
12.  call_user_func_array(array($ob,'__construct'),$params); 
13. } 

Teraz problem jest konstruktornazywa się w linii 09

ale chcę to nazwać ręcznie w linii 11-13

Czy to możliwe? Jeśli tak, to w jaki sposób? Czy masz jakieś pomysł?

+0

Zauważ, że jeśli parametry konstruktora są przekazywane przez odniesienie, a następnie call_user_func_array zawiedzie !! – Sarfraz

Odpowiedz

21

Nie można zapobiec wywołaniu konstruktora podczas budowy obiektu (wiersz 9 w kodzie). Jeśli istnieje pewna funkcjonalność, która ma miejsce w metodzie __construct(), którą chcesz odłożyć do czasu zakończenia budowy, powinieneś przenieść ją do innej metody. Dobrą nazwą tej metody może być init().

Dlaczego po prostu tego nie zrobić?

class Test { 
    public function __construct($param1, $param2, $param3) { 
     echo $param1.$param2.$param3; 
    } 
} 

$ob = new Test('p1', 'p2', 'p3'); 

EDYCJA: Właśnie pomyślałem o hacky sposób można zapobiec konstruktorowi od wywołania (rodzaj). Można podklasę Test i nadpisać konstruktor pustym konstruktorem.

class SubTest extends Test { 
    public function __construct() { 
     // don't call parent::__construct() 
    } 

    public function init($param1, $param2, $param3) { 
     parent::__construct($param1, $param2, $param3); 
    } 
} 

$ob = new SubTest(); 
$ob->init('p1', 'p2', 'p3'); 

To może mieć sens, jeśli masz do czynienia z jakimś kodem, że nie można zmienić z jakiegoś powodu i trzeba obejść pewne irytujące zachowanie źle napisane konstruktora.

0

skonstruować swój przedmiot, a następnie przekazać parametry Twój może próbować w ten sposób:

class Test { 
    public function __CONSTRUCT($p1="Bundy", $p2="house", $p3=10) { 
     $this->init($p1, $p2, $p3); 
    } 
    public function init($p1, $p2, $p3) { 
     //setup your object here 
    } 
} 

to jest możliwe do skonstruowania obiektu i nazywają

$ob->init($p1, $p2, $p3); 

później.

+2

To nie działa, ponieważ PHP nie obsługuje przeciążania metod. Ta strategia działa w języku takim jak Java. – Asaph

+0

Tak, masz rację. Usunąłem przeciążenie konstruktora ... ... wykonałem za dużo java ostatnio – justastefan

+0

OK, dobra edycja, ale twoje '$ ob-> init ($ params); nie wygląda spójnie z twoją definicją funkcji' init() ' która akceptuje 3 argumenty zamiast pojedynczej tablicy. BTW: Nie ma czegoś takiego jak zbyt wiele Java. – Asaph

1

Jeśli oddzielanie instancji od inicjalizacji nie jest ściśle wymagane, istnieją dwie inne możliwości: po pierwsze, statyczna metoda fabryczna.

class Test { 
    public function __construct($param1, $param2, $param3) { 
     echo $param1.$param2.$param3; 
    } 

    public static function CreateTest($param1, $param2, $param3) { 
     return new Test($param1, $param2, $param3); 
    } 
} 

$params = array('p1','p2','p3'); 

if(method_exists($ob,'__construct')) { 
    call_user_func_array(array($ob,'CreateTest'),$params); 
} 

Lub, jeśli używasz PHP 5.3.0 lub nowszy, można użyć lambda:

class Test { 
    public function __construct($param1, $param2, $param3) { 
     echo $param1.$param2.$param3; 
    } 
} 

$params = array('p1','p2','p3'); 

$func = function ($arg1, $arg2, $arg3) { 
    return new Test($arg1, $arg2, $arg3); 
} 

if(method_exists($ob,'__construct')) { 
    call_user_func_array($func, $params); 
} 

Sposób inicjalizacji opisany przez Asafa jest wielki, jeśli z jakiegoś powodu masz potrzebę logicznie oddzielić inicjalizację od tworzenia instancji, ale jeśli obsługa powyższego przypadku użycia jest szczególnym przypadkiem, a nie zwykłym wymogiem, może być niewygodne wymaganie od użytkowników utworzenia i zainicjowania obiektu w dwóch osobnych krokach.

Metoda fabryka jest dobra, ponieważ daje metodę wywoływania w celu uzyskania inicjowanej instancji. Obiekt jest inicjowany i tworzony w tej samej operacji, więc jeśli masz potrzebę oddzielenia dwóch, to nie będzie działać.

I na koniec, polecam lambdę, jeśli ten mechanizm inicjalizacji jest niezmiernie używany i nie chcesz zaśmiecać definicji klasy za pomocą inicjalizacji lub metod fabrycznych, które rzadko będą używane.

9

Zauważ, że jeśli konstruktor (metoda __construct) zawiera argumenty przekazywane przez odniesienia, to funkcja:

call_user_func_array 

zakończy się niepowodzeniem z błędem.

Proponuję zamiast tego użyć klasy Odbicie; oto jak możesz to zrobić:

// assuming that class file is already included. 

$refMethod = new ReflectionMethod('class_name_here', '__construct'); 
$params = $refMethod->getParameters(); 

$re_args = array(); 

foreach($params as $key => $param) 
{ 
    if ($param->isPassedByReference()) 
    { 
     $re_args[$key] = &$args[$key]; 
    } 
    else 
    { 
     $re_args[$key] = $args[$key]; 
    } 
} 

$refClass = new ReflectionClass('class_name_here'); 
$class_instance = $refClass->newInstanceArgs((array) $re_args); 
0

W PHP można tworzyć obiekty bez wywoływania konstruktora. Ale to nie działa przez użycie nowego, ale przez zdereumerowanie instancji obiektu.

Konstruktor można następnie wywołać ręcznie.

Zazwyczaj nie jest to konieczne. Zobacz także: Loading an object from PHP session, does it call constructor?

<?php 

class Test 
{ 
    public function __construct() 
    { 
     echo '__construct called.',"\n"; 
    } 
} 

function old($class) 
{ 
    return unserialize 
    (
     sprintf 
     (
      'O:%d:"%s":0:{}', 
      strlen($class), 
      $class 
     ) 
    ); 
} 

$test = old('Test'); 
$test->__construct(); 
1

ja nie wiem, czy istnieją pewne obawy związane z bezpieczeństwem za pomocą metody eval(), ale można zrobić sobie funkcję tak:

function CreateObj($className,$params) 
{ 
    $strArgs = '$params['.implode('],$params[',array_keys($params)).']'; 
    eval('$ob = new '.$className.'('.$strArgs.');'); 
    return $ob 
} 

a teraz $ ob powinien być teraz zdefiniowany z poprawnymi parametrami, nie przetestowałem go i być może jest błąd w kodzie, ale masz pomysł ....

Powiązane problemy