From f08dc82d066b600cff73720cd040e5d7d50c36eb Mon Sep 17 00:00:00 2001
From: Kleis Auke Wolthuizen <github@kleisauke.nl>
Date: Mon, 6 Sep 2021 10:57:56 +0200
Subject: [PATCH 01/14] Ensure extension is linked with -Wl,-z,nodelete

See: https://github.com/libvips/php-vips-ext/issues/43.
---
 .gitattributes             |  1 +
 config.m4                  |  9 +++++++
 m4/ax_append_flag.m4       | 50 +++++++++++++++++++++++++++++++++++
 m4/ax_append_link_flags.m4 | 44 +++++++++++++++++++++++++++++++
 m4/ax_check_link_flag.m4   | 53 ++++++++++++++++++++++++++++++++++++++
 m4/ax_require_defined.m4   | 37 ++++++++++++++++++++++++++
 package.xml                |  7 +++++
 vips.c                     | 36 --------------------------
 8 files changed, 201 insertions(+), 36 deletions(-)
 create mode 100644 .gitattributes
 create mode 100644 m4/ax_append_flag.m4
 create mode 100644 m4/ax_append_link_flags.m4
 create mode 100644 m4/ax_check_link_flag.m4
 create mode 100644 m4/ax_require_defined.m4

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6f727e1
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+m4/** linguist-generated=true linguist-vendored=true
diff --git a/config.m4 b/config.m4
index 729cc25..b72931e 100644
--- a/config.m4
+++ b/config.m4
@@ -1,6 +1,11 @@
 dnl $Id$
 dnl config.m4 for extension vips
 
+m4_include(m4/ax_require_defined.m4)
+m4_include(m4/ax_append_flag.m4)
+m4_include(m4/ax_check_link_flag.m4)
+m4_include(m4/ax_append_link_flags.m4)
+
 PHP_ARG_WITH(vips, for vips support,
 [  --with-vips             Include vips support])
 
@@ -35,6 +40,10 @@ if test x"$PHP_VIPS" != x"no"; then
   ],[$VIPS_LIBS]
   )
 
+  # Mark DSO non-deletable at runtime.
+  # See: https://github.com/libvips/php-vips-ext/issues/43
+  AX_APPEND_LINK_FLAGS([-Wl,-z,nodelete])
+
   AC_DEFINE(HAVE_VIPS, 1, [Whether you have vips])
   PHP_NEW_EXTENSION(vips, vips.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 $VIPS_CFLAGS)
   PHP_SUBST(VIPS_SHARED_LIBADD)
diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4
new file mode 100644
index 0000000..dd6d8b6
--- /dev/null
+++ b/m4/ax_append_flag.m4
@@ -0,0 +1,50 @@
+# ===========================================================================
+#      https://www.gnu.org/software/autoconf-archive/ax_append_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
+#
+# DESCRIPTION
+#
+#   FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
+#   added in between.
+#
+#   If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
+#   CFLAGS) is used.  FLAGS-VARIABLE is not changed if it already contains
+#   FLAG.  If FLAGS-VARIABLE is unset in the shell, it is set to exactly
+#   FLAG.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 8
+
+AC_DEFUN([AX_APPEND_FLAG],
+[dnl
+AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
+AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
+AS_VAR_SET_IF(FLAGS,[
+  AS_CASE([" AS_VAR_GET(FLAGS) "],
+    [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
+    [
+     AS_VAR_APPEND(FLAGS,[" $1"])
+     AC_RUN_LOG([: FLAGS="$FLAGS"])
+    ])
+  ],
+  [
+  AS_VAR_SET(FLAGS,[$1])
+  AC_RUN_LOG([: FLAGS="$FLAGS"])
+  ])
+AS_VAR_POPDEF([FLAGS])dnl
+])dnl AX_APPEND_FLAG
diff --git a/m4/ax_append_link_flags.m4 b/m4/ax_append_link_flags.m4
new file mode 100644
index 0000000..99b9fa5
--- /dev/null
+++ b/m4/ax_append_link_flags.m4
@@ -0,0 +1,44 @@
+# ===========================================================================
+#   https://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   For every FLAG1, FLAG2 it is checked whether the linker works with the
+#   flag.  If it does, the flag is added FLAGS-VARIABLE
+#
+#   If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is
+#   used. During the check the flag is always added to the linker's flags.
+#
+#   If EXTRA-FLAGS is defined, it is added to the linker's default flags
+#   when the check is done.  The check is thus made with the flags: "LDFLAGS
+#   EXTRA-FLAGS FLAG".  This can for example be used to force the linker to
+#   issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+#   NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG.
+#   Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS.
+#
+# LICENSE
+#
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 7
+
+AC_DEFUN([AX_APPEND_LINK_FLAGS],
+[AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
+AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
+for flag in $1; do
+  AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3], [$4])
+done
+])dnl AX_APPEND_LINK_FLAGS
diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4
new file mode 100644
index 0000000..03a30ce
--- /dev/null
+++ b/m4/ax_check_link_flag.m4
@@ -0,0 +1,53 @@
+# ===========================================================================
+#    https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+#   Check whether the given FLAG works with the linker or gives an error.
+#   (Warnings, however, are ignored)
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   If EXTRA-FLAGS is defined, it is added to the linker's default flags
+#   when the check is done.  The check is thus made with the flags: "LDFLAGS
+#   EXTRA-FLAGS FLAG".  This can for example be used to force the linker to
+#   issue an error when a bad flag is given.
+#
+#   INPUT gives an alternative input source to AC_LINK_IFELSE.
+#
+#   NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+#   macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+#   Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved.  This file is offered as-is, without any
+#   warranty.
+
+#serial 6
+
+AC_DEFUN([AX_CHECK_LINK_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
+AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
+  ax_check_save_flags=$LDFLAGS
+  LDFLAGS="$LDFLAGS $4 $1"
+  AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+    [AS_VAR_SET(CACHEVAR,[yes])],
+    [AS_VAR_SET(CACHEVAR,[no])])
+  LDFLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+  [m4_default([$2], :)],
+  [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_LINK_FLAGS
diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4
new file mode 100644
index 0000000..17c3eab
--- /dev/null
+++ b/m4/ax_require_defined.m4
@@ -0,0 +1,37 @@
+# ===========================================================================
+#    https://www.gnu.org/software/autoconf-archive/ax_require_defined.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_REQUIRE_DEFINED(MACRO)
+#
+# DESCRIPTION
+#
+#   AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
+#   been defined and thus are available for use.  This avoids random issues
+#   where a macro isn't expanded.  Instead the configure script emits a
+#   non-fatal:
+#
+#     ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
+#
+#   It's like AC_REQUIRE except it doesn't expand the required macro.
+#
+#   Here's an example:
+#
+#     AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
+#
+# LICENSE
+#
+#   Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 2
+
+AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
+  m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
+])dnl AX_REQUIRE_DEFINED
diff --git a/package.xml b/package.xml
index f60055a..5a1ea71 100644
--- a/package.xml
+++ b/package.xml
@@ -42,6 +42,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <file role='src' name='php_vips.h'/>
    <file role='src' name='vips.c'/>
 
+   <dir name="m4">
+    <file role='src' name='ax_append_flag.m4'/>
+    <file role='src' name='ax_append_link_flags.m4'/>
+    <file role='src' name='ax_check_link_flag.m4'/>
+    <file role='src' name='ax_require_defined.m4'/>
+   </dir>
+
    <dir name="tests">
     <file role='test' name='001.phpt'/>
     <file role='test' name='002.phpt'/>
diff --git a/vips.c b/vips.c
index 33b47d4..2e96aad 100644
--- a/vips.c
+++ b/vips.c
@@ -13,7 +13,6 @@
 #include "php.h"
 #include "php_ini.h"
 #include "ext/standard/info.h"
-#include "SAPI.h"
 #include "php_vips.h"
 
 #include <vips/vips.h>
@@ -2007,41 +2006,6 @@ static void php_free_gobject(zend_resource *rsrc)
  */
 PHP_MINIT_FUNCTION(vips)
 {
-	if (strcmp(sapi_module.name, "apache2handler") == 0) {
-		/* "apachectl graceful" can cause us terrible problems. What happens:
-		 *
-		 * - the main apache process unloads this extension, vips.so
-		 * - in turn, the C runtime will unload libvips.so, the vips library,
-		 *   since vips.so is the only thing that references it
-		 * - libvips.so in turn uses glib.so, but this is often not unloaded,
-		 *   since other parts of apache can be using it (glib could also
-		 *   possibly be preventing unload itself, I'm not sure)
-		 * - the main apache process then reloads vips.so, which in turn will
-		 *   reload libvips.so as it starts up
-		 * - vips.so tries to init libvips.so
-		 * - libvips.so tries to register its types (such as VipsImage) with
-		 *   glib.so, but finds the types from the previous init still there
-		 * - everything breaks
-		 *
-		 * A simple fix that will always work is just to lock libvips in 
-		 * memory and prevent unload. We intentionally leak refs to the shared
-		 * library. 
-		 *
-		 * We include the binary API version number that this extension needs. 
-		 * We can't just load .so, that's only installed with libvips-dev, 
-		 * which may not be present at runtime.
-		 */
-#ifdef VIPS_SONAME
-		if (!dlopen(VIPS_SONAME, RTLD_LAZY | RTLD_NODELETE)) 
-#else /*!VIPS_SONAME*/
-		if (!dlopen("libvips.so.42", RTLD_LAZY | RTLD_NODELETE)) 
-#endif /*VIPS_SONAME*/
-		{
-			sapi_module.sapi_error(E_WARNING, "php-vips-ext: unable to lock "
-				"libvips -- graceful may be unreliable");
-		}
-	}
-
 	/* If you have INI entries, uncomment these lines
 	REGISTER_INI_ENTRIES();
 	*/

From ffc316f2db3066da8cd21ec5ceec92f2a11c799e Mon Sep 17 00:00:00 2001
From: Kleis Auke Wolthuizen <github@kleisauke.nl>
Date: Mon, 6 Sep 2021 19:29:52 +0200
Subject: [PATCH 02/14] Generate arginfo from PHP stub file

---
 .gitignore     |   4 +
 README.md      |   9 +++
 vips.c         | 202 ++++---------------------------------------------
 vips.stub.php  | 117 ++++++++++++++++++++++++++++
 vips_arginfo.h | 189 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 333 insertions(+), 188 deletions(-)
 create mode 100644 vips.stub.php
 create mode 100644 vips_arginfo.h

diff --git a/.gitignore b/.gitignore
index 3217671..56a1eaa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,9 +30,13 @@ missing
 mkinstalldirs
 modules
 run-tests.php
+gen_stub.php
 tests/*/*.diff
 tests/*/*.out
 tests/*/*.php
 tests/*/*.exp
 tests/*/*.log
 tests/*/*.sh
+
+# Used by gen_stub.php
+PHP-Parser-*
diff --git a/README.md b/README.md
index 655bb34..a46a501 100644
--- a/README.md
+++ b/README.md
@@ -143,6 +143,15 @@ $ make install
 Add `extension=vips.so` to `php.ini`, perhaps in `~/vips/lib/php.ini`, 
 if you configured php as above. 
 
+### Development: regenerate `vips_arginfo.h`
+
+Run:
+
+```
+$ curl -LO https://github.com/php/php-src/raw/ffacda14b88be797a466f472359f306d626e698f/build/gen_stub.php
+$ php gen_stub.php
+```
+
 ### Links
 
 http://php.net/manual/en/internals2.php
diff --git a/vips.c b/vips.c
index 3eace25..ae5a8e5 100644
--- a/vips.c
+++ b/vips.c
@@ -24,6 +24,8 @@
 ZEND_DECLARE_MODULE_GLOBALS(vips)
 */
 
+#include "vips_arginfo.h"
+
 /* True global resources - no need for thread safety here */
 static int le_gobject;
 
@@ -1047,7 +1049,7 @@ vips_php_call_array(const char *operation_name, zval *instance,
 
 /* }}} */
 
-/* {{{ proto mixed vips_php_call(string operation_name, resource instance [, more])
+/* {{{ proto array|long vips_call(string operation_name, resource instance [, mixed args])
    Call any vips operation */
 
 PHP_FUNCTION(vips_call)
@@ -1094,7 +1096,7 @@ PHP_FUNCTION(vips_call)
 }
 /* }}} */
 
-/* {{{ proto resource vips_image_new_from_file(string filename [, array options])
+/* {{{ proto array|long vips_image_new_from_file(string filename [, array options])
    Open an image from a filename */
 PHP_FUNCTION(vips_image_new_from_file)
 {
@@ -1138,7 +1140,7 @@ PHP_FUNCTION(vips_image_new_from_file)
 }
 /* }}} */
 
-/* {{{ proto resource vips_image_new_from_buffer(string buffer [, string option_string, array options])
+/* {{{ proto array|long vips_image_new_from_buffer(string buffer [, string option_string, array options])
    Open an image from a string */
 PHP_FUNCTION(vips_image_new_from_buffer)
 {
@@ -1237,7 +1239,7 @@ PHP_FUNCTION(vips_image_new_from_array)
 }
 /* }}} */
 
-/* {{{ proto resource vips_interpolate_new(string name])
+/* {{{ proto resource vips_interpolate_new(string name)
    make a new interpolator */
 PHP_FUNCTION(vips_interpolate_new)
 {
@@ -1260,7 +1262,7 @@ PHP_FUNCTION(vips_interpolate_new)
 }
 /* }}} */
 
-/* {{{ proto long vips_image_write_to_file(resource image, string filename [, array options])
+/* {{{ proto array|long vips_image_write_to_file(resource image, string filename [, array options])
    Write an image to a filename */
 PHP_FUNCTION(vips_image_write_to_file)
 {
@@ -1311,7 +1313,7 @@ PHP_FUNCTION(vips_image_write_to_file)
 }
 /* }}} */
 
-/* {{{ proto string|long vips_image_write_to_buffer(resource image, string suffix [, array options])
+/* {{{ proto array|long vips_image_write_to_buffer(resource image, string suffix [, array options])
    Write an image to a string */
 PHP_FUNCTION(vips_image_write_to_buffer)
 {
@@ -1354,7 +1356,7 @@ PHP_FUNCTION(vips_image_write_to_buffer)
 }
 /* }}} */
 
-/* {{{ proto resource vips_image_copy_memory(resource image)
+/* {{{ proto array|long vips_image_copy_memory(resource image)
    Copy an image to a memory image */
 PHP_FUNCTION(vips_image_copy_memory)
 {
@@ -1388,7 +1390,7 @@ PHP_FUNCTION(vips_image_copy_memory)
 }
 /* }}} */
 
-/* {{{ proto resource vips_image_new_from_memory(string data, integer width, integer height, integer bands, string format)
+/* {{{ proto array|long vips_image_new_from_memory(string memory, integer width, integer height, integer bands, string format)
    Wrap an image around a memory array. */
 PHP_FUNCTION(vips_image_new_from_memory)
 {
@@ -1431,7 +1433,7 @@ PHP_FUNCTION(vips_image_new_from_memory)
 }
 /* }}} */
 
-/* {{{ proto string vips_image_write_to_memory(resource image)
+/* {{{ proto string|long vips_image_write_to_memory(resource image)
    Write an image to a memory array. */
 PHP_FUNCTION(vips_image_write_to_memory)
 {
@@ -1469,7 +1471,7 @@ PHP_FUNCTION(vips_image_write_to_memory)
 		APPEND(return_value, p[i]); \
 }
 
-/* {{{ proto array vips_image_write_to_array(resource image)
+/* {{{ proto array|long vips_image_write_to_array(resource image)
    Write an image to a PHP array. */
 PHP_FUNCTION(vips_image_write_to_array)
 {
@@ -1588,7 +1590,7 @@ PHP_FUNCTION(vips_foreign_find_load_buffer)
 }
 /* }}} */
 
-/* {{{ proto array vips_image_get(resource image, string field)
+/* {{{ proto array|long vips_image_get(resource image, string field)
    Fetch field from image */
 PHP_FUNCTION(vips_image_get)
 {
@@ -2195,188 +2197,12 @@ PHP_MINFO_FUNCTION(vips)
 }
 /* }}} */
 
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_new_from_file, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
-	ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_new_from_buffer, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, option_string, IS_STRING, 0)
-	ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_new_from_array, 0, 0, 1)
-	ZEND_ARG_ARRAY_INFO(0, array, 0)
-	ZEND_ARG_TYPE_INFO(0, scale, IS_DOUBLE, 0)
-	ZEND_ARG_TYPE_INFO(0, offset, IS_DOUBLE, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_write_to_file, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
-	ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_write_to_buffer, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-	ZEND_ARG_TYPE_INFO(0, suffix, IS_STRING, 0)
-	ZEND_ARG_ARRAY_INFO(0, options, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_copy_memory, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_new_from_memory, 0, 0, 5)
-	ZEND_ARG_TYPE_INFO(0, memory, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, width, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, height, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, bands, IS_LONG, 0)
-	ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_write_to_memory, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_write_to_array, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_foreign_find_load, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_interpolate_new, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_foreign_find_load_buffer, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_call, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
-	ZEND_ARG_TYPE_INFO(0, resource, IS_RESOURCE, 1)
-	ZEND_ARG_VARIADIC_INFO(0, vars)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_get, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_get_typeof, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_set, 0, 0, 3)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
-	ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_type_from_name, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_set_type, 0, 0, 4)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-	ZEND_ARG_TYPE_MASK(0, type, MAY_BE_LONG|MAY_BE_STRING, NULL)
-	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
-	ZEND_ARG_INFO(0, value)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_remove, 0, 0, 2)
-	ZEND_ARG_TYPE_INFO(0, image, IS_RESOURCE, 0)
-	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO(arginfo_vips_error_buffer, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_cache_set_max, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_cache_set_max_mem, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_cache_set_max_files, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_concurrency_set, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_get_max, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_get_max_mem, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_get_max_files, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_get_size, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO(arginfo_vips_concurrency_get, 0)
-ZEND_END_ARG_INFO()
-
-ZEND_BEGIN_ARG_INFO(arginfo_vips_version, 0)
-ZEND_END_ARG_INFO()
-/* {{{ vips_functions[]
- *
- * Every user visible function must have an entry in vips_functions[].
- */
-const zend_function_entry vips_functions[] = {
-	PHP_FE(vips_image_new_from_file, arginfo_vips_image_new_from_file)
-	PHP_FE(vips_image_new_from_buffer, arginfo_vips_image_new_from_buffer)
-	PHP_FE(vips_image_new_from_array, arginfo_vips_image_new_from_array)
-	PHP_FE(vips_image_write_to_file, arginfo_vips_image_write_to_file)
-	PHP_FE(vips_image_write_to_buffer, arginfo_vips_image_write_to_buffer)
-	PHP_FE(vips_image_copy_memory, arginfo_vips_image_copy_memory)
-	PHP_FE(vips_image_new_from_memory, arginfo_vips_image_new_from_memory)
-	PHP_FE(vips_image_write_to_memory, arginfo_vips_image_write_to_memory)
-	PHP_FE(vips_image_write_to_array, arginfo_vips_image_write_to_array)
-	PHP_FE(vips_foreign_find_load, arginfo_vips_foreign_find_load)
-	PHP_FE(vips_foreign_find_load_buffer, arginfo_vips_foreign_find_load_buffer)
-	PHP_FE(vips_interpolate_new, arginfo_vips_interpolate_new)
-
-	PHP_FE(vips_call, arginfo_vips_call)
-	PHP_FE(vips_image_get, arginfo_vips_image_get)
-	PHP_FE(vips_image_get_typeof, arginfo_vips_image_get_typeof)
-	PHP_FE(vips_image_set, arginfo_vips_image_set)
-	PHP_FE(vips_type_from_name, arginfo_vips_type_from_name)
-	PHP_FE(vips_image_set_type, arginfo_vips_image_set_type)
-	PHP_FE(vips_image_remove, arginfo_vips_image_remove)
-	PHP_FE(vips_error_buffer, arginfo_vips_error_buffer)
-	PHP_FE(vips_cache_set_max, arginfo_vips_cache_set_max)
-	PHP_FE(vips_cache_set_max_mem, arginfo_vips_cache_set_max_mem)
-	PHP_FE(vips_cache_set_max_files, arginfo_vips_cache_set_max_files)
-	PHP_FE(vips_concurrency_set, arginfo_vips_concurrency_set)
-	PHP_FE(vips_cache_get_max, arginfo_vips_cache_get_max)
-	PHP_FE(vips_cache_get_max_mem, arginfo_vips_cache_get_max_mem)
-	PHP_FE(vips_cache_get_max_files, arginfo_vips_cache_get_max_files)
-	PHP_FE(vips_cache_get_size, arginfo_vips_cache_get_size)
-	PHP_FE(vips_concurrency_get, arginfo_vips_concurrency_get)
-	PHP_FE(vips_version, arginfo_vips_version)
-
-	PHP_FE_END	/* Must be the last line in vips_functions[] */
-};
-/* }}} */
-
 /* {{{ vips_module_entry
  */
 zend_module_entry vips_module_entry = {
 	STANDARD_MODULE_HEADER,
 	"vips",
-	vips_functions,
+	ext_functions,
 	PHP_MINIT(vips),
 	PHP_MSHUTDOWN(vips),
 	PHP_RINIT(vips),		/* Replace with NULL if there's nothing to do at request start */
diff --git a/vips.stub.php b/vips.stub.php
new file mode 100644
index 0000000..8b6bd99
--- /dev/null
+++ b/vips.stub.php
@@ -0,0 +1,117 @@
+<?php
+
+/** @generate-class-entries */
+
+/**
+ * @return array<string, mixed>|int
+ */
+function vips_image_new_from_file(string $filename, ?array $options = []): array|int {}
+
+/**
+ * @return array<string, mixed>|int
+ */
+function vips_image_new_from_buffer(string $filename, ?string $option_string = "", ?array $options = []): array|int {}
+
+/**
+ * @return resource
+ */
+function vips_image_new_from_array(array $coefficients, ?float $scale = 1.0, ?float $offset = 0.0) {}
+
+/**
+ * @param resource $image
+ * @return array<string, mixed>|int
+ */
+function vips_image_write_to_file($image, string $filename, ?array $options = []): array|int {}
+
+/**
+ * @param resource $image
+ * @return array<string, mixed>|int
+ */
+function vips_image_write_to_buffer($image, string $suffix, ?array $options = []): array|int {}
+
+/**
+ * @param resource $image
+ * @return array<string, resource>|int
+ */
+function vips_image_copy_memory($image): array|int {}
+
+/**
+ * @return array<string, resource>|int
+ */
+function vips_image_new_from_memory(string $memory, int $width, int $height, int $bands, string $format): array|int {}
+
+/**
+ * @param resource $image
+ */
+function vips_image_write_to_memory($image): string|int {}
+
+/**
+ * @param resource $image
+ * @return array<int, mixed>|int
+ */
+function vips_image_write_to_array($image): array|int {}
+
+function vips_foreign_find_load(string $filename): string|int {}
+
+function vips_foreign_find_load_buffer(string $buffer): string|int {}
+
+/**
+ * @return resource
+ */
+function vips_interpolate_new(string $name) {}
+
+/**
+ * @param resource $instance
+ * @return array<string, mixed>|int
+ */
+function vips_call(string $operation_name, $instance, mixed ...$args): array|int {}
+
+/**
+ * @param resource $image
+ * @return array<string, mixed>|int
+ */
+function vips_image_get($image, string $field): array|int {}
+
+/**
+ * @param resource $image
+ */
+function vips_image_get_typeof($image, string $field): int {}
+
+/**
+ * @param resource $image
+ */
+function vips_image_set($image, string $field, mixed $value): int {}
+
+function vips_type_from_name(string $name): int {}
+
+/**
+ * @param resource $image
+ */
+function vips_image_set_type($image, string|int $type, string $field, mixed $value): int {}
+
+/**
+ * @param resource $image
+ */
+function vips_image_remove($image, string $field): int {}
+
+function vips_error_buffer(): string {}
+
+function vips_cache_set_max(int $value): void {}
+
+function vips_cache_set_max_mem(int $value): void {}
+
+function vips_cache_set_max_files(int $value): void {}
+
+function vips_concurrency_set(int $value): void {}
+
+function vips_cache_get_max(): int {}
+
+function vips_cache_get_max_mem(): int {}
+
+function vips_cache_get_max_files(): int {}
+
+function vips_cache_get_size(): int {}
+
+function vips_concurrency_get(): int {}
+
+function vips_version(): string {}
diff --git a/vips_arginfo.h b/vips_arginfo.h
new file mode 100644
index 0000000..4793dc9
--- /dev/null
+++ b/vips_arginfo.h
@@ -0,0 +1,189 @@
+/* This is a generated file, edit the .stub.php file instead.
+ * Stub hash: 2c585e3b2e9c42b5c4b45b08ca338e5aade5614f */
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_new_from_file, 0, 1, MAY_BE_ARRAY|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "[]")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_new_from_buffer, 0, 1, MAY_BE_ARRAY|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option_string, IS_STRING, 1, "\"\"")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "[]")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_new_from_array, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, coefficients, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scale, IS_DOUBLE, 1, "1.0")
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_DOUBLE, 1, "0.0")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_write_to_file, 0, 2, MAY_BE_ARRAY|MAY_BE_LONG)
+	ZEND_ARG_INFO(0, image)
+	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "[]")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_write_to_buffer, 0, 2, MAY_BE_ARRAY|MAY_BE_LONG)
+	ZEND_ARG_INFO(0, image)
+	ZEND_ARG_TYPE_INFO(0, suffix, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "[]")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_copy_memory, 0, 1, MAY_BE_ARRAY|MAY_BE_LONG)
+	ZEND_ARG_INFO(0, image)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_new_from_memory, 0, 5, MAY_BE_ARRAY|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, memory, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, width, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, height, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, bands, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_write_to_memory, 0, 1, MAY_BE_STRING|MAY_BE_LONG)
+	ZEND_ARG_INFO(0, image)
+ZEND_END_ARG_INFO()
+
+#define arginfo_vips_image_write_to_array arginfo_vips_image_copy_memory
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_foreign_find_load, 0, 1, MAY_BE_STRING|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_foreign_find_load_buffer, 0, 1, MAY_BE_STRING|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_interpolate_new, 0, 0, 1)
+	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_call, 0, 2, MAY_BE_ARRAY|MAY_BE_LONG)
+	ZEND_ARG_TYPE_INFO(0, operation_name, IS_STRING, 0)
+	ZEND_ARG_INFO(0, instance)
+	ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_get, 0, 2, MAY_BE_ARRAY|MAY_BE_LONG)
+	ZEND_ARG_INFO(0, image)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_vips_image_get_typeof, 0, 2, IS_LONG, 0)
+	ZEND_ARG_INFO(0, image)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_vips_image_set, 0, 3, IS_LONG, 0)
+	ZEND_ARG_INFO(0, image)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_vips_type_from_name, 0, 1, IS_LONG, 0)
+	ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_vips_image_set_type, 0, 4, IS_LONG, 0)
+	ZEND_ARG_INFO(0, image)
+	ZEND_ARG_TYPE_MASK(0, type, MAY_BE_STRING|MAY_BE_LONG, NULL)
+	ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_vips_image_remove arginfo_vips_image_get_typeof
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_vips_error_buffer, 0, 0, IS_STRING, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_vips_cache_set_max, 0, 1, IS_VOID, 0)
+	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_vips_cache_set_max_mem arginfo_vips_cache_set_max
+
+#define arginfo_vips_cache_set_max_files arginfo_vips_cache_set_max
+
+#define arginfo_vips_concurrency_set arginfo_vips_cache_set_max
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_vips_cache_get_max, 0, 0, IS_LONG, 0)
+ZEND_END_ARG_INFO()
+
+#define arginfo_vips_cache_get_max_mem arginfo_vips_cache_get_max
+
+#define arginfo_vips_cache_get_max_files arginfo_vips_cache_get_max
+
+#define arginfo_vips_cache_get_size arginfo_vips_cache_get_max
+
+#define arginfo_vips_concurrency_get arginfo_vips_cache_get_max
+
+#define arginfo_vips_version arginfo_vips_error_buffer
+
+
+ZEND_FUNCTION(vips_image_new_from_file);
+ZEND_FUNCTION(vips_image_new_from_buffer);
+ZEND_FUNCTION(vips_image_new_from_array);
+ZEND_FUNCTION(vips_image_write_to_file);
+ZEND_FUNCTION(vips_image_write_to_buffer);
+ZEND_FUNCTION(vips_image_copy_memory);
+ZEND_FUNCTION(vips_image_new_from_memory);
+ZEND_FUNCTION(vips_image_write_to_memory);
+ZEND_FUNCTION(vips_image_write_to_array);
+ZEND_FUNCTION(vips_foreign_find_load);
+ZEND_FUNCTION(vips_foreign_find_load_buffer);
+ZEND_FUNCTION(vips_interpolate_new);
+ZEND_FUNCTION(vips_call);
+ZEND_FUNCTION(vips_image_get);
+ZEND_FUNCTION(vips_image_get_typeof);
+ZEND_FUNCTION(vips_image_set);
+ZEND_FUNCTION(vips_type_from_name);
+ZEND_FUNCTION(vips_image_set_type);
+ZEND_FUNCTION(vips_image_remove);
+ZEND_FUNCTION(vips_error_buffer);
+ZEND_FUNCTION(vips_cache_set_max);
+ZEND_FUNCTION(vips_cache_set_max_mem);
+ZEND_FUNCTION(vips_cache_set_max_files);
+ZEND_FUNCTION(vips_concurrency_set);
+ZEND_FUNCTION(vips_cache_get_max);
+ZEND_FUNCTION(vips_cache_get_max_mem);
+ZEND_FUNCTION(vips_cache_get_max_files);
+ZEND_FUNCTION(vips_cache_get_size);
+ZEND_FUNCTION(vips_concurrency_get);
+ZEND_FUNCTION(vips_version);
+
+
+static const zend_function_entry ext_functions[] = {
+	ZEND_FE(vips_image_new_from_file, arginfo_vips_image_new_from_file)
+	ZEND_FE(vips_image_new_from_buffer, arginfo_vips_image_new_from_buffer)
+	ZEND_FE(vips_image_new_from_array, arginfo_vips_image_new_from_array)
+	ZEND_FE(vips_image_write_to_file, arginfo_vips_image_write_to_file)
+	ZEND_FE(vips_image_write_to_buffer, arginfo_vips_image_write_to_buffer)
+	ZEND_FE(vips_image_copy_memory, arginfo_vips_image_copy_memory)
+	ZEND_FE(vips_image_new_from_memory, arginfo_vips_image_new_from_memory)
+	ZEND_FE(vips_image_write_to_memory, arginfo_vips_image_write_to_memory)
+	ZEND_FE(vips_image_write_to_array, arginfo_vips_image_write_to_array)
+	ZEND_FE(vips_foreign_find_load, arginfo_vips_foreign_find_load)
+	ZEND_FE(vips_foreign_find_load_buffer, arginfo_vips_foreign_find_load_buffer)
+	ZEND_FE(vips_interpolate_new, arginfo_vips_interpolate_new)
+	ZEND_FE(vips_call, arginfo_vips_call)
+	ZEND_FE(vips_image_get, arginfo_vips_image_get)
+	ZEND_FE(vips_image_get_typeof, arginfo_vips_image_get_typeof)
+	ZEND_FE(vips_image_set, arginfo_vips_image_set)
+	ZEND_FE(vips_type_from_name, arginfo_vips_type_from_name)
+	ZEND_FE(vips_image_set_type, arginfo_vips_image_set_type)
+	ZEND_FE(vips_image_remove, arginfo_vips_image_remove)
+	ZEND_FE(vips_error_buffer, arginfo_vips_error_buffer)
+	ZEND_FE(vips_cache_set_max, arginfo_vips_cache_set_max)
+	ZEND_FE(vips_cache_set_max_mem, arginfo_vips_cache_set_max_mem)
+	ZEND_FE(vips_cache_set_max_files, arginfo_vips_cache_set_max_files)
+	ZEND_FE(vips_concurrency_set, arginfo_vips_concurrency_set)
+	ZEND_FE(vips_cache_get_max, arginfo_vips_cache_get_max)
+	ZEND_FE(vips_cache_get_max_mem, arginfo_vips_cache_get_max_mem)
+	ZEND_FE(vips_cache_get_max_files, arginfo_vips_cache_get_max_files)
+	ZEND_FE(vips_cache_get_size, arginfo_vips_cache_get_size)
+	ZEND_FE(vips_concurrency_get, arginfo_vips_concurrency_get)
+	ZEND_FE(vips_version, arginfo_vips_version)
+	ZEND_FE_END
+};

From 3cbd72ca82f465703e37e87bb603ccea87ac3deb Mon Sep 17 00:00:00 2001
From: Kleis Auke Wolthuizen <github@kleisauke.nl>
Date: Mon, 6 Sep 2021 20:10:00 +0200
Subject: [PATCH 03/14] Correct variable names

---
 vips.stub.php  | 4 ++--
 vips_arginfo.h | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/vips.stub.php b/vips.stub.php
index 8b6bd99..2638f0f 100644
--- a/vips.stub.php
+++ b/vips.stub.php
@@ -10,12 +10,12 @@ function vips_image_new_from_file(string $filename, ?array $options = []): array
 /**
  * @return array<string, mixed>|int
  */
-function vips_image_new_from_buffer(string $filename, ?string $option_string = "", ?array $options = []): array|int {}
+function vips_image_new_from_buffer(string $buffer, ?string $option_string = "", ?array $options = []): array|int {}
 
 /**
  * @return resource
  */
-function vips_image_new_from_array(array $coefficients, ?float $scale = 1.0, ?float $offset = 0.0) {}
+function vips_image_new_from_array(array $array, ?float $scale = 1.0, ?float $offset = 0.0) {}
 
 /**
  * @param resource $image
diff --git a/vips_arginfo.h b/vips_arginfo.h
index 4793dc9..4d17580 100644
--- a/vips_arginfo.h
+++ b/vips_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 2c585e3b2e9c42b5c4b45b08ca338e5aade5614f */
+ * Stub hash: b0a895aa400527f647c2f9bd728c4e8a94cb0c9b */
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_new_from_file, 0, 1, MAY_BE_ARRAY|MAY_BE_LONG)
 	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
@@ -7,13 +7,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_new_from_file, 0, 1,
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_vips_image_new_from_buffer, 0, 1, MAY_BE_ARRAY|MAY_BE_LONG)
-	ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, option_string, IS_STRING, 1, "\"\"")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "[]")
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_vips_image_new_from_array, 0, 0, 1)
-	ZEND_ARG_TYPE_INFO(0, coefficients, IS_ARRAY, 0)
+	ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scale, IS_DOUBLE, 1, "1.0")
 	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_DOUBLE, 1, "0.0")
 ZEND_END_ARG_INFO()

From 32eaffae8cd865fb691abaf2a0b944d191a8e92f Mon Sep 17 00:00:00 2001
From: Kleis Auke Wolthuizen <github@kleisauke.nl>
Date: Tue, 7 Sep 2021 12:54:50 +0200
Subject: [PATCH 04/14] Ensure compatibility with PHP 7.x

---
 vips.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/vips.c b/vips.c
index ae5a8e5..813345d 100644
--- a/vips.c
+++ b/vips.c
@@ -24,6 +24,42 @@
 ZEND_DECLARE_MODULE_GLOBALS(vips)
 */
 
+/* backward compat macros */
+
+#ifndef IS_VOID
+# define IS_VOID 0
+#endif /* !defined(IS_VOID) */
+
+#ifndef IS_MIXED
+# define IS_MIXED 0
+#endif /* !defined(IS_MIXED) */
+
+#ifndef ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX
+# define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(name, return_reference, num_args, type) \
+  ZEND_BEGIN_ARG_INFO_EX(name, 0, return_reference, num_args)
+#endif /* !defined(ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX) */
+
+#ifndef ZEND_ARG_TYPE_MASK
+# define ZEND_ARG_TYPE_MASK(pass_by_ref, name, type_mask, default_value) \
+  ZEND_ARG_TYPE_INFO(pass_by_ref, name, 0, 0)
+#endif /* !defined(ZEND_ARG_TYPE_MASK) */
+
+#ifndef ZEND_ARG_VARIADIC_TYPE_INFO
+# define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, NULL, type_hint, pass_by_ref, allow_null, 1 },
+#endif /* !defined(ZEND_ARG_VARIADIC_TYPE_INFO) */
+
+#ifndef ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE
+# define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) \
+  ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)
+#endif /* !defined(ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE) */
+
+#if PHP_VERSION_ID < 70200
+# undef ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX
+# define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
+  static const zend_internal_arg_info name[] = { \
+    { (const char*)(zend_uintptr_t)(required_num_args), ( #class_name ), 0, return_reference, allow_null, 0 },
+#endif /* PHP_VERSION_ID < 70200 */
+
 #include "vips_arginfo.h"
 
 /* True global resources - no need for thread safety here */

From 41511bbccfa890cd37a6294941edb306572687ea Mon Sep 17 00:00:00 2001
From: Kleis Auke Wolthuizen <github@kleisauke.nl>
Date: Tue, 7 Sep 2021 12:58:11 +0200
Subject: [PATCH 05/14] Add missing source files to package.xml

---
 package.xml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/package.xml b/package.xml
index f60055a..ded304b 100644
--- a/package.xml
+++ b/package.xml
@@ -41,6 +41,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <file role='src' name='config.w32'/>
    <file role='src' name='php_vips.h'/>
    <file role='src' name='vips.c'/>
+   <file role='src' name='vips.stub.php'/>
+   <file role='src' name='vips_arginfo.h'/>
 
    <dir name="tests">
     <file role='test' name='001.phpt'/>

From dd70794dad575133e2bedc8081dbd55a0dea220e Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Fri, 10 Sep 2021 16:02:16 +0100
Subject: [PATCH 06/14] revise README

to stress even more that users should not use this thing directly
---
 README.md | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 655bb34..a139d88 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,11 @@
 # Low-level PHP binding for libvips 
 
-This extension lets you use the libvips image processing library from PHP 7. It 
-is intentionally very low-level: modules such as 
-https://github.com/libvips/php-vips try to layer a nice API on top of this.
+This extension lets you use the libvips image processing library
+from PHP 7. 
+
+This is not supposed to be used directly! Install this, then use 
+[php-vips](https://github.com/libvips/php-vips) to layer a nice (and
+documented!) API on top of this extension.
 
 libvips is fast and needs little memory. The [`vips-php-bench`](
 https://github.com/jcupitt/php-vips-bench) repository tests

From d94ac8388f3755d02cf14244d3a22dfb68807ab6 Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Wed, 16 Feb 2022 13:28:53 +0000
Subject: [PATCH 07/14] package 1.0.13

---
 ChangeLog                        |   7 +++++++
 README.md                        |   5 +++--
 RELEASE-1.0.12 => RELEASE-1.0.13 |   0
 package.xml                      |  23 +++++++++++++++++++----
 vips-1.0.12.tgz                  | Bin 561446 -> 564367 bytes
 vips-1.0.13.tgz                  | Bin 0 -> 564519 bytes
 6 files changed, 29 insertions(+), 6 deletions(-)
 rename RELEASE-1.0.12 => RELEASE-1.0.13 (100%)
 create mode 100644 vips-1.0.13.tgz

diff --git a/ChangeLog b/ChangeLog
index 122134d..53a912a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 vips extension changelog
 
+Version 1.0.13 (2022-2-xx)
+--------------------------
+	* php8 compatibility [kleisauke]
+	* improve module linking [kleisauke]
+	* improve packaging [kleisauke]
+	* add type hints [jcupitt, kleisauke]
+
 Version 1.0.12 (2020-8-xx)
 --------------------------
 	* vips_image_set_type() can be passed a type name
diff --git a/README.md b/README.md
index 4540bdb..73c29a5 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # Low-level PHP binding for libvips 
 
 This extension lets you use the libvips image processing library
-from PHP 7. 
+from PHP 7 and PHP 8. 
 
 This is not supposed to be used directly! Install this, then use 
 [php-vips](https://github.com/libvips/php-vips) to layer a nice (and
@@ -19,6 +19,7 @@ memory.
 #!/usr/bin/env php
 <?php
 
+# never use this extension directly! this is just an example
 $x = vips_image_new_from_file($argv[1])["out"];
 $x = vips_call("invert", $x)["out"];
 vips_image_write_to_file($x, $argv[2]);
@@ -94,7 +95,7 @@ libvips website, or build your own.
 $ pear package
 ```
 
-To make `vips-1.0.12.tgz`.
+To make `vips-1.0.13.tgz`.
 
 To install by hand:
 
diff --git a/RELEASE-1.0.12 b/RELEASE-1.0.13
similarity index 100%
rename from RELEASE-1.0.12
rename to RELEASE-1.0.13
diff --git a/package.xml b/package.xml
index 87d0370..6910e94 100644
--- a/package.xml
+++ b/package.xml
@@ -15,9 +15,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
   <email>jcupitt@php.net</email>
   <active>yes</active>
  </lead>
- <date>2020-08-29</date>
+ <date>2022-02-16</date>
  <version>
-  <release>1.0.12</release>
+  <release>1.0.13</release>
   <api>1.0.0</api>
  </version>
  <stability>
@@ -26,7 +26,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
  </stability>
  <license filesource="LICENSE.txt">MIT</license>
  <notes>
-	* vips_image_set_type() can be passed a type name
+	* php8 compatibility [kleisauke]
+	* improve module linking [kleisauke]
+	* improve packaging [kleisauke]
+	* add type hints [jcupitt, kleisauke]
  </notes>
  <contents>
   <dir name="/">
@@ -34,7 +37,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <file role='doc' name='CREDITS'/>
    <file role='doc' name='LICENSE.txt'/>
    <file role='doc' name='README.md'/>
-   <file role='doc' name='RELEASE-1.0.12'/>
+   <file role='doc' name='RELEASE-1.0.13'/>
    <file role='doc' name='ChangeLog'/>
 
    <file role='src' name='config.m4'/>
@@ -110,6 +113,18 @@ http://pear.php.net/dtd/package-2.0.xsd">
  </extsrcrelease>
  <changelog>
 
+  <release>
+   <stability><release>stable</release><api>stable</api></stability>
+   <version><release>1.0.13</release><api>1.0.0</api></version>
+   <date>2022-02-16</date>
+   <notes>
+	* php8 compatibility [kleisauke]
+	* improve module linking [kleisauke]
+	* improve packaging [kleisauke]
+	* add type hints [jcupitt, kleisauke]
+   </notes>
+  </release>
+
   <release>
    <stability><release>stable</release><api>stable</api></stability>
    <version><release>1.0.12</release><api>1.0.0</api></version>
diff --git a/vips-1.0.12.tgz b/vips-1.0.12.tgz
index e91539457073f1770ff4c0bba31a3e5da35405f5..e7ed1c72a90c2f1c075beefb95e6e1dc2cc4c95d 100644
GIT binary patch
diff --git a/vips-1.0.13.tgz b/vips-1.0.13.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..4a725a4255c5ce640df13d44577cd85128b1c417
GIT binary patch

From bbfb880180f537adb3f5c5418e99fce1ebb10f76 Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Wed, 16 Feb 2022 13:30:33 +0000
Subject: [PATCH 08/14] oop forgot a version string

---
 php_vips.h      |   2 +-
 vips-1.0.13.tgz | Bin 564519 -> 564517 bytes
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/php_vips.h b/php_vips.h
index a7f3eb0..7f55f2a 100644
--- a/php_vips.h
+++ b/php_vips.h
@@ -5,7 +5,7 @@
 extern zend_module_entry vips_module_entry;
 #define phpext_vips_ptr &vips_module_entry
 
-#define PHP_VIPS_VERSION "1.0.12" 
+#define PHP_VIPS_VERSION "1.0.13" 
 
 #ifdef PHP_WIN32
 #	define PHP_VIPS_API __declspec(dllexport)
diff --git a/vips-1.0.13.tgz b/vips-1.0.13.tgz
index 4a725a4255c5ce640df13d44577cd85128b1c417..ad6695fc2a4f89760d2ae2b60d853fb05868d359 100644
GIT binary patch

From 55b926ea801ca3f3980013a204c5e00ab5cc64ce Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Fri, 22 Apr 2022 13:24:11 +0100
Subject: [PATCH 09/14] note deprecation in README

---
 README.md | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/README.md b/README.md
index 73c29a5..5d4e274 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,10 @@
 # Low-level PHP binding for libvips 
 
+**This extension is deprecated** php-vips 2.0+ has switched to FFI for calls
+into the libvips binary, so this extension is no longer necessary. It will not
+be updated again (though it might still be useful if for some reason you don't
+/ can't update to phop-vips 2.0).
+
 This extension lets you use the libvips image processing library
 from PHP 7 and PHP 8. 
 

From 9dfd56e3b94cc93b77a9e1266d0d0c6fefd6589d Mon Sep 17 00:00:00 2001
From: Jeremy Mikola <jmikola@gmail.com>
Date: Wed, 19 Oct 2022 03:19:03 -0400
Subject: [PATCH 10/14] Fix typo in README

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 5d4e274..8141b5b 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 **This extension is deprecated** php-vips 2.0+ has switched to FFI for calls
 into the libvips binary, so this extension is no longer necessary. It will not
 be updated again (though it might still be useful if for some reason you don't
-/ can't update to phop-vips 2.0).
+/ can't update to php-vips 2.0).
 
 This extension lets you use the libvips image processing library
 from PHP 7 and PHP 8. 

From 6a5603e7b33e35d845c890ea312b5c5958dc49dc Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Wed, 19 Oct 2022 09:15:11 +0100
Subject: [PATCH 11/14] note use php-vips 1.x with this

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 5d4e274..52a0a74 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ This extension lets you use the libvips image processing library
 from PHP 7 and PHP 8. 
 
 This is not supposed to be used directly! Install this, then use 
-[php-vips](https://github.com/libvips/php-vips) to layer a nice (and
+[php-vips 1.x](https://github.com/libvips/php-vips) to layer a nice (and
 documented!) API on top of this extension.
 
 libvips is fast and needs little memory. The [`vips-php-bench`](

From a8bcf741b7f648a4d2da5f5ab60518f510aff596 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Fri, 11 Oct 2024 12:14:37 +0200
Subject: [PATCH 12/14] drop vips_snprintf usage

---
 vips.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/vips.c b/vips.c
index dfd3724..f9b1a89 100644
--- a/vips.c
+++ b/vips.c
@@ -1973,7 +1973,7 @@ PHP_FUNCTION(vips_version)
 {
 	char digits[256];
 
-	vips_snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
+	snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
 
 	RETVAL_STRING(digits);
 }
@@ -2119,37 +2119,37 @@ PHP_MINFO_FUNCTION(vips)
 	php_info_print_table_start();
 	php_info_print_table_header(2, "vips property", "value");
 
-	vips_snprintf(digits, 256, "%d.%d.%d", VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION);
+	snprintf(digits, 256, "%d.%d.%d", VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION);
 	php_info_print_table_row(2, "Vips headers version", digits);
-	vips_snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
+	snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
 	php_info_print_table_row(2, "Vips library version", digits);
-	vips_snprintf(digits, 256, "%d.%d.%d", vips_version(3), vips_version(4), vips_version(5));
+	snprintf(digits, 256, "%d.%d.%d", vips_version(3), vips_version(4), vips_version(5));
 	php_info_print_table_row(2, "Vips ABI version", digits);
 
-	vips_snprintf(digits, 256, "%d", vips_version(0));
+	snprintf(digits, 256, "%d", vips_version(0));
 	php_info_print_table_row(2, "Major version", digits); 
-	vips_snprintf(digits, 256, "%d", vips_version(1));
+	snprintf(digits, 256, "%d", vips_version(1));
 	php_info_print_table_row(2, "Minor version", digits); 
-	vips_snprintf(digits, 256, "%d", vips_version(2));
+	snprintf(digits, 256, "%d", vips_version(2));
 	php_info_print_table_row(2, "Micro version", digits); 
 
-	vips_snprintf(digits, 256, "%zd", vips_cache_get_max_mem());
+	snprintf(digits, 256, "%zd", vips_cache_get_max_mem());
 	php_info_print_table_row(2, "Cache max mem", digits);
-	vips_snprintf(digits, 256, "%d", vips_cache_get_max());
+	snprintf(digits, 256, "%d", vips_cache_get_max());
 	php_info_print_table_row(2, "Cache max operations", digits);
-	vips_snprintf(digits, 256, "%d", vips_cache_get_size());
+	snprintf(digits, 256, "%d", vips_cache_get_size());
 	php_info_print_table_row(2, "Cache current operations", digits);
-	vips_snprintf(digits, 256, "%d", vips_cache_get_max_files());
+	snprintf(digits, 256, "%d", vips_cache_get_max_files());
 	php_info_print_table_row(2, "Cache max open files", digits);
 
-	vips_snprintf(digits, 256, "%d", vips_tracked_get_allocs());
+	snprintf(digits, 256, "%d", vips_tracked_get_allocs());
 	php_info_print_table_row(2, "Memory allocations", digits);
-	vips_snprintf(digits, 256, "%zd", vips_tracked_get_mem());
+	snprintf(digits, 256, "%zd", vips_tracked_get_mem());
 	php_info_print_table_row(2, "Memory currently allocated", digits);
-	vips_snprintf(digits, 256, "%zd", vips_tracked_get_mem_highwater());
+	snprintf(digits, 256, "%zd", vips_tracked_get_mem_highwater());
 	php_info_print_table_row(2, "Memory high water", digits);
 
-	vips_snprintf(digits, 256, "%d", vips_concurrency_get());
+	snprintf(digits, 256, "%d", vips_concurrency_get());
 	php_info_print_table_row(2, "Concurrency", digits);
 
 	php_info_print_table_row(2, "SIMD support with liborc",

From f0fdce8cdec80c904ed19ad89215f6540c850189 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Fri, 11 Oct 2024 12:15:28 +0200
Subject: [PATCH 13/14] clean [-Wunused-variable]

---
 vips.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/vips.c b/vips.c
index f9b1a89..63dbd8c 100644
--- a/vips.c
+++ b/vips.c
@@ -1803,7 +1803,6 @@ PHP_FUNCTION(vips_type_from_name)
 {
 	char *name;
 	size_t name_len;
-	GType gtype;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", 
 		&name, &name_len) == FAILURE) {

From b05b5c5ac4de172bd3fb0d1cfe063384a94cedbb Mon Sep 17 00:00:00 2001
From: John Cupitt <jcupitt@gmail.com>
Date: Sat, 12 Oct 2024 09:39:33 +0100
Subject: [PATCH 14/14] use g_snprintf()

---
 vips.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/vips.c b/vips.c
index 63dbd8c..3048ce6 100644
--- a/vips.c
+++ b/vips.c
@@ -1972,7 +1972,7 @@ PHP_FUNCTION(vips_version)
 {
 	char digits[256];
 
-	snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
+	g_snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
 
 	RETVAL_STRING(digits);
 }
@@ -2118,37 +2118,37 @@ PHP_MINFO_FUNCTION(vips)
 	php_info_print_table_start();
 	php_info_print_table_header(2, "vips property", "value");
 
-	snprintf(digits, 256, "%d.%d.%d", VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION);
+	g_snprintf(digits, 256, "%d.%d.%d", VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION);
 	php_info_print_table_row(2, "Vips headers version", digits);
-	snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
+	g_snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
 	php_info_print_table_row(2, "Vips library version", digits);
-	snprintf(digits, 256, "%d.%d.%d", vips_version(3), vips_version(4), vips_version(5));
+	g_snprintf(digits, 256, "%d.%d.%d", vips_version(3), vips_version(4), vips_version(5));
 	php_info_print_table_row(2, "Vips ABI version", digits);
 
-	snprintf(digits, 256, "%d", vips_version(0));
+	g_snprintf(digits, 256, "%d", vips_version(0));
 	php_info_print_table_row(2, "Major version", digits); 
-	snprintf(digits, 256, "%d", vips_version(1));
+	g_snprintf(digits, 256, "%d", vips_version(1));
 	php_info_print_table_row(2, "Minor version", digits); 
-	snprintf(digits, 256, "%d", vips_version(2));
+	g_snprintf(digits, 256, "%d", vips_version(2));
 	php_info_print_table_row(2, "Micro version", digits); 
 
-	snprintf(digits, 256, "%zd", vips_cache_get_max_mem());
+	g_snprintf(digits, 256, "%zd", vips_cache_get_max_mem());
 	php_info_print_table_row(2, "Cache max mem", digits);
-	snprintf(digits, 256, "%d", vips_cache_get_max());
+	g_snprintf(digits, 256, "%d", vips_cache_get_max());
 	php_info_print_table_row(2, "Cache max operations", digits);
-	snprintf(digits, 256, "%d", vips_cache_get_size());
+	g_snprintf(digits, 256, "%d", vips_cache_get_size());
 	php_info_print_table_row(2, "Cache current operations", digits);
-	snprintf(digits, 256, "%d", vips_cache_get_max_files());
+	g_snprintf(digits, 256, "%d", vips_cache_get_max_files());
 	php_info_print_table_row(2, "Cache max open files", digits);
 
-	snprintf(digits, 256, "%d", vips_tracked_get_allocs());
+	g_snprintf(digits, 256, "%d", vips_tracked_get_allocs());
 	php_info_print_table_row(2, "Memory allocations", digits);
-	snprintf(digits, 256, "%zd", vips_tracked_get_mem());
+	g_snprintf(digits, 256, "%zd", vips_tracked_get_mem());
 	php_info_print_table_row(2, "Memory currently allocated", digits);
-	snprintf(digits, 256, "%zd", vips_tracked_get_mem_highwater());
+	g_snprintf(digits, 256, "%zd", vips_tracked_get_mem_highwater());
 	php_info_print_table_row(2, "Memory high water", digits);
 
-	snprintf(digits, 256, "%d", vips_concurrency_get());
+	g_snprintf(digits, 256, "%d", vips_concurrency_get());
 	php_info_print_table_row(2, "Concurrency", digits);
 
 	php_info_print_table_row(2, "SIMD support with liborc",
