summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore18
-rw-r--r--Makefile.am3
-rw-r--r--README4
-rw-r--r--configure.ac8
-rw-r--r--php/Makefile.am58
-rw-r--r--php/README-PHP54
-rw-r--r--php/extension/config.m424
-rw-r--r--php/extension/guestfs_php_001.phpt17
-rw-r--r--php/extension/guestfs_php_002.phpt36
-rw-r--r--php/extension/guestfs_php_003.phpt39
-rw-r--r--php/guestfs_php.ini2
-rwxr-xr-xphp/run-php-tests.sh35
-rw-r--r--po/POTFILES.in1
-rwxr-xr-xsrc/generator.ml427
-rw-r--r--src/guestfs.pod9
15 files changed, 733 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 56ad9bd1..cfea5495 100644
--- a/.gitignore
+++ b/.gitignore
@@ -181,6 +181,24 @@ perl/Makefile-pl
perl/Makefile-pl.old
perl/Makefile.PL
perl/pm_to_blib
+php/extension/Makefile.fragments
+php/extension/Makefile.global
+php/extension/Makefile.objects
+php/extension/acinclude.m4
+php/extension/build/
+php/extension/config.nice
+php/extension/configure.in
+php/extension/guestfs_php.c
+php/extension/guestfs_php_*.diff
+php/extension/guestfs_php_*.exp
+php/extension/guestfs_php_*.log
+php/extension/guestfs_php_*.out
+php/extension/guestfs_php_*.php
+php/extension/mkinstalldirs
+php/extension/modules/
+php/extension/php_guestfs_php.h
+php/extension/run-tests.php
+php/extension/tmp-php.ini
pod2htm?.tmp
po/*.gmo
po/Makevars.template
diff --git a/Makefile.am b/Makefile.am
index ce2f940c..e9b38eae 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -54,6 +54,9 @@ endif
if HAVE_HASKELL
SUBDIRS += haskell
endif
+if HAVE_PHP
+SUBDIRS += php
+endif
# Virt-inspector, tools and guestmount.
if HAVE_INSPECTOR
diff --git a/README b/README
index 199f595b..f3e348ff 100644
--- a/README
+++ b/README
@@ -18,7 +18,7 @@ FUSE.
Libguestfs is a library that can be linked with C and C++ management
programs (or management programs written in OCaml, Perl, Python, Ruby,
-Java, Haskell or C#). You can also use it from shell scripts or the
+Java, PHP, Haskell or C#). You can also use it from shell scripts or the
command line.
Libguestfs was written by Richard W.M. Jones (rjones@redhat.com) and
@@ -96,6 +96,8 @@ in virt-inspector).
- (Optional) po4a for translating manpages and POD files.
+- (Optional) PHP, phpize if you want to build the PHP bindings
+
Running ./configure will check you have all the requirements installed
on your machine.
diff --git a/configure.ac b/configure.ac
index 935abdc7..5a44d8bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -772,6 +772,11 @@ dnl po4a for translating man pages and POD files (optional).
AC_CHECK_PROG([PO4A],[po4a],[po4a],[no])
AM_CONDITIONAL([HAVE_PO4A], [test "x$PO4A" != "xno"])
+dnl PHP
+AC_CHECK_PROG([PHP],[php],[php],[no])
+AC_CHECK_PROG([PHPIZE],[phpize],[phpize],[no])
+AM_CONDITIONAL([HAVE_PHP], [test "x$PHP" != "xno" -a "x$PHPIZE" != "xno"])
+
dnl Library versioning.
MAX_PROC_NR=`cat $srcdir/src/MAX_PROC_NR`
AC_SUBST(MAX_PROC_NR)
@@ -808,6 +813,7 @@ AC_CONFIG_FILES([Makefile
fuse/Makefile
po-docs/Makefile
po-docs/ja/Makefile
+ php/Makefile
ocaml/META perl/Makefile.PL])
AC_OUTPUT
@@ -834,6 +840,8 @@ echo -n "Java bindings ....................... "
if test "x$HAVE_JAVA_TRUE" = "x"; then echo "yes"; else echo "no"; fi
echo -n "Haskell bindings .................... "
if test "x$HAVE_HASKELL_TRUE" = "x"; then echo "yes"; else echo "no"; fi
+echo -n "PHP bindings ........................ "
+if test "x$HAVE_PHP_TRUE" = "x"; then echo "yes"; else echo "no"; fi
echo -n "virt-inspector ...................... "
if test "x$HAVE_INSPECTOR_TRUE" = "x"; then echo "yes"; else echo "no"; fi
echo -n "virt-* tools ........................ "
diff --git a/php/Makefile.am b/php/Makefile.am
new file mode 100644
index 00000000..49efcde0
--- /dev/null
+++ b/php/Makefile.am
@@ -0,0 +1,58 @@
+# libguestfs PHP bindings
+# Copyright (C) 2010 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+include $(top_srcdir)/subdir-rules.mk
+
+generator_built = \
+ extension/php_guestfs_php.h \
+ extension/guestfs_php.c
+
+EXTRA_DIST = \
+ $(generator_built) \
+ run-php-tests.sh \
+ extension/guestfs_php_*.phpt \
+ extension/config.m4 \
+ README-PHP \
+ guestfs_php.ini
+
+if HAVE_PHP
+
+phpdir = $(sysconfdir)/php.d
+php_DATA = guestfs_php.ini
+
+# In theory: EXTRA_LIBS="-lguestfs" In fact this doesn't work
+# and we need to add the library to EXTRA_LDFLAGS.
+all: extension/config.h
+ $(MAKE) -C extension \
+ EXTRA_INCLUDES="-I$(abs_srcdir)/../src" \
+ EXTRA_LDFLAGS="-L$(abs_srcdir)/../src/.libs -lguestfs" \
+ all
+
+extension/config.h: extension/config.m4 ../config.status
+ cd extension && phpize
+ cd extension && ./configure --prefix=$(prefix) --libdir=$(libdir)
+ test -f "$@" && touch -- $@
+
+TESTS = run-php-tests.sh
+
+clean-local:
+ $(MAKE) -C extension clean
+
+install-data-hook:
+ $(MAKE) -C extension INSTALL_ROOT=$(DESTDIR) install
+
+endif
diff --git a/php/README-PHP b/php/README-PHP
new file mode 100644
index 00000000..b5ad3c3a
--- /dev/null
+++ b/php/README-PHP
@@ -0,0 +1,54 @@
+NOTE: The PHP API is not complete on 32 bit architectures. PHP
+doesn't offer any convenient 64 bit type (on 32 bit). Any 64 bit
+parameters or return values will be truncated to 32 bits on these
+platforms. You should always use these PHP bindings on a 64 bit
+operating system.
+
+To install the extension manually, copy guestfs_php.so into the
+modules directory (eg. /usr/local/lib/php/modules/) and copy
+guestfs_php.ini into the config directory (eg. /etc/php.d/).
+[Note: On packaged Linux distributions you don't need to do this]
+
+The PHP API follows the C API. Refer to guestfs(3) or
+http://libguestfs.org/guestfs.3.html for the details of the C API.
+
+To create a handle, use guestfs_create() like this:
+
+ <?php
+ $g = guestfs_create ();
+ if ($g == false) {
+ echo ("Failed to create guestfs_php handle.\n");
+ exit;
+ }
+ ?>
+
+Handles are closed implicitly by the PHP dtor.
+
+All of the usual functions from the C API are available. By
+convention these return 'false' for errors, so:
+
+ <?php
+ //...
+ if (guestfs_launch ($g) == false) {
+ echo ("Error: ".guestfs_last_error ($g)."\n");
+ exit;
+ }
+ ?>
+
+or:
+
+ <?php
+ //...
+ $version = guestfs_version ($g);
+ if ($version == false) {
+ echo ("Error: ".guestfs_last_error ($g)."\n");
+ exit;
+ }
+ echo ("libguestfs version = ".
+ $version["major"].".".$version["minor"].".".$version["release"].
+ $version["extra"]."\n");
+ ?>
+
+C API structs are mapped to associative arrays. C API lists of
+structs are mapped to arrays of associative arrays. Other C API
+parameters and return values are mapped to natural PHP types.
diff --git a/php/extension/config.m4 b/php/extension/config.m4
new file mode 100644
index 00000000..2bac2ea5
--- /dev/null
+++ b/php/extension/config.m4
@@ -0,0 +1,24 @@
+# libguestfs PHP bindings
+# Copyright (C) 2010 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+PHP_ARG_ENABLE(guestfs_php, enable libguestfs PHP bindings,
+ [ --enable-guestfs-php Enable libguestfs support])
+
+if test "$PHP_GUESTFS_PHP" = "yes"; then
+ AC_DEFINE(HAVE_GUESTFS_PHP, 1, [Whether you have libguestfs PHP bindings])
+ PHP_NEW_EXTENSION(guestfs_php, guestfs_php.c, $ext_shared)
+fi
diff --git a/php/extension/guestfs_php_001.phpt b/php/extension/guestfs_php_001.phpt
new file mode 100644
index 00000000..771592ff
--- /dev/null
+++ b/php/extension/guestfs_php_001.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Load the module and create a handle.
+--FILE--
+<?php
+
+// See comment in php/run-php-tests.sh.
+//putenv ('LIBGUESTFS_DEBUG=1');
+
+$g = guestfs_create ();
+if ($g == false) {
+ echo ("Failed to create guestfs_php handle.\n");
+ exit;
+}
+echo ("Created guestfs_php handle.\n");
+?>
+--EXPECT--
+Created guestfs_php handle.
diff --git a/php/extension/guestfs_php_002.phpt b/php/extension/guestfs_php_002.phpt
new file mode 100644
index 00000000..48ee0b60
--- /dev/null
+++ b/php/extension/guestfs_php_002.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Launch the appliance.
+--FILE--
+<?php
+
+// See comment in php/run-php-tests.sh.
+//putenv ('LIBGUESTFS_DEBUG=1');
+
+$g = guestfs_create ();
+if ($g == false) {
+ echo ("Failed to create guestfs_php handle.\n");
+ exit;
+}
+if (guestfs_add_drive ($g, "/dev/null") == false) {
+ echo ("Error: ".guestfs_last_error ($g)."\n");
+ exit;
+}
+if (guestfs_launch ($g) == false) {
+ echo ("Error: ".guestfs_last_error ($g)."\n");
+ exit;
+}
+$version = guestfs_version ($g);
+if ($version == false) {
+ echo ("Error: ".guestfs_last_error ($g)."\n");
+ exit;
+}
+if (!is_int ($version["major"]) ||
+ !is_int ($version["minor"]) ||
+ !is_int ($version["release"]) ||
+ !is_string ($version["extra"])) {
+ echo ("Error: incorrect return type from guestfs_version\n");
+}
+echo ("OK\n");
+?>
+--EXPECT--
+OK
diff --git a/php/extension/guestfs_php_003.phpt b/php/extension/guestfs_php_003.phpt
new file mode 100644
index 00000000..c4eb5b0a
--- /dev/null
+++ b/php/extension/guestfs_php_003.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Create a disk containing LV and filesystem.
+--FILE--
+<?php
+
+// See comment in php/run-php-tests.sh.
+//putenv ('LIBGUESTFS_DEBUG=1');
+
+$g = guestfs_create ();
+if ($g == false) {
+ die ("Failed to create guestfs_php handle.\n");
+}
+
+$tmp = dirname(__FILE__)."/test.img";
+$size = 100 * 1024 * 1024;
+if (! $fp = fopen ($tmp, 'r+')) {
+ die ("Error: cannot create file '".$tmp."'\n");
+}
+ftruncate ($fp, $size);
+fclose ($fp);
+
+if (! guestfs_add_drive ($g, "test.img") ||
+ ! guestfs_launch ($g) ||
+ ! guestfs_part_disk ($g, "/dev/sda", "mbr") ||
+ ! guestfs_pvcreate ($g, "/dev/sda") ||
+ ! guestfs_vgcreate ($g, "VG", array ("/dev/sda")) ||
+ ! guestfs_lvcreate ($g, "LV", "VG", 64) ||
+ ! guestfs_mkfs ($g, "ext2", "/dev/VG/LV")) {
+ die ("Error: ".guestfs_last_error ($g)."\n");
+}
+echo ("OK\n");
+?>
+--CLEAN--
+<?php
+$tmp = dirname(__FILE__)."/test.img";
+unlink ($tmp);
+?>
+--EXPECT--
+OK
diff --git a/php/guestfs_php.ini b/php/guestfs_php.ini
new file mode 100644
index 00000000..b490a445
--- /dev/null
+++ b/php/guestfs_php.ini
@@ -0,0 +1,2 @@
+; Enable guestfs_php extension module
+extension=guestfs_php.so
diff --git a/php/run-php-tests.sh b/php/run-php-tests.sh
new file mode 100755
index 00000000..38a5e38f
--- /dev/null
+++ b/php/run-php-tests.sh
@@ -0,0 +1,35 @@
+#!/bin/sh -
+# Copyright (C) 2010 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+set -e
+cd extension
+
+TESTS=$(echo guestfs_php_*.phpt)
+echo TESTS: $TESTS
+
+# The PHP test script cleans the environment, so LIBGUESTFS_DEBUG=1
+# won't get passed down to the script. Furthermore, setting
+# LIBGUESTFS_DEBUG=1 isn't very useful anyway because the PHP test
+# script mixes stdout and stderr together and compares this to the
+# expected output, so you'd just get failures for every test. So
+# there is no good way to debug libguestfs failures in PHP tests, but
+# if an individual test fails locally then you can edit the
+# guestfs_php_*.phpt and uncomment the putenv statement, then look at
+# the output.
+unset LIBGUESTFS_DEBUG
+
+make test TESTS="$TESTS"
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3faa1fbc..a06249ab 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -98,6 +98,7 @@ perl/Guestfs.c
perl/bindtests.pl
perl/lib/Sys/Guestfs.pm
perl/lib/Sys/Guestfs/Lib.pm
+php/extension/guestfs_php.c
python/guestfs-py.c
regressions/rhbz501893.c
regressions/test-lvm-mapping.pl
diff --git a/src/generator.ml b/src/generator.ml
index 1d5707dd..2a5ecfbc 100755
--- a/src/generator.ml
+++ b/src/generator.ml
@@ -11978,6 +11978,431 @@ namespace Guestfs
}
"
+and generate_php_h () =
+ generate_header CStyle LGPLv2plus;
+
+ pr "\
+#ifndef PHP_GUESTFS_PHP_H
+#define PHP_GUESTFS_PHP_H 1
+
+#ifdef ZTS
+#include \"TSRM.h\"
+#endif
+
+#define PHP_GUESTFS_PHP_EXTNAME \"guestfs_php\"
+#define PHP_GUESTFS_PHP_VERSION \"1.0\"
+
+PHP_MINIT_FUNCTION (guestfs_php);
+
+#define PHP_GUESTFS_HANDLE_RES_NAME \"guestfs_h\"
+
+PHP_FUNCTION (guestfs_create);
+PHP_FUNCTION (guestfs_last_error);
+";
+
+ List.iter (
+ fun (shortname, style, _, _, _, _, _) ->
+ pr "PHP_FUNCTION (guestfs_%s);\n" shortname
+ ) all_functions_sorted;
+
+ pr "\
+
+extern zend_module_entry guestfs_php_module_entry;
+#define phpext_guestfs_php_ptr &guestfs_php_module_entry
+
+#endif /* PHP_GUESTFS_PHP_H */
+"
+
+and generate_php_c () =
+ generate_header CStyle LGPLv2plus;
+
+ pr "\
+/* NOTE: Be very careful with all macros in PHP header files. The
+ * morons who wrote them aren't good at making them safe for inclusion
+ * in arbitrary places in C code, eg. not using 'do ... while(0)'
+ * or parenthesizing any of the arguments.
+ */
+
+/* NOTE (2): Some parts of the API can't be used on 32 bit platforms.
+ * Any 64 bit numbers will be truncated. There's no easy way around
+ * this in PHP.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <php.h>
+#include <php_guestfs_php.h>
+
+#include \"guestfs.h\"
+
+static int res_guestfs_h;
+
+static void
+guestfs_php_handle_dtor (zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ guestfs_h *g = (guestfs_h *) rsrc->ptr;
+ if (g != NULL)
+ guestfs_close (g);
+}
+
+PHP_MINIT_FUNCTION (guestfs_php)
+{
+ res_guestfs_h =
+ zend_register_list_destructors_ex (guestfs_php_handle_dtor,
+ NULL, PHP_GUESTFS_HANDLE_RES_NAME, module_number);
+}
+
+static function_entry guestfs_php_functions[] = {
+ PHP_FE (guestfs_create, NULL)
+ PHP_FE (guestfs_last_error, NULL)
+";
+
+ List.iter (
+ fun (shortname, style, _, _, _, _, _) ->
+ pr " PHP_FE (guestfs_%s, NULL)\n" shortname
+ ) all_functions_sorted;
+
+ pr " { NULL, NULL, NULL }
+};
+
+zend_module_entry guestfs_php_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ PHP_GUESTFS_PHP_EXTNAME,
+ guestfs_php_functions,
+ PHP_MINIT (guestfs_php),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+#if ZEND_MODULE_API_NO >= 20010901
+ PHP_GUESTFS_PHP_VERSION,
+#endif
+ STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_GUESTFS_PHP
+ZEND_GET_MODULE (guestfs_php)
+#endif
+
+PHP_FUNCTION (guestfs_create)
+{
+ guestfs_h *g = guestfs_create ();
+ if (g == NULL) {
+ RETURN_FALSE;
+ }
+
+ guestfs_set_error_handler (g, NULL, NULL);
+
+ ZEND_REGISTER_RESOURCE (return_value, g, res_guestfs_h);
+}
+
+PHP_FUNCTION (guestfs_last_error)
+{
+ zval *z_g;
+ guestfs_h *g;
+
+ if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, \"r\",
+ &z_g) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE (g, guestfs_h *, &z_g, -1, PHP_GUESTFS_HANDLE_RES_NAME,
+ res_guestfs_h);
+ if (g == NULL) {
+ RETURN_FALSE;
+ }
+
+ const char *err = guestfs_last_error (g);
+ if (err) {
+ RETURN_STRING (err, 1);
+ } else {
+ RETURN_NULL ();
+ }
+}
+
+";
+
+ (* Now generate the PHP bindings for each action. *)
+ List.iter (
+ fun (shortname, style, _, _, _, _, _) ->
+ pr "PHP_FUNCTION (guestfs_%s)\n" shortname;
+ pr "{\n";
+ pr " zval *z_g;\n";
+ pr " guestfs_h *g;\n";
+
+ List.iter (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | Key n
+ | OptString n
+ | BufferIn n ->
+ pr " char *%s;\n" n;
+ pr " int %s_size;\n" n
+ | StringList n
+ | DeviceList n ->
+ pr " zval *z_%s;\n" n;
+ pr " char **%s;\n" n;
+ | Bool n ->
+ pr " zend_bool %s;\n" n
+ | Int n | Int64 n ->
+ pr " long %s;\n" n
+ ) (snd style);
+
+ pr "\n";
+
+ (* Parse the parameters. *)
+ let param_string = String.concat "" (
+ List.map (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | BufferIn n | Key n -> "s"
+ | OptString n -> "s!"
+ | StringList n | DeviceList n -> "a"
+ | Bool n -> "b"
+ | Int n | Int64 n -> "l"
+ ) (snd style)
+ ) in
+
+ pr " if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, \"r%s\",\n"
+ param_string;
+ pr " &z_g";
+ List.iter (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | BufferIn n | Key n
+ | OptString n ->
+ pr ", &%s, &%s_size" n n
+ | StringList n | DeviceList n ->
+ pr ", &z_%s" n
+ | Bool n ->
+ pr ", &%s" n
+ | Int n | Int64 n ->
+ pr ", &%s" n
+ ) (snd style);
+ pr ") == FAILURE) {\n";
+ pr " RETURN_FALSE;\n";
+ pr " }\n";
+ pr "\n";
+ pr " ZEND_FETCH_RESOURCE (g, guestfs_h *, &z_g, -1, PHP_GUESTFS_HANDLE_RES_NAME,\n";
+ pr " res_guestfs_h);\n";
+ pr " if (g == NULL) {\n";
+ pr " RETURN_FALSE;\n";
+ pr " }\n";
+ pr "\n";
+
+ List.iter (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | Key n
+ | OptString n ->
+ (* Just need to check the string doesn't contain any ASCII
+ * NUL characters, which won't be supported by the C API.
+ *)
+ pr " if (strlen (%s) != %s_size) {\n" n n;
+ pr " fprintf (stderr, \"libguestfs: %s: parameter '%s' contains embedded ASCII NUL.\\n\");\n" shortname n;
+ pr " RETURN_FALSE;\n";
+ pr " }\n";
+ pr "\n"
+ | BufferIn n -> ()
+ | StringList n
+ | DeviceList n ->
+ (* Convert array to list of strings.
+ * http://marc.info/?l=pecl-dev&m=112205192100631&w=2
+ *)
+ pr " {\n";
+ pr " HashTable *a;\n";
+ pr " int n;\n";
+ pr " HashPosition p;\n";
+ pr " zval **d;\n";
+ pr " size_t c = 0;\n";
+ pr "\n";
+ pr " a = Z_ARRVAL_P (z_%s);\n" n;
+ pr " n = zend_hash_num_elements (a);\n";
+ pr " %s = safe_emalloc (n + 1, sizeof (char *), 0);\n" n;
+ pr " for (zend_hash_internal_pointer_reset_ex (a, &p);\n";
+ pr " zend_hash_get_current_data_ex (a, (void **) &d, &p) == SUCCESS;\n";
+ pr " zend_hash_move_forward_ex (a, &p)) {\n";
+ pr " zval t = **d;\n";
+ pr " zval_copy_ctor (&t);\n";
+ pr " convert_to_string (&t);\n";
+ pr " %s[c] = Z_STRVAL (t);\n" n;
+ pr " c++;\n";
+ pr " }\n";
+ pr " %s[c] = NULL;\n" n;
+ pr " }\n";
+ pr "\n"
+ | Bool n | Int n | Int64 n -> ()
+ ) (snd style);
+
+ (* Return value. *)
+ let error_code =
+ match fst style with
+ | RErr -> pr " int r;\n"; "-1"
+ | RBool _
+ | RInt _ -> pr " int r;\n"; "-1"
+ | RInt64 _ -> pr " int64_t r;\n"; "-1"
+ | RConstString _ -> pr " const char *r;\n"; "NULL"
+ | RConstOptString _ -> pr " const char *r;\n"; "NULL"
+ | RString _ ->
+ pr " char *r;\n"; "NULL"
+ | RStringList _ ->
+ pr " char **r;\n"; "NULL"
+ | RStruct (_, typ) ->
+ pr " struct guestfs_%s *r;\n" typ; "NULL"
+ | RStructList (_, typ) ->
+ pr " struct guestfs_%s_list *r;\n" typ; "NULL"
+ | RHashtable _ ->
+ pr " char **r;\n"; "NULL"
+ | RBufferOut _ ->
+ pr " char *r;\n";
+ pr " size_t size;\n";
+ "NULL" in
+
+ (* Call the function. *)
+ pr " r = guestfs_%s " shortname;
+ generate_c_call_args ~handle:"g" style;
+ pr ";\n";
+ pr "\n";
+
+ (* Free up parameters. *)
+ List.iter (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | Key n
+ | OptString n -> ()
+ | BufferIn n -> ()
+ | StringList n
+ | DeviceList n ->
+ pr " {\n";
+ pr " size_t c = 0;\n";
+ pr "\n";
+ pr " for (c = 0; %s[c] != NULL; ++c)\n" n;
+ pr " efree (%s[c]);\n" n;
+ pr " efree (%s);\n" n;
+ pr " }\n";
+ pr "\n"
+ | Bool n | Int n | Int64 n -> ()
+ ) (snd style);
+
+ (* Check for errors. *)
+ pr " if (r == %s) {\n" error_code;
+ pr " RETURN_FALSE;\n";
+ pr " }\n";
+ pr "\n";
+
+ (* Convert the return value. *)
+ (match fst style with
+ | RErr ->
+ pr " RETURN_TRUE;\n"
+ | RBool _ ->
+ pr " RETURN_BOOL (r);\n"
+ | RInt _ ->
+ pr " RETURN_LONG (r);\n"
+ | RInt64 _ ->
+ pr " RETURN_LONG (r);\n"
+ | RConstString _ ->
+ pr " RETURN_STRING (r, 1);\n"
+ | RConstOptString _ ->
+ pr " if (r) { RETURN_STRING (r, 1); }\n";
+ pr " else { RETURN_NULL (); }\n"
+ | RString _ ->
+ pr " char *r_copy = estrdup (r);\n";
+ pr " free (r);\n";
+ pr " RETURN_STRING (r_copy, 0);\n"
+ | RBufferOut _ ->
+ pr " char *r_copy = estrndup (r, size);\n";
+ pr " free (r);\n";
+ pr " RETURN_STRING (r_copy, 0);\n"
+ | RStringList _ ->
+ pr " size_t c = 0;\n";
+ pr " array_init (return_value);\n";
+ pr " for (c = 0; r[c] != NULL; ++c) {\n";
+ pr " add_next_index_string (return_value, r[c], 1);\n";
+ pr " free (r[c]);\n";
+ pr " }\n";
+ pr " free (r);\n";
+ | RHashtable _ ->
+ pr " size_t c = 0;\n";
+ pr " array_init (return_value);\n";
+ pr " for (c = 0; r[c] != NULL; c += 2) {\n";
+ pr " add_assoc_string (return_value, r[c], r[c+1], 1);\n";
+ pr " free (r[c]);\n";
+ pr " free (r[c+1]);\n";
+ pr " }\n";
+ pr " free (r);\n";
+ | RStruct (_, typ) ->
+ let cols = cols_of_struct typ in
+ generate_php_struct_code typ cols
+ | RStructList (_, typ) ->
+ let cols = cols_of_struct typ in
+ generate_php_struct_list_code typ cols
+ );
+
+ pr "}\n";
+ pr "\n"
+ ) all_functions_sorted
+
+and generate_php_struct_code typ cols =
+ pr " array_init (return_value);\n";
+ List.iter (
+ function
+ | name, FString ->
+ pr " add_assoc_string (return_value, \"%s\", r->%s, 1);\n" name name
+ | name, FBuffer ->
+ pr " add_assoc_stringl (return_value, \"%s\", r->%s, r->%s_len, 1);\n"
+ name name name
+ | name, FUUID ->
+ pr " add_assoc_stringl (return_value, \"%s\", r->%s, 32, 1);\n"
+ name name
+ | name, (FBytes|FUInt64|FInt64|FInt32|FUInt32) ->
+ pr " add_assoc_long (return_value, \"%s\", r->%s);\n"
+ name name
+ | name, FChar ->
+ pr " add_assoc_stringl (return_value, \"%s\", &r->%s, 1, 1);\n"
+ name name
+ | name, FOptPercent ->
+ pr " add_assoc_double (return_value, \"%s\", r->%s);\n"
+ name name
+ ) cols;
+ pr " guestfs_free_%s (r);\n" typ
+
+and generate_php_struct_list_code typ cols =
+ pr " array_init (return_value);\n";
+ pr " size_t c = 0;\n";
+ pr " for (c = 0; c < r->len; ++c) {\n";
+ pr " zval *z_elem;\n";
+ pr " ALLOC_INIT_ZVAL (z_elem);\n";
+ pr " array_init (z_elem);\n";
+ List.iter (
+ function
+ | name, FString ->
+ pr " add_assoc_string (z_elem, \"%s\", r->val[c].%s, 1);\n"
+ name name
+ | name, FBuffer ->
+ pr " add_assoc_stringl (z_elem, \"%s\", r->val[c].%s, r->val[c].%s_len, 1);\n"
+ name name name
+ | name, FUUID ->
+ pr " add_assoc_stringl (z_elem, \"%s\", r->val[c].%s, 32, 1);\n"
+ name name
+ | name, (FBytes|FUInt64|FInt64|FInt32|FUInt32) ->
+ pr " add_assoc_long (z_elem, \"%s\", r->val[c].%s);\n"
+ name name
+ | name, FChar ->
+ pr " add_assoc_stringl (z_elem, \"%s\", &r->val[c].%s, 1, 1);\n"
+ name name
+ | name, FOptPercent ->
+ pr " add_assoc_double (z_elem, \"%s\", r->val[c].%s);\n"
+ name name
+ ) cols;
+ pr " add_next_index_zval (return_value, z_elem);\n";
+ pr " }\n";
+ pr " guestfs_free_%s_list (r);\n" typ
+
and generate_bindtests () =
generate_header CStyle LGPLv2plus;
@@ -12517,6 +12942,8 @@ Run it from the top source directory using the command
output_to "haskell/Guestfs.hs" generate_haskell_hs;
output_to "haskell/Bindtests.hs" generate_haskell_bindtests;
output_to "csharp/Libguestfs.cs" generate_csharp;
+ output_to "php/extension/php_guestfs_php.h" generate_php_h;
+ output_to "php/extension/guestfs_php.c" generate_php_c;
(* Always generate this file last, and unconditionally. It's used
* by the Makefile to know when we must re-run the generator.
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 24d5aef1..82fd7a1b 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -43,7 +43,7 @@ FUSE.
Libguestfs is a library that can be linked with C and C++ management
programs (or management programs written in OCaml, Perl, Python, Ruby,
-Java, Haskell or C#). You can also use it from shell scripts or the
+Java, PHP, Haskell or C#). You can also use it from shell scripts or the
command line.
You don't need to be root to use libguestfs, although obviously you do
@@ -626,6 +626,13 @@ For documentation see the file C<guestfs.mli>.
For documentation see L<Sys::Guestfs(3)>.
+=item B<PHP>
+
+For documentation see C<README-PHP> supplied with libguestfs
+sources or in the php-libguestfs package for your distribution.
+
+The PHP binding only works correctly on 64 bit machines.
+
=item B<Python>
For documentation do: