From 115b18fc6f4a245d415584090734cd43b52a6f6a Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 5 Aug 2008 14:52:56 +0000 Subject: Move all files related to the php5 binding inside the php5 subdirectory. --- bindings/php5/__init__.py | 0 bindings/php5/lang.py | 44 ++++ bindings/php5/php_code.py | 477 +++++++++++++++++++++++++++++++++++++ bindings/php5/wrapper_header.py | 62 +++++ bindings/php5/wrapper_source.py | 468 ++++++++++++++++++++++++++++++++++++ bindings/php5/wrapper_source_top.c | 319 +++++++++++++++++++++++++ 6 files changed, 1370 insertions(+) create mode 100644 bindings/php5/__init__.py create mode 100644 bindings/php5/lang.py create mode 100644 bindings/php5/php_code.py create mode 100644 bindings/php5/wrapper_header.py create mode 100644 bindings/php5/wrapper_source.py create mode 100644 bindings/php5/wrapper_source_top.c (limited to 'bindings/php5') diff --git a/bindings/php5/__init__.py b/bindings/php5/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bindings/php5/lang.py b/bindings/php5/lang.py new file mode 100644 index 00000000..fbed5d47 --- /dev/null +++ b/bindings/php5/lang.py @@ -0,0 +1,44 @@ +# Lasso - A free implementation of the Liberty Alliance specifications. +# +# Copyright (C) 2004-2007 Entr'ouvert +# http://lasso.entrouvert.org +# +# Authors: See AUTHORS file in top-level directory. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import os +from wrapper_source import WrapperSource +from wrapper_header import WrapperHeader +from php_code import PhpCode + +class Binding: + def __init__(self, binding_data): + self.binding_data = binding_data + + def generate(self): + fd = open('_lasso.c', 'w') + wrapper_source = WrapperSource(self.binding_data, fd) + wrapper_source.generate() + fd.close() + + fd = open('php_lasso.h', 'w') + WrapperHeader(self.binding_data, fd, wrapper_source.functions_list).generate() + fd.close() + + fd = open('lasso.php', 'w') + PhpCode(self.binding_data, fd).generate() + fd.close() + diff --git a/bindings/php5/php_code.py b/bindings/php5/php_code.py new file mode 100644 index 00000000..8b8eef84 --- /dev/null +++ b/bindings/php5/php_code.py @@ -0,0 +1,477 @@ +# Lasso - A free implementation of the Liberty Alliance specifications. +# +# Copyright (C) 2004-2007 Entr'ouvert +# http://lasso.entrouvert.org +# +# Authors: See AUTHORS file in top-level directory. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import re +import sys + +import utils + +class PhpCode: + def __init__(self, binding_data, fd): + self.binding_data = binding_data + self.fd = fd + + def is_object(self, t): + return t not in ['char*', 'const char*', 'gchar*', 'const gchar*', 'GList*', 'GHashTable*', + 'xmlNode*', 'int', 'gint', 'gboolean', 'const gboolean'] + self.binding_data.enums + + def generate(self): + self.generate_header() + for klass in self.binding_data.structs: + self.generate_class(klass) + self.generate_exceptions() + self.generate_footer() + + def generate_header(self): + print >> self.fd, '''\ +_cptr = $cptr; + return $obj; + } + return null; +} + +function lassoGetRequestTypeFromSoapMsg($mesg) { + return lasso_get_request_type_from_soap_msg($mesg); +} + +function lassoRegisterIdWsf2DstService($prefix, $href) { + lasso_register_idwsf2_dst_service($prefix, $href); +} +''' + + def generate_class(self, klass): + class_name = klass.name + + if klass.parent != 'GObject': + inheritence = ' extends %s' % klass.parent + else: + inheritence = '' + + print >> self.fd, '/**' + print >> self.fd, ' * @package Lasso' + print >> self.fd, ' */' + print >> self.fd, 'class %(class_name)s%(inheritence)s {' % locals() + + if klass.members or klass.methods: + self.generate_constructors(klass) + self.generate_getters_and_setters(klass) + self.generate_methods(klass) + + print >> self.fd, '}' + print >> self.fd, '' + + # Add a special class to get an object instance without initialising + print >> self.fd, '/**' + print >> self.fd, ' * @package Lasso' + print >> self.fd, ' */' + print >> self.fd, 'class %(class_name)sNoInit extends %(class_name)s {' % locals() + print >> self.fd, ' public function __construct() {}' + print >> self.fd, '}' + print >> self.fd, '' + + def generate_constructors(self, klass): + method_prefix = utils.format_as_underscored(klass.name) + '_' + for m in self.binding_data.functions: + if m.name == method_prefix + 'new': + php_args = [] + c_args = [] + for arg in m.args: + arg_type, arg_name, arg_options = arg + if arg_options.get('optional'): + php_args.append('$%s = null' % arg_name) + else: + php_args.append('$%s' % arg_name) + + if self.is_object(arg_type): + c_args.append('$%s->_cptr' % arg_name) + else: + c_args.append('$%s' % arg_name) + + php_args = ', '.join(php_args) + c_args = ', '.join(c_args) + # XXX: could check $this->_cptr->typename to see if it got the + # right class type + print >> self.fd, ' public $_cptr = null;' + print >> self.fd, '' + print >> self.fd, ' public function __construct(%s) {' % php_args + print >> self.fd, ' $this->_cptr = %s(%s);' % (m.name, c_args) + print >> self.fd, ' if (is_null($this->_cptr)) { throw new Exception("Constructor for ', klass.name, ' failed "); }' + print >> self.fd, ' }' + print >> self.fd, '' + + if m.name == method_prefix + 'new_from_dump': + if len(m.args) == 1: + print >> self.fd, ' public static function newFromDump($dump) {' + print >> self.fd, ' return cptrToPhp(%s($dump));' % m.name + else: + print >> self.fd, ' public static function newFromDump($server, $dump) {' + print >> self.fd, ' return cptrToPhp(%s($server->_cptr, $dump));' % m.name + # XXX: Else throw an exception + print >> self.fd, ' }' + print >> self.fd, '' + elif m.name == method_prefix + 'new_full': + pass + + def generate_getters_and_setters(self, klass): + + # FIXME: handle objects and GLists + + # Generic getter + print >> self.fd, ' /**' + print >> self.fd, ' * @return mixed' + print >> self.fd, ' */' + print >> self.fd, ' public function __get($attr) {' + print >> self.fd, ' $func = "get_" . $attr;' + print >> self.fd, ' if (method_exists($this, $func)) {' + print >> self.fd, ' return call_user_func(array($this, $func));' + print >> self.fd, ' }' + print >> self.fd, ' return null;' + print >> self.fd, ' }' + print >> self.fd, '' + + # Generic setter + print >> self.fd, ' public function __set($attr, $value) {' + print >> self.fd, ' $func = "set_" . $attr;' + print >> self.fd, ' if (method_exists($this, $func)) {' + print >> self.fd, ' call_user_func(array($this, $func), $value);' + print >> self.fd, ' }' + print >> self.fd, ' }' + print >> self.fd, '' + + for m in klass.members: + mtype = m[0] + mname = utils.format_as_camelcase(m[1]) + options = m[2] + + # Getters + print >> self.fd, ' /**' + print >> self.fd, ' * @return %s' % self.get_docstring_return_type(mtype) + print >> self.fd, ' */' + print >> self.fd, ' protected function get_%s() {' % mname + if self.is_object(mtype): + print >> self.fd, ' return cptrToPhp(%s_%s_get($this->_cptr));' % ( + klass.name, mname) + elif mtype in ('GList*', 'GHashTable*'): + print >> self.fd, ' $array = %s_%s_get($this->_cptr);' % (klass.name, mname) + if self.is_object(options.get('elem_type')): + print >> self.fd, ' $obj_array = array();' + if mtype == 'GList*': + print >> self.fd, ' foreach ($array as $item) {' + print >> self.fd, ' $obj_array[] = cptrToPhp($item);' + else: + print >> self.fd, ' foreach ($array as $key => $item) {' + print >> self.fd, ' $obj_array[$key] = cptrToPhp($item);' + print >> self.fd, ' }' + print >> self.fd, ' $array = $obj_array;' + print >> self.fd, ' return $array;' + else: + print >> self.fd, ' return %s_%s_get($this->_cptr);' % (klass.name, mname) + print >> self.fd, ' }' + + # Setters + print >> self.fd, ' protected function set_%s($value) {' % mname + if self.is_object(mtype): + print >> self.fd, ' %s_%s_set($this->_cptr, $value->_cptr);' % (klass.name, mname) + elif mtype in ('GList*', 'GHashTable*') and self.is_object(options.get('elem_type')): + print >> self.fd, ' $array = array();' + # FIXME: setting an array to NULL should really set it to NULL and not to an empty array + print >> self.fd, ' if (!is_null($value)) {' + if mtype == 'GList*': + print >> self.fd, ' foreach ($value as $item) {' + print >> self.fd, ' $array[] = $item->_cptr;' + else: + print >> self.fd, ' foreach ($value as $key => $item) {' + print >> self.fd, ' $array[$key] = $item->_cptr;' + print >> self.fd, ' }' + print >> self.fd, ' }' + print >> self.fd, ' %s_%s_set($this->_cptr, $array);' % (klass.name, mname) + else: + print >> self.fd, ' %s_%s_set($this->_cptr, $value);' % (klass.name, mname) + print >> self.fd, ' }' + print >> self.fd, '' + + + def generate_methods(self, klass): + methods = klass.methods[:] + + # first pass on methods, removing accessors + for m in klass.methods: + if m.rename: + meth_name = m.rename + else: + meth_name = m.name + if not ('_get_' in meth_name and len(m.args) == 1): + continue + methods.remove(m) + try: + setter_name = meth_name.replace('_get_', '_set_') + setter = [x for x in methods if x.name == setter_name][0] + methods.remove(setter) + except IndexError: + setter = None + mname = re.match(r'lasso_.*_get_(\w+)', meth_name).group(1) + mname =utils.format_as_camelcase(mname) + + print >> self.fd, ' /**' + print >> self.fd, ' * @return %s' % self.get_docstring_return_type(m.return_type) + print >> self.fd, ' */' + print >> self.fd, ' protected function get_%s() {' % mname + if self.is_object(m.return_type): + print >> self.fd, ' $cptr = %s($this->_cptr);' % meth_name + print >> self.fd, ' if (! is_null($cptr)) {' + print >> self.fd, ' return cptrToPhp($cptr);' + print >> self.fd, ' }' + print >> self.fd, ' return null;' + else: + print >> self.fd, ' return %s($this->_cptr);' % meth_name + print >> self.fd, ' }' + if setter: + print >> self.fd, ' protected function set_%s($value) {' % mname + if self.is_object(m.return_type): + print >> self.fd, ' %s($this->_cptr, $value->_cptr);' % setter.name + else: + print >> self.fd, ' %s($this->_cptr, $value);' % setter.name + print >> self.fd, ' }' + print >> self.fd, '' + + # second pass on methods, real methods + method_prefix = utils.format_as_underscored(klass.name) + '_' + for m in methods: + if m.name.endswith('_new') or m.name.endswith('_new_from_dump') or \ + m.name.endswith('_new_full'): + continue + if not m.name.startswith(method_prefix): + print >> sys.stderr, 'W:', m.name, 'vs', method_prefix + continue + + if m.rename: + mname = m.rename + else: + mname = m.name + cname = mname + mname = mname[len(method_prefix):] + php_args = [] + c_args = [] + for arg in m.args[1:]: + arg_type, arg_name, arg_options = arg + arg_name = '$' + arg_name + if arg_options.get('optional'): + if arg_options.get('default'): + defval = arg_options.get('default') + if defval.startswith('c:'): # constant + php_args.append('%s = %s' % (arg_name, defval[2:])) + elif defval.startswith('b:'): # boolean + php_args.append('%s = %s' % (arg_name, defval[2:])) + else: + print >> sys.stderr, "E: don't know what to do with %s" % defval + sys.exit(1) + else: + php_args.append('%s = null' % arg_name) + else: + php_args.append(arg_name) + if arg_type in ('char*', 'const char*', 'gchar*', 'const gchar*') or \ + arg_type in ['int', 'gint', 'gboolean', 'const gboolean'] or \ + arg_type in self.binding_data.enums: + c_args.append(arg_name) + else: + c_args.append('%s._cptr' % arg_name) + + if php_args: + php_args = ', '.join(php_args) + else: + php_args = '' + if c_args: + c_args = ', ' + ', '.join(c_args) + else: + c_args = '' + + if m.docstring: + print >> self.fd, self.generate_docstring(m, mname, 4) + print >> self.fd, ' public function %s(%s) {' % ( + utils.format_underscore_as_camelcase(mname), php_args) + if m.return_type == 'void': + print >> self.fd, ' %s($this->_cptr%s);' % (cname, c_args) + elif m.return_type in ('gint', 'int'): + print >> self.fd, ' $rc = %s($this->_cptr%s);' % (cname, c_args) + print >> self.fd, ' if ($rc == 0) {' + print >> self.fd, ' return 0;' + print >> self.fd, ' } else if ($rc > 0) {' # recoverable error + print >> self.fd, ' return $rc;' + print >> self.fd, ' } else if ($rc < 0) {' # unrecoverable error + print >> self.fd, ' LassoError::throw_on_rc($rc);' + print >> self.fd, ' }' + else: + print >> self.fd, ' return %s($this->_cptr%s);' % (cname, c_args) + print >> self.fd, ' }' + print >> self.fd, '' + + print >> self.fd, '' + + def generate_docstring(self, func, method_name, indent): + docstring = func.docstring.orig_docstring + if func.args: + first_arg_name = func.args[0][1] + else: + first_arg_name = None + + def rep(s): + type = s.group(1)[0] + var = s.group(1)[1:] + if type == '#': # struct + return var + elif type == '%': # %TRUE, %FALSE + if var in ('TRUE', 'FALSE'): + return var + print >> sys.stderr, 'W: unknown docstring thingie: %s' % s.group(1) + elif type == '@': + if var == first_arg_name: + return '$this' + else: + return '$' + var + return s.group(1) + + lines = [] + for l in docstring.splitlines(): + if l.strip() and not lines: + continue + lines.append(l) + s = indent * ' ' + '/**\n' + s += '\n'.join([indent * ' ' + ' * ' + x for x in lines[1:]]) + s += '\n' + indent * ' ' + ' */' + regex = re.compile(r'([\#%@]\w+)', re.DOTALL) + s = regex.sub(rep, s) + s = s.replace('Return value: ', '@return %s ' % self.get_docstring_return_type(func.return_type)) + return s + + def get_docstring_return_type(self, return_type): + if return_type == None: + return '' + elif return_type == 'gboolean': + return 'boolean' + elif return_type in ['int', 'gint'] + self.binding_data.enums: + return 'int' + elif return_type in ('char*', 'gchar*', 'const char*', 'const gchar*', 'xmlNode*'): + return 'string' + elif return_type in ('GList*', 'GHashTable*'): + return 'array' + else: + # Objects + return return_type.replace('*', '') + + def generate_exceptions(self): + done_cats = [] + + for exc_cat in self.binding_data.overrides.findall('exception/category'): + cat = exc_cat.attrib.get('name') + done_cats.append(cat) + parent_cat = exc_cat.attrib.get('parent', '') + print >> self.fd, '''\ +/** + * @package Lasso + */ +class Lasso%sError extends Lasso%sError {} +''' % (cat, parent_cat) + + exceptions_dict = {} + + for c in self.binding_data.constants: + m = re.match(r'LASSO_(\w+)_ERROR_(.*)', c[1]) + if not m: + continue + cat, detail = m.groups() + cat = cat.title().replace('_', '') + detail = (cat + '_' + detail).title().replace('_', '') + if not cat in done_cats: + done_cats.append(cat) + for exc_cat in self.binding_data.overrides.findall('exception/category'): + if exc_cat.attrib.get('name') == cat: + parent_cat = exc_cat.attrib.get('parent') + break + else: + parent_cat = '' + + print >> self.fd, '''\ +/** + * @package Lasso + */ +class Lasso%sError extends Lasso%sError {} +''' % (cat, parent_cat) + + if detail not in exceptions_dict: + print >> self.fd, '''\ +/** + * @package Lasso + */ +class Lasso%sError extends Lasso%sError { + protected $code = %s; +} +''' % (detail, cat, c[1]) + exceptions_dict[detail] = c[1] + + print >> self.fd, '''\ +/** + * @package Lasso + */ +class LassoError extends Exception { + private static $exceptions_dict = array(''' + + for k, v in exceptions_dict.items(): + print >> self.fd, ' %s => "Lasso%sError",' % (v, k) + + print >> self.fd, '''\ + ); + + public static function throw_on_rc($rc) { + $exception = self::$exceptions_dict[$rc]; + if (! class_exists($exception)) { + $exception = "LassoError"; + } + throw new $exception(strError($rc), $rc); + } +} +''' + + def generate_footer(self): + print >> self.fd, '''\ +?>''' + diff --git a/bindings/php5/wrapper_header.py b/bindings/php5/wrapper_header.py new file mode 100644 index 00000000..3cf895af --- /dev/null +++ b/bindings/php5/wrapper_header.py @@ -0,0 +1,62 @@ +# Lasso - A free implementation of the Liberty Alliance specifications. +# +# Copyright (C) 2004-2007 Entr'ouvert +# http://lasso.entrouvert.org +# +# Authors: See AUTHORS file in top-level directory. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +class WrapperHeader: + def __init__(self, binding_data, fd, functions_list): + self.binding_data = binding_data + self.fd = fd + self.functions_list = functions_list + + def generate(self): + self.generate_header() + self.generate_functions_list() + self.generate_footer() + + def generate_header(self): + # FIXME: Get the current version and name + print >> self.fd, '''\ +/* this file has been generated automatically; do not edit */ + +#ifndef PHP_LASSO_H +#define PHP_LASSO_H 1 + +#define PHP_LASSO_EXTNAME "lasso" +#define PHP_LASSO_VERSION "2.1.1" + +#define PHP_LASSO_SERVER_RES_NAME "Lasso Server" + +PHP_MINIT_FUNCTION(lasso); +PHP_MSHUTDOWN_FUNCTION(lasso); +''' + + def generate_functions_list(self): + for m in self.functions_list: + print >> self.fd, 'PHP_FUNCTION(%s);' % m + print >> self.fd, '' + + def generate_footer(self): + print >> self.fd, '''\ +extern zend_module_entry lasso_module_entry; +#define phpext_lasso_ptr &lasso_module_entry + +#endif +''' + diff --git a/bindings/php5/wrapper_source.py b/bindings/php5/wrapper_source.py new file mode 100644 index 00000000..8279d3db --- /dev/null +++ b/bindings/php5/wrapper_source.py @@ -0,0 +1,468 @@ +# Lasso - A free implementation of the Liberty Alliance specifications. +# +# Copyright (C) 2004-2007 Entr'ouvert +# http://lasso.entrouvert.org +# +# Authors: See AUTHORS file in top-level directory. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import sys +import os + +import utils + +class WrapperSource: + def __init__(self, binding_data, fd): + self.binding_data = binding_data + self.fd = fd + self.functions_list = [] + self.src_dir = os.path.dirname(__file__) + + def is_object(self, t): + return t not in ['char*', 'const char*', 'gchar*', 'const gchar*', 'GList*', 'GHashTable*', + 'xmlNode*', 'int', 'gint', 'gboolean', 'const gboolean'] + self.binding_data.enums + + def generate(self): + self.generate_header() + self.generate_constants() + self.generate_middle() + for m in self.binding_data.functions: + self.generate_function(m) + for c in self.binding_data.structs: + self.generate_members(c) + for m in c.methods: + self.generate_function(m) + self.generate_functions_list() + self.generate_footer() + + def generate_header(self): + self.functions_list.append('lasso_get_object_typename') + self.functions_list.append('lasso_init') + self.functions_list.append('lasso_shutdown') + + print >> self.fd, '''\ +/* this file has been generated automatically; do not edit */ +''' + + print >> self.fd, open(os.path.join(self.src_dir,'wrapper_source_top.c')).read() + + for h in self.binding_data.headers: + print >> self.fd, '#include <%s>' % h + print >> self.fd, '' + + print >> self.fd, '''\ +PHP_MINIT_FUNCTION(lasso) +{ + le_lasso_server = zend_register_list_destructors_ex(php_gobject_generic_destructor, NULL, PHP_LASSO_SERVER_RES_NAME, module_number); + lasso_init(); +''' + + def generate_constants(self): + print >> self.fd, ' /* Constants (both enums and defines) */' + for c in self.binding_data.constants: + if c[0] == 'i': + print >> self.fd, ' REGISTER_LONG_CONSTANT("%s", %s, CONST_CS|CONST_PERSISTENT);' % (c[1], c[1]) + elif c[0] == 's': + print >> self.fd, ' REGISTER_STRING_CONSTANT("%s", %s, CONST_CS|CONST_PERSISTENT);' % (c[1], c[1]) + elif c[0] == 'b': + print >> self.fd, '''\ +#ifdef %s + REGISTER_LONG_CONSTANT("%s", 1, CONST_CS|CONST_PERSISTENT); +#else + REGISTER_LONG_CONSTANT("%s", 0, CONST_CS|CONST_PERSISTENT); +#endif''' % (c[1], c[1], c[1]) + else: + print >> sys.stderr, 'E: unknown constant type: %r' % c[0] + print >> self.fd, '' + + def generate_middle(self): + print >> self.fd, '''\ + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(lasso) +{ + lasso_shutdown(); + return SUCCESS; +} + +''' + + def return_value(self, vtype, options, free = False): + if vtype is None: + return + elif vtype == 'gboolean': + print >> self.fd, ' RETVAL_BOOL(return_c_value);' + elif vtype in ['int', 'gint'] + self.binding_data.enums: + print >> self.fd, ' RETVAL_LONG(return_c_value);' + elif vtype in ('char*', 'gchar*'): + print >> self.fd, '''\ + if (return_c_value) { + RETVAL_STRING(return_c_value, 1); + } else { + RETVAL_NULL(); + }''' + if free: + print >> self.fd, ' free(return_c_value);' + elif vtype in ('const char*', 'const gchar*'): + print >> self.fd, '''\ + if (return_c_value) { + RETVAL_STRING((char*)return_c_value, 1); + } else { + RETVAL_NULL(); + }''' + elif vtype == 'xmlNode*': + print >> self.fd, '''\ + { + char* xmlString = get_string_from_xml_node(return_c_value); + if (xmlString) { + RETVAL_STRING(xmlString, 0); + } else { + RETVAL_NULL(); + } + } +''' + elif vtype == 'GList*': + if options.get('elem_type') == 'char*': + print >> self.fd, '''\ + set_array_from_list_of_strings(return_c_value, &return_value); +''' + if free: + print >> self.fd, ' free_glist(&return_c_value, (GFunc)free);' + elif options.get('elem_type') == 'xmlNode*': + print >> self.fd, '''\ + set_array_from_list_of_xmlnodes(return_c_value, &return_value); +''' + if free: + print >> self.fd, ' free_glist(&return_c_value, (GFunc)efree);' + else: + print >> self.fd, '''\ + set_array_from_list_of_objects(return_c_value, &return_value); +''' + if free: + print >> self.fd, ' free_glist(&return_c_value, NULL);' + elif vtype == 'GHashTable*': + if options.get('elem_type') not in ('char*', 'xmlNode*'): + print >> self.fd, '''\ + set_array_from_hashtable_of_objects(return_c_value, &return_value); +''' + else: + print >> self.fd, '''\ + if (return_c_value) { + self = PhpGObjectPtr_New(G_OBJECT(return_c_value)); + ZEND_REGISTER_RESOURCE(return_value, self, le_lasso_server); + } else { + RETVAL_NULL(); + }''' + if free: + print >> self.fd, ' if (return_c_value) {' + print >> self.fd, ' g_object_unref(return_c_value); // If constructor ref is off by one' + print >> self.fd, ' }' + + def generate_function(self, m): + if m.name in ('lasso_init','lasso_shutdown'): + return + if m.rename: + name = m.rename + else: + name = m.name + self.functions_list.append(name) + print >> self.fd, '''PHP_FUNCTION(%s) +{''' % name + parse_tuple_format = [] + parse_tuple_args = [] + for arg in m.args: + arg_type, arg_name, arg_options = arg + if arg_type in ('char*', 'const char*', 'gchar*', 'const gchar*'): + arg_type = arg_type.replace('const ', '') + parse_tuple_format.append('s!') + parse_tuple_args.append('&%s_str, &%s_len' % (arg_name, arg_name)) + print >> self.fd, ' %s %s = NULL;' % ('char*', arg_name) + print >> self.fd, ' %s %s_str = NULL;' % ('char*', arg_name) + print >> self.fd, ' %s %s_len = 0;' % ('int', arg_name) + elif arg_type in ['int', 'gint', 'gboolean', 'const gboolean'] + self.binding_data.enums: + parse_tuple_format.append('l') + parse_tuple_args.append('&%s' % arg_name) + print >> self.fd, ' %s %s;' % ('long', arg_name) + elif arg_type == 'GList*': + parse_tuple_format.append('a!') + parse_tuple_args.append('&zval_%s' % arg_name) + print >> self.fd, ' %s zval_%s = NULL;' % ('zval*', arg_name) + print >> self.fd, ' %s %s = NULL;' % ('GList*', arg_name) + else: + parse_tuple_format.append('r') + parse_tuple_args.append('&zval_%s' % arg_name) + print >> self.fd, ' %s %s = NULL;' % (arg_type, arg_name) + print >> self.fd, ' %s zval_%s = NULL;' % ('zval*', arg_name) + print >> self.fd, ' %s cvt_%s = NULL;' % ('PhpGObjectPtr*', arg_name) + + if m.return_type: + print >> self.fd, ' %s return_c_value;' % m.return_type + if m.return_type is not None and self.is_object(m.return_type): + print >> self.fd, ' PhpGObjectPtr *self;' + print >> self.fd, '' + + parse_tuple_args = ', '.join(parse_tuple_args) + if parse_tuple_args: + parse_tuple_args = ', ' + parse_tuple_args + + print >> self.fd, '''\ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "%s"%s) == FAILURE) { + RETURN_FALSE; + } +''' % (''.join(parse_tuple_format), parse_tuple_args) + + for f, arg in zip(parse_tuple_format, m.args): + if f.startswith('s'): + print >> self.fd, '''\ + %(name)s = %(name)s_str;''' % {'name': arg[1]} + elif f.startswith('r'): + print >> self.fd, ' ZEND_FETCH_RESOURCE(cvt_%s, PhpGObjectPtr *, &zval_%s, -1, PHP_LASSO_SERVER_RES_NAME, le_lasso_server);' % (arg[1], arg[1]) + print >> self.fd, ' %s = (%s)cvt_%s->obj;' % (arg[1], arg[0], arg[1]) + elif f.startswith('a'): + elem_type = arg[2].get('elem_type') + if elem_type == 'char*': + print >> self.fd, ' %(name)s = get_list_from_array_of_strings(zval_%(name)s);' % {'name': arg[1]} + else: + print >> sys.stderr, 'E: In %(function)s arg %(name)s is of type GList<%(elem)s>' % { 'function': m.name, 'name': arg[1], 'elem': elem_type } + elif f == 'l': + pass + else: + raise Exception('%s format inconnu' % f) + + + if m.return_type is not None: + print >> self.fd, ' return_c_value = ', + if 'new' in m.name: + print >> self.fd, '(%s)' % m.return_type, + else: + print >> self.fd, ' ', + print >> self.fd, '%s(%s);' % (m.name, ', '.join([x[1] for x in m.args])) + # Free the converted arguments + + for f, arg in zip(parse_tuple_format, m.args): + if f.startswith('a'): + elem_type = arg[2].get('elem_type') + if elem_type == 'char*': + print >> self.fd, ' if (%(name)s) {' % { 'name': arg[1] } + print >> self.fd, ' free_glist(&%(name)s,(GFunc)free);' % { 'name': arg[1] } + print >> self.fd, ' }' + + self.return_value(m.return_type, {}, m.return_owner) + + print >> self.fd, '}' + print >> self.fd, '' + + def generate_members(self, c): + for m_type, m_name, m_options in c.members: + self.generate_getter(c.name, m_type, m_name, m_options) + self.generate_setter(c.name, m_type, m_name, m_options) + + def generate_getter(self, klassname, m_type, m_name, m_options): + if m_type == 'GList*' and m_options.get('elem_type') not in ('char*', 'xmlNode*') \ + and not self.is_object(m_options.get('elem_type')): + print >> sys.stderr, 'E: GList argument : %s of %s, with type : %s' % (m_name, klassname, m_options.get('elem_type')) + return + + function_name = '%s_%s_get' % (klassname, utils.format_as_camelcase(m_name)) + print >> self.fd, '''PHP_FUNCTION(%s) +{''' % function_name + self.functions_list.append(function_name) + + + if self.is_object(m_type): + print >> self.fd, ' %s return_c_value = NULL;' % m_type + else: + print >> self.fd, ' %s return_c_value;' % m_type + print >> self.fd, ' %s* this;' % klassname + print >> self.fd, ' zval* zval_this;' + print >> self.fd, ' PhpGObjectPtr *cvt_this;' + if self.is_object(m_type): + print >> self.fd, ' PhpGObjectPtr *self;' + print >> self.fd, '' + print >> self.fd, '''\ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zval_this) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(cvt_this, PhpGObjectPtr *, &zval_this, -1, PHP_LASSO_SERVER_RES_NAME, le_lasso_server); + this = (%s*)cvt_this->obj; +''' % (klassname) + + if self.is_object(m_type): + print >> self.fd, ' if (this->%s != NULL) {' % m_name + print >> self.fd, ' return_c_value = this->%s;' % m_name + print >> self.fd, ' }' + else: + print >> self.fd, ' return_c_value = this->%s;' % m_name + + self.return_value(m_type, m_options) + + print >> self.fd, '}' + print >> self.fd, '' + + + def generate_setter(self, klassname, m_type, m_name, m_options): + if m_type == 'GList*' and m_options.get('elem_type') not in ('char*', 'xmlNode*') \ + and not self.is_object(m_options.get('elem_type')): + print >> sys.stderr, 'E: GList argument : %s of %s, with type : %s' % (m_name, klassname, m_options.get('elem_type')) + return + + function_name = '%s_%s_set' % (klassname, utils.format_as_camelcase(m_name)) + print >> self.fd, '''PHP_FUNCTION(%s) +{''' % function_name + self.functions_list.append(function_name) + + print >> self.fd, ' %s* this;' % klassname + print >> self.fd, ' zval* zval_this;' + print >> self.fd, ' PhpGObjectPtr *cvt_this;' + + # FIXME: This bloc should be factorised + parse_tuple_format = '' + parse_tuple_args = [] + arg_type = m_type + arg_name = m_name + arg_options = m_options + if arg_type in ('char*', 'const char*', 'gchar*', 'const gchar*', 'xmlNode*'): + arg_type = arg_type.replace('const ', '') + parse_tuple_format += 's' + parse_tuple_args.append('&%s_str, &%s_len' % (arg_name, arg_name)) + print >> self.fd, ' %s %s_str = NULL;' % ('char*', arg_name) + print >> self.fd, ' %s %s_len = 0;' % ('int', arg_name) + elif arg_type in ['int', 'gint', 'gboolean', 'const gboolean'] + self.binding_data.enums: + parse_tuple_format += 'l' + parse_tuple_args.append('&%s' % arg_name) + print >> self.fd, ' %s %s;' % ('long', arg_name) + # Must also handle lists of Objects + elif arg_type in ('GList*', 'GHashTable*'): + parse_tuple_format += 'a' + parse_tuple_args.append('&zval_%s' % arg_name) + print >> self.fd, ' %s zval_%s;' % ('zval*', arg_name) + else: + parse_tuple_format += 'r' + parse_tuple_args.append('&zval_%s' % arg_name) + print >> self.fd, ' %s zval_%s = NULL;' % ('zval*', arg_name) + print >> self.fd, ' %s cvt_%s = NULL;' % ('PhpGObjectPtr*', arg_name) + + if parse_tuple_args: + parse_tuple_arg = parse_tuple_args[0] + else: + print >> self.fd, '}' + print >> self.fd, '' + return + + print >> self.fd, '' + print >> self.fd, '''\ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r%s", &zval_this, %s) == FAILURE) { + return; + } +''' % (parse_tuple_format, parse_tuple_arg) + + # Get 'this' object + print >> self.fd, '''\ + ZEND_FETCH_RESOURCE(cvt_this, PhpGObjectPtr *, &zval_this, -1, PHP_LASSO_SERVER_RES_NAME, le_lasso_server); + this = (%s*)cvt_this->obj; +''' % klassname + + # Set new value + if parse_tuple_format == 'l': + print >> self.fd, ' this->%s = %s;' % (m_name, m_name) + elif parse_tuple_format == 's': + print >> self.fd, ' if (this->%s) {' % m_name + print >> self.fd, ' g_free(this->%s);' % m_name + print >> self.fd, ' }' + print >> self.fd, ' if (%s_str && strcmp(%s_str, "") != 0) {' % (m_name, m_name) + if arg_type == 'xmlNode*': + print >> self.fd, ' this->%s = get_xml_node_from_string(%s_str);' % (m_name, m_name) + else: + print >> self.fd, ' this->%s = g_strndup(%s_str, %s_len);' % (m_name, m_name, m_name) + print >> self.fd, ' } else {' + print >> self.fd, ' this->%s = NULL;' % m_name + print >> self.fd, ' }' + elif arg_type == 'GList*': + if m_options.get('elem_type') == 'char*': + print >> self.fd, ''' + if (this->%(name)s) { + /* free existing list */ + g_list_foreach(this->%(name)s, (GFunc)g_free, NULL); + g_list_free(this->%(name)s); + } + this->%(name)s = get_list_from_array_of_strings(zval_%(name)s); +''' % { 'name': m_name } + elif m_options.get('elem_type') == 'xmlNode*': + print >> self.fd, ''' + if (this->%(name)s) { + /* free existing list */ + g_list_foreach(this->%(name)s, (GFunc)xmlFreeNode, NULL); + g_list_free(this->%(name)s); + } + this->%(name)s = get_list_from_array_of_xmlnodes(zval_%(name)s); +''' % { 'name': m_name } + else: + print >> self.fd, ''' + free_glist(&this->%(name)s, (GFunc)g_object_unref); + this->%(name)s = get_list_from_array_of_objects(zval_%(name)s); +''' % { 'name': m_name } + elif arg_type == 'GHashTable*' and arg_options.get('elem_type') != 'char*': + print >> self.fd, '''\ + { + GHashTable *oldhash = this->%(name)s; + this->%(name)s = get_hashtable_from_array_of_objects(zval_%(name)s); + g_hash_table_destroy(oldhash); + } +''' % { 'name': m_name } + elif parse_tuple_format == 'r': + print >> self.fd, ' ZEND_FETCH_RESOURCE(cvt_%s, PhpGObjectPtr*, &zval_%s, -1, PHP_LASSO_SERVER_RES_NAME, le_lasso_server);' % (m_name, m_name) + print >> self.fd, ''' + g_object_ref(cvt_%(name)s->obj); + if (this->%(name)s) + g_object_unref(this->%(name)s); + this->%(name)s = (%(type)s)cvt_%(name)s->obj; +''' % { 'name': m_name, 'type': m_type } + + print >> self.fd, '}' + print >> self.fd, '' + + def generate_functions_list(self): + print >> self.fd, '''\ +static function_entry lasso_functions[] = {''' + for m in self.functions_list: + print >> self.fd, ' PHP_FE(%s, NULL)' % m + print >> self.fd, '''\ + {NULL, NULL, NULL} +}; +''' + + def generate_footer(self): + print >> self.fd, '''\ +zend_module_entry lasso_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + PHP_LASSO_EXTNAME, + lasso_functions, + PHP_MINIT(lasso), + PHP_MSHUTDOWN(lasso), + NULL, + NULL, + NULL, +#if ZEND_MODULE_API_NO >= 20010901 + PHP_LASSO_VERSION, +#endif + STANDARD_MODULE_PROPERTIES +}; +''' + diff --git a/bindings/php5/wrapper_source_top.c b/bindings/php5/wrapper_source_top.c new file mode 100644 index 00000000..ba7f1fe6 --- /dev/null +++ b/bindings/php5/wrapper_source_top.c @@ -0,0 +1,319 @@ +#include +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION +#include +#include "php_lasso.h" +#include "../ghashtable.h" + +/* utility functions */ +static void free_glist(GList **list, GFunc free_function); + +/* Define the Lasso PHP module */ + +int le_lasso_server; + +ZEND_GET_MODULE(lasso) + +/* Wrapper around GObject to get the dynamic typename */ + +typedef struct { + GObject *obj; + char *typename; +} PhpGObjectPtr; + +/** FIXME: implement caching of objects inside GObjects using a GQuark */ +static PhpGObjectPtr* +PhpGObjectPtr_New(GObject *obj) +{ + PhpGObjectPtr *self; + + if (obj == NULL) { + return NULL; + } + + self = (PhpGObjectPtr *)malloc(sizeof(PhpGObjectPtr)); + self->obj = g_object_ref(obj); + self->typename = strdup(G_OBJECT_TYPE_NAME(obj)); + //printf("Allocating container %p for object %p of type %s with refcnt %i\n", self, obj, self->typename, obj->ref_count); + + return self; +} +PHP_FUNCTION(lasso_init) +{ + RETURN_NULL(); +} +PHP_FUNCTION(lasso_shutdown) +{ + RETURN_NULL(); +} +PHP_FUNCTION(lasso_get_object_typename) +{ + PhpGObjectPtr *self; + zval *zval_self; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zval_self) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(self, PhpGObjectPtr *, &zval_self, -1, PHP_LASSO_SERVER_RES_NAME, le_lasso_server); + RETURN_STRING(self->typename, 1); +} + +/* Generic destructor for PHP GObject */ +static void php_gobject_generic_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + PhpGObjectPtr* gobject = (PhpGObjectPtr*)rsrc->ptr; + + if (gobject) { + if (gobject->obj) { + //printf("Deallocating container %p\n", gobject); + //printf("Deallocating %p that has %u refcounts\n", gobject->obj, gobject->obj->ref_count); + g_object_unref(G_OBJECT(gobject->obj)); + //printf("now %u refcounts\n", gobject->obj->ref_count); + } + if (gobject->typename) { + free(gobject->typename); + } + free(gobject); + } +} + +/* List handling */ +static void +free_glist(GList **list, GFunc free_function) { + g_return_if_fail(list); + if (*list) { + if (free_function) { + g_list_foreach(*list, free_function, NULL); + } + g_list_free(*list); + } + *list = NULL; +} +/* Conversion functions */ + +static char* +get_string_from_xml_node(xmlNode *xmlnode) +{ + xmlOutputBufferPtr buf; + char *xmlString; + + if (xmlnode == NULL) { + return NULL; + } + + buf = xmlAllocOutputBuffer(NULL); + if (buf == NULL) { + xmlString = NULL; + } else { + xmlNodeDumpOutput(buf, NULL, xmlnode, 0, 1, NULL); + xmlOutputBufferFlush(buf); + if (buf->conv == NULL) { + xmlString = estrdup((char*)buf->buffer->content); + } else { + xmlString = estrdup((char*)buf->conv->content); + } + xmlOutputBufferClose(buf); + } + + return xmlString; +} + +static xmlNode* +get_xml_node_from_string(char *string) +{ + xmlDoc *doc; + xmlNode *node; + + doc = xmlReadDoc((xmlChar*)string, NULL, NULL, XML_PARSE_NONET); + node = xmlDocGetRootElement(doc); + if (node != NULL) { + node = xmlCopyNode(node, 1); + } + xmlFreeDoc(doc); + + return node; +} + +static GList* +get_list_from_array_of_strings(zval* array) +{ + HashTable* hashtable; + HashPosition pointer; + int size; + zval** data; + zval temp; + GList* result = NULL; + + hashtable = Z_ARRVAL_P(array); + size = zend_hash_num_elements(hashtable); + for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer); + zend_hash_get_current_data_ex(hashtable, (void**) &data, &pointer) == SUCCESS; + zend_hash_move_forward_ex(hashtable, &pointer)) { + temp = **data; + zval_copy_ctor(&temp); + convert_to_string(&temp); + result = g_list_append(result, g_strndup(Z_STRVAL(temp), Z_STRLEN(temp))); + zval_dtor(&temp); + } + return result; +} + +static void +set_array_from_list_of_strings(GList* list, zval **array) { + GList* item; + + array_init(*array); + for (item = g_list_first(list); item != NULL; item = g_list_next(item)) { + if (item->data != NULL) { + add_next_index_string(*array, item->data, 1); + } else { + add_next_index_null(*array); + } + } +} + +static GList* +get_list_from_array_of_xmlnodes(zval* array) +{ + HashTable* hashtable; + HashPosition pointer; + int size; + zval** data; + zval temp; + GList* result = NULL; + + hashtable = Z_ARRVAL_P(array); + size = zend_hash_num_elements(hashtable); + for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer); + zend_hash_get_current_data_ex(hashtable, (void**) &data, &pointer) == SUCCESS; + zend_hash_move_forward_ex(hashtable, &pointer)) { + temp = **data; + zval_copy_ctor(&temp); + convert_to_string(&temp); + result = g_list_append(result, get_xml_node_from_string(Z_STRVAL(temp))); + zval_dtor(&temp); + } + return result; +} + +static void +set_array_from_list_of_xmlnodes(GList* list, zval **array) { + GList* item; + + array_init(*array); + for (item = g_list_first(list); item != NULL; item = g_list_next(item)) { + if (item->data != NULL) { + add_next_index_string(*array, get_string_from_xml_node(item->data), 0); + } else { + add_next_index_null(*array); + } + } +} + +static GList* +get_list_from_array_of_objects(zval *array) +{ + HashTable *hashtable; + HashPosition pointer; + int size; + zval **data; + PhpGObjectPtr *cvt_temp; + GList *result = NULL; + + hashtable = Z_ARRVAL_P(array); + size = zend_hash_num_elements(hashtable); + for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer); + zend_hash_get_current_data_ex(hashtable, (void**) &data, &pointer) == SUCCESS; + zend_hash_move_forward_ex(hashtable, &pointer)) { + cvt_temp = (PhpGObjectPtr*) zend_fetch_resource(data TSRMLS_CC, -1, PHP_LASSO_SERVER_RES_NAME, NULL, 1, le_lasso_server); + if (cvt_temp != NULL) { + g_object_ref(cvt_temp->obj); + result = g_list_append(result, cvt_temp->obj); + } else { + result = g_list_append(result, NULL); + } + } + return result; +} + +static void +set_array_from_list_of_objects(GList *list, zval **array) +{ + GList *item = NULL; + zval *zval_item = NULL; + + array_init(*array); + for (item = g_list_first(list); item != NULL; item = g_list_next(item)) { + if (item->data != NULL) { + MAKE_STD_ZVAL(zval_item); + ZEND_REGISTER_RESOURCE(zval_item, PhpGObjectPtr_New(item->data), le_lasso_server); + add_next_index_zval(*array, zval_item); + } else { + add_next_index_null(*array); + } + } +} + +/* FIXME: This function doesn't work yet */ +static GHashTable* +get_hashtable_from_array_of_objects(zval *array) +{ + HashTable *hashtable = NULL; + HashPosition pointer; + int size; + char *key = NULL; + unsigned int key_len; + unsigned long index; + zval **data = NULL; + PhpGObjectPtr *cvt_temp = NULL; + GHashTable *result = NULL; + + result = g_hash_table_new(g_str_hash, g_str_equal); + hashtable = Z_ARRVAL_P(array); + size = zend_hash_num_elements(hashtable); + for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer); + zend_hash_get_current_data_ex(hashtable, (void**) &data, &pointer) == SUCCESS; + zend_hash_move_forward_ex(hashtable, &pointer)) { + cvt_temp = (PhpGObjectPtr*) zend_fetch_resource(data TSRMLS_CC, -1, PHP_LASSO_SERVER_RES_NAME, NULL, 1, le_lasso_server); + if (zend_hash_get_current_key_ex(hashtable, &key, &key_len, &index, 0, &pointer) == HASH_KEY_IS_STRING) { + if (cvt_temp != NULL) { + g_hash_table_insert(result, key, g_object_ref(cvt_temp->obj)); + } else { + g_hash_table_insert(result, key, NULL); + } + } else { + if (cvt_temp != NULL) { + g_hash_table_insert(result, (gpointer)index, g_object_ref(cvt_temp->obj)); + } else { + g_hash_table_insert(result, (gpointer)index, NULL); + } + } + } + return result; +} + +static void +set_array_from_hashtable_of_objects(GHashTable *hashtable, zval **array) +{ + GList *keys = NULL; + GObject *item = NULL; + zval *zval_item = NULL; + + array_init(*array); + for (keys = g_hash_table_get_keys(hashtable); keys; keys = g_list_next(keys)) { + item = g_hash_table_lookup(hashtable, keys->data); + if (item) { + MAKE_STD_ZVAL(zval_item); + ZEND_REGISTER_RESOURCE(zval_item, PhpGObjectPtr_New(item), le_lasso_server); + add_assoc_zval(*array, (char*)keys->data, zval_item); + } else { + add_assoc_null(*array, (char*)keys->data); + } + } + g_list_free(keys); +} + -- cgit