This is yet another post about creating tags for use in SPIP templates.
First I’ll describe how to create tags with multiple “names” (e.g. #INCLURE
and #INCLUDE
) and then I’ll build on this technique, my previous post
Dynamic tags, fake arguments, and AST mangling in
SPIP, and a pattern
used in SPIP’s code to create suites of tags with multiple similar but
slightly different functions.
As usual, you’ll need to know PHP, and the SPIP template language before this will make much sense, and I’d recommend reading my posts about creating static tags, dynamic tags, and faking tag arguments as well.
SPIP tags are just a specially named function that accepts an AST node as a parameter and returns that AST node modified so that the code generator will generate the PHP code to implement the function. With static tags this means filling the node with some PHP code that evaluates to a string (often just a string literal with, perhaps, some variable interpolation). The functions for dynamic tags, on the other hand, fill their AST node with PHP code that, when evaluated by the code generator, generates more PHP. This second piece of PHP will form part of the page template and will [potentially] be evaluated at each request.
Different but same
It’s sometimes desirable to make certain tags available under more than one
name. The canonical example of this from SPIP’s built-in tags is
#INCLURE
/#INCLUDE
. Rather than provide just the French #INCLURE
tag or
just the English (and PHP-ish) #INCLUDE
, SPIP provides both. This can help
make your API more user friendly and is easy enough to do: just define a
second tag function that calls the first (notice that I check for an
overriding version of my original tag implementation and call that instead):
/* The original tag */
function balise_FOO_dist($p) {
$p->code = "'This is foo'";
return $p;
}
/* The additional name */
function balise_BAR_dist($p) {
if ( function_exists('balise_FOO') ) {
return balise_FOO($p);
else {
} return balise_FOO_dist($p);
} }
Similar but different
If you’re looking for identical functionality under a different name (see
#INCLURE and #INCLUDE), this is all you need, but imagine that you,
like me, have a number of tags with related but slightly different
functionality. You could implement each of them – copy-and-pasting the code
and changing perhaps a single variable – or extract the common code into a
set of library functions – slowing things down slightly – or you could
implement a generic tag and a number of wrappers around it. SPIP does this in
a number of places, but the best example is the #ENV
, #CONFIG
and #GET
tags, each of which are actually implemented by the same piece of code (the
balise_ENV_dist
function, as it turns out).
This is easily accomplished by adding a second, optional, parameter to your
main tag function and then passing different values from your stub tags. This
is the approach that #ENV
, #CONFIG
, and #GET
use:
balise_ENV_dist
check the environment by default, but if it’s passed a
second parameter (the '$GLOBALS["meta"]'
that #CONFIG
passes it, for
example), then it operates on that instead.
Mangling for fun and profit
A second, but rather more involved, option for passing values into other tags
is described in my faking tag
arguments post. This
can be useful when you don’t control the code that you are wrapping. Perhaps
you want to modify the #MODELE
tag in such a way that it adds the name and
line number of the tag to the environment of the model it calls. Rather than
duplicating the code for balise_MODELE_dist
and modifying it slightly, you
could write a wrapper around it balise_MODELE
and just add another two
parameters (skeleton
and line
) to the AST before calling
balise_MODELE_dist
. As far as balise_MODEL_dist
is concerned, it’s as
though some conscientious web-master has gone through every template adding
{skeleton=filename}{line=123}
to every call.
The difficult bit is to modify the AST correctly, but that’s not too hard and you can read the post to find out how.
Conclusion
This is a powerful technique that can simplify the code for plug-ins with large numbers of similar tags immensely. Rather than producing large amounts of “boiler-plate” code, or defining large libraries of API functions, I can just reuse my existing generic code in ways that hide it’s complexity and power, thereby making the API much more simple and obvious to my users.