[Libguestfs] [PATCH (for discussion only)] PHP bindings.

Richard W.M. Jones rjones at redhat.com
Fri Sep 3 16:35:02 UTC 2010


The bindings are complete, but it needs a great deal more testing.
Therefore this is just a preview.

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines.  Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
>From 8bca8234fa2856276f6ec7f2dcc3ffc4bcc70240 Mon Sep 17 00:00:00 2001
From: Richard Jones <rjones at redhat.com>
Date: Fri, 3 Sep 2010 12:15:00 +0100
Subject: [PATCH] PHP bindings.

Note that these are not complete on 32 bit architectures.  PHP doesn't
offer any convenient 64 bit type (on 32 bit).  Therefore you should
always use these PHP bindings on 64 bit.
---
 .gitignore                         |   18 ++
 Makefile.am                        |    3 +
 configure.ac                       |    8 +
 php/Makefile.am                    |   50 +++++
 php/extension/config.m4            |   24 ++
 php/extension/guestfs_php_001.phpt |   17 ++
 php/extension/guestfs_php_002.phpt |   36 +++
 php/run-php-tests.sh               |   35 +++
 po/POTFILES.in                     |    1 +
 src/generator.ml                   |  428 ++++++++++++++++++++++++++++++++++++
 src/guestfs.pod                    |    4 +
 11 files changed, 624 insertions(+), 0 deletions(-)
 create mode 100644 php/Makefile.am
 create mode 100644 php/extension/config.m4
 create mode 100644 php/extension/guestfs_php_001.phpt
 create mode 100644 php/extension/guestfs_php_002.phpt
 create mode 100755 php/run-php-tests.sh

diff --git a/.gitignore b/.gitignore
index 56ad9bd..cfea549 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 085202b..3f8143b 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/configure.ac b/configure.ac
index 935abdc..5a44d8b 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 0000000..4bfb596
--- /dev/null
+++ b/php/Makefile.am
@@ -0,0 +1,50 @@
+# 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_libguestfs.h \
+	extension/guestfs_php.c
+
+EXTRA_DIST = \
+	$(generator_built)
+
+if HAVE_PHP
+
+# 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 DESTDIR=$(DESTDIR) install
+
+endif
diff --git a/php/extension/config.m4 b/php/extension/config.m4
new file mode 100644
index 0000000..2bac2ea
--- /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 0000000..771592f
--- /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 0000000..48ee0b6
--- /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/run-php-tests.sh b/php/run-php-tests.sh
new file mode 100755
index 0000000..38a5e38
--- /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 3faa1fb..a06249a 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 1d5707d..3a5ff90 100755
--- a/src/generator.ml
+++ b/src/generator.ml
@@ -11978,6 +11978,432 @@ 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\"
+
+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 +12943,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 24d5aef..44787cd 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -626,6 +626,10 @@ For documentation see the file C<guestfs.mli>.
 
 For documentation see L<Sys::Guestfs(3)>.
 
+=item B<PHP>
+
+This binding only works correctly on 64 bit machines.
+
 =item B<Python>
 
 For documentation do:
-- 
1.7.1



More information about the Libguestfs mailing list