[Xapian-discuss] Another PHP 5 wrapper...
Daniel Ménard
Daniel.Menard at Bdsp.tm.fr
Tue Apr 18 18:33:18 BST 2006
[Olly, Paul, Sam : please forgive me for being absent, I was not able to
follow this thread as i wished...]
Olly Betts a écrit :
>And here's the wrapper file it produces for Xapian
>http://www.oligarchy.co.uk/xapian/patches/xapian.phps
>
>
This is great news!
I looked at the generated wrapper and made some quick tests : it looks
very nice, and it works !
You worked so fast that I'm going to ask more ;-)
>I've used Paul's ideas for coercing parameters and return values, except
>that I'm generating wrapper methods so I can skip doing that when I know
>it won't be needed (currently the check errs on the side of coercing
>when it sometimes isn't needed which doesn't affect correctness, but
>just adds a small overhead).
>
>
It is probably a good thing to generate wrapper methods and not using
the "__call" magic function of php, because it seems to add a
substantial overhead.
I made some quick performance tests (just repeatedly adding some data
and postings to a new document), comparing native calls to the xapian
library with the use of the SWIG's generated wrapper, Paul's one and mine.
Test code is here :
http://www.bdsp.tm.fr/aed/xapian/call_test.php.txt
On my machine, results are as follows :
- using native calls : 8,88 seconds
- using my own wrapper : 13,88 sec
- using Paul's wrapper : 64,12 sec
- using the wrapper generated by SWIG : 37,54 sec.
I think that the difference between the 3 wrappers is only due to the
use of __call, the coercion of parameters, and the use of
call_user_func_array.
SWIG's wrapper don't use __call, but it seems that using
func_get_args/array_walk_recursive/array_unshift/call_user_func_array
for each call still takes time.
Sam, in a previous mail, suggested that it would be possible to do the
coercion of params directly in the generated C code, but I was wondering
if it would be possible to do most of the work during the generation.
I saw in your patch that we have access to all the parameters of the
function we wrap. Perhaps it would be possible to write all the
parameters of the generated PHP method and then do a direct (real) call
to the flat Xapian function, coercizing (?) on the fly all the
parameters for which it is necessary. (I'm not a "C" person, and I don't
know SWIG at all, so forgive me if it is just stupid !)
So instead of having (line 1045 of patched php4.cxx)
Printf(s_oowrappers, "\tpublic function %s() {\n", methodname);
we would have something like :
Printf(s_oowrappers, "\tpublic function %s(", methodname);
p=l; // l is ParmList *l = Getattr(n,"parms");
while(p)
{
if (p!=l) Printf(s_oowrappers, ", ");
Printf(s_oowrappers, p->name);
// Printf(s_oowrappers, "%s %s", p->type, p->name); // php5
only, add type hints for parameters
p=nextSibling(p);
}
Printf(s_oowrappers, ") {\n");
and then, instead of having (lines 1062 and others)
Printf(s_oowrappers, "\t\t$a=func_get_args();\n");
Printf(s_oowrappers,
"\t\tarray_walk_recursive($a,'swig_coerce_param');\n");
Printf(s_oowrappers, "\t\tarray_unshift($a,$this->swig_ptr);\n");
Printf(invoke, "call_user_func_array('%s',$a)", iname);
we would have another loop doing something similar to :
Printf(invoke, "%s($this->swig_ptr", iname);
p=l;
while(p)
{
Printf(invoke, ", ");
if (p->type == T_USER)
{
Printf(invoke,"%s->swig_ptr", p->name);
}
else
{
Printf(invoke, p->name);
}
p=nextSibling(p);
}
Printf(invoke, ")");
I am pretty sure that it would be more complicated than that (handling
parameters default values, for example), but there would be some benefits :
- generated code would be cleaner (just direct calls to the Xapian
functions)
- probably the same speed as my original wrapper
- methods arguments are "documented" (I mean : they appear in the method
signature), so one don't have to look at the doc to guess the parms, and
it allows auto-completion in IDEs
>(in a response to Sam about swig_coerce_param) It's also nice that we can
>automatically do it recursively in PHP - this means that the
>typemap for constructing a Query from a PHP array of Query
>objects just works without having to rewrite it.
>
>
My pseudo solution above won't handle that, except if we add a special
case for that (if arg is an array, walk the array to detect things to
coerce and create a new array). But do we really need it ? only
new_query uses this (?), and it seems to me that it is already very
flexible...
>I'm not renaming methods to lowerCamelCase yet (I've written and
>submitted a patch to add support for this to SWIG so it's easy enough to
>do once that gets applied).
>
>
This is another good news !
Perhaps that it should be a swig option (on : camelize method names, off
: use method names "as-is")
>I could easily wrap non-member functions (...).
>
>Daniel wrapped the functions and constants in a Xapian class in his OO
>bindings, and it does means everything we wrap is in a pseudo namespace,
>which is cleaner.
>
>
I won't be against this solution ;-), but here is two (less clean in my
opinion) alternatives:
- add the static methods to the XapianSwigBase class (they will be
available in all classes... more confusing than usefull)
- if we use camelCase, functions names don't conflict anymore with flat
names
>SWIG's Python backend allows you to generate pydoc
>strings (either automatically, or specified explicitly, or a mix of
>the two). There's no good reason why the PHP backend couldn't too.
>
>
It would be fantastic to have the same thing for PHP, but does it mean
that you have to "duplicate" the doc in xapian.i or something like that
? I guess it can raise a maintenance issue (but I had the same problem
in my wrapper).
>(in a previous mail)
>Is there a way we can write code which uses Countable for PHP 5.1 and
>later but not on 5.0?
>
>
I think that we can all live with a wrapper without the "Countable"
interface...
Using it and supporting both php 5.0 and php 5.1 would require to add
something like this in the wrapper :
if (! interface_exists('Countable'))
{
interface Countable{};
}
But PHP function 'interface_exists' was only added in php 5.0.2, so it
is not a perfect solution.
Some additional remarks about the current wrapper :
I think we should avoid, if we can, having global functions or constants
in the generated wrapper (avoiding "global namespace pollution") :
- I am not sure that the "$XAPIAN_LOADED__" stuff at the top of the file
is really necessary (php has function like include_once or
require_once), but perhaps it is automatically generated by SWIG ?
- in Paul's wrapper, functions like swig_coerce_param and
swig_wrap_return were members of the base class. I think it is a better
solution than putting them global.
The PHP function "is_a" (used in coerce_param) is deprecated in PHP5 ans
is replace by "instanceof".
if ($value instanceof XapianSwigBase) $value=$value->swig_ptr;
Please note that, except that, your generated wrapper doesn't have any
warning, even in E_STRICT mode.
You really did a great job !
Oh, by the way, congratulations for the 0.9.5 release ! I didn't have
time to test, but I will, believe me ;-)
Best regards,
--
Daniel Ménard
Banque de Données Santé Publique
Avenue du Professeur Léon Bernard
35043 Rennes Cédex
Tél. (+33) 2.99.02.29.42
Fax (+33) 2.99.02.26.28
E-mail Daniel.Menard at Bdsp.tm.fr
http://www.bdsp.tm.fr
More information about the Xapian-discuss
mailing list