PHP4 - An Overview
by Rick MoenRevised: 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:
Java servlets:
Slow, proprietary. JVMs die a lot. Poor exception handling. Spawns threads like crazy. (Tomcat, JServ, IBM WebSphere.) The Java language is designed to make it easy for intermediate programmers to be minimally productive, at the cost of ultimate productivity. Must learn a large amount of syntax before you can write much code, especially given exception handling. (Exceptions are commonly caught and then re-thrown.) No multiple inheritance; cannot do callbacks, therefore must use threading in lieu of callbacks: Almost 100% of the time, people inquiring about how to increase Linux's process limits to > 512 per system and > 256 per user (in Linux 2.2.x kernels) turn out to be Java people. Threading can introduce subtle bugs, esp. deadlocks and race conditions. Like PHP, Java handles resource management automatically (freeing RAM and file handles), except that you must keep track of reference counts: Getting reference counts wrong compared to automatic resource deallocation is a frequent cause of blown JVMs. Java servlets were my #1 reason for sleep loss, when I was chief sysadmin at $FIRM.Embedded Perl:
Sizeable RAM requirement (but in fairness not too much greater than PHP4 as an Apache module). Terribly difficult to debug/maintain typical Perl code. You can't even reliably run using the local interpreter Perl scripts written for mod_perl. In other words, embedded Perl is Perl. mod_perl was my #1 reason for sleep loss, when I was chief sysadmin at $FIRM.Cold Fusion:
Proprietary, expensive.Microsoft ASP:
Proprietary, expensive, requires sizeable hardware investment for decent performance. Inflexible.PHP:
Open source, inexpensive. Language is very easy to learn, with C-like syntax, easy to debug: Any PHP code that can be run in Apache-module mode can reliably be run by the local interpreter for debugging. Fairly fast. Low hardware requirements. Stable. Like Java, PHP handles resource management (freeing RAM, file handles), which seems to work more reliably than Java's. PHP does automatically things for which any of the competition requires lines of code. E.g., every form item already has a variable name and value, where Perl requires code to parse the HTTP headers, and sucks the variable names and values into variables. PHP allows very easy and natural intermixing of code and HTML. Until recently, programmatically listing server-side directories in PHP using readdir() function was an awkward operation: Required setting the current directory before each file reference. This has been remedied in recent PHP4 versions. (But there had been an easy workaround: You invoked an external Python or similar procedure. Those integrate easily.)
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)
- Booleans
- $foo = True; // assign the value TRUE to $foo
- $foo = True; // assign the value TRUE to $foo
- Integers
- $a = 1234; # decimal number
- $a = -123; # a negative number
- $a = 0123; # octal number (equivalent to 83 decimal)
- $a = 0x1A; # hexadecimal number (equivalent to 26 decimal)
- // On IA-32, PHP int values
- $a = 1234; # decimal number
- Floating point numbers
- Strings
- Arrays
- Objects
- Resource
- NULL
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 © 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/