If I create a tag extension like this:
$parser->setHook('foobar', 'myCallback'); function myCallback($input, $args, $parser, $frame) { return 'hello world'; }
can the callback "myCallback" efficiently detect the name of the parser tag, "foobar", that invoked it?
The business problem is this: I use the same callback with 20 different tag names, and I'd like the behavior to change slightly depending on which tag name was used. (The tag names are the names of database servers in my company, and the callback accesses the particular database.)
Right now I am using a very inefficient solution: dynamically creating 20 callbacks (one for each tag name), each with slightly customized behavior. I'd rather do it with one callback.
I realize this would be easy if I'd used a single tag name plus a variable argument, but it's too late to make that change. (The tags have been used 10,000 times and are widely known in our company.)
Thank you very much. DanB
ps: I asked this a few years ago, when the answer was "no," but maybe things have changed by now.....
I don't know if you can access that data directly from the extension callback, but you can certainly wire it in without *too* bad a hack:
foreach( array( 'foo', 'bar', 'baz', quok' ) as $var ){ $parser->setHook( $var, "WrapperClass::myCallback_$var" ); }
class WrapperClass { function __callStatic( $fname, $args ){ list( $junk, $var ) = explode( '_', $fname ); $args[] = $var; return call_user_func_array( 'myCallback', $args ); } }
function myCallback( $input, $args, $parser, $frame, $var ) { return 'hello world'; }
It's pretty obviously a retrofitted design change, but it's fairly robust, especially if you keep the first two bits of code together...
--HM
On 21 December 2011 15:29, Daniel Barrett danb@vistaprint.com wrote:
If I create a tag extension like this:
$parser->setHook('foobar', 'myCallback'); function myCallback($input, $args, $parser, $frame) { return 'hello world'; }
can the callback "myCallback" efficiently detect the name of the parser tag, "foobar", that invoked it?
The business problem is this: I use the same callback with 20 different tag names, and I'd like the behavior to change slightly depending on which tag name was used. (The tag names are the names of database servers in my company, and the callback accesses the particular database.)
Right now I am using a very inefficient solution: dynamically creating 20 callbacks (one for each tag name), each with slightly customized behavior. I'd rather do it with one callback.
I realize this would be easy if I'd used a single tag name plus a variable argument, but it's too late to make that change. (The tags have been used 10,000 times and are widely known in our company.)
Thank you very much. DanB
ps: I asked this a few years ago, when the answer was "no," but maybe things have changed by now.....
Wikitech-l mailing list Wikitech-l@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/wikitech-l
On Wed, Dec 21, 2011 at 10:47 AM, Happy Melon happy.melon.wiki@gmail.com wrote:
I don't know if you can access that data directly from the extension callback, but you can certainly wire it in without *too* bad a hack:
foreach( array( 'foo', 'bar', 'baz', quok' ) as $var ){ $parser->setHook( $var, "WrapperClass::myCallback_$var" ); }
class WrapperClass { function __callStatic( $fname, $args ){ list( $junk, $var ) = explode( '_', $fname ); $args[] = $var; return call_user_func_array( 'myCallback', $args ); } }
function myCallback( $input, $args, $parser, $frame, $var ) { return 'hello world'; }
It's pretty obviously a retrofitted design change, but it's fairly robust, especially if you keep the first two bits of code together...
Of course this would only work in 5.3+
-Chad
Happy Melon suggests:
foreach( array( 'foo', 'bar', 'baz', quok' ) as $var ){ $parser->setHook( $var, "WrapperClass::myCallback_$var" ); } ...
Thanks for the suggestion. I am already doing something similar (see my original note about "dynamically creating 20 callbacks" today), but it's inefficient when only 1 tag is actually used on the page, which is 99% of the time. (And actually my number "20" is really more like "75".) I'm hoping for a technique that creates only the callback I need on that page. Hence, the desire to know the name of the tag that called me.
I was thinking of suggesting a core code change that adds the tag name as one of the args of the callback function (e.g., $args['_MW_TAG_NAME_']), but that might break any tag extensions that expect a certain count($args).
I'm still hoping that $parser->getMyTagNameThatCalledMe() function exists somewhere.....?
DanB
How inefficient is it, exactly? Is there a measurable performance impact?
My recommendation under ideal circumstances would be to use closures (needs PHP 5.3) to wrap a parameter to your real callback; on older versions you could use create_function which is ugly, but probably not much worse for performance.
-- brion On Dec 21, 2011 8:48 AM, "Daniel Barrett" danb@vistaprint.com wrote:
Happy Melon suggests:
foreach( array( 'foo', 'bar', 'baz', quok' ) as $var ){ $parser->setHook( $var, "WrapperClass::myCallback_$var" ); } ...
Thanks for the suggestion. I am already doing something similar (see my original note about "dynamically creating 20 callbacks" today), but it's inefficient when only 1 tag is actually used on the page, which is 99% of the time. (And actually my number "20" is really more like "75".) I'm hoping for a technique that creates only the callback I need on that page. Hence, the desire to know the name of the tag that called me.
I was thinking of suggesting a core code change that adds the tag name as one of the args of the callback function (e.g., $args['_MW_TAG_NAME_']), but that might break any tag extensions that expect a certain count($args).
I'm still hoping that $parser->getMyTagNameThatCalledMe() function exists somewhere.....?
DanB
Wikitech-l mailing list Wikitech-l@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/wikitech-l
Brion Vibber wrote:
My recommendation under ideal circumstances would be to use closures (needs PHP 5.3) to wrap a parameter to your real callback; on older versions you could use create_function which is ugly, but probably not much worse for performance.
Thanks Brion. I have switched from create_function (our old implementation) to closures and it's certainly less ugly now. I still wish I could create only the single callback needed, instead of 70 callbacks every single time one tag is used, but I don't have actual measurements to know if this is a noticeable performance drag. It just offends the computer scientist in me. :-)
DanB
On Thu, Dec 22, 2011 at 10:53 AM, Daniel Barrett danb@vistaprint.comwrote:
Brion Vibber wrote:
My recommendation under ideal circumstances would be to use closures
(needs
PHP 5.3) to wrap a parameter to your real callback; on older versions you could use create_function which is ugly, but probably not much worse for performance.
Thanks Brion. I have switched from create_function (our old implementation) to closures and it's certainly less ugly now. I still wish I could create only the single callback needed, instead of 70 callbacks every single time one tag is used, but I don't have actual measurements to know if this is a noticeable performance drag. It just offends the computer scientist in me. :-)
:D
Well, the way to do just one is to use the __call or __callStatic magic like in HappyMelon's recommendation -- that way you don't actually have to make 70 methods, you only need to make one and the other part of the system *thinks* you have 70 methods. ;)
-- brion
On 21/12/11 17:47, Daniel Barrett wrote:
Happy Melon suggests:
foreach( array( 'foo', 'bar', 'baz', quok' ) as $var ){ $parser->setHook( $var, "WrapperClass::myCallback_$var" ); } ...
Thanks for the suggestion. I am already doing something similar (see my original note about "dynamically creating 20 callbacks" today), but it's inefficient when only 1 tag is actually used on the page, which is 99% of the time. (And actually my number "20" is really more like "75".) I'm hoping for a technique that creates only the callback I need on that page. Hence, the desire to know the name of the tag that called me.
I was thinking of suggesting a core code change that adds the tag name as one of the args of the callback function (e.g., $args['_MW_TAG_NAME_']), but that might break any tag extensions that expect a certain count($args).
I'm still hoping that $parser->getMyTagNameThatCalledMe() function exists somewhere.....?
DanB
No. $name is not passed to the hook call. The only -and fragile- way to retrieve it would be something like:
$debug = wfDebugBacktrace(1); if (count($debug)) { $name = $frame->expand( $debug[1]['args'][0] ); } else { $name = 'no name'; }
wikitech-l@lists.wikimedia.org