Funkce callback()

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

Sice se to zcela vymyká zbytku frameworku, ale co vytvořit jednoduchou funkci callback(), aby fungovalo:

$form->onSubmitted[] = callback($this, 'method');

Také lze přidat na Control (asi méně intuitivní):

$form->onSubmitted[] = $this->callback('method');
sodae
Nette Evangelist | 250
+
0
-

Podle mě je to celkem přebytečná blbost, když někod bude chtit vytvořit callback tak jej vytvoří pomocí pole a je to , nevim proč zmatkovat

Patrik Votoček
Člen | 2221
+
0
-

proč to?

$form->onSubmit[] = array($this, 'method');

je pořád méně písmenek a tak je to napsáno rychleji… (nezpomalovalo by to zbytečně?)

blacksun
Člen | 177
+
0
-

Výhodu bych viděl v trochu intuitivnějším zápise, ze kterého je hned jasné, že jde o přiřazení callbacku k nějaké události místo předání pole s $this a „nějakým“ stringem..

ViliamKopecky
Nette hipster | 230
+
0
-
cb($this, "method");

? :-)

Ondřej Brejla
Člen | 746
+
0
-

enoice napsal(a):

cb($this, "method");

? :-)

To je dost „nesamopopisné“, nic neříkající fce cb(). Když už, tak callback(). Ale taky se mi zdá, že je to zbytečné.

Patrik Votoček
Člen | 2221
+
0
-

enoice napsal(a):

cb($this, "method");

? :-)

To by mohlo místo callback evokovak ke CurlyBrackets… :-/

Jod
Člen | 701
+
0
-

Malo by to hádzať CallbackException aby každý vedel, že je to callback :)))

ViliamKopecky
Nette hipster | 230
+
0
-

Jod napsal(a):

Malo by to hádzať CallbackException aby každý vedel, že je to callback :)))

tak tak, házelo by to exception už při vytvoření callbacku, ne až při jeho použití.

vlki
Člen | 218
+
0
-

Ta kontrola callbacku už při vytvoření je lákavá…

Plus také to, že PHP 5.2.0 a 5.2.1, myslím, neumí callbacky na statické funkce pomocí Class::method, ale jen array('Class', 'method'). Nemuselo by se toto tedy řešit až na úrovní volání callbacku (pro což myslím vznikla i funkce fixCallback), ale šlo by přímo v této funkci.

Majkl578
Moderator | 1364
+
0
-

Osobně jsem zvyklý na callback ve formě pole, jelikož se to používá v OOP PHP všude. Nicméně myšlenka okamžité validace callbacku je zajímavá.
Akorát ta „obyčejná“ funkce mi jaksi nesedí do OOP návrhu Nette :)

Editoval Majkl578 (13. 10. 2009 19:58)

kravčo
Člen | 721
+
0
-

Kontrola callbacku pri vytvorení – výborné.

A nebol na niečo takéto kedysi Nette\Callback?

Honza Kuchař
Člen | 1662
+
0
-

Já jsem pro. Když jsem začínal na Nette, tak zrovna ty callbacky byly pro mě naprosto nepochopitelné.

Asi nějak takhle:

Proč mám vytvářet nějaké pole, když chci callback?!

redhead
Člen | 1313
+
0
-

taky si myslím, že by to tam mohlo být, navíc IDE ti krásně napoví i parametry, což se u pole říct nedá…

Jod
Člen | 701
+
0
-

Podla mňa, keď bude možné zadávať callback ako pole a aj cez funkciu nejakého Componentu nič sa tým nepokazí.

Majkl578
Moderator | 1364
+
0
-

Jod napsal(a):

Podla mňa, keď bude možné zadávať callback ako pole a aj cez funkciu nejakého Componentu nič sa tým nepokazí.

Jak by vypadalo toto v praxi?

$form = new AppForm;
//...
$form->onSubmit($this, 'fooFormSubmitted');

Myšleno jako zkratka pro callback při použítí přímo u formuláře.

Editoval Majkl578 (15. 10. 2009 12:25)

_Martin_
Generous Backer | 679
+
0
-

Majkl578 napsal(a):

Jak by vypadalo toto v praxi?

$form = new AppForm;
//...
$form->onSubmit($this, 'fooFormSubmitted');

Myšleno jako zkratka pro callback při použítí přímo u formuláře.

Tohle nejde, tímhle způsobem se právě všechny callbacky události vyvolají.

Jod
Člen | 701
+
0
-

Pozri do prvého príspevku od Davida, tam to máš.

_Martin_
Generous Backer | 679
+
0
-

Jod napsal(a):

Pozri do prvého príspevku od Davida, tam to máš.

Tohle byla reakce na koho?

jasir
Člen | 746
+
0
-

Jsem pro vytvoření funkce callback, implementace pomocí globální funkce je pragmatická.

  • + možnost kontroly správnosti již při vytvoření callbacku se může ukázat jako velmi produktivní
  • + přehlednější kód
  • + zachování možnosti tvorby callbacku pomocí array($this, 'method')
  • – zpomalení, předpokládám ale že neměřitelné

Editoval jasir (19. 10. 2009 15:52)

paranoiq
Člen | 392
+
0
-

jsem pro přidání. hlavně pokud to pomůže odstranit magickou funkci fixCallback(). současný zápis callbacků, kdy se u statických metod předává nevalidní řetězec a až posléze se někde skrytě opraví na validní callback může být matoucí

určitě je lepší přehledný a čitelný kód, než nějaká dogmatická objektovost za každou cenu

amsys
Člen | 20
+
0
-

Když už jsme u fixCallback tak to jen tak snadno nezmizí, globální funkce callback bude vracet vždy pole, jenže v tom případě se to bude prohánět fixCallback i napopodruhé protože nemáme jak jednoduše dát vědět, že už to již jednou bylo upravené, ale stejně to asi nebude znát na výkonu.

kravčo
Člen | 721
+
0
-

paranoiq napsal(a):

jsem pro přidání. hlavně pokud to pomůže odstranit magickou funkci fixCallback(). současný zápis callbacků, kdy se u statických metod předává nevalidní řetězec a až posléze se někde skrytě opraví na validní callback může být matoucí

Funkcia fixCallback() neopravuje neplatne zadané callbacky, ale umožňuje podporu nižších verzií PHP tým, že platné callbacky prepíše do podoby, ktorú zvládnu. Tým umožňuje písať aplikácie prenositeľné medzi verziami PHP.

David Grudl
Nette Core | 8212
+
0
-

S odstupem času jsem dnes funkci callback() implementoval. Nejlepší bude ji ověřit praxí, odstranit se dá vždycky.

Nejprve pár nesouvislých bodů, které jsem měl na mysli během zvažování způsobu implementace:

  • který zápis callbacku by v PHP vypadal srozumitelněji? array($a, $b) vs. callback($a, $b)
  • PHP 5.3 zavádí closures, které lze volat přes $callback($arg, ...)
  • klasické callbacky lze volat pouze přes neobjektové call_user_func($callback, $arg, ...)
  • pokud se volání nezdaří, vygeneruje se pouze warning (!)
  • striktnost lze zajistit pouze důsledným kontrolováním přes is_callable()
  • do verze PHP 5.2.1 nelze použít zápis $callback = 'Trida::metoda', je třeba pole
  • před verzí PHP 5.3 nelze volat objekt s magickou metodou __invoke
  • kontrola callbacků v okamžiku vytvoření může být výhodná při vývoji, může však načítat třídy, které nebudou při běhu nakonec potřeba

Implementace znamenala comeback pro třídu Nette\Callback ;) Funkce callback je pouze její továrničkou. Což jde mimo coding styles frameworku, je to vlastně jediná veřejná funkce v celém frameworku. (Aby nebyla sama, přidal jsem ji ještě funkci dump() volající Debug::dump).

Zdá se mi, že tato úprava framework trošku pročistila. Takže snad je to krok správným směrem.

PetrP
Člen | 587
+
0
-

Zavedl bych i alias pro Callback::__invoke() > Callback::invoke().
Aby se to dalo normálně psát.

(Jinak se mi tenhle krok líbí)

David Grudl
Nette Core | 8212
+
0
-

Nad tímhle furt váhám, protože fakt nemám rád, když se jedna věc dá udělat dvěma způsoby. A __invoke existovat musí. Ale asi jsem našel argument pro invoke(), protože ani v PHP 5.3 nejde udělat:

$methodInfo = new MethodReflection('B', 'foo');
$methodInfo->getCallback()(...) // nelze
$methodInfo->callback(...) // nelze; callback je property getCallback()
$methodInfo->callback->invoke(...)

tudíš se musí psát __invoke.

redhead
Člen | 1313
+
0
-

nebo když už je tam invokeArgs (nebo jak to je) proč neudělat prostě invoke a pod to dát i podporu pro args, který se zjistí přes func_get_args (jako vlastně nepovinnej parametr) seskupit to do jedné metody. Hm?

Jinak přesně nad spojením __invoke do invoke jsem včera přemejšlel, invoke vypadá v kódu rozhodně líp :D

v6ak
Člen | 206
+
0
-

David Grudl: A proč by to mělo fungovat?

Filip Procházka
Moderator | 4668
+
0
-

Davide, malá chybka:)

PHP 5.2.4–2ubuntu5.9 with Suhosin-Patch 0.9.6.2 (cli) (built: Nov 26 2009 14:00:44)
Copyright © 1997–2007 The PHP Group
Zend Engine v2.2.0, Copyright © 1998–2007 Zend Technologies
with Xdebug v2.0.2, Copyright © 2002–2007, by Derick Rethans

<?php

class Trida
{

	function fce1()
	{
		return call_user_func_array(array($this, 'fce2'), func_get_args());
		//Fatal error: func_get_args(): Can't be used as a function parameter
	}

	function fce2()
	{
		var_dump(func_get_args());
	}

}


$t = new Trida();

$t->fce1('arg1', 2, 3.0);
David Grudl
Nette Core | 8212
+
0
-

Neříkám, že by to mělo fungovat, ale že se člověk psaní __invoke() stejně nevyhne a tak má smysl vytvořit alias invoke().

HosipLan napsal(a):

Davide, malá chybka:)

Na kterém řádku konkrétně?

Filip Procházka
Moderator | 4668
+
0
-

Třída callback

85: return call_user_func_array($this->cb, func_get_args());

Nette/Forms/Controls/TextBase.php – tady IMHO nemá být komentář

56: $value = (string) $filter/*->__invoke*/($value);

Nette/ServiceLocator.php – přehrocené komentáře?

139: $service = $factory/**/->__invoke/**/($options);

Nette/Templates/BaseTemplate.php – přehrocené komentáře?

126: $content = $filter/**/->__invoke/**/($content);

Nette/Templates/BaseTemplate.php – přehrocené komentáře?

193: $helper = $loader/**/->__invoke/**/($lname);

když nad tím tak přemýšlím, asi je to tam kvůli převaděči na 5.3 :)
každopádně v Nette/Templates/Filters/LatteMacros.php je nemáš

271: ->__invoke($content, $modifiers);

Nette/Web/Session.php – zase bez komentářů

87: $verKey = (string) callback($this->verificationKeyGenerator)->check('Verification key generator')->__invoke();

Tak snad jsou to všechny nejasnosti :)

Editoval HosipLan (20. 1. 2010 17:40)

David Grudl
Nette Core | 8212
+
0
-

Komentáře jsou kvůli převaděči na PHP 5.3. Zeptám se jinak: narazil jsi na nějakou chybu, nebo máš jen dojem, že je tam chyba? ;)

Filip Procházka
Moderator | 4668
+
0
-

volání

call_user_func_array($whatever, func_get_args());

způsobí fatal error, to ostatní jsou víceméně překlepy až na ten druhý kód co jsem poslal kde je invoke zakomentované úplně

//edit: Teď jsem si to prohlédl pořádně a máš pravdu s těmi komentáři, každopádně ten fatal je oveřenej :)

Editoval HosipLan (20. 1. 2010 19:49)

David Grudl
Nette Core | 8212
+
0
-

A nemáš nějakou starší verzi?

Filip Procházka
Moderator | 4668
+
0
-

vidím že už jsi to vzorově opravil :) ta verze co je na githubu teď už funguje :)

David Grudl
Nette Core | 8212
+
0
-

Vždy prosím zkus, jestli se to týká aktuální verze. Tenhle „bug“ existoval asi hodinu po prvním zveřejnění třídy Callback, což je už mimo mou rozlišovací schopnost a paměť.

Vyki
Člen | 388
+
0
-

Nechtělo by se o tom zmínit v changelogu zde na fóru? Myslím, že je to celkem vélká změna, která by stála za zmíňku :o)

Editoval Vyki (22. 1. 2010 10:54)

nAS
Člen | 277
+
0
-

Nemohla by třída Callback dostat ještě jeden atribut, kam by si ukládala předem parametry se kterými by se pak volala? Abych si mohl v jedné části aplikace nadefinovat callback i s parametry a v jiné jej spustit.

David Grudl
Nette Core | 8212
+
0
-

Tohle řeší PHP 5.3 a tzv. currying.

nAS
Člen | 277
+
0
-

Díky za info, o curryingu jsem neslyšel, rád jsem si doplnil vzdělání.

I tak mi ale přijde, že na základní věci by bylo jednodušší využít callbacků. Viz srovnání:

class MyClass
{
	public function doSomething($param)
	{
		// do something
	}

	public function foobar()
	{
		// pomoci curryingu
		$this2 = $this; // http://bugs.php.net/bug.php?id=49543
		$callback = function () use ($this2) { $this2->doSomething('param'); };

		// pomoci callbacku
		$callback = callback($this, 'doSomething')->setArgs('param');
	}
}

Je jasné, že na případy, kdy se přidávají parametry i při vytváření i při volání je mnohem praktičtější currying, ale mě v naprosté většině případů bude stačit jednoduchý callback. Krom toho je to použitelné i pro lidi s PHP 5.2.

v6ak
Člen | 206
+
0
-

Já bych byl spíše pro nějaký wrapper mimo Callback, něco jako:

<?php
function fixArgs($callback, $args){
  return function()use($callback, $args){
    return call_user_func_array($callback, $args);
  }
}
?>

Za případné chyby a nedostatky v odsazení se předem omlouvám, píšu z mobilu.
Samozřejmě, dají se vymyslet mnohé další věci jako třeba varargs místo array $args a spojování parametrů. Hlavní myšlenka ale je, že to nemusí být jako vlastnost callbacku.

jtousek
Člen | 951
+
0
-

když už něco takovýho řešit tak bych přidal třídě Callback metodu use a pak to používal takhle:

<?php
$callback = callback($object, 'method')->use($arg1, $arg2);
?>
Panda
Člen | 569
+
0
-

jtousek napsal(a):

… přidal třídě Callback metodu use…

use je klíčové slovo PHP, to bych do toho rozhodně netahal, bylo by to zavádějící. Spíš něco typu setInvokeArguments().

jakubkulhan
Člen | 55
+
0
-

Když currying, udělal bych ho tak, jak funguje ve funkcionálních jazycích jako Haskell, OCaml atp. Obal nativního callbacku by při zavolání zkontroloval, jestli dostal alespoň tolik argumentů, kolik nativní callback očekává (to by se asi muselo kontrolovat reflexí). Dostal-li by, zavolal by nativní callback a vrátil jeho návratovou hodnotu. Dostal-li by méně, vrátil by „curryfikovanou“ obálku (callback + některé z jeho argumentů). Nemá cenu podobnými hračkami zpomalovat přímo Callback, protože se to využije asi ne na moc místech. Místo toho by pro curryfikované funkce mohla být třída, řekněme, CurriedCallback.

<?php
function add($a, $b) {
    return $a + $b;
}

$add = curry('add'); // vrátí instanci CurriedCallback
$inc = $add(1); // taktéž vrátí instanci CurriedCallback, s prvním argument inicializovaným na 1

array_map($inc, array(1, 2, 3, 4, 5)); // === array(2, 3, 4, 5, 6)
jtousek
Člen | 951
+
0
-

Panda napsal(a):

jtousek napsal(a):

… přidal třídě Callback metodu use…

use je klíčové slovo PHP, to bych do toho rozhodně netahal, bylo by to zavádějící. Spíš něco typu setInvokeArguments().

jj, zapomněl jsem, samozřejmě by bylo nutné zvolit něco jiného. Ale stále si myslím, že je to nejlepší metoda.

v6ak
Člen | 206
+
0
-

Jo, ale pokud to nemá měnit stav, ale jen vytvořit obálku, pak je název začínající na „set“ poněkud zavádějící.
Jinak obálku jsem navrhoval přesně z tohoto důvodu. Kontrola argumentů je v obálce poněkud problematická, má-li fungovat na obecný callback. Navíc asi i zbytečná, protože to tu obstarává PHP, ne? Je to sice trošku leaky abstraction, ale IMHO je to to méně špatné řešení.