PHP/FI Version 2.0

Table of Contents

  1. Brief History
  2. Installation Instructions
  3. So, what can I do with PHP/FI?
  4. CGI Redirection in Apache HTTPD
  5. Access Control
  6. Access Logging
  7. Relative vs. Absolute URL's - or, Why do my Images Break?
  8. How PHP/FI handles GET and POST method data
  9. GD (a graphics library for GIF creation) Support in PHP
  10. PHP/FI and Virtual Hosts
  11. File Upload Support
  12. mSQL Support
  13. PHP/FI Script Language
  14. Adding your own functions to PHP/FI

Brief History

PHP began life as a simple little cgi wrapper written in Perl. I wrote it in an afternoon during a period between contracts when I needed a quick tool to get an idea of who was reading my online resume. It was never intended to go beyond my own private use. The web server where I had my resume was extremely overloaded and had constant problems forking processes. I rewrote the Perl wrapper in C to get rid of the considerable overhead of having to fork Perl each time my resume was accessed.

Eventually other people on the same web server came across my wrapper and asked if they could use it. Then, as inevitably happens, they started asking for more features. I added more features and finally put together a semi-complete distribution along with documentation, a mailing-list and a FAQ.

At the same time I started playing with databases and wrote a tool to easily embed SQL queries into web pages. It was basically another CGI wrapper that parsed SQL queries and made it easy to create forms and tables based on these queries. This tool was named FI (Form Interpreter).

PHP/FI version 2.0 is a complete rewrite of these two packages combined into a single program. It has now evolved to the point where it is a simple programming language embedded inside HTML files. It eliminates the need for numerous small Perl cgi programs by allowing you to place simple scripts directly in your HTML file. This speeds up the overall performance of your web pages since the overhead of forking Perl several times has been eliminated. It also makes it easier to manage large web sites by placing all components of a web page in a single html file. By including support for various databases, it also makes it trivial to develop database enabled web pages.

Throughout this documentation any references to PHP, FI or PHP/FI all refer to the same thing. The difference between PHP and FI is only a conceptual one. Both are built from the same source distribution. When I build the package without any access loggin or access restriction support, I call my binary FI. When I build with these options, I call it PHP.


Installation Instructions

Before You Begin
If you have absolutely no Unix experience, you may want to ask around for someone with a little bit of Unix knowledge to help you through this installation. Every attempt has been made to make it as simple as possible, but since the software is quite involved and relies on a number of different components, it is not realistic to assume it will go smoothly on all systems. You will probably need someone around who knows the particulars of the destination system well.

Things You Need To Know Before Installing
- Can you run both get and post method cgi programs on your server?
If not, you can not use this package. On many public ISP's CGI programs are either disallowed or severely restricted. If this is the case on your system, talk to your system administrator and ask him/her to have a look at this package and see if they will install it for you.

- If you have mSQL installed on your system, you need to know the base directory of this installation.

- If you are going to be storing log and access configuration files in an NFS-mounted directory and your system does not provide NFS file locking then you will need to define the NFS_HACK variable manually in the src/Makefile and you may want to use a slightly modified version of the gdbm library. See the nfs_hack.txt file in the doc directory for more information on this.

- Note that if you are not interested in using PHP to track accesses to your pages, do not compile this option into the binary. You should also leave out the access restriction code. There is considerable overhead in including these options.

Installation Steps
Step 1.
Run the install program: ./install
You will be asked a number of questions. If you do not understand what is being asked, simply hit return. The default choice should be safe on most systems. This doesn't apply for the questions asking you to specify a directory for your configuration and log files however. Any directory to which the httpd (usually "nobody") has write priviledges. You may create this directory manually somewhere and simply chown nobody directory.

Notes:
If you have gdbm installed on your system and this is not detected by the install script, you have a number of options to fix this:
a) create a link to your libgdbm.a file to a place like /lib or /usr/lib
or
b) change your system's LD_LIBRARY_PATH default setting to include the directory where you keep libgdbm.a
or
c) after running ./install, edit config.h and change the "/* #undef HAVE_GDBM */" to "#define HAVE_GDBM 1" and then edit src/Makefile and add "-L/<directory/lib> -lmsql" to the "LIBS" as well as "-I<directory/include>" to the CPPFLAGS line. <directory/lib> should be the directory where the file, libgdbm.a, is located and <directory/include> should be the directory where the file, gdbm.h, is located.

Step 2.
Go into the src directory: cd src

Step 3.
type: make
This will create the actual executable program file named php.cgi by default. Copy this file to your cgi-bin directory or any directory from which the http daemon can execute cgi programs.

Don't worry about any shift or reduce conflicts reported by either bison or yacc. They are not important.

Step 4.
Copy the php.cgi binary to your system's cgi-bin directory. If you do not have access to do this and wish to install it in your own personal directory, you may do so, but you should set the setuid bit on the executable with: chmod u+s /path/php.cgi

If you do not make set the setuid bit on the binary then any files created by the binary will be owned by the user id under which the web server runs. If this is acceptable, then you can safely leave the setuid bit off.

Testing the software
Once installed you can test to see if your executable works by entering a URL similar to the following in your browser:
http://your.site.domain/cgi-bin/php.cgi
This should show you a page which contains the version number along with various other useful information.

Using the software
To actually use the software on an existing HTML file, you can simply append the path to your file to the above URL. ie.
http://your.site.domain/cgi-bin/php.cgi/path/file.html


So, what can I do with PHP/FI?

The first thing you will notice if you run a page through PHP/FI is that it adds a footer with information about the number of times your page has been accessed. This is just a very small part of what PHP/FI can do for you. It serves another very important role as a form interpreter cgi, hence the FI part of the name. For example, if you create a form on one of your web pages, you need something to process the information on that form. Even if you just want to pass the information to another web page, you will have to have a cgi program do this for you. PHP/FI makes it extremely easy to take form data and do things with it.

A simple example

Suppose you have a form:

<FORM ACTION="/cgi-bin/php.cgi/~userid/display.html" METHOD=POST>
<INPUT TYPE="text" name="name">
<INPUT TYPE="text" name="age">
<INPUT TYPE="submit">
</FORM>


Your display.html file could then contain something like:

<?echo "Hi $name, you are $age years old!<p>">

It's that simple! PHP/FI automatically creates a variable for each form input field in your form. You can then use these variables in the ACTION URL file.

The next step once you have figured out how to use variables is to start playing with some logical flow tags in your pages. For example, if you wanted to display different messages based on something the user inputs, you would use if/else logic. In our above example, we can display different things based on the age the user entered by changing our display.html to:

<?
    if($age>50);
        echo "Hi $name, you are ancient!<p>";
    elseif($age>30);
        echo "Hi $name, you are very old!<p>";
    else;
        echo "Hi $name.";
    endif;
>
PHP/FI provides a very powerful scripting language which will do much more than what the above simple example demonstrates. See the section on the PHP/FI Script Language for more information.

You can also use PHP/FI to configure who is allowed to access your pages. This is done using a built-in configuration screen. With this you could for example specify that only people from certain domains would be allowed to see your pages, or you could create a rule which would password protect certain pages. See the Access Control section for more details.

PHP/FI is also capable of receiving file uploads from any RFC-1867 compliant web browser. This feature lets people upload both text and binary files. With PHP/FI's access control and logical functions, you have full control over who is allowed to upload and what is to be done with the file once it has been uploaded. See the File Upload section for more details.

PHP/FI has support for a database package called mSQL. This allows you to put information into a database and access this information through simple embedded SQL queries right in your .HTML files. Adding a database backend to a web page has never been easier. See the section on mSQL Support for more information.


CGI Redirection in Apache HTTPD

One of the best ways to run PHP/FI is by using a cgi redirection module with the Apache server. There are two of these modules available. One is developed by Dave Andersen and it is available at ftp://ftp.aros.net/pub/util/apache/mod_cgi_redirect.c and the other comes bundled with Apache and is called mod_actions.c. The modules are extremely similar. They differ slightly in their usage. Both have been tested and both work with PHP/FI.

One large caveat at the time of this writing (Feb.11/96) is that the current official Apache release (1.0.2) has a severe limitation which prevents cgi redirected requests from having any post-method data associated with them. I have tracked this down and fixed it in my version of Apache, and there is a apache_1.0.2_patched.tar.gz file at http://www.hyperreal.com/httpd/patches/for_Apache_1.0.2/ which fixes this problem. It also includes a number of other useful patches.

Check the Apache documentation on how to add a module. Generally you add the module name to a file called Configuration. However in the interim apache_1.0.2_patched.tar.gz release you add the module name to a file called config.in. The line to be added if you want to use the mod_actions module is:

Module action_module mod_actions.o

If you are using the mod_cgi_redirect.c module add this line:

Module cgi_redirect_module mod_cgi_redirect.o

Then compile your httpd and install it. To configure the cgi redirection you need to either create a new mime type in your mime.types file or you can use the AddType command in your srm.conf file to add the mime type. The mime type to be added should be something like this:

application/x-httpd-php phtml

If you are using the mod_actions.c module you need to add the following line to your srm.conf file:

Action application/x-httpd-php /cgi-bin/php.cgi

If you are using mod_cgi_redirect.c you should add this line to srm.conf:

CgiRedirect application/x-httpd-php /cgi-bin/php.cgi

Don't try to use both mod_actions.c and mod_cgi_redirect.c at the same time.

Once you have one of these cgi redirection modules installed and configured correctly, you will be able to specify that you want a file parsed by php/fi simply by making the file's extension .phtml. Furthermore, if you add index.phtml to your DirectoryIndex configuration line in your srm.conf file then the top-level page in a directory will be automatically parsed by php if your index file is called index.phtml.


Access Control

If you chose to include access control when you compiled the package, you may append ?config to any URL to edit the access control file. ie.

http://your.machine.domain/cgi-bin/php.cgi/path/file.html?config

Your configuration password will initially be set to your user id. If your user id does not work as your password, it probably means that PHP could not read the /etc/passwd file to locate your user id. If this is the case, the initial password will be set to "php". It is a good idea to change this password. Note that multiple users may maintain their own personal configuration files through a single PHP/FI binary.

Access Control can be quite confusing initially. The ?config screen is divided up into a number of sections. The top section is for changing the password used to make sure that only people who know this password can change access control characteristics. In a system-wide installation, each user has his or her own configuration screen with his or her own password.

The second section of the ?config screen consists of a number of tables. Each table defines a rule-set. The first rule-set is always the default rule-set. This default rule-set is used if a page does not have a rule-set defined specifically for it. After the default rule-set, any number of specific rule-set tables will follow.

To add a rule-set for a specific file, enter the URL of the file in your browser and add ?config to the end of the URL. On the ?config screen that comes up you will see that a rule-set has been added for this page, if it wasn't already there. When a new rule-set is added, it is initially set to be the same as the default rule-set. The following picture shows two simple rule-sets. First a default rule-set which just indicates that hits from all domains should be logged, and second, for the file /~rasmus/test.html and only that file, any users coming from a ".edu" domain will not be granted access.

To edit a rule-set modify the fields until the desired configuration is reached within a rule-set and then hit the "Submit Changes" button. If more rules are needed, hit the "Add Rule" button and then edit the added rule.

To delete a rule, select the checkbox to the right of the rule and hit the "Submit Changes" button. The screen will redraw and the rule should disppear.

Note that you need to enter a regular expression in the pattern field. See the section on regular expressions in this documentation for more details.


Access Logging

Access Logging is another optional feature which can be enabled at compile-time by answering Yes to the question in the install script. If you wish to use this option, you will also need to specify a directory in which log files can be written. PHP will try to create this directory if it doesn't exist, but to make sure it has the proper permissions, you may want to create this directory yourself before running PHP for the first time. The permissions on the directory should be such that the user id under which the PHP cgi program will run can write to the directory.

Access logging stores information about each "hit" on a page. This information can then be summarized by examining these log files. An example log file summarizing script is included in the package. It is the log.html file in the examples directory. To run it, copy it to a directory accessible from your web server and type:

http://your.machine.domain/cgi-bin/php.cgi/path/log.html

By default, if you have compiled PHP with access logging enabled, then your pages will appear with a footer containing some access information. You may not want to see this footer, but still log hits. You may turn these log footers off either by creating a rule in the ?config section for the page, or by adding a tag like this to your page:

<?setshowinfo(0)>


Relative vs. Absolute URL's - or, Why do my Images Break?

A problem common to all CGI wrappers is that the HTTPD program changes the current directory to the directory where whatever it is loading is stored. In the case of a CGI program, the current directory is set to the directory where the CGI program resides. This is normally not a problem, except when it comes to relative URL's.

A relative URL is a URL which relies upon the current directory being the same as the directory where the current HTML file is located. So, for example, if I had the URL:

    http://my.machine/~rasmus/file.html
the actual HTML file might be:
    ~rasmus/public_html/file.html
If within the file.html file I had the tag:
    <img src="pic.gif">
when loaded normally this file gif file is expected to be in ~rasmus/public_html/pic.gif. However, when loaded through a CGI wrapper with a URL like:
    http://my.machine/cgi-bin/php.cgi/~rasmus/file.html
then HTTPD sets the current directory to /cgi-bin (or wherever the ScriptAlias might point) and subsequently when the page is loaded the pic.gif file is expected to be in: /cgi-bin/pic.gif which is usually not the desired effect.

The quick way around this problem is to use absolute URL's. In the above example if the image tag had been:

    <img src="/~rasmus/pic.gif">
then there would have been no problem. Using absolute URL's is not always desirable as it makes pages less portable. An obvious question you may have at this point is, "Why doesn't PHP just change the current directory to the right place?". The answer is that PHP actually does change the current directory to the location of the HTML file it is displaying. Any file paths used inside PHP Script tags, can be relative. The problem is that tags outside of PHP's control such as <img > and <a href > will not be passed through PHP. When they are parsed, PHP is no longer active and the current working directory has been set back to the directory specified by the HTTP Daemon.

The Solution is a compromise. PHP provides a variable called PATH_DIR. It contains the directory portion of the current HTML file at all times. If this PATH_DIR variable is used in the <img > and <a href > tags then the effect of a relative URL can be achieved, although to the server it will look like an absolute URL when parsed. From our above example, the only change we would need to make would be to change the img tag to:

    <img src="<?echo $PATH_DIR>/pic.gif">
By using the above, you can move the file containing this tag around, and the tag will always refer to a pic.gif file in the same directory as the source HTML file.

Another way to handle this is to use the traditional <BASE HREF=...> in the HTML file.


How PHP handles GET and POST method data

PHP will detect both GET and POST method data coming from HTML forms. One important point to understand is that POST method data is always treated first if both are present. If a PHP variable is defined by the POST method data, or if the variable is defined by the HTTP daemon in the Unix environment, then GET method data cannot overwrite it. This is to prevent somebody from adding ?REMOTE_HOST=some.bogus.host to their URL's and thus tricking the PHP logging mechanism into recording this alternate data. POST method data is however allowed to overwrite these variables.

Any component of the GET data (the data following a '?' in the URL) which is of the form, word=something will define the variable $word to contain the value something. Even if the data is not of this form, it can be accessed with the $argv built-in array. For example, in a URL like:

    /cgi-bin/php.cgi/file.html?abc+def+EMAIL_ADDR=rasmus@io.org&var=value
The relevant components of the PHP symbol table will be:
    $argc       = 4
    $argv[0]    = abc
    $argv[1]    = def
    $argv[2]    = EMAIL_ADDR=rasmus@io.org&var=value
    $EMAIL_ADDR = rasmus@io.org
    $var        = value
Notice how the EMAIL_ADDR part of the data shows up both as $argv[2] where it is unparsed, and the $EMAIL_ADDR variable is create to contain rasmus@io.org.

The $EMAIL_ADDR variable was used as an example in the above because it is a useful variable if you are using the logging features of PHP. By adding:

    ?EMAIL_ADDR=
to any links on a page where the user's email address is known, you may propogate it to the next page. The PHP logging system will automatically look for this variable and record its value as the user's e-mail address in the logs. For any users of PHP1, the above serves the same function as adding ?<!--$email--> to the URL used to do in PHP1. It looks a little bit more complex now, but it is also completely general allowing you to build your own complex pages.

In the above example you can also see how multiple variables can be defined right in the GET method data by separating each with the "&" character. This "&" separated list of variables must be the last (or only) component of the GET method data for it to be valid.


GD (a graphics library for GIF creation) Support in PHP

PHP supports the GD library version 1.2 written by Thomas Boutell. There is no GD code in PHP itself. If you wish to use the GD support in PHP/FI, you must obtain the GD library from http://www.boutell.com/gd/gd1.2.tar.Z, install it, and then re-install PHP.

Not all of the GD features are supported by PHP. For a list of supported functions see the Alphabetical List of Functions. All the GD functions start with the word, Image.

More information on the GD package is available at: http://www.boutell.com/gd/.

GD 1.2 is copyright 1994, 1995 Quest Protein Database Center, Cold Springs Harbor Labs.


PHP/FI and Virtual Hosts

PHP works fine on virtual host setups supported by some http daemons. The one problem that may occur on such a setup is related to an inconsistency in the way httpd sets the SCRIPT_NAME environment variable. Normally it is set to the path of the current CGI program in relation to the top-level ROOT_DIR on the httpd server. However, when a virtual domain is used, some httpd's do not correctly set the SCRIPT_NAME variable as the relative path from the virtual domain's top level directory as it should. If the ?config screen gives you an invalid URL error message, you know that this problem exists on your setup. You will need to edit the php.h file and set the VIRTUAL_PATH #define to the path to your php.cgi binary relative to your top-level directory.


File Upload Support

PHP/FI will automatically detect a file upload from a browser which supports the form-based file upload features as proposed by E. Nebel and L. Masinter from Xerox and described in RFC 1867.

A file upload screen can be built by creating a special form which looks something like this:

<FORM ENCTYPE="multipart/form-data" ACTION="_URL_" METHOD=POST>
<INPUT TYPE="hidden" name="MAX_FILE_SIZE" value="1000">
Send this file: <INPUT NAME="userfile" TYPE="file">
<INPUT TYPE="submit" VALUE="Send File">
</FORM>
The _URL_ should point to a php html file. The MAX_FILE_SIZE hidden field must precede the file input field and its value is the maximum filesize accepted. The value is in bytes. In this destination file, the following variables will be defined upon a successful upload:

$userfile
The temporary filename in which the uploaded file was stored on the server machine.

$userfile_name
The original name of the file on the sender's system.

$userfile_size
The size of the uploaded file in bytes.

$userfile_type
The mime type of the file if the browser provided this information. An example would be "image/gif".

The $userfile basename of the above variables will match the NAME field in the upload form.

Files will by default be stored in the server's default temporary directory. This can be changed by setting the environment variable TMPDIR in the environment in which PHP/FI runs. Setting it using a PutEnv() call from within a PHP/FI script will not work though. Alternatively, you may hardcode in a temporary directory by editing php.h and defining the UPLOAD_TMPDIR variable.

The PHP/FI script which receives the uploaded file should implement whatever logic is necessary for determining what should be done with the uploaded file. You can for example use the $file_size variable to throw away any files that are either too small or too big. You could use the $file_type variable to throw away any files that didn't match a certain type criteria. Whatever the logic, you should either delete the file from the temporary directory or move it elsewhere.

Please note that the CERN httpd seems to strip off everything starting at the first whitespace in the content-type mime header it gets from the client. As long as this is the case, CERN httpd will not support the file upload feature.


mSQL Support

mSQL stands for mini-SQL and is a small and simple SQL database engine written by David Hughes. It is available from ftp://ftp.bond.edu.au/pub/Minerva/msql

PHP/FI supports has a rich set of mSQL support functions:

msql()
msql_Connect()
msql_FieldFlags()
msql_FieldLen()
msql_FieldName()
msql_FieldType()
msql_FreeResult()
msql_NumFields()
msql_NumRows()
msql_NumRows()
msql_Result()
In addition to these functions, PHP/FI can also be compiled to automatically escape any forward single quote ( ' ) characters found in GET or POST data. If the MAGIC_QUOTES variable is defined in the php.h file then these quotes will be automatically escaped making it easier to pass form data directly to msql queries.


PHP/FI Script Language

The PHP script language is similar in syntax to the C language in many ways. It supports variables, arrays, function calls, different variable types and most things you might need to write complex cgi programs.

Syntax

Each PHP instruction starts with <? and ends with a >. Or, instructions may be grouped inside a single <? > pair and separated by ; characters.

Variables are supported and are indicated by preceding the variable name with a $. So, for example, to set a variable to 5 and then display this variable, the following is valid:

	<?$a = 5>
	<?echo $a>
This is the same as writing:

	<? $a = 5; echo $a >
Or even:

	<?
	$a = 5;
	echo $a;
	>
Extra white space characters such as spaces, tabs and new-lines are ignored. This fact should be used to format the PHP script blocks to make them easier to read. Case is relevant in variable names, but not in function calls. In the functional overview later on in this documentation, case is only used to make the function names easier to read. In the actual script language you can use any case you wish.

Comments are supported. A comment is written just like comments in the C language. /* starts a comment and */ ends a comment. Comments can be placed anywhere within the <? ... > block.


Variables

Three types of variables are supported. Long integer, Double precision floating point and character strings. They are automatically detected. For example:

	<?$a = 5>
causes $a to become a variable of type INTEGER.

	<?$a = 5.0>
causes $a to become a variable of type DOUBLE.

	<?$a = "5">
causes $a to become a variable of type STRING.

The type of the variable is not generally important. Every variable regardless of its type is converted to all three types internally and the various functions will try to use the correct type. There are only a few functions affected by the type of the variable.

All three variable types may also be treated as arrays by appending [value] to their names. Unlike C, these are actually associative arrays similar to those used in Perl. The following would be valid:

	<?
	  $a[0] = 5;
	  $a["hello"] = 6;
	  echo $a[0];
	  echo $a["hello"];
	>
Note that if a variable is used both as an array and as a normal variable, the normal variable is synonymous with the index "0" entry in the array. ie.
    $a = 1;
Is the same as writing:
    $a[0] = 1;
Also inherent to the language is the fact that the type of the variable determines how certain basic operations will be carried out. For example:
    $a = $b + $c;
can do a couple of different things. If $b is a number, the numerical value of $c is added to $b and the sum is stored in $a. In this case the type of $c is irrelevant. The operation is guided by the type of the first variable. If $b is a string, then the string value of $c is appended to $b and the resultant string is placed in $a. This also leads to some caveats. You should read the section on overloaded operators to get a better understanding of how to deal with them.


Variable Variables

Sometimes it is convenient to be able to have variable variable names. That is, a variable name which can be set and used dynamically. A normal variable is set with a statement such as:
    $a = "hello";
A variable variable takes the value of a variable and treats that as the name of a variable. In the above example, hello, can be used as the name of a variable by using two dollar signs. ie.
    $$a = "world";
At this point two variables have been defined and stored in the PHP/FI symbol tree:
    Variable Name        Variable Content
         a                   hello
         hello               world
Therefore, this statement:
    echo "$a $$a";
produces the exact same output as:
    echo "$a $hello";
ie. they both produce: hello world


Language Constructs

As far as language constructs are concerned, the PHP language is quite simple. The following commands are used to guide the control flow through a file:

The syntax of conditions are similar to that of the C language. == tests for equality. != means not equal. Also supported are: >, <, >=, <=. Conditional AND is &&, conditional OR is ||. Examples:

	<?
	  if($a==5 && $b!=0 );
		  $c = 100 + $a / $b;
	  endif;
	>
Note that another difference between the PHP language and the C language is that no curly braces are used and that you need to put a separator after if and while statements.

It is important to note that the flow of the language is not dependant on the organization of the script blocks within the code. You can start an if expression in one block and have the end of the expression in another. For example:

	<?if($a==5 && $b!=0)>
		  <b>Normal html text</b>
	<?endif>

Mathematical Expressions

Also introduced in the above example was a mathematical expression. PHP supports full mathematical operations anywhere an expression is expected. Order of operations are taken into account. The following are the valid operators:

	<? $a = 2 + 1 > Addition
	<? $a = 2 - 1 > Subtraction
	<? $a = 2 * 1 > Multiplication
	<? $a = 2 / 1 > Division
	<? $a = 2 % 1 > Modulus
Both bracketing and order of operations is supported, so the following is valid:

	<?$a = (2+1)*3+6/3>
The C-like incremental operators += and -= are supported. ie.
    <? $a += $b>
This is equivalent to:
    <? $a = $a + $b>
The C-like bit-wise operators &= and |= are supported. ie.
    <? $a &= 4>
This is equivalent to:
    <? $a = $a & 4>

While Loops

You can loop within a PHP script by using the while(); endwhile; construct.

	<?
	  $a=0;
	  while($a<100);
		  $a++;
		  echo $list[$a];	
	  endwhile;
	>
The above example shows the use of a while loop to display the contents of an array. WARNING although the PHP language supports incremental operators such as ++ and -- to increment and decrement a variable, they are not treated exactly like they would be in the C language. The variable is incremented right away. There is no concept of incrementing the variable before or after the operation as there is in C.


Switch Construct

PHP supports a switch construct very similar to the C equivalent.

	<?
	  $a=0;
	  switch($a);
		case 1;
		  echo "a is 1";
		  break;
		case "hello";
		  echo "a is hello";
		  break;
		default;
		  echo "a is unknown";
		  break;
	  endswitch;
	>
The above is an example of a switch construct. It is similar to a series of if/elseif/else constructs but easier to read. The only difference between the PHP switch construct and that of the C language is that semi-colons are used to terminate every line. There are no braces nor colons.

All of these constructs can of course be nested and used inside each other just like C. The various files in the examples directory of the PHP distribution should provide a good starting point for learning the language.


Secure Variables - Defeating GET method hacks

A previous section talked about GET and POST method data and variables. If you think about it, you may be able to envision a security issue. For example, if on a web page I have obtained some data from a database and I pass this data along in a variable called "data" in a POST method form. In the ensuing page I can access this variable and do something with it. However, if someone accessed this second page directly and put a "?data=something" right in the URL thereby doing a GET method variable set, they have effectively circumvented the original secure POST method form.

PHP provides a SecureVar() function which is used to mark variables names as being secure variables. These secure variables can only be set directly in a PHP script, or they can come from a POST method form. They cannot be set using the GET method variable definition mechanism. From our above scenario, if we placed the line:

    <?SecureVar("data")>
Near the beginning of our second page, then the GET method trick would not work. The "data" variable would appear to be empty unless it came directly from the POST method form on the first page.

The SecureVar() actually takes a regular expression as its argument, so you can mark patterns of variable names that should be treated in this secure manner. For example,

    <?SecureVar(".*data.*")>
Would mark any variable with the word "data" anywhere in their name as being secure.


Overloaded Operators and dealing with variable types

An overloaded operator is an operator like '+' for example which can do different things based on the types of the expressions it is asked to operate on.

The problem is that PHP understands 3 variable types. Integer, Double and String. When a variable is first assigned, PHP automatically figures out the variable type.

ie.

    $a = 1;     Type would be integer
    $b = 1.5;   Type would be double
    $c = "1";   Type would be string
Now, what happens when you do something like:
    $d = $a + $c;
The parser looks at the first part of the arithmetic expression and uses that to type the result and thus also the nature of the arithmetic that is to be done. In the above case since $a is an integer, $d will be an integer and an integer addition is done giving the result:
    $d = 2      Type is integer
Therefore:
    $d = $c + $a
Results in:
    $d = "11"   Type is string
The above makes sense to me, and once you understand it, it is probably workable. However, when more complex expressions are used it can get extremely confusing.

The solution is a simple type casting mechanism. In reality all variables are automatically converted to all 3 types, and an internal flag just marks what type the variable actually is. So, when I say:

    $a = 1;
Internally in the symbol table I store 3 versions.
    Integer:  1    <-- flag
    Double :  1.0
    String :  "1"
The SetType() function can move this flag indicating the type of the variable.
    SetType($a,"double");
This would force $a to be treated as a double from then on.

The GetType() function returns the type. GetType($a) would return "double" in this case.

Functions also exist to return the 3 various types without moving the type flag.

    IntVal($a)     returns  1
    DoubleVal($a)  returns  1.0
    StrVal($a)     returns  "1"
This doesn't change the overloaded operator nature of the PHP variables, but it does give you some tools to better deal with them. PHP is not not a full-fledged Perl look-alike. It has to be small and fast. Perl deals with the overloaded operator pitfall by forcing something like the '+' operator to only work on numbers. If you want to add strings, you must use the '.' operator. Once you start having separate operators for each type you start making the language much more complex. ie. you can't use '==' for stings, you now would use 'eq'. I don't see the point, especially for something like PHP where most of the scripts will be rather simple and in most cases written by non-programmers who want a language with a basic logical syntax that doesn't have too high a learning curve.


Supressing Errors from function calls

It may be desirable in certain circumstances to ignore fatal errors which may be reported by specific PHP functions. For example, you may want to ignore errors from a dbmopen() call and simply check the return value of the call without having error messages appear on the web screen. This can be done by putting the "@" character in front of the function call. ie.
    $err_code = @dbmopen($filename,"w");

The actual error message that would have been printed can be checked by looking at the PHP internal variable, $phperrmsg.


Functions

PHP has a number of built in functions. Functions are called in the same manner as they are called in the C language. Some take one or more arguments, and some return values which can then be assigned to a variable or used as an argument to another function. For example:

	<?$t=time()>
This assigns the return value of the time() function to the t variable.

Alphabetical List of Functions

BinDec(binary_string)
BinDec returns the decimal equivalent of the binary number represented by the binary_string argument. The largest number that can be converted is 31 bits long or 4294967295 in decimal. See also the DecBin() function.

ChDir(dir)
ChDir changes the current working directory to the directory specified in the argument.

ChGrp(file,group)
ChGrp changes the group id of the specified file.

ChMod(file,perms)
ChMod changes the file permissions of the specified file. The perms parameter must be specified in octal.

ChOwn(file,owner)
ChOwn changes the specified file to be owned by the specified owner. Note that this will only work if the PHP/FI binary is running as root (which is not generally a good idea).

closeDir()
closeDir closes a directory opened using the openDir function.

Count(array)
Count returns the number of items in an array variable. If the variable is not an array, the return value will be 1. If the variable is not defined, the return value will be 0.

Crypt(string,[salt])
Crypt will encrypt a string using the standard Unix DES encryption method. Arguments are a string to be encrypted and an optional two-character salt string to base the encryption on. See the Unix man page for your crypt function for more information. If you do not have a crypt function on your Unix system, you can use Michael Glad's public domain UFC-Crypt package which was developed in Denmark and hence not restricted by US export laws as long as you ftp it from an non-US site.

Date(format,time)
The Date function is used to display times and dates in various ways. The function takes a format string and a time as arguments. If the time argument is left off, the current time and date will be used. The time argument is specified as an integer in number of seconds since the Unix Epoch on Jan.1 1970. The format string is used to indicate which date/time components should be displayed and how they should be formatted. The following characters are recognized within the format string. Any unrecognized character is printed verbosely:

dbList(filename)
dbList outputs information about the db support compiled into PHP.

dbmClose(filename)
dbmClose simply closes the specified dbm file. It will also unlock any lock files, so it is important to close any dbm files that have been opened.

dbmDelete(filename,key)
dbmdelete will delete the key/content pair specified by the given key argument.

dbmFetch(filename,key)
dbmFetch will return the content string associated with the given key.

dbmFirstKey(filename)
dbmFirstKey returns the first key in the dbm file. Note that no particular order is guaranteed since the order depends on hash table values calculated within the dbm implementation. You may use the Sort function to sort arrays of data from the dbm file if necessary.

dbmInsert(filename,key,content)
dbmInsert inserts a new key/content data pair into a dbm file. If the key already exists, the insert will fail.

dbmNextKey(filename,key)
dbmNextKey returns the next key after the specified key. By calling dbmfirstkey() followed by successive calls to dbmnextkey() it is possible to visit every key/content pair in the dbm file.

dbmOpen(filename)
dbmOpen() opens a dbm file. The argument is the full-path filename of the dbm file to be opened. If ndbm support is used, ndbm will actually create filename.dir and filename.pag files. gdbm only uses one file, as does the internal flat ascii file support, and Berkeley's libdb create a filename.db file. Note that PHP does its own file locking in addition to any file locking that may be done by the dbm library itself. PHP does not delete the .lck files it creates. It uses these files simply as fixed inodes on which to do the file locking. For more information on dbm files, see your Unix man pages, or obtain GNU's gdbm from ftp://prep.ai.mit.edu/pub/gnu.

dbmReplace(filename,key,content)
dbmReplace is similar to the dbminsert() function, the only difference being that if the key already exists, the old content string will be replaced with the new.

DecBin(number)
DecBin returns a string containing a binary representation of the given number argument. The largest number that can be converted is 31 bits long or 4294967295 in decimal. See also the BinDec() function.

DecHex(number)
DecHex converts a decimal number to a hexadecimal string. See also the HexDec() function.

DecOct(number)
DecOct converts a decimal number to an octal number. See also OctDec().

doubleval(variable)
strval returns the double (floating point) value of the variable. See also the strval() and intval() functions.

Echo [format_string] expression
echo is not a function. ie. you do not put brackets around the arguments to it. It is used to display results of PHP functions or PHP variables. The special escape characters, \n, \r and \t can be used to output newlines, carriage returns and tabs respectively. The format_string is optional and if not present, no output formatting will be done. The format string is similar to the format string of the C printf function. See the man page for printf for more details. One large restriction when using the format string is that only a single argument can be formatted.

Exec(command_string,[array])
Exec executes the given unix command, however it does not output anything. It simply returns the last line from the result of the command. If the array argument is present, then the specified array will be filled with every line of output from the unix command starting at array[0].

Exit
The Exit command is used to terminate parsing right away as soon as this tag is parsed.

fclose(fd)
fclose() closes a file opened by fopen(). The argument is a file descriptor as returned by the fopen() call.

fgets(fd,bytes)
fgets() reads a line from a file opened by fopen(). Arguments are a file descriptor as returned by fopen() and the max number of bytes to read. Reading ends when max number of bytes have been read, or on an end of line. This is similar to the C fgets() call. See also fputs().

fileAtime(filename)
fileAtime returns the time of last data access.

fileCtime(filename)
fileCtime returns the time of last status change.

fileGroup(filename)
fileGroup returns the group id of the owner of the file.

fileInode(filename)
fileInode returns the file's inode.

fileMtime(filename)
fileMtime returns the time of last data modification.

fileOwner(filename)
fileOwner returns the uid of the owner of the file.

filePerms(filename)
filePerms returns the permission bits of the file. This is the st_mode field of the Unix C stat structure.

fileSize(filename)
fileSize returns the size of the file in bytes.

fd = fopen(filename,mode)
fopen() opens a file and returns a file descriptor. If the file could not be opened the function returns -1. It is similar to the C fopen() call. The filename argument is the relative or absolute path to the file to be opened, and the mode argument is one of, "r", "r+", "w", "w+", "a", "a+". See the Unix man page on the fopen() call for more information.

fputs(fd,string)
fputs() writes a line to a file opened by fopen(). Arguments are a file descriptor as returned by fopen() and the string to write. Note that the string argument may contain the special escape characters, \n, \r and \t to output newlines, carriage returns and tabs respectively. See also fgets().

fseek(fd,pos)
fseek() positions a file pointer identified by the fd argument which is the return value of the fopen() call. The file pointer is positioned at the beginning of the file plus the offset specified by the pos argument. See also ftell() and rewind().

pos = ftell(fd)
ftell() returns the position of a file pointer identified by the fd argument which is the return value of the fopen() call. The position can later be used as an argument to fseek(). See also ftell() and rewind().

getAccDir()
getAccDir returns the directory where PHP access configuration files are kept. The access configuration filenames come from the numerical user id of the user whose access configurations they represent.

getHostByName(domain_name)
getHostByName converts the given domain name into an IP address in nnn.nnn.nnn.nnn format.

getHostByAddr(ip_address)
getHostByAddr converts the given IP address in nnn.nnn.nnn.nnn format into a fully qualified domain name.

getLastAccess()
getLastAccess returns the date and time in unix time format of the last time the current page was access. This value can be passed to the Date() function for formatting. This function is only available if PHP was compiled with Access Logging enabled.

getLastbrowser()
getLastBrowser returns the identification string of browser the last user to access the current page used. This function is only available if PHP was compiled with Access Logging enabled.

getLastEmail()
getLastEmail returns the E-Mail address of the last user to access the current page. This function is only available if PHP was compiled with Access Logging enabled.

getLastHost()
getLastHost returns the hostname of the last user to access the current page. This function is only available if PHP was compiled with Access Logging enabled.

getLastMod()
getLastMod returns the date and time in unix time format of the last time the current page was modified. This value can be passed to the Date() function for formatting. This function is only available if PHP was compiled with Access Logging enabled.

getLastref()
getLastRef returns the URL of the referring document of the last user to access the current page. This function is only available if PHP was compiled with Access Logging enabled.

getLogDir()
getLogDir returns the top-level directory under which PHP log files can be found. The actual log files are in directories under this directory. Each subdirectory is the numerical user id of the user to whom the log files belong. Then within each directory a series of dbm log files are found, each with the numerical inode of the file they represent as the primary component of the filename.

getMyInode()
getMyInode returns the numerical inode of the current HTML file.

getMyPid()
getMyPid() returns the current process id of the php parsing process.

getMyUid()
getMyUid returns the numerical user id of the owner of the current HTML file.

getRandMax()
getRandMax returns the maximum random number the Rand function will return. If the value returned does not seem to be accurate, have a look in the php.h source file in the PHP distribution for more information.

getToday()
getToday returns the total number of hits the current page has had since 12 midnight local time. This function is only available if PHP was compiled with Access Logging enabled.

getTotal()
getTotal returns the total number of hits the current page has had since access logging was started on the page. This function is only available if PHP was compiled with Access Logging enabled.

GetType(variable)
GetType returns the type of the variable. The return value is a string and it is one of, "integer", "double" or "string". See also the SetType() function

gmDate(format,time)
gmDate is identical to the Date function except for the fact that it uses Greenwich Mean Time instead of the current local time.

Header "header_string"
The Header command is used at the top of an HTML file to send raw HTTP header strings. See the HTTP Specification for more information on raw http headers.

HexDec(hex_string)
HexDec converts a hexadecimal string to a decimal number. See also the DecHex() function.

ImageArc(im, cx, cy, w, h, s, e, col)
ImageArc draws a partial ellipse centered at cx,cy (top left is 0,0) in the image represented by im. w and h specifies the ellipse's width and height respectively while the start and end points are specified in degrees indicated by the s and e arguments. This function is only available if GD support has been enabled in PHP.

ImageChar(im, size, x, y, c, col)
ImageChar draws the character c in the image identified by im at coordinates x,y (top left is 0,0) in colour col. The size argument can be 1, 2, 3, 4 or 5 indicating the size of the font to be used. 1 is the smallest and 5 is the largest. This function is only available if GD support has been enabled in PHP.

ImageCharUp(im, size, x, y, c, col)
ImageCharUp draws the character c vertically in the image identified by im at coordinates x,y (top left is 0,0) in colour col. The size argument can be 1, 2, 3, 4 or 5 indicating the size of the font to be used. 1 is the smallest and 5 is the largest. This function is only available if GD support has been enabled in PHP.

col = ImageColorAllocate(im, red, green, blue)
ImageColorAllocate returns a colour identifier representing the colour composed of the given RGB components. The im argument is the return from the ImageCreate function. ImageColorAllocate must be called to create each colour that is to be used in the image represented by im. This function is only available if GD support has been enabled in PHP.

ImageColorTransparent(im, col)
ImageColorTransparent sets the transparent colour in the im image to col. im is the image identifier returned by ImageCreate and col is the colour identifier returned by ImageColorAllocate. This function is only available if PHP support has been enabled in PHP.

im = ImageCreate(x_size, y_size)
ImageCreate returns an image identifier representing a blank image of size x_size by y_size. This function is only available if GD support has been enabled in PHP.

ImageDestroy(im)
ImageDestroy frees any memory associated with image im. im is the image identifier returned by the ImageCreate function. This function is only available if GD support has been enabled in PHP.

ImageFill(im, x, y, col)
ImageFill performs a flood fill starting at coordinate x,y (top left is 0,0) with colour col in image im. This function is only available if GD support has been enabled in PHP.

ImageFilledPolygon(im, points, num_points, col)
ImageFilledPolygon creates a filled polygon in image im. points is a PHP array containing the polygon's vertices. ie. points[0] = x0, points[1] = y0, points[2] = x1, points[3] = y1, etc. num_points is the total number of vertices. This function is only available if GD support has been enabled in PHP.

ImageFilledRectangle(im, x1, x2, y1, y2, col)
ImageFilledRectangle creates a filled rectangle of colour col in image im starting at upper left coordinate x1,y1 and ending at bottom right coordinate x2,y2. 0,0 is the top left corner of the image. This function is only available if GD support has been enabled in PHP.

ImageFillToBorder(im, x, y, border, col)
ImageFillToBorder performs a flood fill whose border colour is defined by border. The starting point for the fill is x,y (top left is 0,0) and the region is filled with colour col. This function is only available if GD support has been enabled in PHP.

ImageGif(im, filename)
ImageGif creates the GIF file in filename from the image im. The im argument is the return from the ImageCreate function. This function is only available if GD support has been enabled in PHP.

ImageInterlace(im, interlace)
ImageInterlace turns the interlace bit on or off. If interlace is 1 the im image will be interlaced, and if interlace is 0 the interlace bit is turned off. This functions is only available if GD support has been enabled in PHP.

ImageLine(im, x1, y1, x2, y2, col)
ImageLine draws a line from x1,y1 to x2,y2 (top left is 0,0) in image im of colour col. This function is only available if GD support has been enabled in PHP.

ImagePolygon(im, points, num_points, col)
ImagePolygon creates a polygon in image im. points is a PHP array containing the polygon's vertices. ie. points[0] = x0, points[1] = y0, points[2] = x1, points[3] = y1, etc. num_points is the total number of vertices. This function is only available if GD support has been enabled in PHP.

ImageRectangle(im, x1, x2, y1, y2, col)
ImageRectangle creates a rectangle of colour col in image im starting at upper left coordinate x1,y1 and ending at bottom right coordinate x2,y2. 0,0 is the top left corner of the image. This function is only available if GD support has been enabled in PHP.

ImageSetPixel(im, x, y, col)
ImageSetPixel draws a pixel at x,y (top left is 0,0) in image im of colour col. This function is only available if GD support has been enabled in PHP.

ImageString(im, size, x, y, s, col)
ImageString draws the string s in the image identified by im at coordinates x,y (top left is 0,0) in colour col. The size argument can be 1, 2, 3, 4 or 5 indicating the size of the font to be used. 1 is the smallest and 5 is the largest. This function is only available if GD support has been enabled in PHP.

ImageStringUp(im, size, x, y, s, col)
ImageStringUp draws the string s vertically in the image identified by im at coordinates x,y (top left is 0,0) in colour col. The size argument can be 1, 2, 3, 4 or 5 indicating the size of the font to be used. 1 is the smallest and 5 is the largest. This function is only available if GD support has been enabled in PHP.

Include filename
The Include command can be used to insert other files into the current html file. This is extremely handy for headers and footers which may need to be included in hundreds of HTML files. By using an include command you then only need to modify the header or footer file in one place when it needs to be changed. Since full PHP parsing is done on the included file, you can also use the include command to include common PHP scripts you may have written. Sort of like having a primitive shared library of scripts you can call from your HTML file.

intval(variable)
strval returns the long integer value of the variable. See also the strval() and doubleval() functions.

IsSet(variable)
The IsSet function returns 1 if the given variable is defined, and 0 if it isn't.

Max(array)
Max returns the maximum value of a PHP array. ie. it will search through the entire array looking for the max element. If it is an array of strings, the returned string is the string which would be alphabetically last in the array if it were sorted.

Min(array)
Min returns the minimum value of a PHP array. ie. it will search through the entire array looking for the min element. If it is an array of strings, the returned string is the string which would be alphabetically first in the array if it were sorted.

MkDir(dir,mode)
MkDir creates a directory. The mode parameter must be given in octal notation.

MkTime(hour,min,sec,mon,day,year)
MkTime returns a time in Unix timestamp (long integer) format which corresponds to the date and time specified by the arguments. Arguments may be left out in which case the given component is set to the current value according to the current local time and date. These left out arguments may only be left out from right to left. ie. MkTime(hour,min,sec) is valid, but MkTime(mon,day,year) is not valid.

$result = msql(database,query)
msql sends an mSQL query. Arguments are the database name and the query string. ie. <?msql("MyDatabase"quot;select * from table")>. The return value from this function is a result identifier to be used to access the results from the other msql_ functions. A result identifier is a positive integer. The function returns 0 when no result identifier is created. This is the case with any queries that do not return anything, such as create, update, drop, insert and delete. The function will return -1 if an error occurs. A string describing the error will be placed in $phperrmsg, and unless the function was called as @msql() then this error string will also be printed out. This function is only available if mSQL support has been enabled in PHP.

msql_connect(hostname)
msql_Connect specifies the host name or IP on which the mSQL database engine resides. This is equivalent to the msqlConnect() function in the mSQL C API. The one difference between this function and the C API equivalent is that if the function isn't called, a connection to the local host is made by default on the first call to the msql() function. And, there is no need for an msql_close function since only one connection may be active at any one time. If a second call to msql_connect() is made in a file, then the connection to the first host is automatically closed. To explicitly connect to the msql daemon on the local host, use: <?msql_connect("localhost")>
This function is only available if mSQL support has been enabled in PHP.

msql_FieldFlags(result,i)
msql_FieldFlags returns the field flags of the speficied field. Currently this is either, "not null", "primary key", a combination of the two or "" (an empty string). This function is only available if mSQL support has been enabled in PHP.

msql_FieldLen(result,i)
msql_FieldLen returns the length of the specified field. This function is only available if mSQL support has been enabled in PHP.

msql_FieldName(result,i)
msql_FieldName returns the name of the specified field. Arguments to the function is the result identifier and the field index. ie. msql_FieldName(result,2); will return the name of the second field in the result associated with the result identifier.This function is only available if mSQL support has been enabled in PHP.

msql_FieldType(result,i)
msql_FieldType is similar to the msql_FieldName() function. The arguments are identical, but the field type is returned. This will be one of "int", "char" or "real". This function is only available if mSQL support has been enabled in PHP.

msql_FreeResult(result)
msql_FreeResult only needs to be called if you are worried about using too much memory while your script is running. All result memory will automatically be freed when the script is finished. But, if you are sure you are not going to need the result data anymore in a script, you may call msql_freeresult with the result identifier as an argument and the associated result memory will be freed. This function is only available if mSQL support has been enabled in PHP.

msql_NumFields(result)
msql_NumFields returns the number of fields in a result. The argument is the result identifier returned by the msql() function. This function is only available if mSQL support has been enabled in PHP.

msql_NumRows(result)
msql_NumRows simply returns the number of rows in a result. The argument is the result identifier returned by the msql() function. This function is only available if mSQL support has been enabled in PHP.

msql_RegCase(string)
msql_RegCase takes a string argument and converts it to the regular expression needed to send to mSQL in order to get a case insensitive match. This turns a string like "abc" into "[Aa][Bb][Cc]".

msql_Result(result,i,field)
msql_Result displays a field from a returned record. Arguments are the result identifier returned by the msql() function, an integer which is the index of the record to be viewed and a field name. The field argument supports the "table.field" syntax for handling results from a join. This function is perhaps best illustrated with a complete example:

	<?
	  msql_connect("localhost");
	  $name = "bob";	
	  $result = msql($database,"select * from table where firstname='$name'");
	  $num = msql_numrows($result);
	  echo "$num records found!<p>";
	  $i=0;
	  while($i<$num);
		  echo msql_result($result,$i,"fullname");
		  echo "<br>";
		  echo msql_result($result,$i,"address");
		  echo "<br>";
		  $i++;
	  endwhile;
	>
The above script connects to the mSQL engine on the local machine, sets the name variable to bob and sends a query which asks for all the fields from a table where the firstname field is set to bob. It then displays the number of records it found after which it loops through each of the found records and displays the fullname and address fields for each record. As you can see, it would be trivial to add HTML markup tags around the printed fields to format the results in a table or in whatever manner is desired.

This function is only available if mSQL support has been enabled in PHP.

OctDec(octal_number)
OctDec converts an octal number to a decimal number. See also DecOct().

openDir(directory)
openDir opens the speficied directory and places an internal pointer to the beginning of the directory. Directory entries are read using the readDir function, and an opened directory should be closed with the closeDir function.

PutEnv(string)
PutEnv puts the given string in the environment. Not extremely useful since the local environment variables are wiped out when PHP is done with a page, but in some cases it is useful if other things called from within a PHP script checks environment variables. For example, if you want to run multiple mSQL daemon processes, you will need to use PutEnv to switch back and forth between the different sockets.

Rand()
Rand returns a random number between 0 and RANDMAX. RANDMAX can be determined with the getRandMax function. Normally a specific range is chosen by simply using the modulus operator on the result.

readDir()
readDir reads the next entry from the currently open directory structure. Once an entry is read, the pointer is advanced to the next entry in the directory and the next call to this function will return the next entry in the directory. Use the openDir function to open a directory before calling this function.

reg_Match(expr,arg)
reg_Match returns non-zero if the regular expression is matched in the argument string. For example, the condition, <?if (reg_match("^This.*", "This is an example string")> would be true since the "^This.*" expression says to match the word This at the beginning of the string and then match any characters afterwards.

reg_replace(expr,replace,arg)
reg_Replace scans the entire argument string and replaces any portions of the string matched by the given expression with the replacement string. For example, in the string, "This is an example string" we could very easily replace every space with a dash with the command: reg_replace(" ","-","This is an example string").

reg_Search(expr,arg)
reg_Search will scan the entire argument string for any matches to the given regular expression. If a match is found, it will return the portion of the string starting at where the match occurred. If no match is found a zero-length string is returned.

Rename(old,new)
Rename filename old to new. Similar to the Unix C rename() function.

rewind(fd)
rewind() resets a file pointer identified by the fd argument which is the return value of the fopen() call. The file pointer is positioned at the beginning of the file. See also ftell() and fseek().

rewindDir()
rewindDir moves the current directory pointer back to the beginning of the directory. Use the openDir function to open a directory before calling this function.

SetLogging(arg)
SetLogging() either enables or disables the logging of access statistics for a page. If arg is non-zero, logging will be enabled, if zero, disabled.

SetShowInfo(arg)
SetShowInfo() either enables or disables the information footer at the bottom of all pages loaded through PHP. If arg is non-zero, the footers will be enabled, if zero, disabled.

SetType(variable,type)
SetType sets the type of a variable. The type argument is one of, "integer", "double" or "string". See also the GetType() function.

Sleep(secs)
Sleep will delay for secs seconds. Similar to the Unix C sleep() function. See also the USleep() function.

Sort(array)
Sort is used to sort a PHP array in ascending order. It understands the three variable types and will sort alphabetically if the array contains strings, and numerically if the array contains numbers. In the case of an array which contains a mixture of types, the first type in the array will specify the sort method.

Srand(integer)
Srand seeds the random number generator. This function takes any integer as an argument. One choice is to use the date function to give you the current number of seconds past the minute. ie.
	<?srand(date("s"))>
strchr(string,arg)
strchr and strstr are actually identical functions. They can be used interchangebly and both are included for completeness sake. They will return the portion of the string argument starting at the point where the given sub-string is found. For example, in the string, "This is an example string" above, the call: <echo strstr($string,"an ")> would return the string: "an example string".

strlen(string)
strlen returns the length of the string.

strrchr(string,arg)
strrchr is identical to strchr except for the fact that it will start at the end of the string and search backwards for a match.

strstr(string,arg)
strstr and strchr are actually identical functions. They can be used interchangebly and both are included for completeness sake. They will return the portion of the string argument starting at the point where the given sub-string is found. For example, in the string, "This is an example string" above, the call: <echo strstr($string,"an ")> would return the string: "an example string".

strtok(string,arg)
strtok is used to tokenize a string. That is, if you have a string like "This is an example string" you could tokenize this string into its individual words by using the space character as the token. You would use the following script code:
	<?
	  $string = "This is an example string";
	  $tok = strtok($string," ");
	  while($tok);
		  echo "Word=$tok<br>";
		  $tok = strtok(" ");
	  endwhile;
	>
Note that only the first call to strtok uses the string argument. Every subsequent call to strtok only needs the token to use, as it keeps track of where it is in the current string. To start over, or to tokenize a new string you simply call strtok with the string argument again to initialize it.

strtolower(string)
strtolower converts the string argument to all lower case characters.

strtoupper(string)
strtoupper converts the string argument to all upper case characters.

strval(variable)
strval returns the string value of the variable. See also the intval() and doubleval() functions.

substr(string, start, length)
substr returns a part of the given string. The start position is given by the start argument. The first position in a string is position 0. And the length argument specifies the number of characters to return from the start position.

System(command_string)
System is just like the C system() command in that it excutes the given unix command and outputs the result. See also the Exec function.

TempNam(path, prefix)
TempNam returns a unique filename located in the directory indicated by path with filename prefix given by prefix. It is identical to the Unix C tempnam() function.

Time()
Time simply returns the current local time in seconds since Unix epoch. It is equivalent to calling Date("U").

Unlink(filename)
Unlink deletes the given filename. Similar to the Unix C unlink() function.

USleep(microsecs)
Sleep will delay for the given number of microseconds. Similar to the Unix C usleep() function. See also the Sleep() function.


Adding your own functions to PHP/FI

It may well be that the set of functions provided by PHP/FI does not include a particular function that you may need. By carefully following the steps described below, it is possible for you to add your own functions to PHP/FI.

Before you start hacking away at the internals of PHP/FI you need to grab a copy of the latest version of Bison. Bison is GNU's implementation of YACC (Yet Another Compiler Compiler). The YACC that came with your operating system may or may not be good enough, but just to make sure, go grab Bison. You can find it at ftp://prep.ai.mit.edu/pub/gnu.

You should also have a look at the Makefile and turn on debugging. Simply uncomment the DEBUG line in the Makefile. The output file of debug information is specified by DEBUG_FILE in php.h. It is by default set to /tmp/php.err. You can change this to suit your needs.

A final thing you might want to keep in mind is that php runs as the same user id as httpd on your system, unless of course you are running it with the setuid bit set, and this httpd user does not generally have write access to the various directories. This means that if you do something that causes php to core dump, you will not get a core file. The easy way around this is to make the directory where you keep your test .html files writable to all. PHP changes its current directory to the directory of the .html file it is reading and will thus dump its core there if it can.

In the following steps we will use the Time() function to illustrate how to add a function.

Step 1 - Defining the grammar of your Function
The grammar of your function is defined in the parse.raw file. The first thing to add is a token. A token is an upper case keyword which is usually the same as your function name. All the tokens are defined near the top of the parse.raw file. The order doesn't matter. For the Time() example we add:

%token TIME Next, we add the actual function grammar. Look for a large block preceded by func:. This block contains a list of all the PHP/FI functions. You may add your function anywhere in this list. The Time() entry looks like this:

    | TIME '(' ')'
        {
            if(GetCurrentState(NULL) || inCase || inElseIf) UnixTime();
        }
The fact that there is nothing between the '(' ')' means that the function does not take any arguments. The actual name of the function to be called internally in PHP in this example is UnixTime(). The condition before the function call is essential. The GetCurrentState(NULL) call returns the current state from the state machine. If we are in an inactive state for some reason, the function should not be called. The inCase and inElseIf checks specify that the function is allowed to be called both in case and elseif blocks. This should be true of any function you add.

Step 2 - Adding your function to the lexical analyzer hash table
To do this, edit lex.c and find the hash table near the top of the file. Look for the line, static cmd_table_t cmd_table[22][30] = {, which defines the beginning of the hash table. The [22][30] defines the size of the 2 dimensinal array which holds the hash table. The 22 is one greater than the maximum function name length and the 30 refers to the maximum number of functions in any one hash list. If you exceed either of these limits, simply increase them right here.

This hash table would probably rate as the absolute simplest hash table in the entire world. The hash value is simply the length of the string of the function name to be hashed. So, for our Time() example, we need to add an entry for hash value 4. Therefore we add the following line to the hash list for 4:

      { "time",TIME },
This entry maps a string to the token you defined in parse.raw. The string, in quotes above, is the actual string that you will be using in the .html files to call your function. Keep in mind that PHP/FI function names are not case sensitive.

Step 3 - Write your actual Function
You can actually write your function in any language you like, as long as it is callable through the normal C function call convention and you have a way of creating either an object file or a library file compatible with the linker on your system. In general, we will assume that you are writing your function in C. All the functions that come with PHP/FI have been written in C. The Time() function, or UnixTime() as it is called internally in PHP can be found in date.c and looks like this:
void UnixTime(void) {
    char temp[32];

    sprintf(temp,"%ld",(long)time(NULL));
    Push(temp,LNUMBER);
}
Note that the function is void. This indicates that it doesn't return anything. This may seem confusing to you because obviously the function needs to somehow return the time. The time is returned, but not as the return value of the function. It is pushed onto what is called an expression stack. The expression stack is simply a stack of strings and an associated type. PHP/FI understands only 3 basic variable types: STRING, LNUMBER and DNUMBER. STRING is a character string, LNUMBER is a long integer and DNUMBER is a double, or floating point, value. In this Time() example, the value to be returned is the time expressed in Unix format (number of seconds since Jan.1 1970) and is thus an integer. The epxression stack only accepts strings, so we sprintf the long integer into a string and push it onto the stack indicating that it is actually a long integer with the line: Push(temp,LNUMBER);

Step 4 - Add your function prototype to php.h
In the bottom half of the php.h file you will find a complete list of prototypes of all the functions in php. They are grouped by the files in which they appear. Simply add your prototype to an appropriate place in this file. For our Time() example the following line is added:

void UnixTime(void);

Step 5 - Compile
You have to remember to re-make the parser whenever you make a change to the parse.raw file. Type: make parser to do this. Then do a normal compile by typing: make once that is done.

Step 6 - Send me your additions!
If you would like your functions added to the next release of PHP/FI, send them to me. The best way is probably to make a context-sensitive diff. To do that, you need a copy of a clean unmodified distribution. Simply do a, diff -c on the files you have changed comparing them to the original files. Please don't send me a diff of the changes in parse.c since that file is automatically generated. Send me the diff from parse.raw instead.

The Time() example illustrated the steps involved in adding a function. Chances are that the function you wish to add is quite a bit more complex than this example. You will probably want to be able to pass arguments to your function and have it manipulate these arguments in some manner. You may even want to have it callable in different ways. These concepts will be illustrated by the PHP/FI crypt() function.

The Crypt() Grammar in parse.raw:

%token CRYPT
        .
        .
        .
    | CRYPT '(' expr ',' expr ')'
        {
            if(GetCurrentState(NULL) || inCase || inElseIf) Crypt(1);
        }
    | CRYPT '(' expr ')'
        {
            if(GetCurrentState(NULL) || inCase || inElseIf) Crypt(0);
        }
Here it is shown how to define a grammar which lets you call a function with either 1 or 2 arguments. You could write different functions to handle the two cases, or simply send a mode parameter as is done here to indicate the mode in which the function is called.

The other aspect that is shown is how to actually represent function arguments. In most cases you will want to use the expr identifier. This identifier means that the argument is an expression. An expression can be a literal value, a function call or a combination of many expressions. See parse.raw for the complete yacc grammar definition for expressions for more details.

The Hash Table entry in lex.c:

      { "crypt",CRYPT },
The actual Crypt() function in crypt.c:

/*
 * If mode is non-zero, a salt is expected.
 * If mode is zero, a pseudo-random salt will be selected.
 */
void Crypt(int mode) {
#if HAVE_CRYPT
	Stack *s;
	char salt[8];
	char *enc;
	
	salt[0] = '\0';
	if(mode) {
		s = Pop();
		if(!s) {
			Error("Stack error in crypt");
			return;
		}
		if(s->strval) strncpy(salt,s->strval,2);
	} 
	s = Pop();
	if(!s) {
		Error("Stack error in crypt");
		return;
	}
	if(!salt[0]) {
		salt[0] = 'A' + (time(NULL) % 26);
		salt[1] = 'a' + (time(NULL) % 26);
		salt[2] = '\0';
	}
	enc = (char *)crypt(s->strval,salt);
#if DEBUG
	Debug("Crypt returned [%s]\n",enc);
#endif
	Push(enc,STRING);	

#else
	Error("No crypt support compiled into this version");
#endif
}
The most important aspect of this function is the s = Pop() call. The arguments to the function must be popped off the expression stack one by one. When you write a function which takes multiple arguments, remember that a stack is a LAST-IN, FIRST-OUT data structure. This means that you will be popping your arguments off the stack in reverse order. The last argument is popped off first. In the above example we check to see if we are in the 2 argument mode. If we are, we pop the argument off the stack and save it. Then we pop the next argument off the stack. Pop() returns a pointer to a Stack structure (s). The Stack structure looks like this (from php.h):

/* Expression Stack */
typedef struct Stack {
    short type;  
    unsigned char *strval;
    long intval;
    double douval;
    VarTree *var;
    struct Stack *next;
} Stack;
The type will generally be one of STRING, LNUMBER or DNUMBER. The strval, intval, and douval components are the string, integer and double representations of the value respectively. If the expression is actually a defined variable, the var component contains a pointer to the variable structure which defines this variable.

In our Crypt() function we are only interested in the string value of the argument, so we use s->strval. Many PHP/FI functions can do different things depending on the type of the variable simply by checking s->type and using s->strval, s->intval and/or s->douval appropriately.

After calling the real crypt() function and getting the encrypted string, our Crypt() function calls Push(enc,STRING); to push the return value onto the expression stack. It should be noted that the expression stack is cleared after each PHP/FI line, so if you push expressions onto this stack that is never popped by anything, it won't matter.

The Debug() call in the Crypt() example shows how to add debugging output to your function. Debug() is a varags (variable argument list) function just like printf.