The PHP3 Dynamic Loadable Module HOWTO
Mathieu Kooiman <caps@gge.nl>
v0.02, 13 Dec 1999
This is the PHP3 Dynamic Loadable Module HOWTO, explaining how
to setup PHP to use Dynamic Loadable Modules (DLM) and write your own
PHP extension. In this HOWTO Dynamic Loadable Modules are referred to
as DLM.
Table of Contents
1. General Information.
1.1 What is PHP?
1.2 What are Dynamic Loadable Modules?
1.3 About the Author.
1.4 Copyright.
1.5 Contributors.
2. Compiling PHP for use with DLM.
2.1 Getting PHP.
2.2 Compile PHP a CGI binary.
2.3 Compile PHP as an Apache Module (mod_php3).
3. Writing your own Module.
3.1 Basic setup.
3.2 First example (Hello_World).
3.3 Returning Values.
3.3.1 LONG.
3.3.2 STRINGS.
3.3.3 FLOATING POINT or DOUBLES.
3.3.4 ARRAYS.
3.3.5 OBJECTS.
3.3.6 MyModule.c.
3.4 Creating Variables.
1. General Information.
1.1 What is PHP?
PHP (Hypertext Preprocessor) is a HTML Preprocessor. Enabling
you to create dynamic webpages. If you want more information,
you will find it at http://www.php.net.
1.2 What are Dynamic Loadable Modules?
A Dynamic Loadable Module (DLM) are libraries with functions,
which PHP can load to extends it functionality.
1.3 About the Author.
Mathieu 'CaPS' Kooiman from The Netherlands.
Born in 1983 and has been coding in PHP since March 1999.
I can be found on #PHP on EfNet from time to time, or #NL on IRCnet
more regularly. If you need to know more, email me at caps@gge.nl
1.4 Copyright.
This PHP Dynamic Loadable Module HOWTO(c) 1999 Mathieu Kooiman
may be reproduced in any possible way as long as the author is
notified.
1.5 Contributors.
While writing this HOWTO I received help from some people.
At this moment I just wanna thank Aztech_ helping me proof
read and make adjusts. As did Randall Goguen (aka Ranman) who
wrote the part about compiling Apache for use with
DLM and made some stuff make sense :))
Thanks guys!
2. Compiling PHP for use with DLM.
2.1 Getting PHP.
You can download the PHP source free from:
http://www.php.net/distributions/php-3.0.12.tar.gz
* THE SOURCE FILES SHOWN HERE WILL NOT!! WORK!!! WITH PHP4 *
* THIS IS BECAUSE PHP4 HAS A REDONE THE BASE ENGINE (ZEND) *
2.2 Compile PHP a CGI binary.
tar -xvzf php-3.0.12.tar.gz # Extract the tarball.
cd php-3.0.12
./configure # Configure PHP.
-------------------- HOLD IT ------------------
Here we gotta change the Makefile, fire up your editor
and look for "LDFLAGS" in the Makefile.
You'll have to append -rdynamic to it.
Change the line:
LDFLAGS = $(LDFLAGS_SHLIB) $(LDFLAGS_SHLIB_EXPORT)
To:
LDFLAGS = $(LDFLAGS_SHLIB) $(LDFLAGS_SHLIB_EXPORT) -rdynamic
-------------------- CONTINUE ---------------------
make # Compile PHP.
make install # Install.
2.3 Compile PHP as an Apache Module (mod_php3).
tar -xvzf php-3.0.12.tar.gz # Extract the tarball.
cd php-3.0.12
./configure \ # Configure PHP.
--with-apxs=/usr/local/apache/bin/apxs
make # Compile PHP.
make install # Install.
3. Writing your own Module.
3.1 Basic setup.
To start writing your own PHP module, I advice you to make a
little subdirectory somewhere ie: MyModule.
cd ~ # Go to your Home Directory.
mkdir MyModule # Make the directory.
mkdir MyModule/SRC # I prefer using a SRC dir,
# so lets make that one too.
cd MyModule # Change to the MyModule
# directory.
cp /path/to/php-3.0.12/dl/phpdl.h . # Copy phpdl.h to our directory.
Now, lets create a Makefile in MyModule/ to ease compiling.
I'm using the MyModule.c file as an example.
MyModule.c is describled further below.
* NOTE *
This Makefile works on my machine, which runs
Redhat 5.2 - Linux 2.0.36 - GCC version 2.7.2.3
and also on
Slackware 4.0 - Linux 2.2.12 -GCC version 2.7.2.3
It MIGHT, MIGHT NOT work on your machine.
# WHEN USING A CGI BINARY
# Create a file called ~/MyModule/Makefile that contains the following:
# Note: The use of Tabs are very important.
CC = cc -O2 -Dbool=char -DHAS_BOOL \
-I/usr/local/include \
-I/YourPHPsourceDirectory/ -fpic
LD = cc -shared -L/usr/local/lib -rdynamic
all: MyModule.so
HELLO_OBJS = SRC/MyModule.o
MyModule.so: $(HELLO_OBJS)
$(LD) -o $@ $(HELLO_OBJS)
SRC/MyModule.o: SRC/MyModule.c
$(CC) $(CFLAGS) -DCOMPILE_DL=1 -c -o $@ $<
3.2 First example (Hello_World).
To get you started here's a example with explanations.
I'll creating a PHP function called Hello_World. Place this file
in MyModule/SRC/
cd SRC/ # Change to the SRC directory.
-------------------- MyModule.c --------------------
# Create the file ~/MyModule/SRC/MyModule.c containing the following:
#include "../phpdl.h"
/* Define the functions that will be accessible to PHP */
DLEXPORT void hello_world (INTERNAL_FUNCTION_PARAMETERS);
/* prototype for function_entry is :
* {"PHP_FUNCTION", Your_C_Function, 1}
*/
function_entry MyModule_functions[] = {
{"hello_world", hello_world, NULL},
{NULL, NULL, NULL}
};
/*
* Register your functions within PHP
*/
php3_module_entry MyModule_module_entry = {
"MyModule", MyModule_functions,
NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL
};
#if COMPILE_DL
DLEXPORT php3_module_entry *get_module(void) {
return &MyModule_module_entry;
}
#endif
/* Alright! PHP now "knows" Hello_World exists.
* Now lets write the actual function
*/
DLEXPORT void hello_world (INTERNAL_FUNCTION_PARAMETERS) {
// Well, its ugly, but easy :)
char MyFunction[15];
sprintf(MyFunction, "Hello World!");
/* Create the return value.
* We gotta alloc memory for the string,
* for this we use estrdup()
*/
return_value->value.str.val = estrdup(MyFunction);
return_value->value.str.len = strlen(MyFunction);
return_value->type = IS_STRING;
}
/* And Voila! Our function is complete!!! */
cd ../ # Change back to the ~/MyModule directory.
Alright!! Compiling time!
In ~/MyModule/ do a 'make' and you'll see something like this:
# make
cc -shared -L/usr/local/lib -rdynamic -o MyModule.so SRC/MyModule.o
By now you should have 'MyModule.so' in MyModule/
This is the library PHP can load to extend its functions!
All you need now is a php file to test it with.
test.php3:
<?
dl("/path/to/MyModule.so");
$MyVar = hello_world();
echo $MyVar;
?>
The function dl() loads the library and "adds the functions to PHP".
After loading the library, I assigned the return value of hello_world() to
$MyVar. In theory, $MyVar now gets the value of the C variable MyFunction.
Well, we're all set to test it now!
# php test.php3
Content-type: text/html
Hello World!
Well, whadda ya know. It worked!
# If you compiled PHP as an Apache Module just point your browser at
# http://localhost/test.php3
3.3 Returning Values.
The reason why I talk about RETURNING variables first is,
this probably'll be what you'll use most.
Creating variables in C functions accessible in PHP
just doesn't make to much sense to me.
There are several ways of returning values, especially
several TYPES. There are LONGS, ARRAYS, DOUBLES, STRINGS etc.
The following section handles just that. There are several
macro's defined, which you can use to ease up returning values.
* RETURN_LONG(LongValue);
* RETURN_DOUBLE(DoubleValue);
* RETURN_STRING(String, 1);
* RETURN_STRINGL(String, Length, 1);
* RETURN_TRUE;
* RETURN_FALSE;
3.3.1 LONG.
If you wish to return a "LONG", you'll have to change your
return values;
long MyVar = 100;
return_value->value.lval = MyVar;
return_value->type = IS_LONG;
Notice the 'value.lval'. LVAL stands for Long Value;
Say your function in PHP would be called: Return_100();
$Hundred = Return_100; would make $Hundred 100 :)
The easy way: RETURN_LONG(MyVar);
3.3.2 STRINGS.
We've learned how to return a string in MyModule.c
But I'll paste the code again for simple reference.
char MyFunction[15];
sprintf(MyFunction, "Hello World!");
return_value->value.str.val = estrdup(MyFunction);
return_value->value.str.len = strlen(MyFunction);
return_value->type = IS_STRING;
The easy way: RETURN_STRING(MyFunction, 1);
3.3.3 FLOATING POINT or DOUBLES.
Say you want to calculate VAT, which is 17.5% in The Netherlands,
you can't use a LONG. You'll have to use a FLOAT or DOUBLE in C,
and a DOUBLE in PHP.
double Calculated_VAT;
int Value=100;
double VAT=17.5;
Calculated_VAT=((Value/100) * VAT) + Value;
return_value->value.dval = Calculated_VAT;
return_value->type = IS_DOUBLE;
The easy way: RETURN_DOUBLE(Calculated_VAT);
3.3.4 ARRAYS.
Returning an array is a bit different from returning an other type.
First of all you got two types of arrays.
* Associative arrays......... $foo["bar"]
* Enumerated arrays.......... $foo[0]
They both need different handling.
First of all you'll have to initialize the array. Usually
you would do this with a 'pval *MyArray' but since we are RETURNING
an array. We will init 'return_value' as an array.
int i;
if (array_init(return_value) == FAILURE) {
php3_error(E_ERROR, "Error initializing Array");
return;
}
for (i=0;i<10;i++) {
add_next_index_long(return_value,i);
}
This would return an array:
array[0] = 0 ... array[10] = 10;
Of course you can return an array with other values:
(as taken from /php3.0.12/apidoc.txt)
add_index_long(return_value, index, MyLongValue)
add_index_double(return_value, index, MyDoubleValue)
add_index_string(return_value, index, MyString)
add_index_stringl(return_value, index, MyString, length)
These functions will add a specific variable on
place 'INDEX'.
Returning an associative array is again different from returning
any other TYPE. Again you will have to initialize the return
array.
array_init(return_value);
You might want to add some error handling here, else, if something
goes your module will cause an 'Segmentation Fault'.
if (array_init(return_value) == FAILURE) {
php3_error(E_ERROR, "Couldn't initialize array Return_Value!");
return;
}
After this you might want to add an element:
long MyLongValue=100;
add_assoc_long(return_value, "foobar", MyLongValue);
All functions for adding elements to an associative array:
(as taken from php-3.0.12/apidoc.txt)
add_assoc_long(return_value,Key,LongValue)
add_assoc_double(return_value,Key,DoubleValue)
add_assoc_string(return_value,key,String)
add_assoc_stringl(return_value,key,String,length)
Futheron I'll show an example of returning arrays.
3.3.5 OBJECTS.
An object is (as Rasmus said one day in #php) an encapsulation
of methods and variables. Well, I couldn't agree more :D
Well, we can return these. You can even register a function
on return_value. So you can return a complete class!
First you will have to initialize an Object, this is much like
initializing an array. Simply call object_init();
if (object_init(return_value) == FAILURE) {
php3_error(E_ERROR,"Couldn't init an Object!");
return;
}
Returning a object property is much like returning a regular
variable.
add_property_long(return_value, "MyWealth", 0);
add_property_double(return_value,property_name,Double)
add_property_string(return_value,property_name,String)
add_property_stringl(return_value,property_name,String,length)
Adding a function to your return class is also very easy to do.
First write the method you want to add to your class, I will
show you a simplistic method.
-------------------- MyModule.c --------------------
#include "../phpdl.h
DLEXPORT void My_Returned_Class (INTERNAL_FUNCTION_PARAMETERS);
// The functions accessible for PHP
function_entry MyModule_functions[] = {
{"MyClass", My_Returned_Class, NULL},
{NULL, NULL, NULL}
};
php3_module_entry MyModule_module_entry = {
"MyModule", MyModule_functions,
NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL
};
#if COMPILE_DL
DLEXPORT php3_module_entry *get_module(void) {
return &MyModule_module_entry;
}
#endif
void MyMethod (INTERNAL_FUNCTION_PARAMETERS) {
/*
* This functions takes 2 arguments, substracts them,
* and returns them.. well if THIS aint easy.. WHAT is:)
*/
pval *Number1, *Number2;
if (ARG_COUNT(ht) != 2 ||
getParameters(ht,2,&Number1,&Number2)==FAILURE) {
WRONG_PARAM_COUNT;
}
// Make sure we can use ex. 17.5
convert_to_double(Number1);
convert_to_double(Number2);
// Return the substracted value (the easy way)
RETURN_DOUBLE(Number1->value.dval - Number2->value.dval);
}
// Now.. Add it to your function which returns an Object.
DLEXPORT void My_Returned_Class(INTERNAL_FUNCTION_PARAMETERS) {
// Create an Object
object_init(return_value);
// Register the Method
add_method(return_value, "minus", MyMethod);
}
You might want to create MyModule2/
cd ~ # Go to your home directory.
mkdir MyModule2
mkdir MyModule2/SRC
cp MyModule/Makefile MyModule2/Makefile
cp MyModule/phpdl.h MyModule2/phpdl.h
Then save the above file (MyModule.c) as MyModule2/SRC/MyModule.c
Type 'cd MyModule2' and do a 'make'.
Now make a test.php3 again :))
test.php3
dl("/path/to/MyModule.so");
$MyMathClass = MyClass();
echo $MyMathClass->minus(10,5);
When running test.php3, you would see something like:
# php test.php3
Content-type: text/html
5
And again, it works :))
# If you compiled PHP as an Apache Module just point your browser at
# http://localhost/test.php3
3.4 Creating Variables.
For some strange reason you might want to make variables from
within a function. The only thing I can think of would be making
some global variables accessible with some "statistic" values;
for example with phpIrc, how many users there are on a channel.
Lets create a var called $NUMBER_OF_USERS, and say it would be
called with phpIrc_Join_Channel(); This is hypothetical. I
don't know wether phpIrc has this function. Creating variables
in the symbol table is very easy, there are three macro's available
to help you do so.
* SET_VAR_STRING(name,value)
Be careful with this one! You'll have
to allocate memory for it before you
call the macro.
* SET_VAR_DOUBLE(name,value)
* SET_VAR_LONG(name,value)
You can get the real phpIrc on http://www.phpwizard.net/
By now you MIGHT come across a little typo in php.h.
The macro which defines looks like this:
#define SET_VAR_DOUBLE(n,v) { \
{ \
pval var; \
var.value.dval = (666.6); \
var.type = IS_DOUBLE; \
_php3_hash_update(&GLOBAL(symbol_table)),
// Looky here, an extra ')'!!
(n), \
strlen((n))+1, \
&var, \
sizeof(pval),NULL); \
} \
}
You can simply remove the ')' and recompile or just use this macro
used in the example below.
#include "phpdl.h"
#define SET_MYVAR_DOUBLE(n,v) { \
{ \
pval var; \
var.value.dval = (v); \
var.type = IS_DOUBLE; \
_php3_hash_update(&GLOBAL(symbol_table), \
(n), \
strlen((n))+1, \
&var, \
sizeof(pval),NULL); \
} \
}
DLEXPORT void phpIrc_Join_Channel(INTERNAL_FUNCTION_PARAMETERS);
function_entry MyModule_functions[] = {
{"join_channel", phpIrc_Join_Channel, NULL},
{NULL, NULL, NULL}
};
php3_module_entry phpIrc_module_entry = {
"phpIrc", MyModule_functions,
NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL
};
#if COMPILE_DL
DLEXPORT php3_module_entry *get_module(void) {
return &phpIrc_module_entry; }
#endif
DLEXPORT void phpIrc_Join_Channel(INTERNAL_FUNCTION_PARAMETERS) {
/*
* Imagine about some code sending JOIN #PHP\n\r
* through a socket around here:))
*/
if (_php3_hash_exists(active_symbol_table,"NUMBER_OF_USERS",
sizeof("NUMBER_OF_USERS"))) {
php3_error(E_WARNING, "NUMBER_OF_USERS already exists");
} else {
SET_MYVAR_DOUBLE("NUMBER_OF_USERS", 666.666);
}
}
Congrats!!! You should now should be able to write your own PHP Modules!
Good luck!
Mathieu 'CaPS' Kooiman
caps@gge.nl
Anyone who wishes to make additions or changes to this
PHP Tip email them to caps@gge.nl
Return to the LinuxGuruz PHP Tutorials Page Return to the LinuxGuruz Main Page
|