Getting started with PHP

Introduction

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

Setup

Use PHP to run a basic web server

This works on any platform that has PHP, and doesn't require installing a web server:

c:\php>php -S localhost:8001

http://localhost:8001

If you wish, you can create a sub-directory and put the PHP/HTML files therein:

c:\php>php -S localhost:8001 -t myrootdir/
 
http://localhost:8001/myrootdir/

NT

  1. Install the Apache binary and check that the Apache runs OK, then stop it
  2. Download the PHP Windows package, and unzip it somewhere, eg. C:\PHP
  3. Copy C:\PHP\php.ini-dist as C:\WINNT\php.ini
  4. Copy C:\PHP\php4ts.lib into C:\WINNT\SYSTEM32
  5. Edit Apache's conf/httpd.conf configuration file
    1. Locate the "ScriptAlias /cgi-bin/" line, and add the following right on the next line: ScriptAlias /php/ "C:/PHP/"
    2. Find the line that says "AddType application/x-tar .tgz", and add the following on the next line:  
      AddType application/x-httpd-php .phtml .php
      AddType application/x-httpd-php-source .phps
    3. 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
    4. 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
  6. Go to Apache's htdocs/ directory, and create a test index.php file with the following instruction: <? phpinfo(); ?>
  7. Start the Apache console, and aim your browser to http://localhost . The index.php

Linux

Through RPM

  1. rpm -Uvh apache-1.3.11-4.i386.rpm
  2. rpm -Uvh apache-config-0.0.4-3.i386.rpm
  3. rpm -Uvh apache-devel-1.3.11-4.i386.rpm
  4. rpm -Uvh apache-manual-1.3.11-4.i386.rpm
  5. rpm -Uvh php-3.0.14-3.i386.rpm
  6. Edit /etc/httpd/conf/httpd.conf, and check that the following settings are available:
    AddModule mod_php.c
    AddModule mod_php3.c

    LoadModule php_module modules/mod_php.so
    LoadModule php3_module modules/libphp3.so


    AddType application/x-httpd-php .php3 .php

    <IfModule mod_dir.c>
        ... index.php
    </IfModule>
     
  7. /etc/rc.d/init.d/httpd start

From source code

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:

  1. The PHP script includes session_start() as its first instruction, to avoid the familiar "headers already" (only true when cookies, not GET/POST)
  2. session_start() checks for a session ID (via cookie, GET, or POST) with a specific name (default is PHPSESSID)
  3. 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[]
  4. 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

Add this to every page that is off-limit to non-authorized users:

<? // Has a session already been created? If not, create new one
if($PHPSESSID)
    session_start($PHPSESSID);
else
    session_start();
?>

Here's how to manipulate data that are part of a session:

<? session_register("email"); ?>
<? $email="me@acme.com"; ?>
<? echo $email; ?>
<? session_unregister("email"); ?>
<? session_destroy(); ?>

Here's how to extract information form a session table:

$sql = "select user_id,status,date_created from session where id='" . $PHPSESSID . "'";
$result = @mysql_query($sql) or
    die('Query failed: ' . mysql_error());
 
$row = mysql_fetch_row($result);
echo "user_id = " . $row[0] . "<p>";
echo "status = " . $row[1] . "<p>";
echo "date_created = " . $row[2] . "<p>";

If most data are common to all users, a smarter way is to keep user-specific data in sessions, but keep common data in a cache (APC, MemCacheD, etc.):

session_start();
if(isset($_SESSION['myprivatevalue'])) {
        print $_SESSION['myprivatevalue'] . "<p>\n";
} else {
        $_SESSION['myprivatevalue'] = "verysecret";
}
 
//apc_add('scooby-doo', 'daphne');
print "Scooby-do=" . apc_fetch('scooby-doo');                          
//apc_delete('scooby-doo');

More information:

Using APC to cache data

APC is a single-host cache server that lets you store/retrieve data that are often used, instead of connecting to the database server every time. Use MemcacheD if you need multiple-host caching.

Put this wrapper in eg. apc.php:

function fetch($key) {
    return apc_fetch($key);
}
 
function store($key,$data,$ttl=null) {
    return apc_store($key,$data,$ttl);
}
 
function delete($key) {
    return apc_delete($key);
}

And call those functions like this (using PDO and SQLite):

include("apc.php");
 
$dbh = new PDO("sqlite:test.sqlite");
 
$sql = "CREATE TABLE IF NOT EXISTS mytable (name TEXT)";
$dbh->exec($sql);
 
$sql = "INSERT INTO mytable VALUES (?)";
$insert = $dbh->prepare($sql);
$insert->execute(array("dead"));
 
$sql = "INSERT INTO mytable VALUES (?)";
$insert = $dbh->prepare($sql);
$insert->execute(array("beef"));
 
$sql = "SELECT * FROM mytable";
$sth = $dbh->prepare($sql);
$sth->execute();
store("rows",$sth->fetchAll());
 
$rows = fetch("rows");
foreach($rows as $row) {
        print $row['name'] . "<p>";
}
 
$dbh = null;

Security

(from Professional LAMP, Wrox 2006):

Save this on your server under phpauthforms.php:

<?php
 
/*
CREATE DATABASE mydb;
 
USE mydb;
 
CREATE TABLE auth_users (username varchar(50) PRIMARY KEY, passwd_md5 varchar(32) NOT NULL, passwd_sha1 varchar(40) NOT NULL);
 
INSERT INTO auth_users VALUES ('testuser', MD5('testpass'), SHA1('testpass'));
*/
 
// Define database constants
define('AUTH_HOST', 'localhost');
define('AUTH_USER', 'root');
define('AUTH_PASS', 'test');
define('AUTH_DB', 'mydb');
define('AUTH_TABLE', 'auth_users');
 
function check_connect()
{
        $dbcnx = @mysql_connect(AUTH_HOST, AUTH_USER, AUTH_PASS) or
                die("Unable to connect to the database server at this time: " . @mysql_error . "<p>");
        
        @mysql_select_db(AUTH_DB) or
                die("<p>Unable to locate the " . AUTH_DB . " database at this time.</p>");
        
        @mysql_close($dbcnx);
 
}
 
function check_login($username, $password)
{
        $ret = false;
        if ($username && $password)
        {
                // Check if login matches database values
                $conn = mysql_connect(AUTH_HOST, AUTH_USER, AUTH_PASS);
                if (mysql_select_db(AUTH_DB, $conn))
                {
                        // Search for matches
                        $result =
                        mysql_query("SELECT COUNT(username) AS ucount FROM " . AUTH_TABLE . " WHERE username='" . addslashes($username) . "'
                                AND passwd_md5='" . md5($password) . "'
                                AND passwd_sha1='" . sha1($password) . "'",
                                $conn);
                        // Check if a match was found
                        if (($row = mysql_fetch_array($result)) && $row['ucount'])
                        {
                                $ret = true;
                                $_SESSION['username'] = $username;
                        }
                }
                // Close connection
                mysql_close($conn);
        }
        return $ret;
}
 
session_start();
 
// Check if using valid credentials
if (!(isset($_SESSION['username']) || check_login($_POST['username'], $_POST['passwd'])))
{
        //Self-contained script: form + handling
        ?>
        <form method="post" action="<?php echo $PHP_SELF; ?>">
                <label for="username">Username:</label>
                <input type="text" id="username" name="username" maxlength="50" /><br />
                <label for="passwd">Password:</label>
                <input type="password" id="passwd" name="passwd" /><br />
                <input type="submit" value="Log in" />
        </form>
        <?php
}
?>

Next, add this snippet first thing in each and every PHP page on your site:

<?php
require_once 'phpauthforms.php';
echo "Authentication Successful!";
?>

Application server

PhpLen

http://phplens.com

PhpAppl

http://phpappl.sourceforge.net/

LogiCreate

http://www.logicreate.com

Frameworks for web development

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:

Php.MVC

http://phpmvc.net/

Code Igniter

http://www.codeigniter.com

Savant

"The Savant Template System for PHP - The simple, elegant, and powerful alternative to Smarty."

Smarty

http://smarty.php.net/

Introducing Smarty: A PHP Template Engine by Joao Prado Maia

Separate form and function in PHP applications with Smarty by Martin Streicher

Worldpilot

Midgard Project

PHPGroupware

PHPAppl

PHPortal

eZPublish

Xoops

Typo3

Mambo

ArtiPHP

WebGUI

e107

OOP

Currently (PHP 4.x), PHP does not allow properties to be made private, ie. you can access properties directly instead of being forced to use methods. Multiple inheritance is not supported either. Read Object "Oriented Programming in PHP: The way to large PHP projects".

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

Easy output

As an alternative to print() and echo(), there's heredoc:

print <<<END_OF_TEXT
<html>
<head>
        <meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
        <meta http-equiv="Pragma" content="no-cache">
        <META HTTP-EQUIV="Refresh" CONTENT="60">
        <title>My title</title>
</head>
<body>
END_OF_TEXT;

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:

Using ActiveX objects

More infos on building an ActiveX DLL here. Once it's done, here's how to instantiate an object located in an ActiveX component, and call one of its methods:

<?php
$myobj = new COM("myocx.Class1") or die("Unable to instantiate myocx.Class1");
echo myobj->Hello();
?>

Executing a command onto a SQLite database

$sql = "CREATE TABLE IF NOT EXISTS mytable (name TEXT)";
$dbh->exec($sql);

Inserting/updating data safely in a SQLite database

$sql = "INSERT INTO mytable VALUES (?)";
$insert = $dbh->prepare($sql);
$insert->execute(array("test"));

Querying a SQLite database with PDO

There are two ways to query a database with PDO:

//Each row displayed twice! ASSOC/NUM
foreach($dbh->query($sql) as $row) {                  
    foreach ($row as $key => $val)               
        $row[$key] = (!$val)? "&nbsp;":$val;
    print $row['name'] . "<p>\n";
}

... or:                                                                            

$rows = $dbh->query($sql);         
while($row = $rows->fetch(PDO::FETCH_ASSOC) ) {
    foreach ($row as $key => $val)    
        $row[$key] = (!$val)? "&nbsp;":$val;
    print $row['name'] . "<p>\n";
}

If there's only a single row, or you're only interested in the first row:

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

Introduction to PHP PDO

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

Displaying variables

Here's how to display the input variables available to a page:

<?php
    echo "<pre>";
    print_r($vars);
    echo "</pre>";
?>

Reading a whole file in one go

More infos in File Handling with PHP by Gez Lemon

The long way

$filename = "myfile.exe";
$fp = fopen ($filename, "rb");
$buffer = fread($fp, filesize($filename));
fclose($fp);

The short way:

$filename = "myfile.exe";
$buffer = fread(fopen($filename, "rb"), filesize($filename));

More information in The right way to read files with PHP by Roger McCoy.

Reading a text file line by line

$file_handle = fopen("myfile", "r");
while (!feof($file_handle)) {
   $line = fgets($file_handle);
   echo $line;
}
fclose($file_handle);

Alternative:

$listFile = "list.txt";
if (!($fp = fopen($listFile, "r")))
    exit("Unable to open $listFile.");
 
while (!feof($fp)) {
    //! Stops at first space
    $buffer = fscanf($fp, "%s", $name);
    print "$name<br>\n";
}
 
fclose ($fp);

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:

print md5($file_content);

Extracting a bit using regex

//non-greedy eregi?
eregi('<META NAME="keywords" CONTENT="([^\"]*)">', $file, $regs);
echo "Keywords=$regs[1]";

Alternative:

$url = "http://www.cnn.com";
$response = file_get_contents($url);
preg_match("|<title>(.+?)</title>|smi",$response,$matches);
$response = "Title is = " . $matches[1];
$fp = fopen ("output.html", "w");
fputs ($fp,$response);
fclose($fp);

Replacing a bit using regex

eregi_replace("<body>(.+)</body>", "<BODY>??????</BODY>",$string);
echo "$string";

Alternative:

$url = "http://www.cnn.com";
$response = file_get_contents($url);
$output=preg_replace("|<title>(.+?)</title>|smiU", "TITLE=$1", $response);
$fp = fopen ("output.html", "w");
fputs ($fp,$output);
fclose($fp);

Listing files in a directory

Here's how to return a list of files in a directory in CSV format:

<?
//http://www.acme.com/download/list.php?dir=widgets
//... where widgets is the name of a sub-directory below download/
if($dir) {
        chdir($dir);
} else {
        print "Name of sub-directory missing. Format is list.php?dir=mysubdir";
        exit;
}
 
$dir = opendir(".");
 
while ($file = readdir($dir)) {
        $file = strtolower($file);
        //We're only looking for binaries
        If (ereg('\\.(exe$|jpg$|gif$|ocx$|bmp$)', $file))
                {
                print $file . "\t" .  date("d/m/Y H:i", filectime($file)) . "\t" . filesize($file) . "\n";
                }
}
?>

... and here's how to display this list in VB5:

Private Sub Form_DblClick()
    Dim sBuff As String
    Dim sMyDir(2) As String
    Dim iCounter As Integer
 
    //We'll browse two sub-directories
    sMyDir(0) = "widgets"
    sMyDir(1) = "apps"
 
    For iCounter = 0 To 1
        //Inet1 is the Microsoft Internet Transfert Control
        sMyBuff = Inet1.OpenURL("http://www.acme.com/download/list.php?dir=" & sMyDir(iCounter))
        MsgBox sMyBuff
    Next iCounter
End Sub

Browsing through sub-directories

Here's how to get a list of sub-directories through recursion, and check whether each contains an index file:

(From php.net)
function arborescence($root)
{
   
   $folders  = array();
   $path    = "$root";
   $i    = 0;
 
   // V?rification de l'ouverture du dossier
   if ($handle = opendir("./".$path))
   {
       // Lecture du dossier
       while ( false !== ($file = readdir($handle)) )
       {
           if (filetype($path."/".$file) == 'dir')
           {
               if ($file != '..' && $file != '.' && $file != '' && (substr($file,0,1) != '.'))
               {
                   $folders[$i] = "./".$path."/".$file;
                   $i++;
               }
           }
       }
       closedir($handle);
       
       // Recherche de tous les sous dossiers pour chaque dossiers
       for ( $u = 0; $u < $i; $u++ )
       {
           if ($handle = opendir($folders[$u]))
           {
               // Lecture du dossier
               while ( false !== ($file = readdir($handle)) )
               {
                   if (filetype("./".$folders[$u]."/".$file) == 'dir')
                   {
                       if ($file != '..' && $file != '.' && $file != '' && (substr($file,0,1) != '.'))
                       {
                           $folders[$i] = $folders[$u]."/".$file;
                           $i++;
                       }
                   }
               }
               closedir($handle);
           }
       }
   }
return $folders;
}
 
foreach(arborescence(".") as $repertoire) {
        //remove header ././bla returned by arborescence()
        $repertoire = substr($repertoire, 2);
        if (!file_exists("$repertoire/index.html") && !file_exists("$repertoire/index.htm") && !file_exists("$repertoire/default.htm") ){
           echo "Index file missing in directory $repertoire<br>";
        }
}

Serializing data

A quick way to make data persistent is to use the serialize() and unserialize() functions:

$data = array("2004-03-29-21-28","date","publish_date");
$serialized_data = serialize($data);
echo $serialized_data . "<p>\n";
$serialized_data='a:2:{i:0;s:12:"introduction";i:1;s:12:"Just testing";}';
$postData = unserialize($serialized_data);
foreach ($postData as $value) {
        echo $value . "<p>\n";
}

Listing files by extracting their TITLE

$dir = opendir(".");
print "<ul>\n";
while ($item = readdir($dir)) {
    //Restrict to HTML files only
    if (strstr($item,".htm") {
        $fp = fopen($item, "r");
        $contents = fread($fp, filesize($item));
        fclose($fp);
 
        eregi("<title>(.+)</title>", $contents, $regs);
        if (!$regs[1]) {
            $title = "(Empy title)";
        } else {
            $title = $regs[1];
        }
        print "<li><a href=\"$item\">$title</a>";
    }
}
print "</ul>\n";

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.

Using templates

To build any significant application beyond the usual "Hello world", you need to separate code and layout. There are a bunch of templating engines around, among them FastTemplate, Smarty, PHPLib, TemplateTamer, UltraTemplate, Templeet, etc. You should also use an IDE.

Here's a rough idea of what templating engines do (page called through http://localhost/index.php?page=default):

<?
// functions.inc.php
function template_header() {
    echo '<html><head></head><body>';
}
 
function template_footer() {
    echo '</body></html>';
}
?>
 
<?
// index.php
require_once('./functions.inc.php');
$page = $_GET['page'];
template_header();
require_once("$page.php");
template_footer();
?>
 
<?
// default.php
    echo '<p>Content</p>';
?>  

Here's a sample using FastTemplate:

<!-- bar.tpl -->
<HTML>
<HEAD><TITLE>Feature world - PAGETITLE</TITLE></HEAD>
<BODY>
<H1>PAGETITLE</H1>
PAGECONTENT
</BODY>
</HTML>
 
<!-- foo.tpl -->
This does not do anything obvious. Please look at NAME.
 
<!-- index.php -->
<?php
include "class.FastTemplate.php3";
$tpl = new FastTemplate(".");
$tpl->define(array(foo=>"foo.tpl",bar=>"bar.tpl"));
$tpl->assign(NAME,"me");
$tpl->assign(PAGETITLE,"Welcome!");
 
//Parses foo.tpl, replacing variables with their value, and assigns the output to PAGECONTENT
$tpl->parse(PAGECONTENT,"foo");
$tpl->parse(MAIN,"bar");
 
//Note that bar.tpl contains a reference to variable PAGECONTENT : This is how the two docs are linked
$tpl->FastPrint(MAIN);
?>

Note that you can concatenate the output of several templates by appending a "." to the template's alias:

$tpl->parse(TPL1, "foo");
$tpl->parse(TPL1, ".bar");

Read "PHP Templates: Revisited" and "PHP-HTML Templates: A New Working Relationship".

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.

//upload.php
<form enctype="multipart/form-data" action="validate.php" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="1000">
Send file: <input name="userfile" type="file"><p>
<input type="submit" value="Send file">
</form>
 
//validate.php
<?php
echo $HTTP_POST_FILES['userfile']['name']
?>

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);
        $row = mysql_fetch_row($result);
        
        echo "Updating record #$row[0]<P>";
 
        echo "<form method=post>";
        echo "<input type=hidden name=status value=\"1\">";
        echo "Compte <input type=\"text\" name=\"txtField\" value=\"$row[1]\"><p>";
        echo "<input type=\"submit\" name=\"cmdUpdate\" value=\"Save\"></p>";
        echo "</form>";
        
} else { //Since $status is set, this page is displayed/run in response to the user clicking on the Save button
        $result=mysql_query("update test set field='$txtField' where id=$id", $db);
 
        echo "<form method=post action=index.php>";
        echo "<input type=\"submit\" name=\"cmdUpdate\" value=\"Back to main page\"></p>";
        echo "</form>";
}

Here's another example:

switch($_POST['status']) {
    case "Go":
        $next = "http://" . $_SERVER["HTTP_HOST"] . $_SERVER["SCRIPT_NAME"];
        header("Location: $next");
        exit();
        break;
                                                                                                                            
    default:
        echo "<form method=post>";
        echo "<input type=submit name=status value=Go>";
        echo "</form>";                                                                                             
        break;
}

Importing code from another page

Set the "include_path" key in PHP.INI, and use the "include" instruction to add a reference to another document inside the current page:

<?php
include "/banner.php";
?>

If you don't have access to PHP.INI, use the following trick to have PHP look for banner.php at the root of your site:

<?php
include $_SERVER['DOCUMENT_ROOT'] . "/banner.php";
?>

Displaying the current date and time

$time = time();
$current = date("Y-m-d H:i",$time);

Displaying file date/time according to French locale

On PHP5 at least, it seems like date() only displays data in the US format. If you want to use a locale-independant function, use the following:

setlocale(LC_ALL, 'fr_FR@euro', 'fr_FR', 'fra');
echo strftime('%d %B %Y', $my_date);

On FreeBSD, here's how to get the date in French:

setlocale (LC_TIME, "fr_FR.ISO8859-1");
print strftime("%a %d %b %Y",strtotime("2008-03-15"));

On Linux/FreeBSD, you can check what locales are installed with "locale -a".

If all else fails, someone showed me this work-around:

$mois = array (1 => 'jan','fev','mars','avr','mai','juin','juil','aout','sept','oct','nov','dec');
$time = time();
echo(date("d",$time).' '.$mois[date("n",$time)] .' '.date("Y",$time) . strftime ("- %H:%M", $time) . "\n");

Converting dd/mm/yyyy into yyyy-mm-dd

... so as to turn user-friendly dates into MySQL-friendly dates:

$mydate = $_POST["mydate"];
 
//Date formated as dd/mm/yyyy
list($d, $m, $y) = preg_split('/\//', $mydate);
 
$mydate = sprintf('%4d%02d%02d', $y, $m, $d);
print $mydate;

And here's how to turn MySQL-formated dates into dd/mm/yyyy:

$query = "SELECT name,DATE_FORMAT(mydate,'%d/%m/%Y') FROM mytable";

And another way, in PHP:

$mydate = "2008-12-24";
print date("d/m/Y", strtotime($row['mydate']));

More date/time stuff

Sending an e-mail

PHP's mail() requires setting up an MTA on the local host. To avoid this, and use an external mail server, use the open-source PhpMailer:

require("class.phpmailer.php");
$mail = new PHPMailer();
 
$mail->IsSMTP();
$mail->Host = "smtp.acme.com";
$mail->From = "php@acme.com";
$mail->FromName = "It's me";
$mail->AddAddress("john@example.com);
$mail->AddAddress("jane@example.com);
 
$mail->Subject = "First PHPMailer Message";
$mail->Body = "Hi! \n\n This is my first e-mail sent through PHPMailer.";
$mail->WordWrap = 50;
 
if(!$mail->Send()) {
        echo "Message was not sent.\n";
        echo "Mailer error: " . $mail->ErrorInfo . "\n";
} else {
        echo "Message has been sent.\n";
}

SQLite

Here's how to install the PDO database interface, and the SQLite PDO module:

  1. First, use the familiar phpinfo(); to check that the PHP binary was compiled with PDO and SQLite
  2. Install PDO and the SQLite PDO module (this package installs both PDO proper, and the SQLite PDO module): yum install php-pdo
  3. Check that the module was installed in php.ini's extension_dir: ll /usr/lib/php/modules/
  4. 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
  5. 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:

foreach(PDO::getAvailableDrivers() as $driver)
{
    echo $driver.'<br />';
}

Here's how to connect to a SQLite database with PDO:

try {
    $dbh = new PDO("sqlite:./db.sqlite");
    $dbh->exec("CREATE TABLE IF NOT EXISTS customer (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255))");
    $dbh->exec("INSERT INTO customer (name) VALUES ('Dummy')");
   
    /*
    $sql="SELECT name FROM customer";
    foreach ($dbh->query($sql) as $row) {
        print $row['name'] . "\n";
    }
    */
 
    //If just one row
    $sql="SELECT name FROM customer WHERE id='1'";
    $row = $dbh->query($sql)->fetch();
    print $row['name'] . "\n";
 
    $dbh = null;
} catch(PDOException $e) {
    echo $e->getMessage();
}

To raise an error if the SQL command is wrong:

$dbh->exec($sql) or die(print_r($dbh->errorInfo()));

To check the server and client versions, respectively:

$dbh = new PDO("sqlite:toto.sqlite");
echo $dbh->getAttribute(PDO::ATTR_SERVER_VERSION) . "<p>";
echo $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION) . "<p>";
$dbh = null;

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...

$db = new SQLiteDatabase("db.sqlite");
$db->query("BEGIN;
        CREATE TABLE foo(id INTEGER PRIMARY KEY, name CHAR(255));
        INSERT INTO foo (name) VALUES('Ilia');
        INSERT INTO foo (name) VALUES('Ilia2');
        INSERT INTO foo (name) VALUES('Ilia3');
        COMMIT;");
 
$result = $db->query("SELECT * FROM foo");
while ($result->valid()) {
    $row = $result->current();      
    print_r($row);
    $result->next();
}
 
unset($db);

...or the procedural version:

echo sqlite_libversion();
$db = sqlite_open("db.sqlite");
 
sqlite_query($db , "CREATE TABLE foo (id INTEGER PRIMARY KEY, name CHAR(255))");
sqlite_query($db, "INSERT INTO foo (name) VALUES ('Ilia')");
sqlite_query($db, "INSERT INTO foo (name) VALUES ('Ilia2')");
sqlite_query($db, "INSERT INTO foo (name) VALUES ('Ilia3')");
 
$result = sqlite_query($db, "SELECT * FROM foo");
while ($row = sqlite_fetch_array($result)) {
    print_r($row);
}
 
sqlite_close($db);

SQLite Functions

SQLite Introduction

MySQL

Testing a MySQL connection

<?php
        $base = 'mybase';
 
        $link = mysql_connect('mysql.acme.com', 'mylogin', 'mypasswd') or die('Could not connect: ' . mysql_error());
        echo 'Connected successfully';
 
        mysql_select_db($base) or die('Could not select database');
 
        $query = 'SELECT * FROM ' . $base;
        $result = mysql_query($query) or die('Query failed: ' . mysql_error());
 
        echo "<table>\n";
        while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
                echo "\t<tr>\n";
                foreach ($line as $col_value) {
                        echo "\t\t<td>$col_value</td>\n";
                }
                echo "\t</tr>\n";
        }
        echo "</table>\n";
        mysql_free_result($result);
        
        mysql_close($link);
?>

Some examples

<html>
<body>
 
<?php
 
$db = mysql_connect("localhost", "root");
mysql_select_db("mydb",$db);
$result = mysql_query("SELECT * FROM employees",$db);
printf("First Name: %s<br>\n", mysql_result($result,0,"first"));
printf("Last Name: %s<br>\n", mysql_result($result,0,"last"));
printf("Address: %s<br>\n", mysql_result($result,0,"address"));
printf("Position: %s<br>\n", mysql_result($result,0,"position"));
mysql_free_result($result);
mysql_close($db);
?>
 
</body>
</html>
 
<?php
 
$db=mysql_connect("localhost", "dummy", "dummy");
mysql_select_db("dummy",$db);
 
$result=mysql_query("select * from employees", $db);
 
while ($row = mysql_fetch_row($result))
    {
    #printf("First=%s Last=%s", @mysql_result($result,0,"firstname"));
    printf("First=%s Last=%s<BR>", $row[0],$row[1]);
}
 
mysql_free_result($result);
mysql_close($db);
 
?>

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()?

Warning:  Failed opening '/banner.php' for inclusion (include_path='.:/usr/local/lib/php')

... 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:

extension=extensions/php_gd2.dll

How to format date/time from MySQL to French?

//2007-12-01 -> 01/12/2007
setlocale(LC_ALL, 'fr-FR');
print strftime ("%A %d %B %Y", strtotime($from_mysql));

Resources

Tools

PHP CRUD

Forums

Sites