PHP





What is PHP?

Basically PHP is text interpreter, such Perl. It takes a text as input and it produces text. PHP is mainly used to build dynamic web pages. In the HTML file, PHP's instructions (similar to C) are found between PHP's tags (<?PHP instructions ?>).

Using PHP's instructions we can produce whatever text we want. We can produce HTML of course, but it is not an obligation. You can generate JavaScript, Java, ... anything that will be executed on the client machine. In a Web environment you should produce a Web-compatible code (I meam anything that can be interpreted by the client browser). But you can also generate any kind of text that has nothing to do with Web.

PHP versus CGI

A PHP file (I mean the file that contains PHP's instructions) is not a CGI. That is you should not put it into the "cgi-bin" directory. You should put it in the "htdocs" directory instead (or whatever directory you specified in your "httpd.conf").

A CGI executes itself within the OS. Depending on the method used to pass data to the CGI, the latter will read it from the standard input (POST) or from the environment-variable QUERY_STRING (GET). Then it will produce print text to the standard output (STDOUT).

A PHP file is just a simple text file, this is not a program. This file is parsed by the PHP interpreter to produce whaterver text you want. Usually the PHP interpreter is binded into the Web server (this could be done via dynamically loadable libraries for example).

Installing PHP

The following show you how to install PHP4 as a dymamic module. This assumes that:

  • Apache will be installed under /home/hpptd.
  • MySql will be installed under /home/mysql.
  • libmcrypt will be installed under /home/libmcrypt.
  • PHP4 will be installed under /home/php4. PHP4 will be compiled with mcrypt support.
  • The configuration file "php.ini" will be put under "/home/php4/config".
  • The dynamically loadable modules will be put under "/home/php4/modules".
  • You are using bash.
  • You are running Linux.

Installing Apache

su - root

./configure --prefix=/home/httpd --enable-module=so
notes:

  • "--prefix=/home/httpd" install Apache under "/home/httpd".
  • "--enable-module=so" for PHP (as a dynamically loadable module).
make

make install

OK, now we need to setup the correct path to Apache binaries. Edit your "/etc/profile" and add the following line (before the "export" of course):

PATH=${PATH}:/home/httpd/bin


And the reload your environment (you can logout and login again for example).

Now type "httpd -l" and make sure that you see the following line:

"mod_so.c"

Installing MySql

./configure --prefix=/home/mysql

make

make install

If you are on Linux and have installed shared libraries, make sure the location of these shared libraries are listed in your /etc/ld.so.conf file. For example, if you have:

/home/mysql/lib/mysql/libmysqlclient.so

Make sure /etc/ld.so.conf contains:

/home/mysql/lib/mysql


Then run ldconfig.

OK, now we need to setup the correct path to MySql binaries. Edit your "/etc/profile" and add the following line (before the "export" of course):

PATH=${PATH}:/home/mysql/bin


Then reload your environment.

Create the MySQL grant tables (necessary only if you haven't installed MySQL before): In the MySql install directory, type:

"scripts/mysql_install_db".


To start the server, type:

safe_mysqld & (note that the right PATH should be loaded now)

Compiling mcrypt

There is nothing difficult, just type:


  ./configure --prefix=/home/libmcrypt --disable-posix-threads
  make
  make install
  
Then edit /etc/ld.so.conf and add the line:

/home/libmcrypt/lib


Then run ldconfig

Installing PHP4

This is very simple, just type:
  ./configure --with-mysql=/home/mysql --with-apxs --prefix=/home/php4 --with-mcrypt=/home/libmcrypt --with-config-file-path=/home/php4/config


  make
  make install
  
Add the following line to your "/etc/profile" (before the "export"):

PATH="${PATH}:/home/php4/bin"


Then add the following line to your "/etc/profile":

export PHPRC=/home/php4/lib/


This will tell PHP where to find its libraries.

Then reload your environment.

Copy the generic configuration file to the "/home/php4/config/" directory.

cp ./php.ini-dist /home/php4/config/php.ini


Edit the file "/home/php4/config/php.ini" and set the variable "extension_dir" to "/home/php4/modules". This will tell PHP4 to search for dinamically loadable modules (.so) into the directory "/home/php4/modules".

And then unable Apache to load PHP. Uncomment the PHP4 related lines in your httpd.conf file. This should look like something like:


    AddType application/x-httpd-php .php
    AddType application/x-httpd-php-source .phps
  


Then restart your server (apachectl restart) and you should be able to serve up PHP files now. Note that to start Apache, type "apachectl start", assuming that the correct PATH is set to /home/http/bin.

Testing PHP

Go to the directoty /home/httpd/htdocs and edit the file "ex.php":
  <HTML>
    <BODY>
      <?PHP print "toto"; ?>
    </BODY>
  </HTML>
  
Then run Netscape (or any browser) and enter the URL:

http://localhost/ex.php


You should see "toto".

Writting dinamically loadable modules (in C) for PHP

Example 1

newfunc.h

#ifndef PHP_NEWFUNCTION_H
  #define PHP_NEWFUNCTION_H
  PHP_FUNCTION(newfunction1);
#endif


newfunc.c

#include <stdio.h>
#include "php.h"
#include "php_ini.h"

#include "newfunc.h"

function_entry newfunc_functions_entry[] =
{
        PHP_FE(newfunction1, NULL)
        {NULL, NULL, NULL}
};

zend_module_entry newfunc_module_entry =
{
        "newfunc", /* name */
        newfunc_functions_entry, /* function_entry */
        NULL, /* initializator */
        NULL, /* destructor */
        NULL, /* startup */
        NULL, /* shutdown */
        NULL, /* info */
        STANDARD_MODULE_PROPERTIES
};

DLEXPORT zend_module_entry *get_module(void) { return &newfunc_module_entry; }

PHP_FUNCTION(newfunction1)
{
  pval *arg1, *arg2;

  /************************************************************************/
  /* Make sure that we have two arguments */
  /************************************************************************/

  if (ARG_COUNT(ht) != 2) { WRONG_PARAM_COUNT; }

  /************************************************************************/
  /* Get the two arguments from the arguments' list */
  /************************************************************************/

  if (getParameters(ht,2,&arg1,&arg2)==FAILURE) { WRONG_PARAM_COUNT; }

  /************************************************************************/
  /* Make sure we have 'long' values */
  /************************************************************************/

  convert_to_long(arg1);
  convert_to_long(arg2);

  /************************************************************************/
  /* Calculate arg1 + arg2 */
  /************************************************************************/

  RETURN_LONG(arg1->value.lval + arg2->value.lval);
}


Note 1

If you search into the PHP4 header files, you can find the following peaces of code:

if the file: "main/php3_compat.h":

typedef zval pval;
This means that if you want to write portable code that can be used with PHP3, you should use "pval" instead of "zval".

typedef struct _zval_struct zval;
struct _zval_struct
{
   zvalue_value value;
   zend_uchar type;
   zend_uchar is_ref;
   zend_ushort refcount;
};


The value "type" could be:


   IS_NULL 0
   IS_LONG 1
   IS_DOUBLE 2
   IS_STRING 3
   IS_ARRAY 4
   IS_OBJECT 5
   IS_BOOL 6
   IS_RESOURCE 7
   IS_CONSTANT 8
   IS_CONSTANT_ARRAY 9
With zvalue_value:


typedef union _zvalue_value
{
   long lval; <= long value
   double dval; <= double value
   struct
   {
     char *val;
     int len;
   } str;
   HashTable *ht; <= hash table value
   struct
   {
     zend_class_entry *ce;
     HashTable *properties;
   } obj;
} zvalue_value;
Note 2

the line

RETURN_LONG(arg1->value.lval + arg2->value.lval);

could have been replaced by:

return_value->value.lval = arg1->value.lval + arg2->value.lval;
return_value->type = IS_LONG;


But the first method is certainly better. Indeed, if the "zval" (or "pval") structure changes, then the macro "RETURN_LONG" will be updated and you won't have to change your code.

Note 3

If you look at the file "Zend/zend_operators.h", you find the function used to convert PHP's objects:


ZEND_API void convert_scalar_to_number(zval *op);
ZEND_API void _convert_to_string(zval *op ZEND_FILE_LINE_DC);
ZEND_API void convert_to_long(zval *op);
ZEND_API void convert_to_double(zval *op);
ZEND_API void convert_to_long_base(zval *op, int base);
ZEND_API void convert_to_null(zval *op);
ZEND_API void convert_to_boolean(zval *op);
ZEND_API void convert_to_array(zval *op);
ZEND_API void convert_to_object(zval *op);
ZEND_API void multi_convert_to_long_ex(int argc, ...);
ZEND_API void multi_convert_to_double_ex(int argc, ...);
ZEND_API void multi_convert_to_string_ex(int argc, ...);
ZEND_API int add_char_to_string(zval *result, zval *op1, zval *op2);
ZEND_API int add_string_to_string(zval *result, zval *op1, zval *op2);
#define convert_to_string(op) _convert_to_string((op) ZEND_FILE_LINE_CC)


Makefile

#########################################################
# Generic Makefile for PHP dinamically loadable modules #
#########################################################

CC = gcc

#########################################################
# Where to put PHP's dinamically loadable modules #
#########################################################

PHP_MODULE_PATH = /home/php4/modules

#########################################################
# Where to fing PHP's header files #
#########################################################

PHP_TOP = /home/php4/include/php
PHP_TSRM = ${PHP_TOP}/TSRM
PHP_ZEND = ${PHP_TOP}/Zend
PHP_EXT = ${PHP_TOP}/ext
PHP_MAIN = ${PHP_TOP}/main
PHP_REGEX = ${PHP_TOP}/regex

#########################################################
# Compiler's options #
#########################################################

CFLAGS = -I${PHP_TOP} \
                  -I${PHP_TSRM} \
                  -I${PHP_ZEND} \
                  -I${PHP_EXT} \
                  -I${PHP_MAIN} \
                  -I${PHP_REGEX} \
                  -DCOMPILE_DL

# -shared
# Produce a shared object which can then be linked with other objects to form an executable. Only a
# few systems support this option.
# -fPIC
# If supported for the target machine, emit position-independent code, suitable for dynamic linking,
# even if branches need large displacements.

CFLAGSSO = -shared -fPIC -ldl


newfunc.o: newfunc.c \
           newfunc.h
        ${CC} ${CFLAGS} -c newfunc.c

newfunc.so: newfunc.o
        ${CC} ${CFLAGSSO} -o newfunc.so newfunc.o

all: newfunc.so

install: newfunc.so
        cp newfunc.so ${PHP_MODULE_PATH}/

clean:
        rm newfunc.o newfunc.so


ex.php

<HTML>
  <BODY>
    <?PHP
          print "<BR>Test for the dunamically loadable module <I>newfunc</i></BR>";
          dl("newfunc.so");

          $a=1;
          $b=2;
          print "<BR>a = $a";
          print "<BR>b = $b";
          $c = newfunction1($a,$b);
          print "<BR>c = newfunction1(a,b) = $c";
    ?>
  </BODY>
</HTML>
  

  







result


test for the dunamically loadable module newfunc

a = 1
b = 2
c = newfunction1(a,b) = 3


Example 2

The file "newfunc.c" is modified:

newfunc.c

#include <stdio.h>
#include "php.h"
#include "php_ini.h"

#include "newfunc.h"

function_entry newfunc_functions_entry[] =
{
        PHP_FE(newfunction1, NULL)
        {NULL, NULL, NULL}
};

zend_module_entry newfunc_module_entry =
{
        "newfunc", /* name */
        newfunc_functions_entry, /* function_entry */
        NULL, /* initializator */
        NULL, /* destructor */
        NULL, /* startup */
        NULL, /* shutdown */
        NULL, /* info */
        STANDARD_MODULE_PROPERTIES
};

DLEXPORT zend_module_entry *get_module(void) { return &newfunc_module_entry; }

PHP_FUNCTION(newfunction1)
{
  pval *arg1, *arg2;
  long resultat;

  /************************************************************************/
  /* Make sure that we have two arguments */
  /************************************************************************/

  if (ARG_COUNT(ht) != 2) { WRONG_PARAM_COUNT; }

  /************************************************************************/
  /* Get the two arguments from the arguments' list */
  /************************************************************************/

  if (getParameters(ht,2,&arg1,&arg2)==FAILURE) { WRONG_PARAM_COUNT; }

  /************************************************************************/
  /* Make sure we have 'long' values */
  /************************************************************************/

  convert_to_long(arg1);
  convert_to_long(arg2);

  /************************************************************************/
  /* Calculate arg1 + arg2 */
  /************************************************************************/

  resultat = arg1->value.lval + arg2->value.lval;

  arg1->value.lval *= 10;

  RETURN_LONG(resultat);
}


And the PHP script becomes:

ex.php


<HTML>
  <BODY>
    <?PHP
          print "<BR>Test for the dunamically loadable module <I>newfunc</i></BR>";
          dl("newfunc.so");

          $a=1;
          $b=2;
          print "<BR>a = $a";
          print "<BR>b = $b";
          $c = newfunction1($a,$b);
          print "<BR>c = newfunction1(a,b) = $c";
          print "<BR>a = $a";
    ?>
  </BODY>
</HTML>


The result is:

result

test for the dunamically loadable module newfunc

a = 1
b = 2
c = newfunction1(a,b) = 3
a = 1


As you can see, the value of "a" is 1 and not 10.

Example 3

The following code does exactly the same thing that the previous one (this is just another way to write C code - but it is totally equivalent):

newfunc.c

#include <stdio.h>
#include "php.h"
#include "php_ini.h"

#include "newfunc.h"

...


PHP_FUNCTION(newfunction1)
{
  pval **arg1, **arg2;
  long resultat;

  /************************************************************************/
  /* Make sure that we have two arguments */
  /************************************************************************/

  if (ARG_COUNT(ht) != 2) { WRONG_PARAM_COUNT; }

  /************************************************************************/
  /* Get the two arguments from the arguments' list */
  /************************************************************************/

  if (getParameters(ht,2,arg1,arg2)==FAILURE) { WRONG_PARAM_COUNT; }

  /************************************************************************/
  /* Make sure we have 'long' values */
  /************************************************************************/

  convert_to_long(*arg1);
  convert_to_long(*arg2);

  /************************************************************************/
  /* Calculate arg1 + arg2 */
  /************************************************************************/

  resultat = (*arg1)->value.lval + (*arg2)->value.lval;

  (*arg1)->value.lval *= 10;

  RETURN_LONG(resultat);
}


Example 4

OK, now lets play with strings. One thing you must know is that ALL string returned by a PHP function must have been allocated using emalloc(). PHP will free it (using "efree"), so if you did not use emalloc ... you are in big trouble.

There are two kinds of memory in this program. Memory which is returned to the parser in a variable and memory which you need for temporary storage in your internal function. When you assign a string to a variable which is returned to the parser you need to make sure you first allocate the memory with either emalloc or estrdup. This memory should NEVER be freed by you, unless you later, in the same function overwrite your original assignment (this kind of programming practice is not good though).

For any temporary/permanent memory you need in your functions/library you should use the three emalloc(), estrdup(), and efree() functions. They behave EXACTLY like their counterpart functions. Anything you emalloc() or estrdup() you have to efree() at some point or another, unless it's supposed to stick around until the end of the program, otherwise there will be a memory leak. The meaning of "the functions behave exactly like their counterparts" is if you efree() something which was not emalloc()'ed nor estrdup()'ed you might get a segmentation fault. So please take care and free all of your wasted memory. One of the biggest improvements in PHP 3.0 will hopefully be the memory management.


newfunc.c

#include <stdio.h>
#include "php.h"
#include "php_ini.h"

#include "newfunc.h"

...

char *My_BIN2HEX (char*, const int, int*);

PHP_FUNCTION(newfunction1)
{
  pval *string;
  char *result;
  int new_length;

  /************************************************************************/
  /* Make sure that we have two arguments */
  /************************************************************************/

  if (ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }

  /* or "(ARG_COUNT() != 1) { WRONG_PARAM_COUNT; }" */

  /************************************************************************/
  /* Get the two arguments from the arguments' list */
  /************************************************************************/

  if (getParameters(ht,1,&string)==FAILURE) { WRONG_PARAM_COUNT; }

  /************************************************************************/
  /* Make sure we have a characters' string */
  /************************************************************************/

  convert_to_string(string);

  /* or "convert_to_string_ex(&string);" */

  result = My_BIN2HEX (string->value.str.val, string->value.str.len, &new_length);

  if (result == NULL) { RETURN_FALSE; }

  /* Look at the file "Zend/zend_API.h" to get the list of RETURN_XXX macros */
  /* Here we don't duplicate the resulting string (third arg is 0) since we */
  /* have allocated "result" using "emalloc". */

  RETURN_STRINGL (result, new_length, 0);
}


char *My_BIN2HEX (char *src, const int length, int *new_length)
{
  int i;
  char *result, aux[3];


  /************************************************************************/
  /* Use "emalloc()" instead of "malloc()" */
  /* Remember that under PHP, resulting strings MUST be allocated by */
  /* "emalloc" --- Do NOT use statically allocated strings because PHP */
  /* will free it. */
  /************************************************************************/

  result = (char*)emalloc(2*length*sizeof(char));
  if (result == NULL) { return NULL; }

  for (i=0; i<length; i++)
  {
    sprintf (aux, "%2X", (unsigned char)src[i]);
    result[2*i] = aux[0];
    result[2*i+1] = aux[1];
  }

  *new_length = 2*length*sizeof(char);

  return result;
}


Now, lets look at this simple PHP script:

ex.php

<HTML>
  <BODY>
    <?PHP
          print "<BR>Test for the dunamically loadable module <I>newfunc</i></BR>";
          dl("newfunc.so");

          $a=1;
          $b=2;
          print "<BR>a = $a";
          print "<BR>b = $b";
          $c = newfunction1($a);
          print "<BR>c = newfunction1(\"$a\") = $c";
          $c = newfunction1("ABCDEFGH");
          print "<BR>c = newfunction1(\"ABCDEFGH\") = $c";
    ?>
  </BODY>
</HTML>


The result is:

result

Test for the dunamically loadable module newfunc

a = 1
b = 2
c = newfunction1("1") = 31
c = newfunction1("ABCDEFGH") = 4142434445464748