Compiling PHP Extension with correct API Version on Windows - php

I have finally created a very simple "Hello World" style PHP Extension dll on windows, after immeasurable hassle. However, although I have successfully created a DLL, and put it in the extensions folder, and told php.ini about it, now I get this:
PHP Warning: PHP Startup: \x81\xc2\xc0\x03L&\xc0\x03: Unable to initialize module\nModule compiled with module API=16777522\nPHP compiled with module API=20090626\nThese options need to match\n in Unknown on line 0
Warning: PHP Startup: ÂÀL&À: Unable to initialize module
Module compiled with module API=16777522
PHP compiled with module API=20090626
These options need to match
in Unknown on line 0
It seems that my PHP_API_VERSION is 20090626, but for some reason my DLL thinks it's PHP_API_VERSION is 16777522.
The tutorial below was some help in compiling an extension dll:
http://www.talkphp.com/vbarticles.php?do=article&articleid=49&title=creating-custom-php-extensions
Having written it myself, I have access to all of the source code for the php extension in question - But, where is it that I control the PHP_API_VERSION that ends up in the DLL?
I am compiling the dll successfully with Borland C++ Builder v5.5, not Visual Studio.
Here is the complete source, in case it matters:
// Needed to make following two #includes compatible with borland header files
void __fastcall __assume(int t) {
return;
}
typedef unsigned int socklen_t;
typedef enum BOOL
{
false=0,
true
} bool;
// end Borland compatibility code
#include "php.h"
#include "zend_config.w32.h"
ZEND_FUNCTION(fetch_LinkGrammar_links);
zend_function_entry LinkGrammar_ext_functions[] = {
ZEND_FE(fetch_LinkGrammar_links, NULL)
{NULL, NULL, NULL}
};
zend_module_entry LinkGrammar_ext_module_entry = {
STANDARD_MODULE_HEADER,
"LinkGrammar Extension",
LinkGrammar_ext_functions,
NULL, NULL, NULL, NULL, NULL,
"1.0",
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(LinkGrammar_ext);
ZEND_FUNCTION(fetch_LinkGrammar_links)
{
bool World = false;
char *RetVal= "";
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &World) == FAILURE)
{
RETURN_STRING("Missing Parameter", true);
}
if (World == true)
{
RetVal= "Hello World";
}
else
{
RetVal= "Hello";
}
RETURN_STRING(RetVal, true);
}
What can I change to eliminate the PHP Startup Error that the API must match?

It turns out it was the "Data Alignment" - My DLL was being compiled using "Word" alignment and it needed to be double-word.

Sounds like you're compiling against a different version of PHP than the one you're running.
Take a peek in php.h and look for #define PHP_API_VERSION -- that's what you're compiling against.
Is that the same version that's running on your server?

Check your include paths, locate file php.h and check that the version there matches php that you are running (running version is found if you check phpinfo() output).

You should change the API version in zend_modules.h to the API version which your PHP server indicates in phpinfo().
For example if the PHP Extension API in phpinfo() is 20090523, you should change the API number in zend_modules.h file to 20090523 and then rebuild your project.

Related

How to get threads working in PHP on Windows?

I want to run PHP Thread class code on Windows. I tried some manual installation guides like e.g. this one but they didn't work. PHP cannot load the pthreads DLL even though the file exists at this location:
PHP Warning: PHP Startup: Unable to load dynamic library 'php_pthreads.dll' (tried: C:\xampp\php\ext\php_pthreads.dll (The specified module could not be found), C:\xampp\php\ext\php_php_pthreads.dll.dll (The specified module could not be found)) in Unknown on line 0
I downloaded php_pthreads-3.1.6-7.0-ts-vc14-x64.zip. My PHP version is: PHP 8.1.6 (cli) (built: May 11 2022 08:55:59) (ZTS Visual C++ 2019 x64). Generally I'm still not entirely sure which pthread version to select since there are many choices here. Are any of them even going to work since they seem a bit outdated? My phpinfo says:
Can I maybe just use Windows threads since they are already available on my system?
After the pthreads installation, when I run my Thread class example code
<?php
class AsyncOperation extends Thread
{
public function __construct($arg)
{
$this->arg = $arg;
}
public function run()
{
if ($this->arg) {
printf("Hello %s\n", $this->arg);
}
}
}
$thread = new AsyncOperation("World");
if ($thread->start())
$thread->join();
?>
I still get the warning above and the following error:
PHP Fatal error: Uncaught Error: Class "Thread" not found
What am I doing wrong? Is there maybe a way to do it with less "manual" effort in e.g. 1 composer command or an easier way to get threading working?

mb_detect_encoding() missing in PHP?

Using PHP 5.6.13 Windows CLI from http://windows.php.net/download#php-5.5, this:
include 'simple_html_dom.php'; //V1.5
$html = file_get_html('http://google.com');
fails with
Call to undefined function mb_detect_encoding()
and SO suggests this is due to the multibyte string extension missing. Indeed phpinfo() says
Zend Multibyte Support => disabled
which is is a surprise and disappointment.
What's the remedy? I want to remain with an official build.
The build package from windows.php.net contains everything you need to run the mbstring extension; it's just not enabled by default.
use <?php echo get_cfg_var('cfg_file_path'); to check which php.ini you have to edit
make sure the extension_dir=... directive points to the {yourPHPDir}/ext directory
add or uncomment* the line extension=php_mbstring.dll
(if php runs as a httpd module: restart the webserver; not the case here)
*) any line starting with a ; or (before php 7) # is concidered a comment.

custom PHP extension

I need to create custom extension for PHP. All things are going well till I want to load compiled extension into PHP. Than I'm getting this error message (by using php -m): "Warning: PHP Startup: Invalid library (maybe not a PHP library) 'first.so' in Unknown on line 0"
OS:
OS X 10.8.5
Here is my compiling process thru which there is no error at all :
$ phpize54
$ sudo ./configure --with-php-config=/opt/local/bin/php-config54
$ sudo make install
I'm registering the extension by .ini file in dir for additional .ini files
extension=first.so
I'm using no another additional tools. Whan I was trying to comtile some extension right from the extensions included in sources of PHP distribution (posix, to be concrete) in this way, everything works and extension was loaded correctly.
Does anybody see any mistake in my source code below? Thank U for hellp.
config.m4:
PHP_ARG_ENABLE(first,whether to enable FIRST functions,
[ --disable-first Disable FIRST functions], yes)
if test "$PHP_FIRST" = "yes"; then
AC_DEFINE(HAVE_FIRST, 1, [whether to include FIRST functions])
PHP_NEW_EXTENSION(first, first.c, $ext_shared)
fi
first.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"
extern zend_module_entry first_module_entry;
#define first_module_ptr &first_module_entry
#define phpext_first_ptr first_module_ptr
static PHP_MINFO_FUNCTION(first)
{
php_info_print_table_start();
php_info_print_table_row(2, "Revision", "$Id: 01 $");
php_info_print_table_end();
}
static PHP_MINIT_FUNCTION(first)
{
return SUCCESS;
}
PHP_FUNCTION(hallo)
{
RETURN_STRING("FIRST extension function works\n", 1);
}
ZEND_BEGIN_ARG_INFO_EX(arginfo_hallo, 0, 0, 0)
ZEND_END_ARG_INFO()
const zend_function_entry first_functions[] =
{
PHP_FE(hallo, arginfo_hallo)
PHP_FE_END
};
zend_module_entry first_module_entry = {
STANDARD_MODULE_HEADER,
"first",
first_functions,
PHP_MINIT(first),
NULL,
NULL,
NULL,
PHP_MINFO(first),
NO_VERSION_YET,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_POSIX
ZEND_GET_MODULE(first)
#endif
#ifdef COMPILE_DL_POSIX
ZEND_GET_MODULE(first)
#endif
This looks wrong. In config.m4 you claim your extension is called first (PHP_NEW_EXTENSION(first, ...) but there you are checking whether an extension called posix is built shared.
Background: PHP can load extensions (meaning PHP modules not to be confused with Zend Extensions like xdebug or opcache - they are a different topi) in two ways. Either statically compiled into PHP or shared.
Statically means that your extension is built as part of PHP and built right in the php binary. (put it in php-src/ext/, run buildconf, configure) This works by configure creating a file main/internal_functions.c which includes all php_foo.h files and creating an array of module_entry which is processed at PHP start.
If an extension is build shared (using --with-foo=shared in php-src or phpize) and loading it via php.ini PHP will look for a function get_module() using dlopen() and dlsym() system apis (or LoadDll on Windows) which will return the module structure. In order to not habe to write that function get_module() all the time the macro ZEND_GET_MODULE does that. But get_module is a common name, and during a static build it has to be prevented that each extension creates it as that would cause a conflict to have the same function multiple times in the same binary, that's why the build system creates COMPILED_DL_FOO defines which can be used as guards.
So in your case PHP opens the library, looks for a get_module() but can't find it, shrugs and reports an error.
References:
http://lxr.php.net/xref/PHP_TRUNK/ext/standard/dl.c#100 - PHP's module loading routine
http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_API.h#135 - ZEND_GET_MODULE
The config.h file in your tree after running configure (search for COMPILE_DL there)

PHP Extension for C is failing to find function in C Library

Problem
I am learning to create a php extension. I have created a php extension for a basic hello world app. But the moment I try to include functions from the C library I am trying to entend then I get the following error message:
PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib/php5/20121212/hello.so' - /usr/lib/php5/20121212/hello.so: undefined symbol: nc_open in Unknown on line 0
This nc_open() function is the key function in the libary I am trying to extend
Process
I have created by header and config files and my c extension file which contains:
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_hello.h"
#include <netcdf.h>
...
PHP_FUNCTION(hello_world)
{
int status;
int ncid;
status = nc_open("sample.nc", 0, &ncid);
RETURN_LONG(status);
}
I have set up a directory (on Ubuntu) and run the phpize and configure steps. I run the make command which runs with no errors.
In my php.ini file I have the hello.so extension which points to symbolic link to the file I make.
I then restart the apache server on my local machine and then run:
php -r 'echo hello_world();'
and get:
- /usr/lib/php5/20121212/hello.so: undefined symbol: nc_open in Unknown on line 0
PHP Warning: Module 'xdebug' already loaded in Unknown on line 0
PHP Fatal error: Call to undefined function hello_world() in Command line code on line 1
PHP Stack trace:
PHP 1. {main}() Command line code:0
The error only occurs when I have a function from netcdf.h
The error does not occur from just having the include statement.
What I've tried:
Firstly I tried writing a basic c program to check that it would work, I managed to succeed by compiling it with:
gcc test.c -lnetcdf -o Test
and running ./Test
I tried using the following MakeFile
LDLIBS = -lnetcdf
but was unsuccessful.
What I would like:
Preferably to know how to get this library to work in my extension but I would settle for being pointed to documents or examples to help me understand what I need to know to accomplish this.
Update
Following Elliott Frisch's comments I have tried the
Setting the PHP_RPATHS=/usr/local
Setting /etc/ld.so.conf to:
include /etc/ld.so.conf.d/*.conf
/usr/lib
Changing the MakeFile to:
LDLIBS = -lnetcdf
LDFLAGS = -static
I am making the assumption that /usr/local is the correct directory due to it containing netcdf.so
I have added the following lines to my config.m4 file according to try adding the suggested functions
PHP_ADD_INCLUDE(/usr/lib)
PHP_ADD_LIBRARY_WITH_PATH(netcdf, /usr/lib, HELLO_SHARED_LIBADD)
Unfortunately I am still getting the problem so I am trying to understand what these functions mean to make sure I using them correctly. (The library I need to connect to is /usr/lib/libnetcdf.a and need to include netcdf.h from that file.)
You'll need to use PHP_ADD_LIBRARY_WITH_PATH & PHP_ADD_INCLUDE macros in config.m4 to handle linking. Autoconf's macros are also helpful for find & verifying existing libraries.
The best examples can be found in the ext directory of PHP's source code. Like ext/zlib
PHP_ADD_LIBPATH($ZLIB_DIR/$PHP_LIBDIR, ZLIB_SHARED_LIBADD)
PHP_ZLIB_DIR=$ZLIB_DIR
PHP_ADD_LIBRARY(z,, ZLIB_SHARED_LIBADD)
PHP_ADD_INCLUDE($ZLIB_INCDIR)
And more detailed examples in ext/oracle (from apple's open source labs)
PHP_ADD_LIBRARY(clntsh, 1, ORACLE_SHARED_LIBADD)
PHP_ADD_LIBPATH($ORACLE_DIR/lib, ORACLE_SHARED_LIBADD)
The issue was with an incorrectly configured config.m4 file.
I added the following that I modified from examples I found to get it to work:
if test "$PHP_HELLO" != "no"; then
SEARCH_PATH="/usr/local /usr" # you might want to change this
SEARCH_FOR="/include/netcdf.h" # you most likely want to change this
if test -r $PHP_HELLO/$SEARCH_FOR; then # path given as parameter
HELLO_INC_DIR=$PHP_HELLO/include
else # search default path list
AC_MSG_CHECKING([for netcdf.h in default path])
for i in $SEARCH_PATH ; do
if test -r $i/$SEARCH_FOR; then
HELLO_INC_DIR=$i/include
AC_MSG_RESULT(found in $i)
fi
done
fi
if test -z "$HELLO_INC_DIR"; then
AC_MSG_RESULT([not found])
fi
SEARCH_PATH="/usr/local /usr" # you might want to change this
SEARCH_FOR="/lib/libnetcdf.a" # you most likely want to change this
if test -r $PHP_HELLO/$SEARCH_FOR; then # path given as parameter
HELLO_LIB_DIR=$PHP_HELLO/lib
else # search default path list
AC_MSG_CHECKING([for libnetcdf in default path])
for i in $SEARCH_PATH ; do
if test -r $i/$SEARCH_FOR; then
HELLO_LIB_DIR=$i/lib
AC_MSG_RESULT(found in $i)
fi
done
fi
if test -z "$HELLO_LIB_DIR"; then
AC_MSG_RESULT([not found])
AC_MSG_ERROR([Please check the netCDF distribution])
fi
# --with-netcdf -> add include path
PHP_ADD_INCLUDE($HELLO_INC_DIR)
# --with-netcdf -> check for lib and symbol presence
LIBNAME=netcdf # you may want to change this
dnl O_LDFLAGS=$LDFLAGS
dnl LDFLAGS="$LDFLAGS -L$NETCDF_LIB_DIR -l$LIBNAME"
PHP_ADD_LIBRARY($LIBNAME)
LIBSYMBOL=nc_inq_libvers # you most likely want to change this
PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
[
PHP_ADD_LIBRARY($LIBNAME)
AC_DEFINE(HAVE_HELLO,1,[Build netCDF extension])
],[
AC_MSG_ERROR([wrong netCDF library version or lib not found])
])
PHP_SUBST(HELLO_SHARED_LIBADD)
HELLO_SHARED_LIBADD=-l$LIBNAME

mysql is enabled in php.ini but I'm still getting Call to undefined function mysql_connect()

We have a Windows 2008 server, this morning when we came to the office, we saw that mysql is not working
We are getting this error for a simple connect function
Fatal error: Call to undefined function mysql_connect()
but the strange thing is that mysql extensions are enabled in php.ini file.
extension=php_mysql.dll
extension=php_mysqli.dll
extension=php_pdo_mysql.dll
According to phpinfo() , php using the ini file in C:\PHP and the DLL files exist in the ext folder.
I cannot find a way to solve or at least to find out what is causing this.
I would be grateful if someone could give me some advice
Thanks in advance
for debugging purposes try
if ( !function_exists('mysql_connect') ) {
echo '<pre>mysql extension loaded: ', extension_loaded('mysql') ? 'yes':'no', "\r\n";
$cf = get_cfg_var('cfg_file_path');
echo 'ini file: ', $cf, "\r\n";
if ( !$cf || !file_exists($cf) ) {
echo "no config file\r\n";
}
else {
echo "mysql config options:\r\n";
$mc = array_filter( file($cf), function($e) { return false!==stripos($e, 'mysql') && false!==stripos($e, 'extension'); });
echo join("", $mc);
}
die('no function mysql_connect</pre>');
}
the output should be something like
<pre>mysql extension loaded: no
ini file: C:\Develop\php\php.ini
mysql config options:
;extension=php_mysql.dll
;extension=php_mysqli.dll
extension=php_pdo_mysql.dll
no function mysql_connect</pre>
which indicates that the line extension=php_mysql.dll has been commented out (in my php.ini).
In case this script shows that the php_mysql.dll indeed should have been loaded try to increase the log level of both php and your webserver and check the log file. Maybe windows couldn't load the dll because it depends on another dll which isn't present (in the correct version) anymore. E.g. the php_mysql.dll in "my" ext directory depends on MSVCR110.DLL which is present because I have Visual Studio installed, otherwise installing the Visual Studio 2012 redistributables would be required. Tools like Dependency Walker can show you which dlls are required to run an application and/or dll.
Or it could be that the php_mysql.dll implements a different API version than your php core. E.g. if you have PHP 5.4 installed but the php_mysql.dll is for php 5.1 you will see an API magic key error in the log file.
Restart Your WAMP Or XAMPP Server. You can check with Contral Panel of XAMPP Or WAMP

Categories