Rozšíření Object o metodu factory()
- Milo
- Nette Core | 1283
Často u objektů používám statickou metodu factory, abych mohl při vytváření objektu rovnou použít fluent. A jelikož už jsem si na to tak zvyknul, napadlo mě, jestli o tuto funkčnost nerozšířit i Nette.
namespace Nette;
abstract class ObjectFactory extends Object
{
public static function factory()
{
$class = get_called_class();
$ref = new \ReflectionClass($class);
if (!$ref->hasMethod('__construct')) {
return new $class;
}
$args = func_get_args();
return $ref->newInstanceArgs($args);
}
}
class MyClass extends ObjectFactory
{
}
$o = MyClass::factory()->fluent();
Chybou krásy je potřeba php 5.3. U php 5.2 se dá
get_called_class()
nahradit snad jen debug_backtrace()
varinatou.
- Filip Procházka
- Moderator | 4668
Docela používané je newInstance()
.
Editoval HosipLan (2. 11. 2011 8:04)
- Honza Marek
- Člen | 1664
Myslim, že v Nette\Objectu by mělo být co nejméně funkčnosti. Stačilo by mi, kdyby dělal jen takové ty věci jako vyhazování výjimek při přístupu k neinicializované property a podobně. Pokud někdo potřebuje tuhle metodu (a nepopírám její jistou užitečnost), nechť si Nette\Object podědí a používá ten.
- Filip Procházka
- Moderator | 4668
Extension method je dobrý nápad, metodu by totiž obsahovaly i Nette třídy. Osobně si myslím, že je to jenom „zbytečný syntactic sugar“, naštěstí nijak nepřekáží.
- Milo
- Nette Core | 1283
S čistotou Nette\Objectu souhlasím. Pokud by se statická metoda přidala
přímo jako Object::construct()
(varianta newInstance() taky
pěkná), nelze ji v potomkovi předefinovat jako nestatickou. Pokud by ji
někdo v potomkovi předefinoval jako statickou s jiným počtem povinných
parametrů, dostane E_STRICT
.
extensionMethod()
použít nejde, metoda se pak nedá volat
staticky. Navíc get_called_class()
lze volat jen v určitých
případech.
Měl jsem spíš na mysli přidat novou třídu, která by tuto funkčnost
poskytovala a rozšiřovala Nette\Object
. Ostatně tak to dělám
teď já.
namespace Nette;
abstract class ObjectFactory extends Object // název třídy berte s rezervou, to mi fakt nejde
{
public static function construct()
{
$class = get_called_class();
$ref = new \ReflectionClass($class);
if (!$ref->hasMethod('__construct')) {
return new $class;
}
$args = func_get_args();
return $ref->newInstanceArgs($args);
}
}
Editoval Milo (2. 11. 2011 18:02)
- Patrik Votoček
- Člen | 2221
Milo napsal(a):
Navíc
get_called_class()
lze volat jen v určitých případech.
Why?
- Milo
- Nette Core | 1283
To jsem blbě napsal. Funkci get_called_class()
nelze volat
z extension metody tak, aby vrátila název třídy ke které je extension
metoda přidaná. Extension metoda by musela nějak zjišťovat, ke které
třídě je přidaná. Příklad:
class MyClass extends Nette\Object
{
}
function construct()
{
// Jak tady zjistit, že jsem na třídě MyClass abych mohl vrátit instanci MyClass?
}
MyClass::extensionMethod('construct', 'construct');
// Ale extension metodu teď nelze volat staticky, takže to je stejně jedno
$o = MyClass::construct()->fluent(); // nelze
- Milo
- Nette Core | 1283
HosipLan napsal(a):
Osobně si myslím, že je to jenom „zbytečný syntactic sugar“, naštěstí nijak nepřekáží.
Ještě k tomuto. Zbytečný mi zas tak nepřijde, hezky zkracuje zápis.
$o = new MyClass($parameter);
foreach ($o->id($id) AS $record) {
}
// narozdíl od
foreach (MyClass::construct($parameter)->id($id) AS $record) {
}
// anebo
$cache = new Cache(MyStorage::construct()->setParam('x', 'y'));
Editoval Milo (3. 11. 2011 9:23)
- Honza Marek
- Člen | 1664
Stačí počkat pár let na PHP 5.5 a nebude to potřeba ;)
Nebo pokud ti jde o stručnost, tak si můžeš udělat takovou tu zvrácenou funkci
function vratSe($object)
{
return $object;
}
vratSe(new Vec())->fluent();
- Honza Marek
- Člen | 1664
Těžko říct. Mám takovej neurčitej pocit, že Rasmus na WebExpu povidal, že tohle se do 5.4 nedostane.
- David Grudl
- Nette Core | 8218
Honza Marek napsal(a):
Stačí počkat pár let na PHP 5.5 a nebude to potřeba ;)
Nette\Object je vlastně jen fixem na to čekání ;-) Proto mi metoda newInstance příp newInstanceArgs připadá fajn, protože řeší něco, co se musí v PHP zatím obcházet.
- Milo
- Nette Core | 1283
HosipLan napsal(a):
@Milo: Znáš __callStatic() ?
Znám. Ale teď mi asi uplně nedochází, jak ji využít. Současná
implementace Nette\Object::__callStatic()
vyvolá
MemberAccessException
.
Stále mi přijde nejsnazší řešení vytvořit třídu
ObjectFactory
která bude tuto funkčnost implementovat. Současná
kompatibilita se nezmění, kdo bude chtít funkčnost využít, nepodědí
Object
ale ObjectFactory
. Jako nevýhodu vidím
nefunkčnost v PHP <= 5.2 a špatný soubor:řádek ve fatal erroru při
chybném počtu parametrů předaných konstruktoru.
@Honza Marek: Zvrácenou funkci jsem jeden čas používal, ale pak jsem přestal protože se mi objevil hloupý konflikt s cizí knihovnou. Ale zase fungovala ve všech verzích PHP.
Editoval Milo (3. 11. 2011 16:59)
- Milo
- Nette Core | 1283
HosipLan napsal(a):
@Milo: Znáš __callStatic() ?
Asi mi docvalko. Máš na mysli… ?
abstract class Nette\Object
{
public static function __callStatic($name, $args)
{
if($name === 'construct') {
$class = get_called_class();
$ref = new \ReflectionClass($class);
if (!$ref->hasMethod('__construct')) {
return new $class;
}
return $ref->newInstanceArgs($args);
}
return ObjectMixin::callStatic(get_called_class(), $name, $args);
}
}
- Filip Procházka
- Moderator | 4668
Přesně tak :) Jenom se mi moc nelíbí construct, jsem spíš pro
newInstance()
.
- Milo
- Nette Core | 1283
Tak co třeba takhle? Má cenu psál pull a testy?
public static function __callStatic($name, $args)
{
$methodName = strtolower($name);
if ($methodName === 'newinstance' || $methodName === 'newinstanceargs') {
$class = get_called_class();
$ref = new \ReflectionClass($class);
if (!$ref->hasMethod('__construct')) {
return new $class;
}
if ($methodName === 'newinstanceargs') {
if (!array_key_exists(0, $args)) {
$args = array();
} elseif ($args[0] instanceof \Traversable) {
$args = iterator_to_array($args[0], false);
} elseif (!is_array($args[0])) {
throw new InvalidArgumentException("First parameter of $class::newInstanceArgs() must be an array, " . gettype($args[0]) . " given.");
} else {
$args = & $args[0];
}
}
return $ref->newInstanceArgs($args);
}
return ObjectMixin::callStatic(get_called_class(), $name, $args);
}
- internally
- Člen | 1
FYI PHP 5.4 již patch pro „instance method call“ obsahuje, http://svn.php.net/…PHP_5_4/NEWS?…, takže bych si dal pohov a věnoval se raději nějakým těm bugům a pull requestům, na githubu jich vidím požehnaně
- Majkl578
- Moderator | 1364
Tenhle návrh (jak už tu padlo) přichází s křížkem po funuse. Totiž v době PHP 5.4 chtít něco výhradně pro PHP 5.2 je k zamyšlení (5.2 je už skoro něco jako PHP 4) – v 5.3 jde snadno workaroundovat a v 5.4 je to podporované nativně:
(new Something)->foo();
(new Something(1, 'arg2'))->foo();
Milo napsal(a):
Člověče, Tebe to nějak rozčílilo…
Ale má pravdu. :)
- Majkl578
- Moderator | 1364
Milo napsal(a):
Jaký snadný workaround pro 5.3 máš na mysli? Na nic univerzálnějšího jsem nepřišel.
Vpodstatě to s get_called_class (nepřijde mi to tak problematické), příp. třeba:
use Nette\Reflection\ClassType;
ClassType::from(get_called_class())->newInstance(1, 'arg');
>
Za jak dlouho myslíte, že 5.4 bude stable?
Do konce roku, maximálně v lednu. Už je RC1.
- David Grudl
- Nette Core | 8218
internally napsal(a):
FYI PHP 5.4 již patch pro „instance method call“ obsahuje, http://svn.php.net/…PHP_5_4/NEWS?…, takže bych si dal pohov a věnoval se raději nějakým těm bugům a pull requestům, na githubu jich vidím požehnaně
To je dobrá zpráva, že se chceš věnovat bugům, ale za celý měsíc mi od tebe žádný fix nepřišel. Na čem to vázne?