I want to write a test php extension, which will provide a test class. I want to extract class declaration into a separate C-File and call class registration from module init function of myext.c file. I have the following files:
testlib/
test_class.c
myext.c
php_myext.h
config.m4
...
Now config.m4 file is quite simple:
PHP_ARG_ENABLE(myext, [whether to enable myext support], [ --enable-myext Enable myext support])
if test "$PHP_MYEXT" != "no"; then
PHP_NEW_EXTENSION(myext, myext.c, $ext_shared)
fi
How to configure config.m4 to be able to add test_class.c to extension building?
UPDATE:
How to configure config.m4 to make it searching .c files in a specific folder and add to extension building automatically?
It's pretty straightforward:
PHP_NEW_EXTENSION(myext, myext.c testlib/test_class.c, $ext_shared)
Related
I have difficulty trying to write a php extension that uses an external shared library:
The content of config.m4 is as follows:
PHP_ARG_WITH([foo],
[for foo support],
[AS_HELP_STRING([--with-foo],
[Include foo support])])
if test "$PHP_FOO" != "no"; then
PKG_CHECK_MODULES([LIBFREETYPE2], [freetype2 >= 23.4.17])
PHP_EVAL_INCLINE($LIBFREETYPE2_CFLAGS)
PHP_EVAL_LIBLINE($LIBFREETYPE2_LIBS, FOO_SHARED_LIBADD)
PHP_NEW_EXTENSION(foo, foo.c, $ext_shared)
fi
The code of the main function in foo.c is as follows(Just a very simple example of using the external FreeType library):
PHP_FUNCTION(foo_test) {
ZEND_PARSE_PARAMETERS_NONE();
FT_Library ft;
if (FT_Init_FreeType(&ft)) {
php_printf("FreeType library failed to load\n");
return;
}
php_printf("FreeType library successfully loaded\n");
FT_Done_Library(ft);
php_printf("FreeType library unloaded\n");
}
phpize, ./configure, make and make install all succeeded, and then I added the extension in php.ini:
extension = foo.so
So far so good, but then I ran the following command
echo "<?php foo_test();" | php
I got the following error message:
php: symbol lookup error: /the-path-of-foo/foo.so: undefined symbol:
FT_Init_FreeType
It looks like PHP doesn't load the external shared FreeType library my PHP extension intends to use.
How do I do to make PHP load the external shared FreeType library?
Thanks for help.
Resolved this issue by adding PHP_SUBST(FOO_SHARED_LIBADD) after PHP_EVAL_LIBLINE($LIBFREETYPE2_LIBS, FOO_SHARED_LIBADD):
PHP_ARG_WITH([foo],
[for foo support],
[AS_HELP_STRING([--with-foo],
[Include foo support])])
if test "$PHP_FOO" != "no"; then
PKG_CHECK_MODULES([LIBFREETYPE2], [freetype2 >= 23.4.17])
PHP_EVAL_INCLINE($LIBFREETYPE2_CFLAGS)
PHP_EVAL_LIBLINE($LIBFREETYPE2_LIBS, FOO_SHARED_LIBADD)
PHP_SUBST(FOO_SHARED_LIBADD)
PHP_NEW_EXTENSION(foo, foo.c, $ext_shared)
fi
Your config.m4 shoud be like
...
PHP_EVAL_LIBLINE($LIBFREETYPE2_LIBS, EXTRA_CFLAGS)
PHP_SUBST(EXTRA_CFLAGS)
PHP_NEW_EXTENSION(foo, foo.c, $ext_shared)
fi
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)
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
I have a C++ library I would like to link to and use in PHP: libsigx.a
I would like to link with it by putting it in my config.m4 file (Zend).
[config.m4]
PHP_ARG_ENABLE(sigx,
[Whether to enable the "sigx" extension],
[ --enable-sigx Enable "sigx" extension support])
if test $PHP_SIGX != "no"; then
PHP_REQUIRE_CXX()
PHP_SUBST(SIGX_SHARED_LIBADD)
PHP_ADD_LIBRARY(stdc++, 1, SIGX_SHARED_LIBADD)
PHP_ADD_LIBRARY(stdc++, 1, "libsigx.a")
PHP_NEW_EXTENSION(sigx, sigx.cc, $ext_shared)
fi
This is not working for me. I can run phpize and ./configure just fine, but when I go to run a test script, it complains about objects in the library not being available.
What I said in my comment worked.
PHP_ADD_LIBRARY_WITH_PATH(sigx, ., SIGX_SHARED_LIBADD)
I wrote an extension in C++ that uses libtidy, and it runs perfectly under PHP when I compile PHP --with-tidy.
However, it would be nice to have the extension run on a vanilla PHP. When I try to use the extension, I get something like:
PHP Warning:
PHP Startup:
Unable to load dynamic library 'extension.so':
undefined symbol: tidyCleanAndRepair in Unknown on line 0
and the extension is not loaded.
Obviously, the official tidy extension works fine. I have the relevant libtidy development packages installed on the system, and it compiles+links without a problem. I have tried to look through the code for the tidy extension, but it is a huge mass of macros - copying pieces at random felt like cargo code.
Besides linking to the library with PHP_ADD_LIBRARY_WITH_PATH(tidy, $TIDY_LIBDIR, TIDY_SHARED_LIBADD), Is there a PHP extension or C statement that fixes this error?
Thanks in advance!!
EDIT: here is the entire config.m4 file:
dnl config.m4 for extension htmlparser
PHP_ARG_ENABLE(htmlparse, whether to enable htmlparser support,
[ --enable-htmlparser Enable htmlparser support])
if test "$PHP_HTMLPARSER" != "no"; then
if test -r $PHP_LIBXML2/lib/libxml2.a; then
LIBXML2_DIR=$PHP_LIBXML2
else
AC_MSG_CHECKING(for libxml2 in default path)
for i in /usr/local /usr; do
if test -r $i/lib/libxml2.a; then
LIBXML2_DIR=$i
AC_MSG_RESULT(found in $i)
fi
done
fi
if test -z "$LIBXML2_DIR"; then
AC_MSG_RESULT(not found)
AC_MSG_ERROR(Please reinstall the libxml2 distribution - libxml2.h should
be in <libxml2-dir>/include and libxml2.a should be in <libxml2- dir>/lib)
fi
PHP_ADD_INCLUDE($LIBXML2_DIR/include/libxml2)
PHP_ADD_LIBRARY_WITH_PATH(libxml2, $LIBXML2_DIR/lib, LIBXML2_SHARED_LIBADD)
AC_MSG_CHECKING(for boost in default path)
for i in /usr/local /usr; do
if test -r $i/include/boost; then
BOOST_DIR=$i
AC_MSG_RESULT(found in $i)
fi
done
if test -z "$BOOST_DIR"; then
AC_MSG_RESULT(not found)
AC_MSG_ERROR(Please reinstall the boost distribution!!!)
fi
PHP_ADD_INCLUDE($BOOST_DIR/include/boost/)
TIDY_SEARCH_DIRS="/usr/local /usr"
for i in $TIDY_SEARCH_DIRS; do
if test -f $i/include/tidy/tidy.h; then
TIDY_DIR=$i
TIDY_INCDIR=$i/include/tidy
elif test -f $i/include/tidy.h; then
TIDY_DIR=$i
TIDY_INCDIR=$i/include
fi
done
if test -z "$TIDY_DIR"; then
AC_MSG_ERROR(Cannot find libtidy)
fi
TIDY_LIBDIR=$TIDY_DIR/lib
PHP_ADD_INCLUDE($TIDY_INCDIR)
PHP_ADD_LIBRARY_WITH_PATH(tidy, $TIDY_LIBDIR, TIDY_SHARED_LIBADD)
PHP_CHECK_LIBRARY(tidy,tidyOptGetDoc,
[
AC_DEFINE(HAVE_TIDYOPTGETDOC,1,[ ])
],[],[])
AC_DEFINE(HAVE_HTMLPARSER, 1, [Whether you want htmlparser support])
PHP_SUBST(HTMLPARSER_SHARED_LIBADD)
PHP_ADD_LIBRARY_WITH_PATH(stdc++, 1, HTMLPARSER_SHARED_LIBADD)
PHP_REQUIRE_CXX()
PHP_NEW_EXTENSION(htmlparser, php_htmlparser.cpp parsehtml.cpp StringBuilder.cpp, $ext_shared)
fi
did you compile your extension with the same php version as a vanilla php? you should use php sources with the same version.
besides, make sure that in php.ini file you loaded 'libtidy' extension before your extension.