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
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
- 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 .php
AddType 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.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>
- /etc/rc.d/init.d/httpd start
From source code
The following compiles PHP as a static module (as shown by the --with-apache
switch):
- tar xzvf apache_1.3.x.tar.gz
- tar xzvf php-x.x.x.tar.gz
- cd apache_1.3.x
- ./configure --prefix=/usr/local/apache
- cd ../php-x.x.x
- ./configure --with-mysql=/usr/local/mysql --with-apache=../apache_1.3.x
--enable-track-vars
- make
- make install
- cd ../apache_1.3.x
- ./configure --prefix=/usr/local/apache --activate-module=src/modules/php4/libphp4.a
- make
- make install
- Edit Apache's httpd.conf configuration file, and add or uncomment
Addtype application/x-httpd-php .php. Also edit The IfModule mod_dir.c
section to add index.php
- Start Apache
Here's how to build PHP as a dynamic module (DSO):
- Compile Apache with ./configure --prefix=/usr/local/apache --enable-module=most --enable-shared=max;
make; make install
- tar xzvf php-x.x.x.tar.gz
- ./configure --with-apxs=/usr/local/web/apache/bin/apxs
--with-mysql=/usr/local/mysql
- make
- make install
- 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
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:
- 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}
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
- http://www.xoops.org
- "There are plenty of business using PHP-Nuke, of which Xoops is
a project fork"
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
- 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
- Check http://de2.php.net/ignore_user_abort
and http://de3.php.net/set_time_limit
- Try to use AJAX. Redirect user to "Please wait..." page which
one will check if operations are completed and make automatic redirect.
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:
- 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():
<?php
//very
first connection?
if(!isset($HTTP_COOKIE_VARS["PHPSESSID"])){
//Already
shown form or not?
if
( $_SERVER['REQUEST_METHOD']=='GET' ) {
?>
<form
action=<?php echo $PHP_SELF; ?> method=POST>
<input
type=submit name=mybutton value="Click me!">
</form>
<?php
}
else {
session_start();
//here,
handle form
header("Location:
" . $PHP_SELF);
}
}
else {
print "Form
has been shown and handled, and sessionID created: done!";
}
?>
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)?
" ":$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)?
" ":$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:
- First, use the familiar phpinfo(); to check that the PHP binary was
compiled with PDO and SQLite
- Install PDO and the SQLite PDO module (this package installs both PDO
proper, and the SQLite PDO module): yum install php-pdo
- 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:
- 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