PHP4 - An Overview

by Rick Moen

Revised: Friday, 2001-11-16

Master version will be at http://linuxmafia.com/faq/Web/php4.html, and I'll try to improve
it there.

Competitive Comparison:

History and General Comments:

Danish-born (Greenland-born, technically) Rasmus Lerdorf wrote what he originally called "Personal Home Page" around fall 1994 as a set of Perl CGI scripts to find out who was reading his resume from his Web server. (These details go into the archive of Embarrassing Facts about the Early History of Open-Source Projects, along with the name "Kool Desktop Environment".)

Lerdorf then extended his CGI architecture, replacing the Perl with a C wrapper, and using the result for forms handling, eventually for access to the MySQL, Oracle, Sybase, and other databases -- and revising the official explanation of the language's name to the recursive "PHP: Hypertext Preprocessor". Through PHP v. 3, it was licensed 100% GPL. Starting PHP v. 4, it incorporates the high-performance Zend scripting engine (http://www.zend.com/), which is under an odd but OSD-compliant licence.

In general terms, it's an interpreted scripting language with C-like syntax, designed to generate dynamic Web pages -- but also cable of generating on-the-fly image files, PDFs, Flash animations. Also now usable to write standalone GUI applications (PHP-GTK: http://gtk.php.net/).

This talk, however, will concentrate on PHP v. 4's use as a server-side scripting language, invoked by as an Apache module, e.g.,

  LoadModule php4_module  libexec/libphp4.so
  AddModule mod_php4.c
  AddType application/x-httpd-php .php

in Apache's httpd.conf.

It can also work as a module under fhttpd, AOLServer, Netscape Commerce Server/iPlanet and other NSAPI-compliant httpds, Microsoft Internet Information Server / MS-Personal Web Server and other ISAPI-compliant httpds (OmniHTTPd, O'Reilly Website Pro), , phttpd, Pi3Web, Caudium, Roxen, thttpd, and Zeus -- or called via CGI by any other httpd (OmniHTTPd, Xitami, others), at the cost of fork-and-exec'ing a new process.

PHP access to databases:

Adabas D
Berkeley (Sleepycat Software) dbm, db2, db3
dBase
Borland InterBase
Direct MS-SQL
Empress
FilePro (read-only)
FrontBase SQL
Hyperwave
FSF's gdbm
IBM DB2
Informix
Ingres
LDAP
mSQL
MySQL
ndbm
OpenLink ODBC
Oracle (OCI7 and OCI8)
Ovrimos
PostgreSQL
Solid
Sybase
Unified ODBC Interface
Velocis

Functionality in the Web context:

PHP can do anything CGI can do: collect forms data, generate dynamic pages, send & receive cookies, talk IMAP, SNMP, POP3, NNTP, HTTP, HTTPS, various raw socket handling, various other protocols.

PHP4 setup:

PHP must have been compiled with support for Apache and vice-versa. This is already true of the precompiled versions in Red Hat. PHP should also be compiled with support for PostggreSQL, if you're using that. Again, this is provided in Red Hat's binary version. (Support can be either as modules or compiled-in statically.)

When PHP4 is compiled straight, what results is the standalone interpreter -- which is what gets called in CGI mode. Advantage of the latter is you can run diverse PHP-enabled pages under different UIDs.

php.ini:

PHP4 reads /etc/php.ini (can be in other directory, if so compiled) at startup. Some values are also established by Apache configuration.

   php_value name value
   php_flag name on|off
   php_admin_value name value
   php_admin_flag name on|off

Uses semicolon or [] pair as comment character.

Example phi.ini from SourceForge 3.0:

[PHP]

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Language Options
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

engine                         = on
short_open_tag                 = on
asp_tags                       = off
precision                      = 14
y2k_compliance                 = off
output_buffering               = off
output_handler                 =
implicit_flush                 = off
allow_call_time_pass_reference = on

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Safe Mode and Security
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

safe_mode                      = off
safe_mode_exec_dir             =
safe_mode_allowed_env_vars     = PHP_
safe_mode_protected_env_vars   = LD_LIBRARY_PATH
disable_functions              =
expose_php                     = on

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Syntax Highlighting
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

highlight.string               = #DD0000
highlight.comment              = #FF8000
highlight.keyword              = #007700
highlight.bg                   = #FFFFFF
highlight.default              = #0000BB
highlight.html                 = #000000

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Resource Limits
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

max_execution_time             = 1200
memory_limit                   = 128M

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Error Handling and Logging
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
error_reporting                = E_ALL & ~E_NOTICE
display_errors                 = off
display_startup_errors         = off
log_errors                     = on
track_errors                   = off
;error_log                      = /sfos/php/errors_log
warn_plus_overloading          = off

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Data Handling
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

variables_order                = "EGPCS"
register_globals               = on
register_argc_argv             = on
post_max_size                  = 100M
gpc_order                      = "GPC"
magic_quotes_gpc               = on
magic_quotes_runtime           = off
magic_quotes_sybase            = off
auto_prepend_file              = 
auto_append_file               = 
default_mimetype               = "text/html"
;default_charset               = "iso-8859-1"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Paths and Directories
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

include_path                   =
.:/sourceforge/sfee/www/include:/sourceforge/sf
ee
doc_root                       = 
user_dir                       = 
extension_dir                  = ./
enable_dl                      = on

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; File Uploads and fopen
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

file_uploads                   = on
;upload_tmp_dir                = 
upload_max_filesize            = 100M
allow_url_fopen                = off

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Module Settings
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

[syslog]
define_syslog_variables        = off

[mail function]
SMTP                           = localhost
sendmail_from                  = me@localhost.com
sendmail_path                  = "/usr/sbin/sendmail -t -i"

[debugger]
debugger.host                  = localhost
debugger.port                  = 7869
debugger.enabled               = false

[sql]
sql.safe_mode                  = off

[postgressql]
pgsql.allow_persistent         = on
pgsql.max_persistent           = -1
pgsql.max_links                = -1

[bcmath]
bcmath.scale                   = 0

[browscap]
;browscap                      = extra/browscap.ini

[sockets]
sockets.use_system_read        = on

PHP Syntax Basics:

PHP Delimiters, Comments:

The PHP interpreter ignores all input except what is delimited specifically as PHP content, using one of these formats:

  <? echo ("this is the simplest, an SGML processing instruction\n"); ?
  <?= expression ?>  This is a shortcut for "<? echo expression ?>

Works if "short_open_tag on" is in php.ini, or PHP was compiled with option --enable-short-tags.

  <?php echo("if you want to serve XHTML or XML documents, do this\n");
?>

  <script language="php">
      echo ("some editors (like FrontPage) don't
      like processing instructions");
  </script>

  <% echo ("Evil, awful, ASP-style syntax"); %>
  <%= $variable; # This is a shortcut for "<%echo .." %>

Within any PHP block, statements terminate with semicolons.

Comment lines/blocks can be in C, C++, or Unix-shell styles:

   <?php
        echo "This is a test"; // This is a one-line c++ style comment
        /* This is a multi line comment
        yet another line of comment */
        echo "This is yet another test";
        echo "One Final Test"; # This is shell-style style comment
   ?>

Data Types (Primitives)

Usually, types aren't declared; the interpreter infers them from context at runtime. But paranoics can set them explicitly using the settype() function or a cast construct:

   $foo = 10;   // $foo is an integer
   $bar = (float) $foo;   // $bar is a float

And the var_dump() function will tell you a primitive's type and value.


Syntax example: "Autobale". This is a draft replacement for my http://linuxmafia.com/bale/ Web page that my wife and I were working on, that would have back-ended the site into a MySQL database, and rendered it dynamically generated. The actual replacement will be PostgreSQL-based.


index.php:

<?php
$nummonths = 3;
$title="Upcoming GNU/Linux Events in the San Francisco Bay Area";

require("cottonbale.php");
require("dbstart.php");
require("header.php");

// make the dates for the months of the events

for ($i=0; $i< ($nummonths + 1); $i++)
{
$t = mktime(0,0,0,date("m")+$i,1, date("Y"));
$months[$i] = date("M", $t);
$years[$i] = date("Y", $t);
$dates[$i] = date("Y-m-d", $t);
}

// now put in the html for the tables

for ($i=0; $i<$nummonths; $i++)
{
require("monthhead.php");
$j = $i + 1;
$query = "select edate, starttime, endtime, ecity, ename,
description from events where edate >= '$dates[$i]'
and edate <= '$dates[$j]' order by edate, starttime";
$res = mysql_query($query);
$k = 0;
while(list($edate, $start, $end, $city, $name,
$descrip)=mysql_fetch_row($res))
{
$fg = rowcolor($k);
echo "<tr><td bgcolor=\"$fg\">";
echo $start == 0 ? "<em>" : "<strong>";
echo dateExpand($edate);
echo $start == 0 ? "</em></td><td bgcolor=\"$fg\">" :
"</strong></td><td bgcolor=\"$fg\">";

if ($start == 0)
{
echo "<center>-</center>";
}
else
{
echo "<strong>";
echo timeExpand($start, true);
echo "-";
echo timeExpand($end, true);
echo "</strong>";
}
echo "</strong></td><td bgcolor=\"$fg\">$city</td><td bgcolor=\"$fg\">$descrip</td></tr>\n";
$k++;
}
echo "</table>\n\n\n";
}

// put in the bridging html between the table and the groups

require("notes.php");
require("footer.php");

?>

dbstart.php:

<?
// change if you need different parameters obviously

$db = mysql_connect("127.0.0.1");
mysql_select_db('events');
?>

header.php:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML>

<HEAD>
<TITLE>The BALE -- Bay Area Linux Events</TITLE>
<LINK REV="made" HREF="mailto:rick@linuxmafia.com">
</HEAD>

<? /*<BODY BGCOLOR="#FFFFDD"> */ ?>
<BODY bgcolor="lightsteelblue" text="black" vlink="steelblue"
link="midnightblue">

<A NAME="TOP"><center>
<table width="80%">
<tr><td bgcolor="midnightblue" align="center"><font color="white">
<H1></A>
<? echo $title; ?></H1>
<H2><em>Your one-stop source for all the GNUs that's fit to print</em></H2>
</font></td></tr></table>
</center>

<P><em>Hurry, take me to the
<a href="http://www.pootpoot.com/poot/pootify?URL=
http%3A%2F%2Flinuxmafia.com%2Fbale%2F&options=none">pootified version</a>!</em>
No, wait! The <a href="http://www.80s.com/cgi-bin/valley.cgi?url=
http://linuxmafia.com/bale/">Valley-girl version</a>.

<p>
<em>Are you a Linux user group leader? Please see <a
href="http://linuxmafia.com/~rick/essays/newlug.html">this
essay</a>.</em>

<HR>

cottonbale.php:

<?

// change these functions to represent date/time differently

function dateExpand($adate)
{
$d = explode('-', $adate);
$t = mktime(0, 0, 0, $d[1], $d[2], $d[0]);
$date = date("D. M. j", $t);
return $date;
};
?>

<?
function timeExpand($atime, $addap=false) // time as in 1800
{
$rem = $atime % 100;
$hr = (int) ($atime / 100);
$pm = $hr > 12 ? 1 : 0;
$hr = $hr > 12 ? $hr - 12 : $hr;
$hour = (string)$hr;
if ($rem > 0)
{
$hour = $hour . ':' . (string)$rem;
}

if ($addap)
{
$p = $atime < 1200 ? "a" : "p";
$hour = $hour . $p;
}
return $hour;
};
?>

<?
function rowcolor($count)
{
$i = $count % 2;
$fg = ($i == 0 ? "lightsteelblue" : "CCCCCC" );
return $fg;
};
?>

footer.php:

<BR>

<H1>Tote That BALE!</H1>
Don't just sit there! If you have information on local Linux events,
or resources BALE should know about, use this-here handy
<a href="mailto:bale@linuxmafia.com">BALEing Wire</a> to give your
fellow Linuxers a shout. Thanks.

<p><hr>

<table cols=3>
<tr><td align="left">
<IMG SRC="images/cgg_small.png" ALIGN=center ALT="[Choice of a GNU Generation]">
</td><td align="center">
<IMG SRC="images/linux-box-400x100.jpeg" ALIGN=center ALT="[Powered by Linux]">
</td><td align="right">
<IMG SRC="images/pwblinux.png" ALIGN=right ALT="[Powered by Linux]">
</td></tr></table>

<hr><p>

<ADDRESS>
<A HREF="http://linuxmafia.com/~rick/">Rick Moen</A> copyright &copy; 2000<BR>
<A HREF="mailto:bofh@linuxmafia.com">bofh@linuxmafia.com</A>
</ADDRESS>

</BODY>
</HTML>

notes.php:

<ul>
<li><strong><a href="extra.html">Additional dates (tentative)</a></strong>
</ul>

<P><strong>Notes:</strong><BR>
1. SFpcUG Linux SIG appears to have died, and the parent group's Web server
has been defunct for some months running.<BR>
2. CCSF LUG has not yet announced regular meetings for 2000.<BR>
3. BAFUG-SF has moved to Whistle Communications, Foster City.<BR>
4. SJSU LUG died because nobody took over when its founder left for a
job position elsewhere.<BR>
5. Slug LUG changed its meeting date to the last Thursday of each month.<BR>
6. LUGOD has changed from first Mondays and third Tuesdays to first
Tuesdays and third Mondays.<BR>
7. LUGS changed its name to SacLUG.<BR>
8. BUUG has slightly altered its schedule.<BR>
9. BayPIGgies has moved to Aspect Development, in Mountain View.<BR>
10. Every effort is made to keep these listings accurate. If they aren't,
please <a href="mailto:bale@linuxmafia.com">
let BALE know</a>, so we can fix the problem.

<BR>
<BR>
<H2><a name="other"></a>To the <a href="other.html">Other Local Linux
Resources</a> Page<BR>
(Linux-oriented businesses in the Bay Area)</H2>

PEAR (PHP Extension and Application Repository):

Serves as the CPAN of PHP. See:
http://vulcanonet.com/soft/pear_tut/
http://php.weblogs.com/php_pear_tutorials
http://www.onlamp.com/pub/a/php/2001/07/19/pear.html

Performance tips:

1) Run PHP as Apache server module, not as a CGI. (Save a fork-and-exec.)
2) Compile any PHP modules into PHP itself. (Do not use the dl() function.)
3) Base your php.ini on php.ini-optimized . (Note that the contents of php.ini are ignored until you move php.ini-dist and php.ini-optimized away from the PHP config directory.)

Security:

In CGI mode (in which, ordinarily, the PHP interpreter lives in the cgi-bin directory), PHP won't run system files specified as command-line arguments. E.g., it won't execute http://my.host/cgi-bin/php?/bin/sh .

The security risk from CGIs can be limited in the following alternative ways: (1) via Apache's password or IP-based control, (2) via mod_ssl, (2) by compiling with --enable-force-cgi-redirect and using redirection of CGIs of filename *.php, as follows:

Action php-script /cgi-bin/php
AddHandler php-script .php

This limits parsing of CGIs to just the specified directory.

(4) By locating CGIs within a directory specified using the doc_root and/or user_dir directives to specify where they may live. This works like the prior example except that scripts located there may be executed but not displayed. (The user_dir directive adds access to user-specific subdirectories under the doc_root one.)

(5) By moving the PHP interpreter outside the Web document tree entirely, invoking it in each CGI with a specific shebang (#!/usr/local/bin/php), and chmod'ing the CGI executable,

In module mode, PHP runs as the Apache daemon's user (in Sourceforge, sf-httpd). Access control to any databases should be enforced via the database's access controls, Apache/mod_ssl, .htaccess, LDAP login, etc., rather than relying on Apache.

In module mode, PHP has whatever access to system files and devices its effective UID grants it. So, do careful input validation on user strings that will be parsed to specify files and paths.

Leaving debugging error-reporting enabled on a live site is dangerous, as it reveals too much internal information (files or lines to exploit for coding errors, hidden variables, unchecked syntax, and other weaknesses). Disable error-reporting ("error_reporting(0);" in scripts, "error_reporting 0" in php.ini, or "php_error_reporting off" in Apache httpd.conf, or preface any PHP directive with "@" to disable error reporting other than parse errors for that statement only) or create a custom, limited error-handler.

Running with "register_globals off" in php.ini blocks users setting variables that become automatically valid in PHP scripts, via cookies, arguments, or CGI GET/POST operations. (This will work only in directories for which Apache has option "AllowOveride All" set.)

One can hide PHP from public view by setting "expose_php = off" in php.ini.

Further Reading:

Introductory Tutorial
http://www.php.net/tut.php

PHP4 Manual
http://www.php.net/docs.php

Resources for PHP Coders
http://www.phpbuilder.com/

Misc. PHP links
http://www.php.net/links.php

The PHP-GTK Programming Tool
http://gtk.php-coder.net/