diff --git a/bindings/Makefile.am b/bindings/Makefile.am
index 42ccf450..ccef733d 100644
--- a/bindings/Makefile.am
+++ b/bindings/Makefile.am
@@ -6,6 +6,9 @@ endif
if PHP5_ENABLED
SUBDIRS += php5
endif
+if PHP7_ENABLED
+SUBDIRS += php7
+endif
if JAVA_ENABLED
SUBDIRS += java
endif
@@ -15,7 +18,10 @@ endif
CLEANFILES = bindings.pyc lang_java.pyc lang_python.pyc lang_php5.pyc \
utils.pyc lang_php5_helpers/__init__.pyc lang_php5_helpers/php_code.pyc \
- lang_php5_helpers/wrapper_header.pyc lang_php5_helpers/wrapper_source.pyc
+ lang_php5_helpers/wrapper_header.pyc lang_php5_helpers/wrapper_source.pyc \
+ lang_php7.pyc \
+ lang_php7_helpers/__init__.pyc lang_php7_helpers/php_code.pyc \
+ lang_php7_helpers/wrapper_header.pyc lang_php7_helpers/wrapper_source.pyc
EXTRA_DIST = bindings.py \
overrides.xml \
diff --git a/bindings/bindings.py b/bindings/bindings.py
index 6a74d281..07cb0679 100644
--- a/bindings/bindings.py
+++ b/bindings/bindings.py
@@ -629,6 +629,11 @@ def main():
php5_binding = lang.Binding(binding)
php5_binding.generate()
+ elif options.language == 'php7':
+ from php7 import lang
+
+ php7_binding = lang.Binding(binding)
+ php7_binding.generate()
elif options.language == 'java':
from java import lang
diff --git a/bindings/overrides.xml b/bindings/overrides.xml
index 88cedcaa..1efa75f2 100644
--- a/bindings/overrides.xml
+++ b/bindings/overrides.xml
@@ -198,10 +198,10 @@
-
-
-
-
+
+
+
+
diff --git a/bindings/php7/Makefile.am b/bindings/php7/Makefile.am
new file mode 100644
index 00000000..ea1fca9c
--- /dev/null
+++ b/bindings/php7/Makefile.am
@@ -0,0 +1,41 @@
+CLEANFILES = lasso.php php_lasso.h _lasso.c
+DISTCLEANFILES = __init__.pyc lang.pyc php_code.pyc wrapper_header.pyc wrapper_top.pyc wrapper_source.pyc
+SUBDIRS = examples tests
+
+AM_CPPFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ $(SASL_CFLAGS)
+
+php_extensiondir = ${prefix}@PHP7_UNPREFIXED_EXTENSION_DIR@
+php_extension_LTLIBRARIES = lasso.la
+
+php_includedir = @PHP7_INCLUDE_DIR@
+nodist_php_include_DATA = lasso.php
+
+php_configdir=@PHP7_CONFIG_DIR@
+php_config_DATA = lasso.ini
+
+lasso_la_CFLAGS = -fno-strict-aliasing $(LASSO_CORE_CFLAGS) -I$(top_srcdir) -I$(top_builddir) $(PHP7_INCLUDES) $(AM_CFLAGS)
+lasso_la_CFLAGS += -Wno-unused-parameter -Wno-sign-compare # problem in zend.h
+lasso_la_LDFLAGS = -export-dynamic -prefer-pic -module -avoid-version
+lasso_la_LIBADD = $(top_builddir)/lasso/liblasso.la $(LASSO_LIBS) $(PHP7_LDFLAGS)
+nodist_lasso_la_SOURCES = _lasso.c
+
+BUILT_SOURCES = _lasso.c
+
+if WSF_ENABLED
+EXTRA_ARGS = --enable-id-wsf
+endif
+
+
+lasso.php _lasso.c: lang.py wrapper_source.py wrapper_header.py wrapper_source_top.c php_code.py ../overrides.xml
+ $(AM_V_GEN) $(PYTHON) $(top_srcdir)/bindings/bindings.py -l php7 --src-dir=$(top_srcdir)/lasso/ $(EXTRA_ARGS)
+
+doc:
+ phpdoc -o HTML:frames:earthli -f lasso.php -t docs
+
+
+.PHONY: doc
+
+EXTRA_DIST = lasso.ini lang.py php_code.py wrapper_header.py wrapper_source.py __init__.py wrapper_source_top.c
diff --git a/bindings/php7/__init__.py b/bindings/php7/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/bindings/php7/examples/Makefile.am b/bindings/php7/examples/Makefile.am
new file mode 100644
index 00000000..8488755b
--- /dev/null
+++ b/bindings/php7/examples/Makefile.am
@@ -0,0 +1,2 @@
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST = get_attributes_from_assertion.php
diff --git a/bindings/php7/examples/get_attributes_from_assertion.php b/bindings/php7/examples/get_attributes_from_assertion.php
new file mode 100644
index 00000000..3ac48159
--- /dev/null
+++ b/bindings/php7/examples/get_attributes_from_assertion.php
@@ -0,0 +1,11 @@
+/* Example SP PHP5 code to get attributes from an assertion */
+
+foreach ($assertion->attributeStatement[0]->attribute as $attribute) {
+ if ($attribute->name == LASSO_SAML2_ATTRIBUTE_NAME_EPR) {
+ continue;
+ }
+ echo 'attribute : ' . $attribute->name . "\n";
+ foreach ($attribute->attributeValue as $value) {
+ echo ' value : ' . $value->any[0]->content . "\n";
+ }
+}
diff --git a/bindings/php7/lang.py b/bindings/php7/lang.py
new file mode 100644
index 00000000..9395d249
--- /dev/null
+++ b/bindings/php7/lang.py
@@ -0,0 +1,43 @@
+# 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, see .
+
+import os
+from php7.wrapper_source import WrapperSource
+from php7.wrapper_header import WrapperHeader
+from php7.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/php7/lasso.ini b/bindings/php7/lasso.ini
new file mode 100644
index 00000000..5543f498
--- /dev/null
+++ b/bindings/php7/lasso.ini
@@ -0,0 +1,2 @@
+; configuration for php PDO module
+extension=lasso.so
diff --git a/bindings/php7/php_code.py b/bindings/php7/php_code.py
new file mode 100644
index 00000000..35bdf028
--- /dev/null
+++ b/bindings/php7/php_code.py
@@ -0,0 +1,511 @@
+# 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, see .
+
+import re
+import sys
+import six
+
+from utils import *
+
+class PhpCode:
+ def __init__(self, binding_data, fd):
+ self.binding_data = binding_data
+ self.fd = fd
+
+ def is_object(self, t):
+ return is_object(t) and not is_int(t, self.binding_data)
+
+ 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):
+ six.print_('''\
+_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);
+}
+''', file=self.fd)
+
+ def generate_class(self, klass):
+ class_name = klass.name
+
+ if klass.parent != 'GObject':
+ inheritence = ' extends %s' % klass.parent
+ else:
+ inheritence = ' extends LassoObject'
+
+ six.print_('/**', file=self.fd)
+ six.print_(' * @package Lasso', file=self.fd)
+ six.print_(' */', file=self.fd)
+ six.print_('class %(class_name)s%(inheritence)s {' % locals(), file=self.fd)
+
+ if klass.members or klass.methods:
+ self.generate_constructors(klass)
+ self.generate_getters_and_setters(klass)
+ self.generate_methods(klass)
+
+ six.print_('}', file=self.fd)
+ six.print_('', file=self.fd)
+
+ # Add a special class to get an object instance without initialising
+ six.print_('/**', file=self.fd)
+ six.print_(' * @package Lasso', file=self.fd)
+ six.print_(' */', file=self.fd)
+ six.print_('class %(class_name)sNoInit extends %(class_name)s {' % locals(), file=self.fd)
+ six.print_(' public function __construct() {}', file=self.fd)
+ six.print_('}', file=self.fd)
+ six.print_('', file=self.fd)
+
+ def generate_constructors(self, klass):
+ method_prefix = format_as_underscored(klass.name) + '_'
+ for m in self.binding_data.functions:
+ name = m.rename or m.name
+ 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
+ six.print_(' public $_cptr = null;', file=self.fd)
+ six.print_('', file=self.fd)
+ six.print_(' public function __construct(%s) {' % php_args, file=self.fd)
+ six.print_(' $this->_cptr = %s(%s);' % (m.name, c_args), file=self.fd)
+ six.print_(' if (is_null($this->_cptr)) { throw new Exception("Constructor for ', klass.name, ' failed "); }', file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_('', file=self.fd)
+
+ elif name.startswith(method_prefix) and m.args \
+ and clean_type(unconstify(m.args[0][0])) != klass.name:
+ if m.rename:
+ php_name = m.rename
+ else:
+ mname = m.name
+ mname = mname[len(method_prefix):]
+ if 'new' in mname and not mname.startswith('new'):
+ continue
+ php_name = format_underscore_as_camelcase(mname)
+ 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)
+ six.print_(' public static function %s(%s) {' % (php_name, php_args), file=self.fd)
+ six.print_(' return cptrToPhp(%s(%s));' % (m.name, c_args), file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_('', file=self.fd)
+
+
+
+ def generate_getter(self, c, m):
+ d = {
+ 'type': arg_type(m),
+ 'name': format_as_camelcase(arg_name(m)),
+ 'docstring': self.get_docstring_return_type(arg_type(m)),
+ 'class': c.name
+ }
+
+ six.print_('''\
+ /**
+ * @return %(docstring)s
+ */
+ protected function get_%(name)s() {''' % d, file=self.fd)
+ six.print_(' $t = %(class)s_%(name)s_get($this->_cptr);' % d, file=self.fd)
+ if self.is_object(m):
+ six.print_(' $t = cptrToPhp($t);', file=self.fd)
+ elif (is_glist(m) or is_hashtable(m)) and self.is_object(element_type(m)):
+ six.print_(' foreach ($t as $key => $item) {', file=self.fd)
+ six.print_(' $t[$key] = cptrToPhp($item);', file=self.fd)
+ six.print_(' }', file=self.fd)
+ elif is_hashtable(m) or (is_glist(m) and (is_cstring(element_type(m)) \
+ or is_xml_node(element_type(m)))) or is_int(m, self.binding_data) \
+ or is_boolean(m) or is_cstring(m) or is_xml_node(m):
+ pass
+ else:
+ raise Exception('Cannot generate a Php getter %s.%s' % (c,m))
+ six.print_(' return $t;', file=self.fd)
+ six.print_(' }', file=self.fd)
+
+ def generate_setter(self, c, m):
+ d = { 'type': arg_type(m), 'name': format_as_camelcase(arg_name(m)),
+ 'docstring': self.get_docstring_return_type(arg_type(m)), 'class': c.name }
+ six.print_(' protected function set_%(name)s($value) {' % d, file=self.fd)
+ if self.is_object(m):
+ six.print_(' $value = $value->_cptr;', file=self.fd)
+ elif (is_glist(m) or is_hashtable(m)) and self.is_object(element_type(m)):
+ six.print_(' $array = array();', file=self.fd)
+ six.print_(' if (!is_null($value)) {', file=self.fd)
+ six.print_(' foreach ($value as $key => $item) {', file=self.fd)
+ six.print_(' $array[$key] = $item->_cptr;', file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_(' $value = $array;', file=self.fd)
+ elif is_hashtable(m) or (is_glist(m) and (is_cstring(element_type(m)) \
+ or is_xml_node(element_type(m)))) or is_int(m, self.binding_data) \
+ or is_boolean(m) or is_cstring(m) or is_xml_node(m):
+ pass
+ else:
+ raise Exception('Cannot generate a Php setter %s.%s' % (c,m))
+ six.print_(' %(class)s_%(name)s_set($this->_cptr, $value);' % d, file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_('', file=self.fd)
+
+ def generate_getters_and_setters(self, klass):
+ for m in klass.members:
+ self.generate_getter(klass, m)
+ self.generate_setter(klass, m)
+
+ 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 = format_as_camelcase(mname)
+
+ six.print_(' /**', file=self.fd)
+ six.print_(' * @return %s' % self.get_docstring_return_type(m.return_type), file=self.fd)
+ six.print_(' */', file=self.fd)
+ six.print_(' protected function get_%s() {' % mname, file=self.fd)
+ if self.is_object(m.return_type):
+ six.print_(' $cptr = %s($this->_cptr);' % meth_name, file=self.fd)
+ six.print_(' if (! is_null($cptr)) {', file=self.fd)
+ six.print_(' return cptrToPhp($cptr);', file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_(' return null;', file=self.fd)
+ else:
+ six.print_(' return %s($this->_cptr);' % meth_name, file=self.fd)
+ six.print_(' }', file=self.fd)
+ if setter:
+ six.print_(' protected function set_%s($value) {' % mname, file=self.fd)
+ if self.is_object(m.return_type):
+ six.print_(' %s($this->_cptr, $value->_cptr);' % setter.name, file=self.fd)
+ else:
+ six.print_(' %s($this->_cptr, $value);' % setter.name, file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_('', file=self.fd)
+
+ # second pass on methods, real methods
+ method_prefix = 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 = []
+ outarg = None
+ for arg in m.args[1:]:
+ arg_type, arg_name, arg_options = arg
+ arg_name = '$' + arg_name
+ if is_out(arg):
+ assert not outarg
+ outarg = arg
+ 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 is_xml_node(arg) or is_boolean(arg) or is_cstring(arg) or \
+ is_int(arg, self.binding_data) or is_glist(arg) or \
+ is_hashtable(arg) or is_time_t_pointer(arg):
+ c_args.append(arg_name)
+ elif self.is_object(arg):
+ c_args.append('%s->_cptr' % arg_name)
+ else:
+ raise Exception('Does not handle argument of type: %s' % ((m, arg),))
+ if is_out(arg):
+ php_args.pop()
+ php_args.append(arg_name)
+ c_args.pop()
+ c_args.append(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:
+ six.print_(self.generate_docstring(m, mname, 4), file=self.fd)
+ six.print_(' public function %s(%s) {' % (
+ format_underscore_as_camelcase(mname), php_args), file=self.fd)
+ if m.return_type == 'void':
+ six.print_(' %s($this->_cptr%s);' % (cname, c_args), file=self.fd)
+ elif is_rc(m.return_type):
+ six.print_(' $rc = %s($this->_cptr%s);' % (cname, c_args), file=self.fd)
+ six.print_(' if ($rc == 0) {', file=self.fd)
+ six.print_(' return 0;', file=self.fd)
+ six.print_(' } else if ($rc > 0) {', file=self.fd) # recoverable error
+ six.print_(' return $rc;', file=self.fd)
+ six.print_(' } else if ($rc < 0) {', file=self.fd) # unrecoverable error
+ six.print_(' LassoError::throw_on_rc($rc);', file=self.fd)
+ six.print_(' }', file=self.fd)
+ else:
+ six.print_(' return %s($this->_cptr%s);' % (cname, c_args), file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_('', file=self.fd)
+
+ six.print_('', file=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\' in \'%s\'' % (s.group(1), func.docstring.orig_docstring)
+ 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', '')
+ six.print_('''\
+/**
+ * @package Lasso
+ */
+class Lasso%sError extends Lasso%sError {}
+''' % (cat, parent_cat), file=self.fd)
+
+ 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 = ''
+
+ six.print_('''\
+/**
+ * @package Lasso
+ */
+class Lasso%sError extends Lasso%sError {}
+''' % (cat, parent_cat), file=self.fd)
+
+ if detail not in exceptions_dict:
+ six.print_('''\
+/**
+ * @package Lasso
+ */
+class Lasso%sError extends Lasso%sError {
+ protected $code = %s;
+}
+''' % (detail, cat, c[1]), file=self.fd)
+ exceptions_dict[detail] = c[1]
+
+ six.print_('''\
+/**
+ * @package Lasso
+ */
+class LassoError extends Exception {
+ private static $exceptions_dict = array(''', file=self.fd)
+
+ for k, v in exceptions_dict.items():
+ six.print_(' %s => "Lasso%sError",' % (v, k), file=self.fd)
+
+ six.print_('''\
+ );
+
+ public static function throw_on_rc($rc) {
+ $exception = self::$exceptions_dict[$rc];
+ if (! class_exists($exception)) {
+ $exception = "LassoError";
+ }
+ throw new $exception(strError($rc), $rc);
+ }
+}
+''', file=self.fd)
+
+ def generate_footer(self):
+ six.print_('''\
+?>''', file=self.fd)
+
diff --git a/bindings/php7/tests/Makefile.am b/bindings/php7/tests/Makefile.am
new file mode 100644
index 00000000..993104c6
--- /dev/null
+++ b/bindings/php7/tests/Makefile.am
@@ -0,0 +1,8 @@
+MAINTAINERCLEANFILES = Makefile.in
+if PHP7_ENABLED
+TESTS_ENVIRONMENT=env "SRCDIR=$(srcdir)/" "PHP7=$(PHP7)"
+TESTS = profile_tests.sh binding_tests.sh
+endif
+
+EXTRA_DIST = profile_tests.php binding_tests.php profile_tests.sh binding_tests.sh
+
diff --git a/bindings/php7/tests/binding_tests.php b/bindings/php7/tests/binding_tests.php
new file mode 100644
index 00000000..21efc0bc
--- /dev/null
+++ b/bindings/php7/tests/binding_tests.php
@@ -0,0 +1,211 @@
+#!/usr/bin/php
+.
+
+require("../lasso.php");
+
+define("DATA_DIR", getenv("SRCDIR") . "../../../tests/data/");
+
+function test01() {
+ echo "Get an xmlNode* from a Lasso function... ";
+
+ $organisation_string = '
+ Name of the organization
+ ';
+
+ $server = new LassoServer(
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "sp1-la/certificate.pem");
+ assert(!is_null($server->organization));
+ assert($server->organization == $organisation_string);
+
+ echo "OK.\n";
+}
+
+function test02() {
+ echo "Get and set a list of strings... ";
+
+ $requestAuthnContext = new LassoLibRequestAuthnContext();
+ $requestAuthnContext->authnContextClassRef = array(LASSO_LIB_AUTHN_CONTEXT_CLASS_REF_PASSWORD);
+ assert(! is_null($requestAuthnContext->authnContextClassRef));
+ assert(sizeof($requestAuthnContext->authnContextClassRef) == 1);
+ assert($requestAuthnContext->authnContextClassRef[0] == LASSO_LIB_AUTHN_CONTEXT_CLASS_REF_PASSWORD);
+
+ echo "OK.\n";
+}
+
+function test03() {
+ echo "Get and set a list of xmlNode*... ";
+
+ $server = new LassoServer(
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "sp1-la/certificate.pem");
+ $server->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/public-key.pem",
+ DATA_DIR . "idp1-la/certificate.pem");
+ $login = new LassoLogin($server);
+ $login->initAuthnRequest();
+ $requestAuthnContext = new LassoLibRequestAuthnContext();
+ $extension1 = '
+ do
+';
+ $extension2 = '
+ do action 2do action 3
+';
+ $extensionList = array($extension1, $extension2);
+ $login->request->extension = $extensionList;
+ assert($login->request->extension == $extensionList);
+ assert($login->request->extension[0] == $extension1);
+ assert($login->request->extension[1] == $extension2);
+
+ echo "OK.\n";
+}
+
+function test04() {
+ echo "Get and set a list of Lasso objects... ";
+
+ $response = new LassoSamlpResponse();
+ assert(!$response->assertion);
+
+ $assertions = array();
+ $assertion1 = new LassoSamlAssertion();
+ $assertion1->assertionId = "assertion 1";
+ $assertions[] = $assertion1;
+ assert($assertions[0]->assertionId == "assertion 1");
+ $assertion2 = new LassoSamlAssertion();
+ $assertion2->assertionId = "assertion 2";
+ $assertions[] = $assertion2;
+ $response->assertion = $assertions;
+ assert($response->assertion[0]->assertionId == "assertion 1");
+ assert($response->assertion[1]->assertionId == "assertion 2");
+ unset($assertions);
+ assert($response->assertion[0]->assertionId == "assertion 1");
+ assert($response->assertion[1]->assertionId == "assertion 2");
+ $assertions = $response->assertion;
+ assert($assertions[0]->assertionId == "assertion 1");
+ assert($assertions[1]->assertionId == "assertion 2");
+
+ echo "OK.\n";
+}
+
+function test05() {
+ echo "Get and set a hashtable of objects... ";
+
+ $server = new LassoServer(
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "sp1-la/certificate.pem");
+ $server->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/public-key.pem",
+ DATA_DIR . "idp1-la/certificate.pem");
+ assert(!is_null($server->providers));
+ assert($server->providers["https://idp1/metadata"]->providerId == "https://idp1/metadata");
+ assert($server->providers["https://idp1/metadata"]->providerId == "https://idp1/metadata");
+ $tmp_providers = $server->providers;
+ $server->providers = NULL;
+ assert(!$server->providers);
+ $server->providers = $tmp_providers;
+ $provider = $server->providers["https://idp1/metadata"];
+ assert($server->providers["https://idp1/metadata"]->providerId == "https://idp1/metadata");
+
+ echo "OK.\n";
+}
+
+function test06() {
+ echo "Get and set SAML 2.0 assertion attribute values... ";
+
+ $attribute1_name = "first attribute";
+ $attribute1_string = "first string";
+ $attribute2_name = "second attribute";
+ $attribute2_string = "second string";
+ $attribute3_string = "third string";
+
+ $expected_assertion_dump = 'first stringsecond stringthird string';
+
+ $text_node1 = new LassoMiscTextNode();
+ $text_node1->content = $attribute1_string;
+ $any1 = array();
+ $any1[] = $text_node1;
+ $attribute_value1 = new LassoSaml2AttributeValue();
+ $attribute_value1->any = $any1;
+ $attribute_values1 = array();
+ $attribute_values1[] = $attribute_value1;
+ $attribute1 = new LassoSaml2Attribute();
+ $attribute1->name = $attribute1_name;
+ $attribute1->attributeValue = $attribute_values1;
+
+ $text_node2 = new LassoMiscTextNode();
+ $text_node2->content = $attribute2_string;
+ $any2 = array();
+ $any2[] = $text_node2;
+ $attribute_value2 = new LassoSaml2AttributeValue();
+ $attribute_value2->any = $any2;
+
+ $text_node3 = new LassoMiscTextNode();
+ $text_node3->content = $attribute3_string;
+ $any3 = array();
+ $any3[] = $text_node3;
+ $attribute_value3 = new LassoSaml2AttributeValue();
+ $attribute_value3->any = $any3;
+
+ $attribute_values2 = array();
+ $attribute_values2[] = $attribute_value2;
+ $attribute_values2[] = $attribute_value3;
+
+ $attribute2 = new LassoSaml2Attribute();
+ $attribute2->name = $attribute2_name;
+ $attribute2->attributeValue = $attribute_values2;
+
+ $attributes = array();
+ $attributes[] = $attribute1;
+ $attributes[] = $attribute2;
+
+ $attributeStatement = new LassoSaml2AttributeStatement();
+ $attributeStatement->attribute = $attributes;
+ $attributeStatements = array();
+ $attributeStatements[] = $attributeStatement;
+
+ $assertion = new LassoSaml2Assertion();
+ $assertion->attributeStatement = $attributeStatements;
+
+ assert($assertion->dump() == $expected_assertion_dump);
+
+ echo "OK.\n";
+}
+
+lasso_init();
+test01();
+test02();
+test03();
+test04();
+//test05();
+test06();
+lasso_shutdown();
+
diff --git a/bindings/php7/tests/binding_tests.sh b/bindings/php7/tests/binding_tests.sh
new file mode 100755
index 00000000..e653dc0b
--- /dev/null
+++ b/bindings/php7/tests/binding_tests.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+${PHP7:?PHP7 variable is not defined} -n -d extension_dir=../.libs -d extension=lasso.so ${SRCDIR}binding_tests.php
diff --git a/bindings/php7/tests/profile_tests.php b/bindings/php7/tests/profile_tests.php
new file mode 100644
index 00000000..0ab08ec5
--- /dev/null
+++ b/bindings/php7/tests/profile_tests.php
@@ -0,0 +1,224 @@
+#!/usr/bin/php
+.
+
+require("../lasso.php");
+
+define(DATA_DIR, getenv('SRCDIR') . '../../../tests/data/');
+
+function test01() {
+ echo "Server construction, dump & newFromDump... ";
+
+ $server = new LassoServer(
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "sp1-la/certificate.pem");
+ $server->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/public-key.pem",
+ DATA_DIR . "idp1-la/certificate.pem");
+
+ $dump = $server->dump();
+ assert(! is_null($dump));
+ assert($dump != "");
+ $server2 = LassoServer::newFromDump($dump);
+ $dump2 = $server2->dump();
+ assert($dump == $dump2);
+
+ echo "OK.\n";
+}
+
+function test02() {
+ echo "Server construction with no optional argument, dump & newFromDump... ";
+
+ $server = new LassoServer(DATA_DIR . "sp1-la/metadata.xml");
+ $server->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/public-key.pem",
+ DATA_DIR . "idp1-la/certificate.pem");
+
+ $dump = $server->dump();
+ $server2 = LassoServer::newFromDump($dump);
+ $dump2 = $server2->dump();
+ assert($dump == $dump2);
+
+ echo "OK.\n";
+}
+
+function test03() {
+ echo "SP login; testing access to authentication request... ";
+
+ $server = new LassoServer(
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "sp1-la/certificate.pem");
+ $server->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/public-key.pem",
+ DATA_DIR . "idp1-la/certificate.pem");
+
+ $login = new LassoLogin($server);
+ $result = $login->initAuthnRequest();
+ assert(! is_null($login->request));
+ assert(get_class($login->request) == "LassoLibAuthnRequestNoInit");
+ $dump = $login->request->dump();
+ $login->request->protocolProfile = LASSO_LIB_PROTOCOL_PROFILE_BRWS_ART;
+ assert($login->request->protocolProfile == LASSO_LIB_PROTOCOL_PROFILE_BRWS_ART);
+ $dump2 = $login->request->dump();
+ assert($dump != $dump2);
+
+ echo "OK.\n";
+}
+
+function test04() {
+ echo "SP login; testing processing of an empty Response... ";
+
+ $server = new LassoServer(
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "sp1-la/certificate.pem");
+ $server->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/public-key.pem",
+ DATA_DIR . "idp1-la/certificate.pem");
+
+ $login = new LassoLogin($server);
+ try {
+ $login->processResponseMsg("");
+ }
+ catch (LassoProfileInvalidMsgError $error) {
+ }
+
+ echo "OK.\n";
+}
+
+function test05() {
+ echo "Conversion of a lib:AuthnRequest with an AuthnContext into a query and back... ";
+
+ $spServer = new LassoServer(
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "sp1-la/certificate.pem");
+ $spServer->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/public-key.pem",
+ DATA_DIR . "idp1-la/certificate.pem");
+
+ $spLogin = new LassoLogin($spServer);
+ $spLogin->initAuthnRequest();
+ $requestAuthnContext = new LassoLibRequestAuthnContext();
+ $requestAuthnContext->authnContextClassRef = array(LASSO_LIB_AUTHN_CONTEXT_CLASS_REF_PASSWORD);
+ assert($requestAuthnContext->authnContextClassRef[0] == LASSO_LIB_AUTHN_CONTEXT_CLASS_REF_PASSWORD);
+ $spLogin->request->requestAuthnContext = $requestAuthnContext;
+ assert(! is_null($spLogin->request->requestAuthnContext));
+ $spLogin->request->protocolProfile = LASSO_LIB_PROTOCOL_PROFILE_BRWS_ART;
+ assert($spLogin->request->protocolProfile == LASSO_LIB_PROTOCOL_PROFILE_BRWS_ART);
+ $spLogin->buildAuthnRequestMsg();
+ assert(! is_null($spLogin->msgUrl));
+ assert($spLogin->msgUrl != "");
+
+ $idpServer = new LassoServer(
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "idp1-la/certificate.pem");
+ $idpServer->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/public-key.pem",
+ DATA_DIR . "sp1-la/certificate.pem");
+
+ $idpLogin = new LassoLogin($idpServer);
+ list($urlBase, $authnRequestQuery) = explode("?", $spLogin->msgUrl, 2);
+ assert($authnRequestQuery != "");
+ $idpLogin->processAuthnRequestMsg($authnRequestQuery);
+ assert(! is_null($idpLogin->request));
+ assert(! is_null($idpLogin->request->requestAuthnContext));
+ assert($idpLogin->request->requestAuthnContext != "");
+ assert(sizeof($idpLogin->request->requestAuthnContext->authnContextClassRef) == 1);
+ assert($idpLogin->request->requestAuthnContext->authnContextClassRef[0] ==
+ LASSO_LIB_AUTHN_CONTEXT_CLASS_REF_PASSWORD);
+
+ echo "OK.\n";
+}
+
+function test06() {
+ echo "SP logout without session and identity; testing initRequest... ";
+
+ $server = new LassoServer(
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "sp1-la/certificate.pem");
+ $server->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/public-key.pem",
+ DATA_DIR . "idp1-la/certificate.pem");
+
+ $logout = new LassoLogout($server);
+ try {
+ $logout->initRequest();
+ echo "logout.initRequest without having set identity before should fail\n";
+ assert(False);
+ }
+ catch (LassoProfileSessionNotFoundError $error) {
+ }
+
+ echo "OK.\n";
+}
+
+function test07() {
+ echo "IDP logout without session and identity; testing logout.getNextProviderId... ";
+
+ $server = new LassoServer(
+ DATA_DIR . "idp1-la/metadata.xml",
+ DATA_DIR . "idp1-la/private-key-raw.pem",
+ NULL,
+ DATA_DIR . "idp1-la/certificate.pem");
+ $server->addProvider(
+ LASSO_PROVIDER_ROLE_IDP,
+ DATA_DIR . "sp1-la/metadata.xml",
+ DATA_DIR . "sp1-la/public-key.pem",
+ DATA_DIR . "sp1-la/certificate.pem");
+
+ $logout = new LassoLogout($server);
+ assert(is_null($logout->next_providerID));
+
+ echo "OK.\n";
+}
+
+test01();
+test02();
+test03();
+test04();
+test05();
+test06();
+test07();
diff --git a/bindings/php7/tests/profile_tests.sh b/bindings/php7/tests/profile_tests.sh
new file mode 100755
index 00000000..ca57f5e0
--- /dev/null
+++ b/bindings/php7/tests/profile_tests.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+${PHP7:?PHP7 variable is not defined} -n -d extension_dir=../.libs -d extension=lasso.so ${SRCDIR}profile_tests.php
diff --git a/bindings/php7/wrapper_header.py b/bindings/php7/wrapper_header.py
new file mode 100644
index 00000000..5236571a
--- /dev/null
+++ b/bindings/php7/wrapper_header.py
@@ -0,0 +1,64 @@
+# 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, see .
+import six
+
+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
+ six.print_('''\
+/* this file has been generated automatically; do not edit */
+
+#include "../../config.h"
+
+#ifndef PHP_LASSO_H
+#define PHP_LASSO_H 1
+
+#define PHP_LASSO_EXTNAME "lasso"
+#define PHP_LASSO_VERSION VERSION
+
+#define PHP_LASSO_SERVER_RES_NAME "Lasso Server"
+
+PHP_MINIT_FUNCTION(lasso);
+PHP_MSHUTDOWN_FUNCTION(lasso);
+''', file=self.fd)
+
+ def generate_functions_list(self):
+ for m in self.functions_list:
+ six.print_('PHP_FUNCTION(%s);' % m, file=self.fd)
+ six.print_('', file=self.fd)
+
+ def generate_footer(self):
+ six.print_('''\
+extern zend_module_entry lasso_module_entry;
+#define phpext_lasso_ptr &lasso_module_entry
+
+#endif
+''', file=self.fd)
+
diff --git a/bindings/php7/wrapper_source.py b/bindings/php7/wrapper_source.py
new file mode 100644
index 00000000..c429e563
--- /dev/null
+++ b/bindings/php7/wrapper_source.py
@@ -0,0 +1,550 @@
+# 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, see .
+
+import sys
+import os
+import six
+
+from utils import *
+
+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*', 'GType',
+ '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')
+
+ six.print_('''\
+/* this file has been generated automatically; do not edit */
+''', file=self.fd)
+
+ six.print_(open(os.path.join(self.src_dir,'wrapper_source_top.c')).read(), file=self.fd)
+
+ for h in self.binding_data.headers:
+ six.print_('#include <%s>' % h, file=self.fd)
+ six.print_('', file=self.fd)
+
+ six.print_('''\
+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();
+''', file=self.fd)
+
+ def generate_constants(self):
+ six.print_(' /* Constants (both enums and defines) */', file=self.fd)
+ for c in self.binding_data.constants:
+ if c[0] == 'i':
+ six.print_(' REGISTER_LONG_CONSTANT("%s", %s, CONST_CS|CONST_PERSISTENT);' % (c[1], c[1]), file=self.fd)
+ elif c[0] == 's':
+ six.print_(' REGISTER_STRING_CONSTANT("%s", (char*) %s, CONST_CS|CONST_PERSISTENT);' % (c[1], c[1]), file=self.fd)
+ elif c[0] == 'b':
+ six.print_('''\
+#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]), file=self.fd)
+ else:
+ six.print_('E: unknown constant type: %r' % c[0], file=sys.stderr)
+ six.print_('', file=self.fd)
+
+ def generate_middle(self):
+ six.print_('''\
+ return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(lasso)
+{
+ lasso_shutdown();
+ return SUCCESS;
+}
+
+''', file=self.fd)
+
+ def set_zval(self, zval_name, c_variable, type, free = False):
+ '''Emit code to set a zval* of name zval_name, from the value of the C variable called c_variable type, type.
+ '''
+ # first we free the previous value
+ p = (zval_name, c_variable)
+ q = { 'zval_name' : zval_name, 'c_variable' : c_variable }
+ six.print_(' zval_dtor(%s);' % zval_name, file=self.fd)
+ if is_pointer(type):
+ six.print_(' if (! %s) {' % c_variable, file=self.fd)
+ six.print_(' ZVAL_NULL(%s);' % zval_name, file=self.fd)
+ six.print_(' } else {', file=self.fd)
+ if is_int(type, self.binding_data):
+ six.print_(' ZVAL_LONG(%s, %s);' % p, file=self.fd)
+ elif is_boolean(type):
+ six.print_(' ZVAL_BOOL(%s, %s);' % p, file=self.fd)
+ elif is_cstring(type):
+ six.print_(' ZVAL_STRING(%s, (char*)%s);' % p, file=self.fd)
+ if free and not is_const(type):
+ six.print_('g_free(%s)' % c_variable, file=self.fd)
+ elif arg_type(type) == 'xmlNode*':
+ six.print_('''\
+ {
+ char* xmlString = get_string_from_xml_node(%(c_variable)s);
+ if (xmlString) {
+ ZVAL_STRING(%(zval_name)s, xmlString);
+ } else {
+ ZVAL_NULL(%(zval_name)s);
+ }
+ }
+''' % q, file=self.fd)
+ elif is_glist(type):
+ elem_type = make_arg(element_type(type))
+ if not arg_type(elem_type):
+ raise Exception('unknown element-type: ' + repr(type))
+ if is_cstring(elem_type):
+ function = 'set_array_from_list_of_strings'
+ free_function = 'free_glist(&%(c_variable)s, (GFunc)free);'
+ elif arg_type(elem_type).startswith('xmlNode'):
+ function = 'set_array_from_list_of_xmlnodes'
+ free_function = 'free_glist(&%(c_variable)s, (GFunc)xmlFree);'
+ elif is_object(elem_type):
+ function = 'set_array_from_list_of_objects'
+ free_function = 'g_list_free(%(c_variable)s);'
+ else:
+ raise Exception('unknown element-type: ' + repr(type))
+ six.print_(' %s((GList*)%s, &%s);' % (function, c_variable, zval_name), file=self.fd)
+ if free:
+ six.print_(' ', free_function % q, file=self.fd)
+ elif is_object(type):
+ six.print_('''\
+ if (G_IS_OBJECT(%(c_variable)s)) {
+ PhpGObjectPtr *obj = PhpGObjectPtr_New(G_OBJECT(%(c_variable)s));
+ zend_resource *res = zend_register_resource(obj, le_lasso_server);
+ ZVAL_RES(%(zval_name)s, res);
+ } else {
+ ZVAL_NULL(%(zval_name)s);
+ }''' % q, file=self.fd)
+ if free:
+ six.print_('''\
+ if (%(c_variable)s) {
+ g_object_unref(%(c_variable)s); // If constructor ref is off by one'
+ }''' % q, file=self.fd)
+
+ else:
+ raise Exception('unknown type: ' + repr(type) + unconstify(arg_type(type)))
+ if is_pointer(type):
+ six.print_(' }', file=self.fd)
+
+
+
+ def return_value(self, arg, free = False):
+ if arg is None:
+ return
+
+ if is_boolean(arg):
+ six.print_(' RETVAL_BOOL(return_c_value);', file=self.fd)
+ elif is_int(arg, self.binding_data):
+ six.print_(' RETVAL_LONG(return_c_value);', file=self.fd)
+ elif is_cstring(arg):
+ six.print_('''\
+ if (return_c_value) {
+ RETVAL_STRING((char*)return_c_value);
+ } else {
+ RETVAL_NULL();
+ }''', file=self.fd)
+ if free:
+ six.print_(' free(return_c_value);', file=self.fd)
+ elif is_xml_node(arg):
+ six.print_('''\
+ {
+ char* xmlString = get_string_from_xml_node(return_c_value);
+ if (xmlString) {
+ RETVAL_STRING(xmlString);
+ } else {
+ RETVAL_NULL();
+ }
+ }
+''', file=self.fd)
+ if free:
+ six.print_(' lasso_release_xml_node(return_c_value);', file=self.fd)
+ elif is_glist(arg):
+ el_type = element_type(arg)
+ if is_cstring(el_type):
+ six.print_('''\
+ set_array_from_list_of_strings((GList*)return_c_value, &return_value);
+''', file=self.fd)
+ if free:
+ six.print_(' lasso_release_list_of_strings(return_c_value);', file=self.fd)
+ elif is_xml_node(el_type):
+ six.print_('''\
+ set_array_from_list_of_xmlnodes((GList*)return_c_value, &return_value);
+''', file=self.fd)
+ if free or is_transfer_full(arg):
+ six.print_(' lasso_release_list_of_xml_node(return_c_value);', file=self.fd)
+ elif is_object(el_type):
+ six.print_('''\
+ set_array_from_list_of_objects((GList*)return_c_value, &return_value);
+''', file=self.fd)
+ if free:
+ six.print_(' lasso_release_list_of_gobjects(return_c_value);', file=self.fd)
+ else:
+ raise Exception('cannot return value for %s' % (arg,))
+ elif is_hashtable(arg):
+ el_type = element_type(arg)
+ if is_object(el_type):
+ six.print_('''\
+ set_array_from_hashtable_of_objects(return_c_value, &return_value);
+''', file=self.fd)
+ else:
+ if not is_cstring(arg):
+ print >>sys.stderr, 'W: %s has no explicit string annotation' % (arg,)
+ six.print_('''\
+ set_array_from_hashtable_of_strings(return_c_value, &return_value);
+''', file=self.fd)
+ elif is_object(arg):
+ six.print_('''\
+ if (return_c_value) {
+ PhpGObjectPtr *self;
+ self = PhpGObjectPtr_New(G_OBJECT(return_c_value));
+ zend_resource *res = zend_register_resource(self, le_lasso_server);
+ ZVAL_RES(return_value, res);
+ } else {
+ RETVAL_NULL();
+ }''', file=self.fd)
+ if free:
+ six.print_(' lasso_release_gobject(return_c_value);', file=self.fd)
+ else:
+ raise Exception('cannot return value for %s' % (arg,))
+
+ 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)
+ six.print_('''PHP_FUNCTION(%s)
+{''' % name, file=self.fd)
+ parse_tuple_format = []
+ parse_tuple_args = []
+ for arg in m.args:
+ if is_out(arg):
+ six.print_(' zval *php_out_%s = NULL;' % arg_name(arg), file=self.fd)
+ six.print_(' %s %s;' % (var_type(arg), arg_name(arg)), file=self.fd)
+ parse_tuple_format.append('z!')
+ parse_tuple_args.append('&php_out_%s' % arg_name(arg))
+ elif is_cstring(arg):
+ parse_tuple_format.append('s!')
+ parse_tuple_args.append('&%s_str, &%s_len' % (arg_name(arg), arg_name(arg)))
+ six.print_(' %s %s = NULL;' % ('char*', arg_name(arg)), file=self.fd)
+ six.print_(' %s %s_str = NULL;' % ('char*', arg_name(arg)), file=self.fd)
+ six.print_(' %s %s_len = 0;' % ('size_t', arg_name(arg)), file=self.fd)
+ elif is_int(arg, self.binding_data) or is_boolean(arg):
+ parse_tuple_format.append('l')
+ parse_tuple_args.append('&%s' % arg_name(arg))
+ six.print_(' %s %s;' % ('long', arg_name(arg)), file=self.fd)
+ elif is_time_t_pointer(arg):
+ parse_tuple_format.append('l')
+ parse_tuple_args.append('&%s' % (arg_name(arg),))
+ print >>self.fd, ' time_t %s = 0;' % (arg_name(arg),)
+ elif is_xml_node(arg):
+ parse_tuple_format.append('s!')
+ parse_tuple_args.append('&%s_str, &%s_len' % (arg_name(arg), arg_name(arg)))
+ six.print_(' %s %s = NULL;' % ('xmlNode*', arg_name(arg)), file=self.fd)
+ six.print_(' %s %s_str = NULL;' % ('char*', arg_name(arg)), file=self.fd)
+ six.print_(' %s %s_len = 0;' % ('size_t', arg_name(arg)), file=self.fd)
+ elif is_glist(arg):
+ parse_tuple_format.append('a!')
+ parse_tuple_args.append('&zval_%s' % arg_name(arg))
+ six.print_(' %s zval_%s = NULL;' % ('zval*', arg_name(arg)), file=self.fd)
+ six.print_(' %s %s = NULL;' % ('GList*', arg_name(arg)), file=self.fd)
+ elif is_object(arg):
+ parse_tuple_format.append('r')
+ parse_tuple_args.append('&zval_%s' % arg_name(arg))
+ six.print_(' %s %s = NULL;' % (arg_type(arg), arg_name(arg)), file=self.fd)
+ six.print_(' %s zval_%s = NULL;' % ('zval*', arg_name(arg)), file=self.fd)
+ six.print_(' %s cvt_%s = NULL;' % ('PhpGObjectPtr*', arg_name(arg)), file=self.fd)
+ else:
+ raise Exception('Unsupported type %s %s' % (arg, m))
+
+ if m.return_type:
+ six.print_(' %s return_c_value;' % m.return_type, file=self.fd)
+ if m.return_type is not None and self.is_object(m.return_arg):
+ six.print_(' G_GNUC_UNUSED PhpGObjectPtr *self;', file=self.fd)
+ six.print_('', file=self.fd)
+
+ parse_tuple_args = ', '.join(parse_tuple_args)
+ if parse_tuple_args:
+ parse_tuple_args = ', ' + parse_tuple_args
+
+ six.print_('''\
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "%s"%s) == FAILURE) {
+ RETURN_FALSE;
+ }
+''' % (''.join(parse_tuple_format), parse_tuple_args), file=self.fd)
+
+ for f, arg in zip(parse_tuple_format, m.args):
+ if is_out(arg):
+ continue
+ elif is_xml_node(arg):
+ six.print_('''\
+ %(name)s = get_xml_node_from_string(%(name)s_str);''' % {'name': arg[1]}, file=self.fd)
+ elif f.startswith('s'):
+ six.print_('''\
+ %(name)s = %(name)s_str;''' % {'name': arg[1]}, file=self.fd)
+ elif f.startswith('r'):
+ six.print_(' if ((cvt_%s = (PhpGObjectPtr *)zend_fetch_resource(Z_RES_P(zval_%s), PHP_LASSO_SERVER_RES_NAME, le_lasso_server)) == NULL) {' % (arg[1], arg[1]), file=self.fd)
+ six.print_(' RETURN_FALSE;', file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_(' %s = (%s)cvt_%s->obj;' % (arg[1], arg[0], arg[1]), file=self.fd)
+ elif f.startswith('a'):
+ el_type = element_type(arg)
+ if is_cstring(el_type):
+ six.print_(' %(name)s = get_list_from_array_of_strings(zval_%(name)s);' % {'name': arg[1]}, file=self.fd)
+ elif is_object(el_type):
+ six.print_(' %(name)s = get_list_from_array_of_objects(zval_%(name)s);' % {'name': arg[1]}, file=self.fd)
+ else:
+ six.print_('E: In %(function)s arg %(name)s is of type GList<%(elem)s>' % { 'function': m.name, 'name': arg[1], 'elem': el_type }, file=sys.stderr)
+ elif f == 'l':
+ pass
+ else:
+ raise Exception('%s format inconnu' % f)
+
+
+ if m.return_type is not None:
+ six.print_(' return_c_value = ', file=self.fd)
+ if 'new' in m.name:
+ six.print_('(%s)' % m.return_type, file=self.fd)
+ else:
+ six.print_(' ', file=self.fd)
+ def special(x):
+ if is_time_t_pointer(x):
+ return '%(name)s ? &%(name)s : NULL' % { 'name': arg_name(x) }
+ else:
+ return ref_name(x)
+ six.print_('%s(%s);' % (m.name, ', '.join([special(x) for x in m.args])), file=self.fd)
+ # Free the converted arguments
+
+ for f, arg in zip(parse_tuple_format, m.args):
+ argtype, argname, argoptions = arg
+ if is_out(arg):
+ # export the returned variable
+ free = is_transfer_full(unref_type(arg))
+ self.set_zval('php_out_%s' % argname, argname, unref_type(arg), free = free)
+ pass
+ elif argtype == 'xmlNode*':
+ six.print_(' xmlFree(%s);' % argname, file=self.fd)
+ elif f.startswith('a'):
+ el_type = element_type(arg)
+ if is_cstring(el_type):
+ six.print_(' if (%(name)s) {' % { 'name': arg[1] }, file=self.fd)
+ six.print_(' free_glist(&%(name)s,(GFunc)free);' % { 'name': arg[1] }, file=self.fd)
+ six.print_(' }', file=self.fd)
+
+ try:
+ self.return_value(m.return_arg, is_transfer_full(m.return_arg, default=True))
+ except:
+ raise Exception('Cannot return value for function %s' % m)
+
+ six.print_('}', file=self.fd)
+ six.print_('', file=self.fd)
+
+ def generate_members(self, c):
+ for m in c.members:
+ self.generate_getter(c, m)
+ self.generate_setter(c, m)
+
+ def generate_getter(self, c, m):
+ klassname = c.name
+ name = arg_name(m)
+ type = arg_type(m)
+
+ function_name = '%s_%s_get' % (klassname, format_as_camelcase(name))
+ six.print_('''PHP_FUNCTION(%s)
+{''' % function_name, file=self.fd)
+ self.functions_list.append(function_name)
+
+ six.print_(' %s return_c_value;' % type, file=self.fd)
+ six.print_(' %s* this;' % klassname, file=self.fd)
+ six.print_(' zval* zval_this;', file=self.fd)
+ six.print_(' PhpGObjectPtr *cvt_this;', file=self.fd)
+ six.print_('', file=self.fd)
+ six.print_('''\
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zval_this) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ if ((cvt_this = (PhpGObjectPtr *)zend_fetch_resource(Z_RES_P(zval_this), PHP_LASSO_SERVER_RES_NAME, le_lasso_server)) == NULL) {
+ RETURN_FALSE;
+ }
+ this = (%s*)cvt_this->obj;
+''' % (klassname), file=self.fd)
+ six.print_(' return_c_value = (%s)this->%s;' % (type, name), file=self.fd)
+ self.return_value(m)
+ six.print_('}', file=self.fd)
+ six.print_('', file=self.fd)
+
+ def generate_setter(self, c, m):
+ klassname = c.name
+ name = arg_name(m)
+ type = arg_type(m)
+ function_name = '%s_%s_set' % (klassname, format_as_camelcase(name))
+ six.print_('''PHP_FUNCTION(%s)
+{''' % function_name, file=self.fd)
+ self.functions_list.append(function_name)
+
+ six.print_(' %s* this;' % klassname, file=self.fd)
+ six.print_(' zval* zval_this;', file=self.fd)
+ six.print_(' PhpGObjectPtr *cvt_this;', file=self.fd)
+
+ # FIXME: This bloc should be factorised
+ parse_tuple_format = ''
+ parse_tuple_args = []
+ if is_cstring(m) or is_xml_node(m):
+ # arg_type = arg_type.replace('const ', '')
+ parse_tuple_format += 's'
+ parse_tuple_args.append('&%s_str, &%s_len' % (name, name))
+ six.print_(' %s %s_str = NULL;' % ('char*', name), file=self.fd)
+ six.print_(' %s %s_len = 0;' % ('size_t', name), file=self.fd)
+ elif is_int(m, self.binding_data) or is_boolean(m):
+ parse_tuple_format += 'l'
+ parse_tuple_args.append('&%s' % name)
+ six.print_(' %s %s;' % ('long', name), file=self.fd)
+ # Must also handle lists of Objects
+ elif is_glist(m) or is_hashtable(m):
+ parse_tuple_format += 'a'
+ parse_tuple_args.append('&zval_%s' % name)
+ six.print_(' %s zval_%s;' % ('zval*', name), file=self.fd)
+ elif is_object(m):
+ parse_tuple_format += 'r'
+ parse_tuple_args.append('&zval_%s' % name)
+ six.print_(' %s zval_%s = NULL;' % ('zval*', name), file=self.fd)
+ six.print_(' %s cvt_%s = NULL;' % ('PhpGObjectPtr*', name), file=self.fd)
+ else:
+ raise Exception('Cannot make a setter for %s.%s' % (c,m))
+
+ if parse_tuple_args:
+ parse_tuple_arg = parse_tuple_args[0]
+ else:
+ six.print_('}', file=self.fd)
+ six.print_('', file=self.fd)
+ return
+
+ six.print_('', file=self.fd)
+ six.print_('''\
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r%s", &zval_this, %s) == FAILURE) {
+ return;
+ }
+''' % (parse_tuple_format, parse_tuple_arg), file=self.fd)
+
+ # Get 'this' object
+ six.print_('''\
+ if ((cvt_this = (PhpGObjectPtr *)zend_fetch_resource(Z_RES_P(zval_this), PHP_LASSO_SERVER_RES_NAME, le_lasso_server)) == NULL) {
+ RETURN_FALSE;
+ }
+ this = (%s*)cvt_this->obj;
+''' % klassname, file=self.fd)
+
+ # Set new value
+ d = { 'name': name, 'type': type }
+ if is_int(m, self.binding_data) or is_boolean(m):
+ six.print_(' this->%s = %s;' % (name, name), file=self.fd)
+ elif is_cstring(m):
+ six.print_(' lasso_assign_string(this->%(name)s, %(name)s_str);' % d, file=self.fd)
+ elif is_xml_node(m):
+ six.print_(' lasso_assign_new_xml_node(this->%(name)s, get_xml_node_from_string(%(name)s_str));' % d, file=self.fd)
+ elif is_glist(m):
+ el_type = element_type(m)
+ if is_cstring(el_type):
+ six.print_(' lasso_assign_new_list_of_strings(this->%(name)s, get_list_from_array_of_strings(zval_%(name)s));' % d, file=self.fd)
+ elif is_xml_node(el_type):
+ six.print_(' lasso_assign_new_list_of_xml_node(this->%(name)s, get_list_from_array_of_xmlnodes(zval_%(name)s))' % d, file=self.fd)
+ elif is_object(el_type):
+ six.print_(' lasso_assign_new_list_of_gobjects(this->%(name)s, get_list_from_array_of_objects(zval_%(name)s));' % d, file=self.fd)
+ else:
+ raise Exception('Cannot create C setter for %s.%s' % (c,m))
+ elif is_hashtable(m):
+ el_type = element_type(m)
+ six.print_('''\
+ {
+ GHashTable *oldhash = this->%(name)s;''' % d, file=self.fd)
+ if is_object(el_type):
+ six.print_(' this->%(name)s = get_hashtable_from_array_of_objects(zval_%(name)s);' % d, file=self.fd)
+ else:
+ six.print_(' this->%(name)s = get_hashtable_from_array_of_strings(zval_%(name)s);' % d, file=self.fd)
+ six.print_(' g_hash_table_destroy(oldhash);', file=self.fd)
+ six.print_(' }', file=self.fd)
+ elif is_object(m):
+ six.print_(' if ((cvt_%(name)s = (PhpGObjectPtr*)zend_fetch_resource(Z_RES_P(zval_%(name)s), PHP_LASSO_SERVER_RES_NAME, le_lasso_server)) == NULL) {' % d, file=self.fd)
+ six.print_(' RETURN_FALSE;', file=self.fd)
+ six.print_(' }', file=self.fd)
+ six.print_(' lasso_assign_gobject(this->%(name)s, cvt_%(name)s->obj);' % d, file=self.fd)
+
+ six.print_('}', file=self.fd)
+ six.print_('', file=self.fd)
+
+ def generate_functions_list(self):
+ six.print_('''\
+static zend_function_entry lasso_functions[] = {''', file=self.fd)
+ for m in self.functions_list:
+ six.print_(' PHP_FE(%s, NULL)' % m, file=self.fd)
+ six.print_('''\
+ {NULL, NULL, NULL, 0, 0}
+};
+''', file=self.fd)
+
+ def generate_footer(self):
+ six.print_('''\
+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
+};
+''', file=self.fd)
+
diff --git a/bindings/php7/wrapper_source_top.c b/bindings/php7/wrapper_source_top.c
new file mode 100644
index 00000000..8f579eb5
--- /dev/null
+++ b/bindings/php7/wrapper_source_top.c
@@ -0,0 +1,380 @@
+#include
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include
+#include "php_lasso.h"
+#include "../ghashtable.h"
+#define LASSO_LOG_STATIC
+
+#if defined(__GNUC__)
+# define lasso_log(level, filename, line, function, format, args...) \
+ g_log("Lasso", level, "%s:%i:%s" format, filename, line, function, ##args)
+#elif defined(HAVE_VARIADIC_MACROS)
+# define lasso_log(level, format, line, function, ...) \
+ g_log("Lasso", leve, "%s:%i:%s" format, filename, line, function, __VA_ARGS__)
+#else
+static inline void lasso_log(GLogLevelFlags level, const char *filename,
+ int line, const char *function, const char *format, ...)
+{
+ va_list ap;
+ char s[1024];
+ va_start(ap, format);
+ g_vsnprintf(s, 1024, format, ap);
+ va_end(ap);
+ g_log("Lasso", level, "%s:%i:%s %s", filename, line, function, s);
+}
+#define lasso_log lasso_log
+#endif
+
+#include "../../lasso/utils.h"
+#include "../utils.c"
+
+/* 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;
+ }
+
+ if ((self = (PhpGObjectPtr *)zend_fetch_resource(Z_RES_P(zval_self), PHP_LASSO_SERVER_RES_NAME, le_lasso_server)) == NULL) {
+ RETURN_FALSE;
+ }
+ RETURN_STRING(self->typename);
+}
+
+/* Generic destructor for PHP GObject */
+static void php_gobject_generic_destructor(zend_resource *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) {
+ lasso_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 xmlBuffer*
+xmlnode_to_xmlbuffer(xmlNode *node)
+{
+ xmlOutputBufferPtr output_buffer;
+ xmlBuffer *buffer;
+
+ if (! node)
+ return NULL;
+
+ buffer = xmlBufferCreate();
+ output_buffer = xmlOutputBufferCreateBuffer(buffer, NULL);
+ xmlNodeDumpOutput(output_buffer, NULL, node, 0, 0, NULL);
+ xmlOutputBufferClose(output_buffer);
+ xmlBufferAdd(buffer, BAD_CAST "", 1);
+
+ return buffer;
+}
+
+static char*
+get_string_from_xml_node(xmlNode *xmlnode)
+{
+ xmlBuffer *buffer;
+ char *result;
+
+ if (xmlnode == NULL) {
+ return NULL;
+ }
+ buffer = xmlnode_to_xmlbuffer(xmlnode);
+ if (buffer == NULL) {
+ result = NULL;
+ } else {
+ result = estrdup((char*)xmlBufferContent(buffer));
+ xmlBufferFree(buffer);
+ }
+ return result;
+}
+
+static xmlNode*
+get_xml_node_from_string(char *string)
+{
+ return lasso_string_fragment_to_xmlnode(string, 0);
+}
+
+static GList*
+get_list_from_array_of_strings(zval* array)
+{
+ HashTable* hashtable;
+ HashPosition pointer;
+ zval* data;
+ zval temp;
+ GList* result = NULL;
+
+ hashtable = Z_ARRVAL_P(array);
+ for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer);
+ (data = zend_hash_get_current_data_ex(hashtable, &pointer)) != NULL;
+ 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);
+ } else {
+ add_next_index_null(*array);
+ }
+ }
+}
+
+static GList*
+get_list_from_array_of_xmlnodes(zval* array)
+{
+ HashTable* hashtable;
+ HashPosition pointer;
+ zval* data;
+ zval temp;
+ GList* result = NULL;
+
+ hashtable = Z_ARRVAL_P(array);
+ for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer);
+ (data = zend_hash_get_current_data_ex(hashtable, &pointer)) != NULL;
+ zend_hash_move_forward_ex(hashtable, &pointer)) {
+ xmlNode *value;
+
+ temp = *data;
+ zval_copy_ctor(&temp);
+ convert_to_string(&temp);
+ value = get_xml_node_from_string(Z_STRVAL(temp));
+ if (value) {
+ lasso_list_add_new_xml_node(result, value);
+ }
+ 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));
+ } else {
+ add_next_index_null(*array);
+ }
+ }
+}
+
+static GList*
+get_list_from_array_of_objects(zval *array)
+{
+ HashTable *hashtable;
+ HashPosition pointer;
+ zval *data;
+ PhpGObjectPtr *cvt_temp;
+ GList *result = NULL;
+
+ hashtable = Z_ARRVAL_P(array);
+ for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer);
+ (data = zend_hash_get_current_data_ex(hashtable, &pointer)) != NULL;
+ zend_hash_move_forward_ex(hashtable, &pointer)) {
+ cvt_temp = (PhpGObjectPtr*) zend_fetch_resource(Z_RES_P(data), PHP_LASSO_SERVER_RES_NAME, 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;
+ zend_resource *res_item;
+ zval zval_item;
+
+ array_init(*array);
+ for (item = g_list_first(list); item != NULL; item = g_list_next(item)) {
+ if (item->data != NULL) {
+ res_item = zend_register_resource(PhpGObjectPtr_New(item->data), le_lasso_server);
+ ZVAL_RES(&zval_item, res_item);
+ 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;
+ zend_string *key;
+ zend_ulong 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);
+ for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer);
+ (data = zend_hash_get_current_data_ex(hashtable, &pointer)) != NULL;
+ zend_hash_move_forward_ex(hashtable, &pointer)) {
+ cvt_temp = (PhpGObjectPtr*) zend_fetch_resource(Z_RES_P(data), PHP_LASSO_SERVER_RES_NAME, le_lasso_server);
+ if (zend_hash_get_current_key_ex(hashtable, &key, &index, &pointer) == HASH_KEY_IS_STRING) {
+ g_hash_table_insert(result, ZSTR_VAL(key), lasso_ref(cvt_temp->obj));
+ } else {
+ /* FIXME: throw an exception */
+ }
+ }
+ return result;
+}
+
+G_GNUC_UNUSED static GHashTable*
+get_hashtable_from_array_of_strings(zval *array)
+{
+ HashTable *hashtable = NULL;
+ HashPosition pointer;
+ zend_string *key = NULL;
+ zend_ulong index;
+ zval *data = NULL;
+ GHashTable *result = NULL;
+
+ result = g_hash_table_new(g_str_hash, g_str_equal);
+ hashtable = Z_ARRVAL_P(array);
+ for (zend_hash_internal_pointer_reset_ex(hashtable, &pointer);
+ (data = zend_hash_get_current_data_ex(hashtable, &pointer)) != NULL;
+ zend_hash_move_forward_ex(hashtable, &pointer)) {
+ if (Z_TYPE_P(data) == IS_STRING) {
+ if (zend_hash_get_current_key_ex(hashtable, &key, &index, &pointer) == HASH_KEY_IS_STRING) {
+ g_hash_table_insert(result, g_strdup(ZSTR_VAL(key)), g_strdup(Z_STRVAL_P(data)));
+ } else {
+ /* FIXME: throw an exception */
+ }
+ }
+ }
+ return result;
+}
+
+static void
+set_array_from_hashtable_of_objects(GHashTable *hashtable, zval **array)
+{
+ GList *keys = NULL;
+ GObject *item = NULL;
+ zend_resource *res_item;
+ zval zval_item;
+
+ 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) {
+ res_item = zend_register_resource(PhpGObjectPtr_New(item), le_lasso_server);
+ ZVAL_RES(&zval_item, res_item);
+ add_assoc_zval(*array, (char*)keys->data, &zval_item);
+ } else {
+ add_assoc_null(*array, (char*)keys->data);
+ }
+ }
+ g_list_free(keys);
+}
+
+G_GNUC_UNUSED static void
+set_array_from_hashtable_of_strings(GHashTable *hashtable, zval **array)
+{
+ GList *keys = NULL;
+ zval zval_item;
+
+ array_init(*array);
+ for (keys = g_hash_table_get_keys(hashtable); keys; keys = g_list_next(keys)) {
+ char *item = g_hash_table_lookup(hashtable, keys->data);
+ if (item) {
+ ZVAL_STRING(&zval_item, item);
+ add_assoc_zval(*array, (char*)keys->data, &zval_item);
+ } else {
+ add_assoc_null(*array, (char*)keys->data);
+ }
+ }
+ g_list_free(keys);
+}
+
diff --git a/configure.ac b/configure.ac
index 2270015d..fdccc121 100644
--- a/configure.ac
+++ b/configure.ac
@@ -131,6 +131,8 @@ dnl AC_CHECK_PROGS(JAR, fastjar jar)
AC_CHECK_PROGS(PERL, perl)
AC_CHECK_PROGS(PHP5, php5 php)
AC_CHECK_PROGS(PHP5_CONFIG, php-config5 php-config)
+AC_CHECK_PROGS(PHP7, php7.4 php7.3 php7.2 php7.1 php7.0 php7)
+AC_CHECK_PROGS(PHP7_CONFIG, php-config7.4 php-config7.3 php-config7.2 php-config7.1 php-config7.0 php-config7)
AC_CHECK_PROGS(PYTHON, python3 python python2)
AC_CHECK_PROGS(SWIG, swig)
@@ -433,6 +435,81 @@ AM_CONDITIONAL([PHP5_ENABLED], [test "x$enable_php5" = "xyes"])
AC_SUBST(PHP5_VERSION)
+# -----------
+# PHP 7 binding
+# -----------
+
+dnl Check if php is explicitly enabled.
+AC_ARG_ENABLE(php7, [ --enable-php7 enable the PHP 7 binding],,
+ enable_php7="yes")
+
+AC_ARG_WITH(php7-config,
+ [ --with-php7-config=(PHP7_CONFIG) Specify full path to php-config7.])
+
+AC_ARG_ENABLE(php7-force, [ --enable-php7-force always enable of the PHP 7 binding (win32)],
+ [ENABLE_PHP7_FORCE="yes"],
+ [ENABLE_PHP7_FORCE="no"])
+
+
+dnl Check if user passed a specific php-config program.
+if test "X$with_php7_config" != "X" ; then
+ PHP7_CONFIG=$with_php7_config
+fi
+
+if test "X$PHP7_CONFIG" != "X" ; then
+ PHP7_INCLUDES=`$PHP7_CONFIG --includes`
+ PHP7_LDFLAGS=`$PHP7_CONFIG --ldflags`
+ PHP7_LIBS=`$PHP7_CONFIG --libs`
+ PHP7_PREFIX=`$PHP7_CONFIG --prefix`
+ PHP7_QUOTED_PREFIX=$(echo $PHP7_PREFIX | $SED 's/\//\\\//g')
+ PHP7_UNPREFIXED_EXTENSION_DIR=$($PHP7_CONFIG --extension-dir | $SED "s/$PHP7_QUOTED_PREFIX//g")
+else
+ # We assume PHP are in /usr/local directory.
+ if test $MINGW -eq 1; then
+ CFLAGS="$CFLAGS -DZTS -DZEND_WIN32 -DWIN32 -D_MBCS"
+ fi
+ PHP7_INCLUDES="-I/usr/local/include/php7 -I/usr/local/include/php7/main -I/usr/local/include/php7/Zend -I/usr/local/include/php7/TSRM -I/usr/local/include/php7/win32"
+ PHP7_LDFLAGS=
+ PHP7_LIBS="-lphp7ts -lxmlparse -lxmltok"
+ PHP7_UNPREFIXED_EXTENSION_DIR=
+ PHP7_PREFIX=
+fi
+
+AC_ARG_WITH(php7,
+ [ --with-php7=(PHP) Specify full path to php 7 executable.],
+ [PHP7="$withval"],[PHP7_INCLUDE_DIR=php7])
+
+AC_ARG_WITH(php7-include-dir,
+ [ --with-php7-include-dir=(PHP7_INCLUDE_DIR) Specify full path to php 7 include dir.],
+ [PHP7_INCLUDE_DIR="$withval"],[PHP7_INCLUDE_DIR=${datadir}/php])
+
+AC_ARG_WITH(php7-config-dir,
+ [ --with-php7-config-dir=(PHP7_CONFIG_DIR) Specify full path to php 7 config dir.],
+ [PHP7_CONFIG_DIR="$withval"],[PHP7_CONFIG_DIR=${sysconfdir}/php7/conf.d/])
+
+AC_SUBST(PHP7_INCLUDES)
+AC_SUBST(PHP7_LDFLAGS)
+AC_SUBST(PHP7_LIBS)
+AC_SUBST(PHP7_UNPREFIXED_EXTENSION_DIR)
+AC_SUBST(PHP7_EXTENSION_DIR)
+AC_SUBST(PHP7_PREFIX)
+AC_SUBST(PHP7_INCLUDE_DIR)
+AC_SUBST(PHP7_CONFIG_DIR)
+
+AC_MSG_CHECKING(for PHP 7 development files)
+if $PHP7_CONFIG --version | grep -q "^7" || test "x$ENABLE_PHP7_FORCE" = "xyes"
+then
+ PHP7_VERSION=`$PHP7_CONFIG --version 2> /dev/null`
+ languages_available="$languages_available php7($PHP7_VERSION)"
+else
+ enable_php7=no
+fi
+
+AC_MSG_RESULT($enable_php7)
+AM_CONDITIONAL([PHP7_ENABLED], [test "x$enable_php7" = "xyes"])
+AC_SUBST(PHP7_VERSION)
+
+
### # ----------
### # C# binding (disabled for the moment)
### # ----------
@@ -827,6 +904,9 @@ bindings/python/tests/Makefile
bindings/php5/Makefile
bindings/php5/examples/Makefile
bindings/php5/tests/Makefile
+bindings/php7/Makefile
+bindings/php7/examples/Makefile
+bindings/php7/tests/Makefile
bindings/perl/Makefile
])
@@ -854,6 +934,7 @@ Available languages: ${languages_available}
Java binding: ${enable_java}
Perl binding: ${enable_perl}
PHP 5 binding: ${enable_php5}
+PHP 7 binding: ${enable_php7}
Python binding: ${enable_python}
C API references: ${enable_gtk_doc}