Form::add{Component}
- ViliamKopecky
- Nette hipster | 230
Trošku se mi nepozdává prototypování Form třídy jednotlivýma komponentama z extras (navíc neprototypujou stejným způsobem), tak jsem si udělal potomka AppForm, který z MyForm::add{Component}(…) vytvoří danou komponentu, v případě že taková třída s rozhraním IComponent existuje.
Jinak tady v těch call_user_func
apod. si nejsem uplně
jistej, takže možná/určitě bude nějaké elegantnější řešení.
Za přípomínky budu rád. Případná implementace přímo do Nette by byla pěkná, ale tohle už by asi Framework řešit neměl.
/**
*
* @author Viliam Kopecký
* @version 0.8
* @copyright 2009
*/
class MyForm extends AppForm {
/**
* Call to undefined method.
* Cares of not-native FormControls
*
* @param string $ method name
* @param array $ arguments
* @return mixed
* @throws Exception
*/
public function __call($name, $args)
{
if (String::startsWith($name, "add") && class_exists($class = substr($name, 3))) {
$name = array_shift($args);
$component = @new $class;
if ($component instanceof IComponent) {
call_user_func_array(array($component, "__construct"), $args);
$this->addComponent($component, $name);
return $this[$name];
} else {
throw new Exception("'$class' must implement 'IComponent'");
}
}
return parent::__call($name, $args);
}
}
Editoval enoice (24. 6. 2009 21:01)
- PetrP
- Člen | 587
Huh,
call_user_func_array(array($component, "__construct"), $args);
je
nesmyl, protože construct se volá při vytváření instance, taky nechápu
proč potlačuješ chyby v @new $class;
Ale hlavně je hloupost vytvařet instanci když není instanceof
IComponent.
Dělal bych to možná nějak takto:
if (in_array('IComponent',class_implements($class)))
{
$component = new $class;
}
Taky proč toto: $name = array_shift($args);
?
// EDIT
taky by hlavně mělo v případě Formsů implementovat
IFormControl
a né jen IComponent
Editoval PetrP (25. 6. 2009 14:27)
- ViliamKopecky
- Nette hipster | 230
Jo, máš pravdu, ale stále… Jak předat parametry constructoru?
„If eval() is the answer, you're almost certainly asking the wrong question.“ – Rasmus Lerdorf
- ViliamKopecky
- Nette hipster | 230
jasir napsal(a):
enoice napsal(a):
Jo, máš pravdu, ale stále… Jak předat parametry constructoru?
„If eval() is the answer, you're almost certainly asking the wrong question.“ – Rasmus Lerdorf
<?php $a = new $class(arguments); ?>
No… Máš argumenty v poli a potřebuješ je předat do constructoru. Pro
normální funkci nebo metodu se využije call_user_func
.
<?php
// například pro RadioList
$class = "RadioList";
$args = array("rl", "Vyberte možnost", array("option 1", "option 2"));
$object = new $class(- sem dostat $args -);
?>
Editoval enoice (25. 6. 2009 16:04)
- zaxeeq
- Člen | 17
Já použil něco jako
private static $INSTANCE_CACHE = array();
public static function createInstance($name, $args = NULL) {
$count = count($args);
switch($count) {
case 0:
return new $name();
case 1:
return new $name($args[0]);
case 2:
return new $name($args[0], $args[1]);
case 3:
return new $name($args[0], $args[1], $args[2]);
}
//more than three parameters
if (isset(self::$INSTANCE_CACHE[$name])) {
$classObj = self::$INSTANCE_CACHE[$name];
} else {
$classObj = new ReflectionClass($name);
self::$INSTANCE_CACHE[$name] = $classObj;
}
return $classObj->newInstanceArgs($args);
}
- ViliamKopecky
- Nette hipster | 230
Tak jsme zase objevil nějaké zákoutí PHP. A myslím, že tohle je docela čisté řešení.
<?php
/**
*
* @author Viliam Kopecký
* @version 0.9
* @copyright 2009
*/
class MyForm extends AppForm {
/**
* Call to undefined method.
* Cares of not-native FormControls
*
* @param string $ method name
* @param array $ arguments
* @return mixed
* @throws Exception
*/
public function __call($method_name, $args)
{
if (String::startsWith($method_name, "add") && class_exists($class_name = substr($method_name, 3))) {
$name = array_shift($args);
if (in_array('IFormControl', class_implements($class_name))) {
$class = new ReflectionClass($class_name);
$instance = $class->newInstanceArgs($args);
$this->addComponent($instance, $name);
return $this[$name];
}
}
return parent::__call($method_name, $args);
}
}
?>
- ViliamKopecky
- Nette hipster | 230
zaxeeq napsal(a):
Já použil něco jako
…
Vypadá to zvláštně, ale asi je to v případě switche rychlejší, než vytvářet ReflectionClass. Hmmm
- zaxeeq
- Člen | 17
enoice napsal(a):
zaxeeq napsal(a):
Já použil něco jako
…
Vypadá to zvláštně, ale asi je to v případě switche rychlejší, než vytvářet ReflectionClass. Hmmm
Vycházel jsem z http://blog.liip.ch/…is-slow.html a zkusil benchmark a ReflectionClass::newInstanceArgs je skutečně pomalejší (vyšlo mě to cca o 30%)
- ViliamKopecky
- Nette hipster | 230
class MyForm extends AppForm {
private static $reflections = array();
/**
* Call to undefined method.
* Cares of non-native FormControls
*
* @param string $ method name
* @param array $ arguments
* @return mixed
* @throws Exception
*/
public function __call($method_name, $args)
{
if (String::startsWith($method_name, "add") && class_exists($class_name = substr($method_name, 3))) {
$name = array_shift($args);
if (in_array('IFormControl', class_implements($class_name))) {
if (($count = count($args)) <= 3) {
switch ($count) {
case 1:
$this->addComponent(new $class_name($args[0]), $name);
break;
case 2:
$this->addComponent(new $class_name($args[0], $args[1]), $name);
break;
case 3:
$this->addComponent(new $class_name($args[0], $args[1], $args[2]), $name);
break;
}
return $this[$name];
}
if (!isset(self::$reflections[$class_name])) {
self::$reflections[$class_name] = new ReflectionClass($class_name);
}
$instance = self::$reflections[$class_name]->newInstanceArgs($args);
$this->addComponent($instance, $name);
return $this[$name];
}
}
return parent::__call($method_name, $args);
}
}
- David Grudl
- Nette Core | 8227
Switch je megaprasárna ;)
Tohle se dá asi skutečně řešit jen přes
$class->newInstanceArgs($args)
, což je ale pěkné a čisté.
Když už se vytváří reflexe třídy, doporučil bych nahradit
class_implements za $class->implementsInterface('IFormControl')
.
A klidně i $class->isInstantiable()
, ať se odfiltrují
abstraktní třídy.
- ViliamKopecky
- Nette hipster | 230
@Dave: Takže košér takhle?
public function __call($method_name, $args)
{
if (String::startsWith($method_name, "add") && class_exists($class_name = substr($method_name, 3))) {
$name = array_shift($args);
if (!isset(self::$reflections[$class_name])) {
self::$reflections[$class_name] = new ReflectionClass($class_name);
}
$class = &self::$reflections[$class_name];
if ($class->implementsInterface('IFormControl') && $class->isInstantiable()) {
$instance = $class->newInstanceArgs($args);
$this->addComponent($instance, $name);
return $this[$name];
}
}
return parent::__call($method_name, $args);
}
- David Grudl
- Nette Core | 8227
Jo, to je super.
(osobně bych asi reflexe nekešoval, ale to je fuk)
Důvod, proč něco takového není ve frameworku, jsou namespaces. Ve kterých hledat?
- Patrik Votoček
- Člen | 2221
Nastavitelné pole s kde by byly ony namespace? Defaultně by tam bylo jenom „Nette\Form“ …? Nebo v názvu metody (typu fomsojidní „componenty“) používat znak/řetězec který nahradí \ pokud tam tohle nebude tak se použije klasické „Nette\Form“… Je to jenom nápad…
- ViliamKopecky
- Nette hipster | 230
Nojo, na namespaces jsem nemyslel, přitom už se nám (možná) brzy finálně objeví.