Rozšíření Object o metodu factory()

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Milo
Nette Core | 1283
+
0
-

Č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.

David Grudl
Nette Core | 8138
+
0
-

Nebylo by lepší místo factory() použít třeba, …, construct()?

Milo
Nette Core | 1283
+
0
-

Bylo. I ObjectFactory je zavádějící, ale nemůžu přijít na názvosloví, jak tu třídu výstižněji pojmenovat.

Filip Procházka
Moderator | 4668
+
0
-

Docela používané je newInstance().

Editoval HosipLan (2. 11. 2011 8:04)

Honza Marek
Člen | 1664
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

Milo napsal(a):

Navíc get_called_class() lze volat jen v určitých případech.

Why?

Milo
Nette Core | 1283
+
0
-

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
+
0
-

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
+
0
-

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();
Filip Procházka
Moderator | 4668
+
0
-

@Milo: Znáš __callStatic() ?

paranoiq
Člen | 392
+
0
-

@Honza Marek: já myslel, že to má být už v 5.4: new Cls()->doSomething()...

Honza Marek
Člen | 1664
+
0
-

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 | 8138
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

Přesně tak :) Jenom se mi moc nelíbí construct, jsem spíš pro newInstance().

Milo
Nette Core | 1283
+
0
-

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);
}
Milo
Nette Core | 1283
+
0
-

Ha, ze mě je Nette guru a Dibi guru. Ego jásá, ale nemáte tam něco slabšího? Pak ze mě vypadne nějaký trapný dotaz a pohaním tak ostatní guru :-)

internally
Člen | 1
+
0
-

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ě

Milo
Nette Core | 1283
+
0
-

Člověče, Tebe to nějak rozčílilo… Dobrá zpráva s tím member access, jen než se jí dočkáme ve stable. A bohužel do Nette nevidím natolik, abych něco bugfixoval ikdyž bych rád přispěl.

Majkl578
Moderator | 1364
+
0
-

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. :)

Milo
Nette Core | 1283
+
0
-

Tohle řešení stejně pro PHP 5.2 nefunguje, zmiňoval jsem proč. Je pro PHP 5.3. Jaký snadný workaround pro 5.3 máš na mysli? Na nic univerzálnějšího jsem nepřišel.

Každej má svojí pravdu :)

Za jak dlouho myslíte, že 5.4 bude stable?

Majkl578
Moderator | 1364
+
0
-

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 | 8138
+
0
-

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?