From e006b456dc844f27b94f7f7a2657f5baacec6441 Mon Sep 17 00:00:00 2001 From: "Tomohiro \"Tomo-p\" KATO" Date: Wed, 5 Dec 2018 05:22:07 +0900 Subject: [PATCH 1/3] Add PHP 7 binding (#28608) License: MIT --- bindings/Makefile.am | 8 +- bindings/bindings.py | 5 + bindings/overrides.xml | 8 +- bindings/php7/Makefile.am | 41 ++ bindings/php7/__init__.py | 0 bindings/php7/examples/Makefile.am | 2 + .../get_attributes_from_assertion.php | 11 + bindings/php7/lang.py | 43 ++ bindings/php7/lasso.ini | 2 + bindings/php7/php_code.py | 511 ++++++++++++++++ bindings/php7/tests/Makefile.am | 8 + bindings/php7/tests/binding_tests.php | 211 +++++++ bindings/php7/tests/binding_tests.sh | 3 + bindings/php7/tests/profile_tests.php | 224 +++++++ bindings/php7/tests/profile_tests.sh | 3 + bindings/php7/wrapper_header.py | 64 ++ bindings/php7/wrapper_source.py | 550 ++++++++++++++++++ bindings/php7/wrapper_source_top.c | 380 ++++++++++++ configure.ac | 81 +++ 19 files changed, 2150 insertions(+), 5 deletions(-) create mode 100644 bindings/php7/Makefile.am create mode 100644 bindings/php7/__init__.py create mode 100644 bindings/php7/examples/Makefile.am create mode 100644 bindings/php7/examples/get_attributes_from_assertion.php create mode 100644 bindings/php7/lang.py create mode 100644 bindings/php7/lasso.ini create mode 100644 bindings/php7/php_code.py create mode 100644 bindings/php7/tests/Makefile.am create mode 100644 bindings/php7/tests/binding_tests.php create mode 100755 bindings/php7/tests/binding_tests.sh create mode 100644 bindings/php7/tests/profile_tests.php create mode 100755 bindings/php7/tests/profile_tests.sh create mode 100644 bindings/php7/wrapper_header.py create mode 100644 bindings/php7/wrapper_source.py create mode 100644 bindings/php7/wrapper_source_top.c 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..999e8ed4 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) +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} -- 2.23.0.rc1