24 SWIG and PHP4

Caution: This chapter (and module!) is still under construction

In this chapter, we discuss SWIG's support of PHP4. The PHP4 module is still under development so some of the features below may not work properly (or at all!.)

The PHP4 module has undergone a lot of changes recently affecting the way proxy classes are implemented so you should read this document even if you thought you were familiar with what it said. The major change is that proxy classes are implemented inside the php module in C++ instead of in the generated .php file in php.

24.1 Preliminaries

In order to use this module, you will need to have a copy of the PHP 4.0 (or above) include files to compile the SWIG generated files. You can find these files by running 'php-config --includes'. To test the modules you will need either the php binary or the Apache php module. If you want to build your extension into php directly (without having the overhead of loading it into each script), you will need the complete PHP source tree available.

24.2 Building PHP4 Extensions

To build a PHP4 extension, run swig using the -php4 option as follows :

swig -php4 example.i

This will produce 3 files by default. The first file, example_wrap.c contains all of the C code needed to build a PHP4 extension. The second file, php_example.h contains the header information needed to link the extension into PHP. The third file, example.php can be included by your php scripts. It will attempt to dynamically load your extension, and is a place-holder for extra code specified in the interface file. If you want to build your extension using the phpize utility, or if you want to build your module into PHP directly, you can specify the -phpfull command line argument to swig.

The -phpfull will generate three extra files. The first extra file, config.m4 contains the shell code needed to enable the extension as part of the PHP4 build process. The second extra file, Makefile.in contains the information needed to build the final Makefile after substitutions. The third and final extra file, CREDITS should contain the credits for the extension.

To finish building the extension, you have two choices. You can either build the extension as a seperate object file which will then have to be explicitly loaded by each script. Or you can rebuild the entire php source tree and build the extension into the php executable/library so it will be available in every script. The first choice is the default, however it can be changed by passing the '-phpfull' command line switch to select the second build method.

24.2.1 Building a loadable extension

To build a dynamic module for PHP, you have two options. You can use the phpize utility, or you can do it manually.

To build manually, use a compile string similar to this (different for each OS):

	cc -I.. $(PHPINC) -fpic -c example_wrap.c
	cc -shared example_wrap.o -o libexample.so

To build with phpize, after you have run swig you will need to run the 'phpize' command (installed as part of php) in the same directory. This re-creates the php build environment in that directory. It also creates a configure file which includes the shell code from the config.m4 that was generated by SWIG, this configure script will accept a command line argument to enable the extension to be run ( by default the command line argument is --enable-modulename, however you can edit the config.m4 file before running phpize to accept --with-modulename. You can also add extra tests in config.m4 to check that a correct library version is installed or correct header files are included, etc, but you must edit this file before running phpize. ) If you like SWIG can generate simple extra tests for libraries and header files for you.

	swig -php4 -phpfull -withlibs "xapian omquery" --withincs "om.h"

Will include in the config.m4 search for libxapian.a or libxapian.so and search for libomquery.a or libomquery.so as well as a search for om.h

If you depend on source files not generated by SWIG, before generated configure file, you may need to edit the Makefile.in file. This contains the names of the source files to compile (just the wrapper file by default) and any additional libraries needed to be linked in. If there are extra C files to compile, you will need to add them to the Makefile.in, or add the names of libraries if they are needed. In simple cases SWIG is pretty good at generating a complete Makefile.in and config.m4 which need no further editing.

You then run the configure script with the command line argument needed to enable the extension. Then run make, which builds the extension. The extension object file will be left in the modules sub directory, you can move it to wherever it is convenient to call from your php script.

To test the extension from a PHP script, you need to load it first. You do this by putting the line,

	dl("/path/to/modulename.so");	// Load the module

at the start of each PHP file. SWIG also generates a php module, which attempts to do the dl() call for you:

	include("example.php");

A more complicated method which builds the module directly into the php executable is described below.

24.2.2 Basic PHP4 interface

24.2.3 Functions

C functions are converted into PHP functions. Default/optional arguments are also allowed. An interface file like this :

%module default
int foo(int a);
double bar(double, double b = 3.0);
...

Will be accessed in PHP like this :

dl("default.so"); $a = foo(2);
$b = bar(3.5, -1.5);
$c = bar(3.5);		# Use default argument for 2nd parameter

24.2.4 Global Variables

Global variables are difficult for PHP to handle, unlike Perl, their is no 'magic' way to intercept modifications made to variables, so changes in a PHP variable will not be reflected in its C equivalent. To get around the problem, two extra function are generated, Swig_sync_c() and Swig_sync_php(). These functions are called at the start and end of every function call, ensuring changes made in PHP are updated in C ( and vice versa. ) Because this is handled for you, you can modify the variables in PHP as normal, e.g.

%module example;
...
double seki = 2;
...
int example_func(void);

is accessed as follow :

dl("example.so");
print $seki;
$seki = $seki * 2;	# Does not affect C variable, still equal to 2
example_func();		# Syncs C variable to PHP Variable, now both 4

SWIG supports global variables of all C datatypes including pointers and complex objects.

24.2.5 Pointers

Pointers to C/C++ objects no longer represented as character strings such as:_523d3f4_Circle_p, instead they are represented as PHP resources, rather like MySQL connection handles.

You can also explicitly create a NULL pointer as a string "NULL" or by passing a null or empty value.

24.2.6 Structures and C++ classes

For structures and classes, SWIG produces accessor fuction for each member function and data. For example :

%module vector

class Vector {
public:
	double x,y,z;
	Vector();
	~Vector();
	double magnitude();
};

This gets turned into the following collection of PHP functions :

Vector_x_set($obj);
Vector_x_get($obj);
Vector_y_set($obj);
Vector_y_get($obj);
Vector_z_set($obj);
Vector_z_get($obj);
new_Vector();
delete_Vector($obj);
Vector_magnitude($obj);

To use the class, simply use these functions. However, SWIG also has a mechanism for creating proxy classes that hides these functions and uses an object oriented interface instead - see below

24.2.7 Constants

These work in much the same way as in C/C++, constants can be defined by using either the normal C pre-processor declarations, or the %constant SWIG directive. These will then be available from your PHP script as a PHP constant, (e.g. no dollar sign is needed to access them. ) For example, with a swig file like this,

%module example

#define PI 3.14159

%constant int E  = 2.71828

you can access from in your php script like this,

dl("libexample.so");

echo "PI = " . PI . "\n";

echo "E = " . E . "\n";

There are two peculiarities with using constants in PHP4. The first is that if you try to use an undeclared constant, it will evaulate to a string set to the constants name. For example,

%module example

#define EASY_TO_MISPELL	0

accessed incorrectly in PHP,

dl("libexample.so");

if(EASY_TO_MISPEL) {
	....
} else {
	....
}

will issue a warning about the undeclared constant, but will then evalute it and turn it into a string ('EASY_TO_MISPEL'), which evaluates to true, rather than the value of the constant which would be false. This is a feature.

The second 'feature' is that although constants are case sensitive (by default), you cannot declare a constant twice with alternative cases. E.g.,

%module example

#define TEST	Hello
#define Test	World

accessed from PHP,

dl("libexample.so");

echo TEST, Test;

will output "Hello Test" rather than "Hello World". This is because internally, all constants are stored in a hash table by their lower case name, so 'TEST' and 'Test' will map to the same hash element ('Test'). But, because we declared them case sensitive, the Zend engine will test if the case matches with the case the constant was declared with first.

So, in the example above, the TEST constant was declared first, and will be stored under the hash element 'test'. The 'Test' constant will also map to the same hash element 'test', but will not overwrite it. When called from the script, the TEST constant will again be mapped to the hash element 'test' so the constant will be retrieved. The case will then be checked, and will match up, so the value ('Hello') will be returned. When 'Test' is evaulated, it will also map to the same hash element 'test'. The same constant will be retrieved, this time though the case check will fail as 'Test' != 'TEST'. So PHP will assume that Test is a undeclared constant, and as explained above, will return it as a string set to the constant name ('Test'). Hence the script above will print 'Hello Test'. If they were declared non-case sensitive, the output would be 'Hello Hello', as both point to the same value, without the case test taking place. ( Apologies, this paragraph needs rewritting to make some sense. )

24.2.8 Proxy classes

To avoid having to call the various accessor function to get at structures or class members, we can turn C structs and C++ classes into PHP classes that can be be used directly in PHP scripts as objects and object methods. This is done by writing additional PHP code that builds PHP classes on top of the low-level SWIG interface. These PHP classes shadow or proxy an underlying C/C++ class.

To have SWIG create proxy classes, use the -proxy option :

% swig -php4 -proxy tbc.i

This will produce the same files as before except that the final module will declare internal PHP classes with the same names as the classes in your .i file. No longer are the proxy classes defined in the .php file, it will not contain significantly more support PHP code.

For the most part, the code is the same except that we can now access members of complex data structures using -> instead of the low level access or functions like before.

.... ( more examples on the way ) ....

24.2.9 Constructors and Destructers

Constructors are used in PHP as in C++, they are called when the object is created and any arguments are passed to them. However, function overloading is not allowed in PHP so only one constructor can be used. This creates a problem when copying objects, as we cannot avoid creating a whole new one when all we want is to make it point to the same value as the original. This is currently worked around by doing the following,

This is rather convoluted and hopefully will be improved upon in a later release.

Because the internal wrapped objects are wrapped in PHP resources, PHP handles the cleaning up when there are no more references to the wrapped object. 'RegisterShutdownFunction' is no longer needed for this. I am not sure if PHP resources are all freed at the end of a script, or when they each go out of scope.

24.2.10 Static Member Variables

Class variables are not supported in PHP, however class functions are, using '::' syntax. Static member variables are therefore accessed using a class function with the same name, which returns the current value of the class variable. For example

%module example

class Ko {
	static int threats;
	...
};

would be accessed in PHP as,

dl("libexample.so");

echo "There has now been " . Ko::threats() . " threats\n";

To set the static member variable, pass the value as the argument to the class function, e.g.


Ko::threats(10);

echo "There has now been " . Ko::threats() . " threats\n";

24.2.11 PHP4 Pragmas

There are a few pragmas understood by the PHP4 module. The first, include adds a file to be included by the generated PHP module. The second, code adds literal code to the generated PHP module. The third, phpinfo inserts code to the function called when PHP's phpinfo() function is called.

	/* example.i */

	%pragma(php4) include="foo.php"
	%pragma(php4) code="
	  function foo($bar) {
		/* do something */
	  }
	"
	%pragma(php4) phpinfo="
	  zend_printf("An example of PHP support through SWIG\n");
	  php_info_print_table_start();
	  php_info_print_table_header(2, \"Directive\", \"Value\");
	  php_info_print_table_row(2, \"Example support\", \"enabled\");
	  php_info_print_table_end();
	"

	%include "example.h"

24.2.12 Building extensions into php

This method, selected with the -phpfull command line switch, involves rebuilding the entire php source tree. Whilst more complicated to build, it does mean that the extension is then available without having to load it in each script.

After running swig with the -phpfull switch, you will be left with a shockingly similiar set of files to the previous build process. However you will then need to move these files to a subdirectory within the php source tree, this subdirectory you will need to create under the ext directory, with the name of the extension ( e.g mkdir php-4.0.6/ext/modulename .)

After moving the files into this directory, you will need to run the 'buildall' script in the php source directory. This rebuilds the configure script and includes the extra command line arguments from the module you have added.

Before running the generated configure file, you may need to edit the Makefile.in. This contains the names of the source files to compile ( just the wrapper file by default) and any additional libraries needed to link in. If their are extra C files to compile you will need to add them to the Makefile, or add the names of libraries if they are needed. In most cases Makefile.in will be complete, especially if you make use of -withlibs and -withincs

	swig -php4 -phpfull -withlibs "xapian omquery" --withincs "om.h"

Will include in the config.m4 and Makefile.in search for libxapian.a or libxapian.so and search for libomquery.a or libomquery.so as well as a search for om.h

You then need to run the configure command and pass the necessary command line arguments to enable your module ( by default this is --enable-modulename, but this can be changed by editing the config.m4 file in the modules directory before running the buildall script. In addition, extra tests can be added to the config.m4 file to ensure the correct libraries and header files are installed.)

Once configure has completed, you can run make to build php. If this all compiles correctly, you should end up with a php executable/library which contains your new module. You can test it with a php script which does not have the 'dl' command as used above.

24.2.13 To be furthered...