Getting started with PHP

Introduction

Why use PHP instead of Python, .Net, etc.?

It is open-source (hence, free), portable, easy to learn, easy to deploy,
feature-rich. .Net requires a Windows server license, so .Net applications
are more costly to deploy

To avoid having scripts parsed and compiled every time they're called,
use tools like apc, eAccelerator, XCache, Zen Guard, etc.

There also key-value caches like Memcached

Scripts are usually very small and the cause for slow sites is usually
to be found in the database server; Only very big sites might see performance
issues with PHP

PHP was built from the start to write web sites

Setup

NT

Install the Apache binary and check that the Apache runs OK, then stop
it

Download the PHP Windows package, and unzip it somewhere, eg. C:\PHP

Copy C:\PHP\php.ini-dist as C:\WINNT\php.ini

Copy C:\PHP\php4ts.lib into C:\WINNT\SYSTEM32

Edit Apache's conf/httpd.conf configuration file

Locate the "ScriptAlias /cgi-bin/" line, and add the following
right on the next line: ScriptAlias /php/ "C:/PHP/"

Find the line that says "AddType application/x-tar .tgz",
and add the following on the next line: AddType application/x-httpd-php
.phtml .phpAddType application/x-httpd-php-source .phps

Locate the comment that mentions "# Action lets you define
media types that will execute a script whenever", and add the following
line: Action application/x-httpd-php/php/php.exe

Locate the line that says "DirectoryIndex index.html index.html.var",
and add "index.php" so that Apache knows that it should also
look for those files when a user tries to access a directory without
mentioning a file specifically

Go to Apache's htdocs/ directory, and create a test index.php file with
the following instruction: <? phpinfo(); ?>

Start the Apache console, and aim your browser to http://localhost
. The index.php

Linux

Through RPM

rpm -Uvh apache-1.3.11-4.i386.rpm

rpm -Uvh apache-config-0.0.4-3.i386.rpm

rpm -Uvh apache-devel-1.3.11-4.i386.rpm

rpm -Uvh apache-manual-1.3.11-4.i386.rpm

rpm -Uvh php-3.0.14-3.i386.rpm

Edit /etc/httpd/conf/httpd.conf, and check that the following settings
are available:AddModule mod_php.cAddModule mod_php3.c

Edit httpd.conf, and check that the LoadModule, AddModule, AddType,
and IfModule sections are correctly set

Note: If Apache fails starting because it cannot find the libmysql
libraries, edit /etc/ld.so.conf, make sure it includes /usr/local/mysql/lib/mysql,
and run ldconfig -v | grep mysql.

Testing PHP: Your first program!

Open your favorite text editor, and create /var/www/htdocs/test.php3:

<HTML>
<? print "PHP is kool!"; ?>
</HTML>

Launch your favorite web browser, and type the following URL:

http://localhost/test.php3

Using sessions

Since HTTP is a stateless protocol, you need a way to identify a user as
he moves from page to page, especially is those pages are off-limit to non-authorized
users. The easiest way is to use sessions identified by a unique, dynamically-generated
ID number, where each session contains user-specific information like whether
he successfully logged on, etc. To make this session ID available between pages,
or even between sessions, the easiest way is to store this ID in the browser
as a cookie, and then use this session ID to look up additional data in a database
on the server, eg. was the user successfully authenticated, etc.

Here's own sessions work:

The PHP script includes session_start() as its first instruction, to
avoid the familiar "headers already" (only true when cookies,
not GET/POST)

session_start() checks for a session ID (via cookie, GET, or POST) with
a specific name (default is PHPSESSID)

If the session ID matches a filename in the directory where it saves
sessions (see php.ini > session.save_path), all data from that session
file are loaded in $_SESSION[]

If the browser didn't send the expected session ID or it doesn't match
any session file on the server, session_start() generates a new session
ID and sends it to the browser

Tikiwiki

PHITE

PHP-based, light alternative to Zope. Available at http://www.dreammask.com/PHITE.php?sitesig=PT
. To set things up, just untar/unzip the package into the htdocs/ directory
of the web server, edit PHITE.php in the root directory (eg. $siteroot and $def_sitesig),
and customize the tree of sub-directories. You might want to rename PHITE.php
as index.php

Things to know:

PHITE can handle more than one set of directories under index.php. It
knows which directories belong to which site by checking the $def_site prefix
that you set in index.php . For instance, if $def_site is set to PT, only
sub-directories that start with the "PT_" prefix will be checked
for content by PHITE. Other directories are ignored

When accessing the root of your site, stuff located in {sig}_main is
loaded

Prefixes for other components (eg. BODY, FOOTER, etc.) are set in the
$elements[] statements in index.php. (CHECK) If no suffix
is added (eg. body.inc), this file will be used by default if no directory-specific
file is located closer to the file that calls this component. If a suffix
is used (eg. footer_main.inc), this file is only used in the ad hoc directory
(eg. PT_main/). Elements are used in the .tpl template files

Blocks are groups of elements, eg. left-blocks to build a navigator,
right-blocks to build a "latest news" section. By default, files
starting with LB_ or RB_ will appear on the left and right sides of the
screen, respectively

PHITE provides some ready-to-use variables that you can use in templates
: {SITENAME}, {PAGETITLE}, {META}

Programming Tips

Best practices

Use a good IDE to write your code, and keep its formating clean (eg. phpCodeBeautifier.)

Using a three-tier architecture (separating code, presentation, and data)
is even more important in web applications than dedicated applications, since
HTTP is a state-less protocol and a web interface offers much less control to
the programmer. Most PHP books present samples with code and HTML mixed together,
which is a recipe for disaster. Here are some good articles:

Avoiding browser time-out during lenghty operations

If possible, change the server's PHP.INI to raise the time-out option.
Take a look at mod_php's timeout too

To solve browser timeout issues, (if you don't need to output any HTTP
headers as part of this long-running code, as body output obviously prevents
the header() function from working) slowly feed it some dummy data, eg.
echo "<!-- x -->\n"; OR use ob_start() and ob_flush() to
send some stuff to the browser repeatedly

Avoiding FireFox's "The page you are trying to view contains POSTDATA"

This is a warning that FireFox displays whenever you refresh the action page
that is called after submitting a form, just to remind the developper that doing
this might replay the action. Here's how it works:

<?php

if ( $_SERVER['REQUEST_METHOD']=='POST' ) {

print "block post";

} else {

?>

<form action=<?php
echo $PHP_SELF; ?> method=POST>

<input
type=submit name=mybutton value="Click">

</form>

<?php

}

?>

There are two ways that I know to avoid this warning:

handle the form in another page, ie. "action" should point
to another page, and, once done, that another page should redirect the user
to the original page, using either the META tag or PHP's header() function

while still using the same page for the form and handling, perform POST
handling, and reload the page via header():

Calling an executable from PHP

Since PHP is a server-side scripting tool, the EXE that you wish to call
from a script should not display any GUI, ie. all the interactions between this
EXE and a PHP script should be done through command line switches (input) and
return values (output).

Are there security settings in PHP that limits running EXEs in specific directories?
Yes, if you are running in safe mode, you can use the safe_mode_exec_dir setting
in php.ini

Reading from an INI file

An INI file can have one or more [sections], each section can containe one
or more "key=value" lines. PHP's parse_ini_file() builds a hashed
array where each key in turn points to an array that contains the list of key/value's
that live in this section:

//This script called through eg. http://www.acme.com/check.php?ini_file=myfile.ini

$ini_array = parse_ini_file($_GET['ini_file'], true);

foreach ($ini_array as $key => $item) {

//Let's display the [section] name

$content .= "[$key]\n";

//Let's display all the key=value tuples in
this section

foreach ($item as $key2 => $item2) {

$content .= "size
= " . filesize($item2) . "\n";

}

$content .= "\n";

}

print $content;

?>

Writing into a text file

$fp = fopen ("myfile.txt", "w");

fputs ($fp,"Here be dragons\r\n");

fclose($fp);

Generating a unique identifier for a file

You can use the md5() function to hash a file and generate a unique ID for
this file:

Extracting META tags

Here's how to open an HTML file, and read its description and author META
tags:

$dir = opendir(".");

while ($item = readdir($dir)) {

if(strstr($item,".htm")) {

$tags = get_meta_tags($item);

if (!$tags['description'])
{

$title
= "(Description non renseignée)";

} else {

$title
= $tags['description'];

}

if (!$tags['author'])
{

$author
= "(Auteur non renseigné)";

} else {

$author
= "(" . $tags['author'] .")";

}

print "<a href=\"$item\">$title</a>
$author<p>";

}

}

PEAR

PHP Extension and Application Repository". PEAR
is a framework and distribution system similar to .Net for reusable PHP components.
An important component from PEAR is PEAR DB, a unified API for accessing SQL
databases. Read PEAR
Primer.

Uploading files

This is achieved by using two scripts, one to present a form which include
the usual "..." button to trigger the "Open File" dialog
box, and a second script which decides what to do with the file the user wishes
to upload, and is a good place to set security parameters.

Using forms

//index.php

<form method=post action="compute.php">

Item: <input type=text name="data"><br>

<input type=submit>

//compute.php

<? echo $data ?>

Note: I couldn't combine those two scripts into one, use the "echo"
command, and run it successfully under OmniHTTPd. I had to use either two scripts
(if using a basic <form>), or one (using <form action="index.php">,
and the "print $HTTP_POST_VARS['username'];" command. The following
instructions didn't display anything:

<?php

print $_REQUEST['username'];

print $username;

echo $username;

echo $_GET["username"];

echo $_POST["username"];

?>

Here's one way to use the same page to display a form, let the user click
on a command button, and execute some code:

//If $status is not set, we're running this page
for the first time

if (!isset($status)) {

//$id is a parameter
provided as input to this page through either the GET or POST method

$result=mysql_query("select
* from test where id=$id order by id", $db);

Check that the module was installed in php.ini's extension_dir: ll /usr/lib/php/modules/

Note: The "PHP Warning: Module 'PDO' already loaded in Unknown
on line 0" warning when running command-line PHP scripts is apparently
due to the fact that "extension=pdo.so" in not needed in php.ini,
since it's already found in /etc/php.d/pdo.ini, which is installed by the
php-pdo package

If using Lighttpd, use "service lighttpd restart" so it forces
the FastCGI PHP to reload

To test that PDO and SQLite are installed correctly, put this in a PHP script:

php_pdo_sqlite has SQLite linked in. Depending on your Linux distribution
and/or your PHP version that might be an obsolete version of SQLite. php_pdo_sqlite_external
uses any SQLite3 library you offer it, for example the compiled amalgamation.

As an alternative to the DB-agnostic PDO interface, you can compile PHP5
to support SQLite, either directly or by calling an external library. You can
either use the OO version...

Q&A

How to get rid of "PHP Notice: Undefined index"?

This is a warning that PHP sends if you try to use a POST/GET variable without
first checking if it's valid. Add this:

if (!isset($_POST['status']))

$_POST['status']=null;

How to find if a record exists in a database?

$sql = "SELECT count(*) FROM phones WHERE phones_tel='123-1234'";

$numrows = $dbh->query($sql)->fetchColumn();

if($numrows) {

Alternatively:

$sql = "SELECT 1 AS number FROM phones WHERE phones_tel='123-1234'";

$row = $dbh->query($sql)->fetch();

if(!$row['number']) {

Why so many different functions doing the same thing?

"One cause of PHP’s overlapping set of functions is its early existence
as a mere wrapper over Perl’s and later C’s own libraries. Users familiar with
those languages’ function libraries would find their PHP equivalents going by
the same names with the same calling conventions, overlaid with PHP’s memory
management and type handling. Extensions exacerbated this—with different DBMSs
exposing different APIs, PHP introduced different sets of functions for each
DBMS it supported. When two extensions boasted functions for two similar things,
PHP provided both."

Include(), include_once(), require(), require_once()?

... even though both PHP.INI and Apache's DocumentRoot are correct? Only
works if pointing directly to the file (eg. ../banner.php) or leaving path infos
entirely (include "banner.php" instead of "/banner.php")

How to add the GD library in Windows?

It's probably already part of the binary package, in the extensions/ sub-directory
(php_gd2.dll). If it's there, just edit php.ini to change the relevant line: