From 382ad9def2cf0f39546b6d19c8202e477d458152 Mon Sep 17 00:00:00 2001 From: William Storey Date: Mon, 14 Sep 2026 16:09:29 +0000 Subject: [PATCH 1/6] Append the libmaxminddb source to the version row in one cell php_info_print_table_row() emits one cell per argument and no colspan, so the three-argument row added in #289 was wrong output rather than an unusual layout. In text mode it read libmaxminddb library version => 1.14.0 => (bundled) which has the Directive => Local Value => Master Value shape of an ini table, so the version row stopped being key => value. In HTML it was a three-cell in a table whose other rows have two, leaving a ragged empty column. The row keeps the shape #289 intended, with the source as a suffix in the same cell: libmaxminddb library version => 1.14.0 (bundled) Selecting the string ahead of the call also removes the `);` that was duplicated across both #ifdef arms, and the hunk now formats cleanly under .clang-format, whose BinPackArguments: false would otherwise have rewritten it. lint.yml does not run clang-format, so CI had not noticed. Co-Authored-By: Claude Fable 5.1 --- ext/maxminddb.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ext/maxminddb.c b/ext/maxminddb.c index 32692a2..1848d4c 100644 --- a/ext/maxminddb.c +++ b/ext/maxminddb.c @@ -792,18 +792,23 @@ PHP_MINIT_FUNCTION(maxminddb) { } static PHP_MINFO_FUNCTION(maxminddb) { +#ifdef HAVE_LIBMAXMINDDB_BUNDLED + const char *lib_source = "bundled"; +#else + const char *lib_source = "system"; +#endif + char *lib_version; + php_info_print_table_start(); php_info_print_table_row(2, "MaxMind DB Reader", "enabled"); php_info_print_table_row( 2, "maxminddb extension version", PHP_MAXMINDDB_VERSION); - php_info_print_table_row( - 3, "libmaxminddb library version", MMDB_lib_version(), -#ifdef HAVE_LIBMAXMINDDB_BUNDLED - "(bundled)"); -#else - "(system)"); -#endif + + spprintf(&lib_version, 0, "%s (%s)", MMDB_lib_version(), lib_source); + CHECK_ALLOCATED(lib_version); + php_info_print_table_row(2, "libmaxminddb library version", lib_version); + efree(lib_version); php_info_print_table_end(); } From 845eb095bc68085029f4dc9b49da4eff726cbd72 Mon Sep 17 00:00:00 2001 From: William Storey Date: Mon, 14 Sep 2026 16:09:29 +0000 Subject: [PATCH 2/6] Describe the bundled macro rather than the option that sets it The AC_DEFINE description read "Use bundled or system libmaxminddb". That describes --with-maxminddb-bundled, not the macro, and autoheader copies it verbatim into the comment above the #define, so the generated header read /* Use bundled or system libmaxminddb */ #define HAVE_LIBMAXMINDDB_BUNDLED 1 which resolves nothing for whoever reads it. Say what being defined means instead, following the convention libmaxminddb's own configure.ac uses for MMDB_LITTLE_ENDIAN ("System is big-endian" / "System is little-endian"). Co-Authored-By: Claude Fable 5.1 --- ext/config.m4 | 2 +- ext/config.w32 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/config.m4 b/ext/config.m4 index 18f9a49..772d96d 100644 --- a/ext/config.m4 +++ b/ext/config.m4 @@ -78,7 +78,7 @@ if test $PHP_MAXMINDDB != "no"; then maxminddb_sources="$maxminddb_sources libmaxminddb/src/maxminddb.c libmaxminddb/src/data-pool.c" - AC_DEFINE([HAVE_LIBMAXMINDDB_BUNDLED], [1], [Use bundled or system libmaxminddb]) + AC_DEFINE([HAVE_LIBMAXMINDDB_BUNDLED], [1], [Defined when the bundled libmaxminddb is compiled into the extension]) else AC_PATH_PROG(PKG_CONFIG, pkg-config, no) diff --git a/ext/config.w32 b/ext/config.w32 index b405b11..a4bbd66 100644 --- a/ext/config.w32 +++ b/ext/config.w32 @@ -82,7 +82,7 @@ if (PHP_MAXMINDDB == "yes") { * ask for it explicitly too, and the dependency is ours now. */ ADD_FLAG("LIBS_MAXMINDDB", "ws2_32.lib"); - AC_DEFINE("HAVE_LIBMAXMINDDB_BUNDLED", 1, "Use bundled or system libmaxminddb"); + AC_DEFINE("HAVE_LIBMAXMINDDB_BUNDLED", 1, "Defined when the bundled libmaxminddb is compiled into the extension"); EXTENSION("maxminddb", "maxminddb.c"); From 7052ab8a0a2fcaee6ab7916e3d0623db9555e74d Mon Sep 17 00:00:00 2001 From: William Storey Date: Mon, 14 Sep 2026 16:09:29 +0000 Subject: [PATCH 3/6] Define the bundled macro on every path and test it with #if A macro that is defined on one path and absent on the other cannot tell "system build" apart from "the define never arrived", so the phpinfo() row would report "system" for a bundled object and nobody would know. On Windows that is not hypothetical. Under phpize, configure.js writes AC_DEFINE results to /include/main/config.pickle.h, which is shared state in the PHP install rather than the build tree, and generate_config_pickle_h() removes only the lines whose keys the current run defines. Configure with --with-maxminddb-bundled, reconfigure without it, and the stale `#define HAVE_LIBMAXMINDDB_BUNDLED 1` survives, so a system build reports "bundled". Fresh CI containers never see this; a developer's machine does. Both build systems now define the macro as 1 or 0 on every path that builds the extension, so a system configure run overwrites whatever a bundled one left behind, and maxminddb.c tests the value with #if. An #ifndef guard turns a definition that never arrives into a compile error instead of a wrong row. Co-Authored-By: Claude Fable 5.1 --- ext/config.m4 | 4 +++- ext/config.w32 | 4 +++- ext/maxminddb.c | 11 ++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ext/config.m4 b/ext/config.m4 index 772d96d..70fd8d1 100644 --- a/ext/config.m4 +++ b/ext/config.m4 @@ -78,8 +78,10 @@ if test $PHP_MAXMINDDB != "no"; then maxminddb_sources="$maxminddb_sources libmaxminddb/src/maxminddb.c libmaxminddb/src/data-pool.c" - AC_DEFINE([HAVE_LIBMAXMINDDB_BUNDLED], [1], [Defined when the bundled libmaxminddb is compiled into the extension]) + AC_DEFINE([HAVE_LIBMAXMINDDB_BUNDLED], [1], [1 if the bundled libmaxminddb is compiled into the extension, 0 if a system libmaxminddb is linked]) else + AC_DEFINE([HAVE_LIBMAXMINDDB_BUNDLED], [0], [1 if the bundled libmaxminddb is compiled into the extension, 0 if a system libmaxminddb is linked]) + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) AC_MSG_CHECKING(for libmaxminddb) diff --git a/ext/config.w32 b/ext/config.w32 index a4bbd66..ce2fe2d 100644 --- a/ext/config.w32 +++ b/ext/config.w32 @@ -82,7 +82,7 @@ if (PHP_MAXMINDDB == "yes") { * ask for it explicitly too, and the dependency is ours now. */ ADD_FLAG("LIBS_MAXMINDDB", "ws2_32.lib"); - AC_DEFINE("HAVE_LIBMAXMINDDB_BUNDLED", 1, "Defined when the bundled libmaxminddb is compiled into the extension"); + AC_DEFINE("HAVE_LIBMAXMINDDB_BUNDLED", 1, "1 if the bundled libmaxminddb is compiled into the extension, 0 if a system libmaxminddb is linked"); EXTENSION("maxminddb", "maxminddb.c"); @@ -112,6 +112,8 @@ if (PHP_MAXMINDDB == "yes") { "maxminddb", "libmaxminddb"); } else if (CHECK_HEADER_ADD_INCLUDE("maxminddb.h", "CFLAGS_MAXMINDDB", PHP_MAXMINDDB + ";" + PHP_PHP_BUILD + "\\include\\maxminddb") && CHECK_LIB("libmaxminddb.lib;maxminddb.lib", "maxminddb", PHP_MAXMINDDB)) { + AC_DEFINE("HAVE_LIBMAXMINDDB_BUNDLED", 0, "1 if the bundled libmaxminddb is compiled into the extension, 0 if a system libmaxminddb is linked"); + EXTENSION("maxminddb", "maxminddb.c"); } else { WARNING('Could not find maxminddb.h, libmaxminddb.lib, or maxminddb.lib; skipping'); diff --git a/ext/maxminddb.c b/ext/maxminddb.c index 1848d4c..5c5d35b 100644 --- a/ext/maxminddb.c +++ b/ext/maxminddb.c @@ -26,6 +26,15 @@ #include "ext/standard/info.h" #include +/* Both build files define this as 1 or 0 on every path, so a missing + * definition is a build-file mistake, not a system build. Fail rather than + * report "system" for a bundled object. Unlike php-src's HAVE_GD_BUNDLED, + * which is absent in a system build, this must be tested with #if: #ifdef + * is true for the 0 too. */ +#ifndef HAVE_LIBMAXMINDDB_BUNDLED +#error "HAVE_LIBMAXMINDDB_BUNDLED must be defined by the build files" +#endif + #ifdef ZTS #include #endif @@ -792,7 +801,7 @@ PHP_MINIT_FUNCTION(maxminddb) { } static PHP_MINFO_FUNCTION(maxminddb) { -#ifdef HAVE_LIBMAXMINDDB_BUNDLED +#if HAVE_LIBMAXMINDDB_BUNDLED const char *lib_source = "bundled"; #else const char *lib_source = "system"; From 58f2bdada8f5a9034686d053aca0191ac51844bf Mon Sep 17 00:00:00 2001 From: William Storey Date: Mon, 14 Sep 2026 16:09:29 +0000 Subject: [PATCH 4/6] Say why the bundled macro exists and how it reaches maxminddb.c The two build files are the most heavily commented in the repository and the AC_DEFINE added in #289 was the one line in them with no rationale. Worse, config.w32 says a few lines above it that "the Windows build never defines HAVE_CONFIG_H" and that defining it "would make our own maxminddb.c include a config.h that does not exist". A maintainer trusting that prose could reasonably conclude the AC_DEFINE is dead and delete it. It is not: under phpize the value is written to \include\main\config.pickle.h and reaches maxminddb.c through php.h, zend_portability.h, zend_config.w32.h and main\config.w32.h, a route that has nothing to do with HAVE_CONFIG_H. Both files now say what the macro is for, why it is defined as 0 rather than left absent on the system path, and, in config.w32, the include chain it travels and the stale-pickle failure that the 0 exists to overwrite. The HAVE_CONFIG_H paragraph points at that comment so the two are not read as contradicting each other. Co-Authored-By: Claude Fable 5.1 --- ext/config.m4 | 5 +++++ ext/config.w32 | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/ext/config.m4 b/ext/config.m4 index 70fd8d1..8262e62 100644 --- a/ext/config.m4 +++ b/ext/config.m4 @@ -78,8 +78,13 @@ if test $PHP_MAXMINDDB != "no"; then maxminddb_sources="$maxminddb_sources libmaxminddb/src/maxminddb.c libmaxminddb/src/data-pool.c" + dnl phpinfo() reports this. It is 1 here and 0 in the system branch, + dnl never absent, so maxminddb.c can tell a system build from a + dnl definition that never arrived and refuse to compile. config.w32 + dnl says why that is a real risk on Windows. AC_DEFINE([HAVE_LIBMAXMINDDB_BUNDLED], [1], [1 if the bundled libmaxminddb is compiled into the extension, 0 if a system libmaxminddb is linked]) else + dnl See the bundled branch above for why this is 0 rather than absent. AC_DEFINE([HAVE_LIBMAXMINDDB_BUNDLED], [0], [1 if the bundled libmaxminddb is compiled into the extension, 0 if a system libmaxminddb is linked]) AC_PATH_PROG(PKG_CONFIG, pkg-config, no) diff --git a/ext/config.w32 b/ext/config.w32 index ce2fe2d..25349f1 100644 --- a/ext/config.w32 +++ b/ext/config.w32 @@ -57,6 +57,7 @@ if (PHP_MAXMINDDB == "yes") { * The Windows build never defines HAVE_CONFIG_H, so libmaxminddb * already does the right thing, and defining it here would instead * make our own maxminddb.c include a config.h that does not exist. + * The AC_DEFINE below reaches maxminddb.c by another route; see there. * * MSVC has no unsigned __int128, so the byte array is the only option * for uint128 values. Both this file and config.m4 have to define @@ -82,6 +83,19 @@ if (PHP_MAXMINDDB == "yes") { * ask for it explicitly too, and the dependency is ours now. */ ADD_FLAG("LIBS_MAXMINDDB", "ws2_32.lib"); + /* phpinfo() reports this. It reaches maxminddb.c through php.h, not + * through any config header of ours: as of PHP 8.4, under phpize, + * configure.js writes AC_DEFINE results to + * \include\main\config.pickle.h, which main\config.w32.h + * includes (in a php-src tree they land in that file directly). + * + * It is 1 here and 0 in the system branch below, never absent, + * because config.pickle.h is shared state in the PHP install and + * generate_config_pickle_h() removes only the lines whose keys the + * current run defines. A bundled configure followed by a system one + * would leave this 1 in place and the system build would report + * "bundled". The 0 overwrites it, and maxminddb.c refuses to compile + * if neither arrives. */ AC_DEFINE("HAVE_LIBMAXMINDDB_BUNDLED", 1, "1 if the bundled libmaxminddb is compiled into the extension, 0 if a system libmaxminddb is linked"); EXTENSION("maxminddb", "maxminddb.c"); @@ -112,6 +126,7 @@ if (PHP_MAXMINDDB == "yes") { "maxminddb", "libmaxminddb"); } else if (CHECK_HEADER_ADD_INCLUDE("maxminddb.h", "CFLAGS_MAXMINDDB", PHP_MAXMINDDB + ";" + PHP_PHP_BUILD + "\\include\\maxminddb") && CHECK_LIB("libmaxminddb.lib;maxminddb.lib", "maxminddb", PHP_MAXMINDDB)) { + /* See the bundled branch above for why this is 0 rather than absent. */ AC_DEFINE("HAVE_LIBMAXMINDDB_BUNDLED", 0, "1 if the bundled libmaxminddb is compiled into the extension, 0 if a system libmaxminddb is linked"); EXTENSION("maxminddb", "maxminddb.c"); From c151c7e2fc013dea6138893d92071354286d525c Mon Sep 17 00:00:00 2001 From: William Storey Date: Mon, 14 Sep 2026 16:09:29 +0000 Subject: [PATCH 5/6] Add a changelog entry for the phpinfo() libmaxminddb source row The new row is user-visible output and #289 shipped without noting it. 1.14.0 is already released at the top of the file, so this opens the next section, undated until release the way 1.14.0 was. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca3a058..9982f6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ CHANGELOG ========= +1.15.0 +------------------- + +* `phpinfo()` and `php --ri maxminddb` now show whether the extension was built + with the bundled libmaxminddb or links a system library. The + `libmaxminddb library version` row reads, for example, `1.14.0 (bundled)` or + `1.9.1 (system)`. Pull request by Remi Collet. GitHub #289. + 1.14.0 (2026-09-10) ------------------- From 8ad6b04dff035b1178fa59d2878cb479af5ea153 Mon Sep 17 00:00:00 2001 From: William Storey Date: Mon, 14 Sep 2026 16:09:29 +0000 Subject: [PATCH 6/6] Correct the claim that no Windows workflow checks config.w32 The comment above the version scrape said "there is no Windows workflow in this repository" and that nothing would notice a stale PACKAGE_VERSION. 5e4a6db added the windows-bundled job after 22297cc's comment-correction pass, so the claim was missed; that job compares MMDB_LIB_VERSION against the submodule's configure.ac, which is exactly the check the comment said was absent. The same paragraph said config.m4 keeps a literal version. It has not since the literal moved to ext/bundled-include/maxminddb_config.h, and config.m4's own comment says so. The paragraph now names where the Unix literal lives and which jobs verify both. Co-Authored-By: Claude Fable 5.1 --- ext/config.w32 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/config.w32 b/ext/config.w32 index 25349f1..312a5cb 100644 --- a/ext/config.w32 +++ b/ext/config.w32 @@ -12,12 +12,12 @@ if (PHP_MAXMINDDB == "yes") { ERROR("--with-maxminddb-bundled needs the bundled libmaxminddb sources; run \"git submodule update --init\""); } - /* Read the version out of the submodule rather than repeating it here. - * Nothing else checks this file -- there is no Windows workflow in this - * repository -- so a bump that updated config.m4 and forgot this line - * would ship a PACKAGE_VERSION that lies, with nothing to notice it. - * config.m4 keeps a literal because test-bundled.yml compares - * MMDB_LIB_VERSION against configure.ac on every build. */ + /* Read the version out of the submodule so a bump cannot leave this + * line behind. The Unix build keeps a literal in + * ext/bundled-include/maxminddb_config.h instead; that header says + * why. The bundled and windows-bundled jobs in test-bundled.yml check + * both by comparing MMDB_LIB_VERSION against configure.ac on every + * build. */ var maxminddb_ac = file_get_contents(maxminddb_bundled + "\\configure.ac"); /* The match is captured rather than read back out of RegExp.$1, which is * engine-global state shared with every other config.w32 evaluated in