PHP Base Library Documentation, Release phplib_7
Boris Erdmann, be@shonline.de, Kristian Köhntopp,
kk@shonline.de and Sascha Schumann, sascha@schumann.cx
$Id: documentation.sgml,v 1.6 1999/10/24 13:29:34 kk Exp $
____________________________________________________________
Table of Contents
1. Quick Start
1.1 License
1.2 Target Group and Prerequisites
1.3 Quick Guide to Installation
1.4 Using core features of PHPLIB
1.5 Testing
2. Overview and Installation
2.1 Files, classes and functions
2.1.1 Customization
2.1.2 Core functionality
2.1.3 Extended functionality
2.1.4 HTML widgets
2.2 Downloading and unpacking the distribution
2.3 Requirements and things to check for
2.3.1 Interpreter requirements
2.3.2 Database requirements
2.3.3 Name space requirements
2.3.4 Year 2000 compliance statement
2.4 Installation procedure
2.5 Using
2.6 PHPLIB with mod_php (Apache module)
3. Core Functionality
3.1 DB_Sql
3.1.1 Instance variables
3.1.2 Instance methods
3.1.2.1 Accessible instance methods
3.1.2.2 Internal instance methods
3.1.3 Example
3.1.4 Additional information about database connections
3.1.5 Using
3.2 Page Management
3.2.1 Accessible Functions
3.2.2 Example
3.2.3 The "cart" feature is gone
3.3 CT_Sql
3.3.1 Instance variables
3.3.2 Example
3.4 CT_Split_Sql
3.4.1 Instance variables
3.4.2 Example
3.5 CT_Shm
3.5.1 Instance variables
3.5.2 Example
3.6 CT_Dbm
3.6.1 Instance variables
3.6.2 Example
3.7 CT_Ldap
3.7.1 Instance variables
3.7.2 Example
3.8 Session
3.8.1 Instance variables
3.8.2 Instance methods
3.8.2.1 Accessible instance methods
3.8.2.2 Internal instance methods
3.8.3 Example
3.8.4 Using "auto_init"
3.8.5 Unregistering variables and deleting sessions
3.8.6 Reading and understanding session data for debugging
3.8.7 How "serialize()" operates
3.9 Auth
3.9.1 Instance variables
3.9.2 Instance methods
3.9.2.1 Accessible instance methods
3.9.2.2 Internal instance methods
3.9.3 Example
3.9.4 Using default authentication
3.9.5 Using Challenge-Response Authentication
3.9.6 The complete guide to authentication and user variables
3.9.6.1 How is the
3.9.6.2 How does
3.9.6.3 How do $sess and $auth interact?
3.9.6.4 Where is the beef?
3.9.6.5 I still do not understand! What am I supposed to code?
3.9.6.6 Ok, I did that and it works. I even understood it. Now, what exactly is that uid used for?
3.9.6.7 But is the uid used internally by PHPLIB?
3.10 Perm
3.10.1 Instance variables
3.10.2 Instance methods
3.10.2.1 Accessible instance methods
3.10.2.2 Internal instance methods
3.10.3 Example
3.10.4 How permissions work
3.11 User
3.11.1 Instance variables
3.11.2 Instance methods
3.11.2.1 Accessible instance methods
3.11.2.2 Internal instance methods
3.11.3 Example
4. Extended functionality
4.1 Cart
4.1.1 Instance variables
4.1.2 Instance methods
4.1.2.1 Accessible instance methods
4.1.3 Example
4.1.4 On using Cart
4.2 Template
4.2.1 Instance variables
4.2.2 Instance methods
4.2.2.1 Accessible instance methods
4.2.2.2 Internal instance methods
4.2.3 Example
5. HTML Widgets Classes
5.1 Sql_Query
5.1.1 Instance variables
5.1.2 Instance methods
5.1.2.1 Accessible instance methods
5.1.2.2 Internal instance methods
5.1.3 Example
5.2 Table and CSV_Table
5.2.1 Instance variables
5.2.2 Instance methods
5.2.2.1 High-level instance methods
5.2.2.2 Mid-level instance methods
5.2.2.3 Low-level instance methods
5.2.3 Example
5.3 Form
5.3.1 Using OOH Forms
5.3.2 Customizing OOH Forms
5.4 tpl_form
5.4.1 Instance variables
5.4.2 Instance methods
5.4.2.1 Accessible instance methods
5.4.2.2 Internal instance methods
5.4.3 Example
5.5 Tree
5.5.1 Instance variables
5.5.2 Instance methods
5.5.2.1 Accessible instance methods
5.5.3 The Tree Array
5.5.4 Example
5.5.5 Known Bugs / Tips
5.6 STRINGS2 function set
6. Acknowledgments
______________________________________________________________________
11.. QQuuiicckk SSttaarrtt
The Quick Start chapter tries to give you a ten-minute introduction to
PHPLIB installation, outlines a few simple testing procedures and
closes with an overview of PHPLIB features.
11..11.. LLiicceennssee
PHPLIB consists of the files in this directory and all its
subdirectories. It is made available as free software under the
LIBRARY GNU General Public license, as spelled out in the file COPYING
in this directory. Also, it is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license
for more details.
11..22.. TTaarrggeett GGrroouupp aanndd PPrreerreeqquuiissiitteess
PHPLIB targets the PHP application developer. You need to have good
knowledge of the PHP language, at least basic SQL database knowhow and
at least basic knowledge on how to operate your web server to be able
to use the library.
The library will help you to write medium to large sized data-driven
web applications. "Medium to large sized applications" are
applications that consist of multiple database queries, have to
generate tables from database data, need a user interface that
generates SQL queries or need a comfortable and user-friendly way to
protect pages or functionality on pages. "Data-driven" applications
are applications that make use of a supported SQL-database to create
HTML content and that use HTML forms to drive database transactions.
To make use of the library you obviously need access to a web server
with a working installation of a current PHP interpreter (we recommend
3.0.12 or newer for this release of the library) and access to a
supported SQL database (currently, PHPLIB supports MySQL, PostgreSQL,
mSQL, Oracle 7 and Oracle 8, Sybase, Microsoft SQL Server and ODBC
databases). You need to be able to create and drop database tables in
that database and your web server must be able to execute SELECT,
INSERT, UPDATE and DELETE statements on these tables.
Throughout this manual, we assume that you are using the MySQL
database server. PHPLIB will run with any supported SQL server, but we
are using MySQL in the development of PHPLIB.
PHPLIB can be used in conjunction with the CGI version of PHP and with
mod_php, integrated into Apache. Usage of the CGI version has an
impact on overall speed, because you cannot take advantage of
persistent database connection. We recommend the Apache module over
the CGI version, although we personally use the CGI version for
various reasons (easier to update and can be run with Apache suexec).
PHP 4 is still in beta. We do not support deployment of this library
with beta software.
11..33.. QQuuiicckk GGuuiiddee ttoo IInnssttaallllaattiioonn
These instructions apply to PHPLIB running with CGI PHP. Most of them
are valid for mod_php as well, though. _V_E_R_Y _I_M_P_O_R_T_A_N_T _N_O_T_E_: This is a
quick installation guide to get you started if you have an
installation where you control the web server, PHP interpreter and
database server completely. They are not suitable for a web hosting
setup where you have only limited to no control over the installation.
Refer to Chapter 2 of this documentation for the complete installation
instructions and troubleshooting information.
Before installing PHPLIB, get your web server up and running and have
it executing files with the extension .php3. Check that with a simple
script. Make sure the web server accepts index.php3
as well as index.html as a default file for URLs ending in "/"
(Apache: DirectoryIndex index.html index.php3).
Get your MySQL database server up an running. Create an empty database
for your application and make sure the owner of your web server
processes can access this database with SELECT, INSERT, UPDATE and
DELETE access. Don't forget the mysqladmin reload after changing the
user and db tables.
SStteepp 11
Create an include directory named php parallel to your web
servers document root directory. Do not put the include
directory below your web servers document root.
SStteepp 22
Unpack your PHPLIB distribution. Move the contents of the php
distribution directory into the php directory you just created.
SStteepp 33
Get to the php3.ini file for your web servers PHP interpreter
and update the include_path statement so that it points to that
php directory. Update the auto_prepend_file statement so that it
points to the prepend.php3 file in that include directory.
If you do not have control over your php3.ini file, you did not
read the _V_E_R_Y _I_M_P_O_R_T_A_N_T _N_O_T_E above.
SStteepp 44
Also check that track_vars are enabled and that you have enabled
magic_quotes_gpc. While you are at it, you might want to check
sendmail_path, if you plan to send mail from your application.
It has to be set to /usr/lib/sendmail -t on most UNIX systems to
work.
If you do not have control over your php3.ini file, you did not
read the _V_E_R_Y _I_M_P_O_R_T_A_N_T _N_O_T_E above.
SStteepp 55
cd into the php include directory. Edit local.inc. In class
DB_Example supply the appropriate parameters for your database
connection.
SStteepp 66
For this database, run create_database.mysql from the
distribution to create active_sessions and auth_user. auth_user
will be populated with a sample user named kris with a password
test.
SStteepp 77
Move the contents of the pages directory and all its
subdirectories into your document root directory.
SStteepp 88
Access the "/" URL of your web server with cookies enabled. If
no index.html is present, index.php3 will be displayed. If you
reload that page, the number shown must increment. Access your
database with the mysql command client and select * from
active_sessions. Check that there is a single session record for
your browser and see how the text in val changes when you reload
the page and select * from active_sessions again. If this works,
the session class is functional with cookie mode.
SStteepp 99
Now access showoff.php3. Try to login as kris, password test.
Check active_sessions again. You now should have a
Example_Session entry (see the name column) and a Example_User
entry in your table. Both should increment on reload.
SStteepp 1100
Try again with cookies disabled. You should get a new session
(the cookie is lost) and you should be able to see your session
id as the get parameter part of your URL.
11..44.. UUssiinngg ccoorree ffeeaattuurreess ooff PPHHPPLLIIBB
Many applications don't use PHPLIB's advanced features, but see PHPLIB
as a convenient way to protect pages or functionality with passwords.
This section covers such core functionality usage of PHPLIB.
CCuussttoommiizziinngg tthhee llooggiinn ssccrreeeenn
Edit loginform.ihtml in the include directory to suit your
needs.
CCuussttoommiizziinngg tthhee ppeerrmmiissssiioonn lleevveellss
Edit local.inc and change the class Example_Perm to enumerate
your permissions. Your users in auth_user must have one or more
comma separated permission names from that list. Edit
perminvalid.ihtml for a suitable error message.
CCrreeaattiinngg NNeeww UUsseerrss
Use new_user.php3 from the pages/admin directory of the
distribution. If you followed the installation instructions, it
should be available under the /admin URL of your web server.
To manually create a user, run print md5(uniqid("some magic
string") to get a user id. insert into auth_user values ( "that
userid", "username", "password", "permissions");.
CCrreeaattiinngg aann uunnpprrootteecctteedd sseessssiioonn ppaaggee
Begin that page with
___________________________________________________________________
"Example_Session")); ?>
___________________________________________________________________
End that page with
___________________________________________________________________
___________________________________________________________________
CCrreeaattiinngg aa pprrootteecctteedd sseessssiioonn ppaaggee
Begin that page with
___________________________________________________________________
"Example_Session",
"auth" => "Example_Auth",
"perm" => "Example_Perm"));
$perm->check("desired protection");
?>
___________________________________________________________________
and end that page with
___________________________________________________________________
___________________________________________________________________
CCrreeaattiinngg pprrootteecctteedd ffuunnccttiioonnaalliittyy
Begin that page with
___________________________________________________________________
"Example_Session",
"auth" => "Example_Auth",
"perm" => "Example_Perm"));
?>
___________________________________________________________________
and end that page with
___________________________________________________________________
___________________________________________________________________
Enclose the protected functionality in
___________________________________________________________________
have_perm("desired protection")):
?>
Put protected HTML or PHP here
___________________________________________________________________
_N_o_t_e_: desired protection is any combination of permissions from
Example_Perm. Using the default values from Example_Perm, "user",
"user,author" or "admin" are all valid sample values. A user can
access a page, if that user has all permissions that are being
requested in a $perm->check() or $perm->have_perm() call.
_N_o_t_e_: Users can have multiple permission in their perms column of
auth_user. A user with perms "user,author,editor" can access all
pages requesting any combination of these permissions.
_N_o_t_e_: Don't use spaces. "user,author,editor" works. "user, author,
editor" does not.
_N_o_t_e_: If $auth->auth["uid"] is set on a protected page _a_n_d if (time
< auth->auth["exp"]), then and only then the authentication is
valid. You may then use $auth->auth["uname"] as the user name,
$auth->auth["uid"] as a unique user id and $auth->auth["perm"] for
the current permissions of that user. Actually, you never want to
touch $auth->auth["perm"] manually, but use $perm->have_perm("...")
for that.
GGeettttiinngg aa ggrriipp oonn PPHHPPLLIIBB
Read on. Then read the source. Read it again -
Session->serialize() and Auth->start() are ugly. Get a CVS
account. Contribute. Become famous. Buy a ferrari.
_N_o_t_e_: You want to understand what registered variables are. You
want to understand in what order form variables and session
variables are imported into your page. You want to understand
how to copy values from form values into session values without
killing yourself. You do not want to make form variables
persistent, ever. Then you will live happily thereafter...
11..55.. TTeessttiinngg
These instructions apply to PHPLIB running with CGI PHP. Most of them
is valid for mod_php as well, though. This section offers an
incremental approach to find installation problems, should the above
installation process fail.
We do have a support mailing list available under the address
phplib@lists.netuse.de. To subscribe to the list, send the command
subscribe to the address phplib-request@lists.netuse.de.
CChheecckkiinngg tthhaatt tthhee wweebb sseerrvveerr iiss uupp aanndd rruunnnniinngg
Make sure your web server is up and serving the virtual host you
just set up. To do this, construct a small file test1.html in
your DocumentRoot and access test1.html through your web server.
CChheecckkiinngg tthhaatt tthhee wweebb sseerrvveerr iiss eexxeeccuuttiinngg CCGGII pprrooggrraammss
Make sure your web server is up and does run CGI. Check the
current directory, the UID/GID it is running programs under and
have a look at the environment variables. Install the shell
script
___________________________________________________________________
#! /bin/sh --
echo "Content-Type: text/plain"
echo
id
echo
pwd
echo
env | sort
echo
___________________________________________________________________
in your cgi directory under the name of cgi-test and in your
document root under the name of cgi-test.cgi. Make it executable.
Try to access /cgi/cgi-test?par1=one&par2=two and /cgi-
test.cgi?par1=one&par2=two and check the output. What UID/GID are
you running under, what is the output of pwd and what environment
variables are set? What does QUERY_STRING look like? What does the
PATH variable look like, what does the LD_LIBRARY_PATH variable
look like and are all libraries needed by PHP accessible to PHP
running in the CGI environment (Check by running the Unix ldd
command on PHP).
In particular, if you built Oracle support into PHP and linked
libclntsh dynamically: Can it be loaded from the CGI environment?
If not, PHP will not come up later in the next step.
CChheecckkiinngg tthhaatt tthhee PPHHPP iinntteerrpprreetteerr iiss rruunnnniinngg ((AAssssuummiinngg CCGGII
PHP)" Copy your PHP binary into the cgi binary directory (which
should NOT be below DocumentRoot!) and make it executable. Copy
php3.ini into the same directory. In DocumentRoot, create a
test2.php3 and put into it.
Are you running Apache? Add
___________________________________________________________________
Action php3-script /cgi/php
AddHandler php3-script .php3
DirectoryIndex index.php3 index.html index.htm
FancyIndexing on
___________________________________________________________________
to your config. This will map all requests to files ending in .php3
to the php3-script handler and define /cgi/php as the URL handling
php3-script requests internally.
Request /test2.php3 and see that it is being executed. Make
changes to your php3.ini (preferable some color definitions) and
reload. Are they reflected in the output of phpinfo()? If not, your
php3.ini is not being found and your are having a problem.
Recompile with proper settings.
Check the output of phpinfo() carefully! Is your PHP version
current (We have tested and developed this release with PHP
3.0.12)? Are your database interfaces present in the output of
phpinfo()? If not, recompile again.
Can you access /test2.php3 under the URL /cgi/php/test2.php3 as
well? If so, you did not compile your PHP interpreter with
--enable-force-cgi-redirect. PHPLIB will not work with this
interpreter. Recompile with the switch being set.
PPHHPP iinntteerrpprreetteerr ((AAssssuummiinngg mmoodd__pphhpp))
Assuming your server is already correctly setup (don't forget to
activate the PHP lines in srm.conf!), enter the following file
and save it as test2.php3 under your DocumentRoot.
___________________________________________________________________
phpinfo() ?>
___________________________________________________________________
If you access this using a web browser now, it should spit out much
info about PHP, Apache and its environment.
CChheecckkiinngg PPHHPPLLIIBB iinncclluussiioonn
Does you PHP include PHPLIB properly? Check your php3.ini file.
It must include the following settings:
___________________________________________________________________
include_path = pathname to directory with all the .inc files
auto_prepend_file = path to prepend.php3
track_vars = On
___________________________________________________________________
It should contain the following settings, too:
___________________________________________________________________
magic_quotes_gpc = On
___________________________________________________________________
If PHPLIB is included properly by your setup, the following page
will execute without errors:
___________________________________________________________________
\n";
?>
___________________________________________________________________
CChheecckkiinngg ddaattaabbaassee ccoonnnneeccttiivviittyy
PHPLIB installation requires that you adapt local.inc properly.
Particularly, the provided class DB_Example must be customized
for your database connection. Test that your web server can
access the database with the following page:
___________________________________________________________________
query("select * from auth_user");
$t = new Table;
$t->heading = "on";
$t->show_result($db);
?>
___________________________________________________________________
When executing properly, this page will show you the user entry for
kris, password test, permissions admin from the auth_user table. If
this does not happen, your DB_Example definition in local.inc is
broken.
CChheecckkiinngg tthhaatt sseessssiioonnss wwoorrkk
Access the page /index.php3 that has been provided with the
distribution. This page will try to set a cookie in your
browser. Allow that cookie to be set.
The page will display a headline with a counter. Reload that
page. The counter must increment. If not, either your browser
cannot deal properly with cookies or PHPLIB cannot properly read
or write the table active_sessions in your database. Check that
the cookie is being set by viewing the output of phpinfo() (the
fourth table will report the cookie and other per-call data).
Check your database permissions with your database command line
interface.
CChheecckkiinngg tthhaatt AAuutthheennttiiccaattiioonn wwoorrkkss
Try loading /showoff.php3 that has been provided with the
distribution. This page will require a login. Login as kris,
using a password of test. If the login is successful, you will
see the per-session counter and a per-user counter again. Reload
that page: The counters must increment.
If you can't login, you probably have a problem with cookies.
Check again that your browser accepts and sends session cookies.
Another problem may be access to the auth_user table. You must
be able to SELECT on that table and there must be at an entry
for the user you are trying to login.
22.. OOvveerrvviieeww aanndd IInnssttaallllaattiioonn
The following sections discuss the installation, verification and
layout of PHPLIB: How to install PHPLIB? Which functionality and class
definitions are contained in which files? How do you layout a web
server with PHPLIB installed? Which installation options are available
and how do these affect performance?
22..11.. FFiilleess,, ccllaasssseess aanndd ffuunnccttiioonnss
PHPLIB contains a set of core classes and functions that offer session
tracking, per-session and per-user persistent variables, user
authentication and permission checking. Building upon this core
functionality, PHPLIB offers a set of commonly needed "background"
classes and a set of "HTML widgets", classes that allow you to quickly
generate HTML based user interfaces.
All PHPLIB definitions are designed that you don't need to change any
of these files. Your customization of PHPLIB can be contained in two
or three files, depending on the setup: local.inc, setup.inc and, in
some cases, prepend.php3. You _N_E_V_E_R need to change any other file with
PHPLIB. Details are outlined below.
22..11..11.. CCuussttoommiizzaattiioonn
The following three files are the only files from PHPLIB that require
changes in normal PHPLIB applications.
AApppplliiccaattiioonn ccoonnffiigguurraattiioonn iinn local.inc:
Your application will almost certainly not work with the default
values supplied by the above classes. You are supposed to extend
the classes described below as you see fit.
In your subclasses, you only have to specify what is different
in your application. These are things like database host names,
database names, table names and username/password combinations.
You need to provide login screen definitions (HTML) and user
validation functions (SQL) to make the example work.
The distribution provides a local.inc to illustrate a typical
setup. These definitions are also needed to get the
administration and testing scripts provided with the
distribution to run.
The file is required and you must change it for your setup.
AApppplliiccaattiioonn sseettuupp iinn setup.inc:
The Session class provides the ability to execute initialization
code at session setup. See the class description for
instructions on how to set this up.
Per convention, we store such code in setup.inc in the include
directory. The code is being executed whenever a new user
connection to out application and a new session is started.
The file is optional. No default is provided.
SSeelleeccttiioonn ooff aauuttoommaattiiccaallllyy llooaaddeedd ccllaasssseess iinn prepend.php3
The file prepend.php3 determines which code is being loaded for
all PHP3 interpreted pages. Normally, we include the class
definitions for all core classes in this file: db_mysql.inc,
session.inc, auth.inc, perm.inc, user.inc, then your local
customizations from local.inc and the page management functions
from page.inc.
You must change prepend.php3 to reflect the database interface
that you are using: Change the require statement for
db_mysql.inc appropriately.
If you are not using some core features from PHPLIB in your
application or if you want some other features to be present on
all your pages, you can delete or add require statements for
their respective include files here.
The file is required. You must change it for your setup, unless
you are using MySQL.
22..11..22.. CCoorree ffuunnccttiioonnaalliittyy
The following files are included from prepend.php3 and provide
definitions for the core classes of PHPLIB. We recommend that you
always include all of them, as they are a tightly integrated set of
classes with many dependencies among them.
CCllaassss DB_Sql defined in exactly one of
db_mysql.inc, db_msql.inc, db_pgsql.inc, db_odbc.inc,
db_sybase.inc, db_mssql.inc, db_oracle.inc or db_oci8.inc:"
A database access class for your database server. PHPLIB depends
on the presence of a SQL database server. Depending on the type
of your database server, you have to select the appropriate
include file. The file contains the definition of a class DB_Sql
suitable for your database server.
The class manages a database connection (connection setup is
implicit) and result memory is managed automatically.
An independent class.
CCllaassss Session defined in session.inc:
Manages an arbitrary amount of arbitrarily named session
variables of scalar, array and object types (Object support
requires that you implement two instance variables in your
classes). Tracks sessions via cookies or a get-variable appended
to each URL.
Depends on DB_Sql.
CCllaassss Auth defined in auth.inc:
Manages session authentication. Sessions are authenticated
against usernames and passwords in a database. Authentication
can be time limited.
Depends on Session and DB_Sql.
CCllaassss Perm defined in perm.inc:
Manages permission checks on authenticated session pages.
Protected pages are only accessible to users with the specified
rights.
Depends on Auth, Session and DB_Sql.
CCllaassss User defined in user.inc:
Manages user dependent variables. Unlike session variables these
are bound to a user id, not to a session id. They are persistent
over multiple sessions, but are only available after a user has
been authenticated.
Depends on Auth, Session and DB_Sql, extension of Session.
ffuunnccttiioonnss page_open() and page_close()
defined in page.inc:" Setup and Shutdown functions, must be
present on any session page.
Depend on Session.
22..11..33.. EExxtteennddeedd ffuunnccttiioonnaalliittyy
The extended functionality classes offer GUI-less background features
that are commonly needed in HTML-applications. They may make use of
core functionality (indicated for each class below).
Cart in cart.inc:
Manages a simple shopping cart. Items can be put into the cart,
taken out of the cart and the carts contents can be enumerated.
Depends on Session to be useful. Requires that you add the
statement require("cart.inc") to prepend.php3.
Template in template.inc:
Manages templates and variable replacement. Templates can be
stored in files. They are loaded on demand and variables are
replaced in these files.
An independent class. Requires that you add the statement
require("template.inc") to prepend.php3 or that you include it
manually on each page where you want to use it.
22..11..44.. HHTTMMLL wwiiddggeettss
HTML widgets are classes that generate some HTML-code (often forms or
tables) to display GUI-elements. We try to provide functionality
commonly used in applications, in a way that the actual look of the
GUI-elements can be easily customized.
CSV_Table in csv_table.inc:
Creates a dump of a two dimensional array or a query result in
CSV format, suitable for loading into a database or a
spreadsheet program.
Depends on Table, extension of Table.
Sql_Query in sql_query.inc:
Create a selection widget that allows a user to choose arbitrary
conditions on one or more table columns. SQL is being created
from these selections that can be used in the where-clause of a
larger SQL select statement.
Depends on Session and DB_Sql. Requires that you add the
statement require("sqlquery.inc") to prepend.php3.
Table in table.inc:
Creates HTML tables from two dimensional arrays or from database
query results. The class can either filter out the desired
columns from an array or you can explicitly name which columns
to show. A heading can be turned on if desired. All generated
HTML elements are tagged with a classname you specify for
stylesheet support, if needed. When used in a form tag, each
table row can be prefixed with a checkbox input element to allow
for row selection.
An independent class.
Form in oohforms.inc:
Creates HTML forms from feature->value arrays. This provides a
single syntax for creating all of the different types of form
elements. The class provides easy access to Javascript and
server side validation, and supports 'freezing' some or all of
the form elements to display static data. In addition, the
library relies on object oriented implementations for the
various form elements and these can easily be extended and
customized.
An independent class.
22..22.. DDoowwnnllooaaddiinngg aanndd uunnppaacckkiinngg tthhee ddiissttrriibbuuttiioonn
The base library is supplied at the PHP Base Library download
location. Two different formats are provided: A tar.gz version and a
shar version.
If you are on a windows system, you can use phplib.tar.gz, if you have
WinZIP installed. Current versions of WinZIP know how to handle
compressed tar archives. The uncompressed files may be installed on
your windows system or transferred to your Unix system.
If you can't handle binary files, you may download phplib.shar. This
is a pure ASCII file containing a self extracting shell script. Just
save the file, make it executable and feed it to your Unix shell (for
example, by typing sh phplib.shar).
The PHPLIB support mailing list is available should you run into
problems with the library. To subscribe send the command subscribe to
the mailing list subscription address.
22..33.. RReeqquuiirreemmeennttss aanndd tthhiinnggss ttoo cchheecckk ffoorr
22..33..11.. IInntteerrpprreetteerr rreeqquuiirreemmeennttss
The PHP base library requires a working web server with CGI capability
and the CGI version of PHP 3.0.12 or higher installed. Alternatively
mod_php can be used. Lower versions of PHP do not work at all: The
session class uses the base64_encode() and base64_decode() functions
which are known to be buggy in lower versions (up to 3.0.7) of the
library. Also, the OOH Forms classes are using constructor syntax,
which has been introduced into the PHP language in 3.0.5 and later
versions. An issue with the $PHP_SELF variable and CGI PHP has been
resolved with version 3.0.5 and later. Perl regular expression
functions are being used in the Template class and these are not
really avilable up to 3.0.12.
_N_o_t_e_: If you are using CGI PHP, it _m_u_s_t have been compiled with the
--enable-force-cgi-redirect switch for $PHP_SELF to have the correct
value.
Basically, if PHP_SELF is the exact local part of your $URL, all is
well. If it instead contains the modified URL with /your cgi-bin/php
prefixed, you have a buggy version of CGI PHP. Either upgrade your
version of PHP or replace all occurrences of $PHP_SELF with $PATH_INFO
in PHPLIB.
_N_o_t_e_: PHPLIB requires that you have track_vars compiled in and
enabled.
_N_o_t_e_: PHPLIB does not require short_open_tag to be enabled. The
library always uses .inc. The require() statement has to be adapted
to reflect this).
Assuming your database server is named database.netuse.de and
your CGI user is webuser and you are accessing the database
myapp, do
___________________________________________________________________
mysql -h database -u webuser myapp
___________________________________________________________________
If it does not work, connect your database as administrator and
create the proper mysql access permissions. Adapt and run
create_database.mysql from the stuff subdirectory of the
distribution to create the databases active_sessions and auth_user
as well as the sample user kris with password test. Try again to
connect like shown above. Can you do select * from active_sessions?
and insert into active_sessions values ("1", "2", "3", "") as well
as delete from active_sessions? Can you select * from auth_user?
_N_o_t_e_: Additional database creation scripts are provided for several
different databases in the stuff directory of the distribution.
MMeerrggiinngg tthhee lliibbrraarryy wwiitthh yyoouurr PPHHPP ffiilleess
Decide if you want to use include or auto_prepend_file. We do
use auto_prepend_file here and we add the statement
auto_prepend_file =
/home/www/servers/phplib.netuse.de/php/prepend.php3 to our
php3.ini.
Not all classes are included/required by prepend.php3, only core
functionality files are: db_xxx.inc, ct_sql.inc, session.inc,
auth.inc, perm.inc, user.inc, local.inc and page.inc. The
library provides other, less essential classes that can be
included manually on a page-by-page basis. Some classes make
themselves persistent, if used. These classes require that you
include their definitions in the prepend.php3 file where
indicated to function correctly. See the usage instructions for
these classes for details.
Having done this, access /index.php3. The counter should
increment when that page is being reloaded. Also, checking
active_sessions in the database should reflect that session.
SSuubbssccrriibbee ffoorr ssuuppppoorrtt
Subscribe to the mailing list phplib@lists.netuse.de. Do so by
sending a mail body of subscribe to phplib-
request@lists.netuse.de and follow instructions. Share your
experiences.
22..55.. UUssiinngg iinncclluuddee(()) iinnsstteeaadd ooff aauuttoo__pprreeppeenndd__ffiillee==
If you do not want to use auto_prepend_file to load the PHPLIB core
functionality, you can load the class definitions for the core
manually on each page that requires them.
You will have to define a valid include_path=-statement in your
php3.ini file as outlined previously to reflect the location of the
*.inc files. Then, all core functionality can be loaded with
include("prepend.php3") as the first statement at the top of each
page.
To further optimize performance, you can minimize the contents of the
prepend file, if you do not need all core functionality. You _m_a_y
leave out auth.inc, perm.inc and user.inc, if you do not require these
features (note that there are dependencies among these classes!).
22..66.. PPHHPPLLIIBB wwiitthh mmoodd__pphhpp ((AAppaacchhee mmoodduullee))
Installing PHPLIB onto a web server that has PHP3 as a module
(actually Apache) mainly differs in where you can set up runtime
settings for PHP3 itself. PHP3 can be compiled with a wealth of
parameters (see the PHP section in phpinfo()), most of which can get
overridden by the php3.ini file. The location of this file is shows as
part of the output of phpinfo().
With PHP3 as a module you have a wider choice on placing these
settings: they are overridden, in this order, by what is defined in
httpd.conf and in your per-directory .htaccess file. Directives in
these files are identical to their php3.ini brothers, but are prefixed
with php_ to avoid clashes with Apache configuration keywords. Also,
as they are Apache configuration keywords, they have no equals ("=")
sign in them. If x=y is a configuration directive from php3.ini, you
should be using php3_x y within the Apache configuration instead. That
is, you should prepend php3_ to the keyword and omit the equals sign.
If you misspell a configuration directive, you will get an error 500
from your webserver and find more details about the error in the
logfile you configured with ErrorLog in your webserver setup.
_E_x_a_m_p_l_e_: If below we talk about setting in your php3.ini the
configuration
______________________________________________________________________
include_path = "/bla"
______________________________________________________________________
mod_php users may alternatively configure in their httpd.conf the
following:
______________________________________________________________________
php3_include_path "/bla"
______________________________________________________________________
Of special interest to PHPLIB users are the following directives:
______________________________________________________________________
;;;;;;;;;;;;;;;;;
; Data Handling ;
;;;;;;;;;;;;;;;;;
magic_quotes_gpc = Off ; magic quotes for incoming
; GET/POST/Cookie data
magic_quotes_runtime = Off; magic quotes for runtime-generated data,
; e.g. data from SQL, from exec(), etc.
magic_quotes_sybase = Off ; Use Sybase-style magic quotes
; (escape ' with '' instead of \')
track_vars = On ; enable $PHP_GET_VARS[], $PHP_POST_VARS[]
; and $PHP_COOKIE_VARS[] arrays
; automatically add files before or after any PHP 3.0 document
auto_prepend_file = (add path to prepend.php3 here)
auto_append_file =
;;;;;;;;;;;;;;;;;;;;;;;;;
; Paths and Directories ;
;;;;;;;;;;;;;;;;;;;;;;;;;
include_path = (add path to the directory with all .inc files)
______________________________________________________________________
All of this comes very handy when you have multiple virtual hosts
(e.g. you are an ISP). In this case you can comfortably place the php3
directives in the block or in an .htaccess file in the
client directory.
33.. CCoorree FFuunnccttiioonnaalliittyy
Each class contains instance variables and instance methods. Some of
these variables and methods are available for customization, some are
internal to the classes themselves. All are documented, but tampering
with internal variables and methods is not supported. Internal
interfaces are subject to change without notice from one version of
the library to another.
This section covers PHPLIB core functionality in reference form.
Classes are presented in order of dependency, though, because the core
structure is easier understood in this order. You will need to
understand the complete core structure to successfully use all of
PHPLIB's features.
33..11.. DDBB__SSqqll
DB_Sql is used by CT_Sql and Auth to access a SQL database. You are
encouraged to use it directly, too.
33..11..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
Internal instance variables.
33..11..22.. IInnssttaannccee mmeetthhooddss
33..11..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
DDBB__SSqqll(($$qquueerryy ==
Constructor. When creating an instance, you may optionally
supply a query string.
___________________________________________________________________
$db = new DB_Sql_Subclass("select * from mytable");)
___________________________________________________________________
qquueerryy(($$qquueerryy__ssttrriinngg))
query_string is a SQL statement that is sent to the database.
After sending the statement, Error and Errno are updated. If
the query is syntactically incorrect (no valid result id is
being produced), halt() is called with a meaningful error
message.
If there is no active link to the database, a pconnect() is made
using the information from the Host, Database, User and Password
instance variables.
Returns the result of the query() statement, which is guaranteed
to be a valid result id (or false, if Halt_On_Error isn't
"yes").
nneexxtt__rreeccoorrdd(())
next_record() advances the cursor through the current query
result and updates the Record, Row, Errno and Error instance
variables.
Returns true, if there is a new result record. Returns false, if
done with the current result set. If Auto_Free is true,
free_result() is called automatically before false is returned.
nnuumm__rroowwss(()),, nnff(())
Returns the number of rows returned by the current SELECT query.
_N_o_t_e_: This information is not available in all database
interfaces. Some of the more advanced databases begin to return
query results asynchronously while the backend is still
appending result rows. In such environments the complete size of
the result set is never known.
You should duplicate your WHERE clause of the query in such
environments and ask for the COUNT(*). This will be less
inefficient as it seems as the query path and query result have
been cached by the database.
aaffffeecctteedd__rroowwss(())
Returns the number of rows affected by the current INSERT,
UPDATE or DELETE query.
nnuumm__ffiieellddss(())
Returns the number of columns returned by the current query.
nnpp(())
Prints the number of rows returned by the current query.
ff(($$ffiieelldd))
Identical to accessing Record[$field].
pp(($$ffiieelldd))
Identical to printing Record[$field].
hhaallttmmssgg(($$mmssgg))
This function is called by halt() and will actually print the
database error message. You may override this method in your
subclass of DB_Sql and format the error message to be consistent
with the layout of the rest of your application. You may also
add additional error handling such as informing the application
operator by mail that a database error has occured.
sseeeekk(($$ppooss))
Positions the Row pointer within the result set. Useful for
reading the same result set twice or otherwise jumping around
within the result. $pos is not checked in any way for validity.
_N_o_t_e_: If Auto_Free is true, seek() may not be useable, because
the result set has already been free'ed when next_record() when
behind the last record of the result set.
_N_o_t_e_: Not all database interfaces provide a cursor that is
capable of seeking. This function will be unavailable in such
environments.
lliinnkk__iidd(())
This function will return the current link ID, as returned by
the pconnect() executed internally by the database class.
You should not need this information.
qquueerryy__iidd(())
This function will return the current result ID, as returned by
the query() executed internally by the database class.
You should not need this information.
mmeettaaddaattaa(($$ttaabbllee ==
$table is a SQL table name in the current database. The function
returns an array of hashes indexed on the (0 based) column
number of $table. Each hash is indexed by table (table of which
this column is part of), name (name of this column), type
(column data type), len (column width) and flags (database
specific column flags, if applicable) with one row per table
column. Each row describes a column in your table.
The data returned by metadata() is suitable for passing it to
the Table class. If you specify the full parameter, an
additional column meta is added, which is indexed by field name
and returns the field number of that name. Also, a column
num_fields is added, containing the width of the table.
If $table is omitted, the function returns metadata on the
result of the last executed query. _N_o_t_e_: This is currently
implemented only for the MySQL interface. You are encouraged to
implement this feature for other interfaces.
_N_O_T_E_: At the moment, the PostgreSQL and ODBC interface only
report the table, name and type data reliably. You are
encouraged to fix this.
ttaabbllee__nnaammeess(())
Returns an array with table name and tablespace name.
___________________________________________________________________
table name : $return[$i]["table_name"]
tablespace_name : $return[$i]["tablespace_name"]
___________________________________________________________________
Tables are from $i=0 to last table;
Implemented in db_oracle.inc,db_oci8.inc,db_mysql.inc,db_pgsql.inc
nneexxttiidd(($$sseeqquueennccee__nnaammee))
This function will return a sequence number from the sequence
named by $sequence_name. This number is guaranteed to be
obtained in an atomic manner and can be used as a primary key.
33..11..22..22.. IInntteerrnnaall iinnssttaannccee mmeetthhooddss
ccoonnnneecctt(())
Used internally to generate a Link_ID, if necessary. Link
creation is implicit, there is no need to call connect()
manually, ever.
hhaalltt(($$mmssgg))
Used by query() if the initial database connection cannot be
made or the target database does not exist. Depending on the
setting of Halt_On_Error, this method will call haltmsg() to
report the error.
ffrreeee(())
Used internally by next_record() to free the result set, if so
configured.
33..11..33.. EExxaammppllee
Use a subclass to provide the appropriate parameters for a database
connect. You may overwrite halt() to customize the error message,
although a sensible default is provided.
______________________________________________________________________
class DB_Article extends DB_Sql {
var $classname = "DB_Article";
var $Host = "sales.doma.in";
var $Database = "shop_project";
var $User = "webuser";
var $Password = "";
function haltmsg($msg) {
printf("Database error: %s
\n", $msg);
printf("MySQL Error: %s (%s)
\n",
$this->Errno, $this->Error);
printf("Please contact shopmaster@doma.in and report the ");
printf("exact error message.
\n");
}
}
______________________________________________________________________
Use an instance of the subclass to manage your queries:
______________________________________________________________________
$q = new DB_Article;
$query = sprintf("select * from articles where article like '%%%s%%'",
$searchword);
$q->query($query);
while($q->next_record()) {
printf("%s | %s |
\n",
$q->f("art_id"),
$q->f("article"));
}
______________________________________________________________________
33..11..44.. AAddddiittiioonnaall iinnffoorrmmaattiioonn aabboouutt ddaattaabbaassee ccoonnnneeccttiioonnss
PHP reuses connections, if possible. When a connection is being made
to the same Host with the same Username and Password as an existing
connection, no second connection is being made by PHP. Instead the
existing connection is returned to the caller. This is true for both,
the *_connect() and *_pconnect() calls of all PHP database interfaces.
This has implications for MySQL users: Never use the MySQL "use"
command to change the current database. If you do, session management
will fail to operate properly. Instead, create all PHPLIB tables as
part of your application.
Some databases (for example Oracle) have very expensive connect()
operations. For these databases, performance is dramatically improved
if you switch from CGI PHP to mod_php. This is, because PHPLIB uses
the "*_pconnect()" method to connect to your database. In mod_php, the
database connection is kept around by the web server process after the
page has been processed and is reused if a further connect requires a
connection with the same Host/Username/Password pattern.
This means that there will be at most "number of web server processes"
times "number of Host/Username/Password-combinations" many
simultaneous connections to your database server. Keep that in mind
when planning licenses and server load. Using CGI PHP will probably
reduce the number of concurrent connects to your database server at
the expense of connection setup time. For database servers where
connection setup time is negligible (MySQL for example) this is a
viable solution (don't try it with Oracle) though.
33..11..55.. UUssiinngg nneexxttiidd(())
The nextid() function can be used to obtain a sequence number which
can be used as a primary key. The function manages an arbitrary number
of named sequences, you have to provide the name of a sequence upon
call.
______________________________________________________________________
$db = new DB_Article;
$artnr = $db->nextid("article_sequence");
$query = sprintf("insert into articles ( artnr, ...) values ('%s', ...)",
$artnr, ...);
$db->query($query);
reset($articles);
while(list($itemnr, $itemdesc) = each($articles)) {
$itemnr = $db->nextid("item_sequence");
$query = sprintf("insert into items (artnr, itemnr, ...) values ('%s', '%s', ...)",
$artnr, $itemnr, ...);
$db->query($query);
}
______________________________________________________________________
33..22.. PPaaggee MMaannaaggeemmeenntt
33..22..11.. AAcccceessssiibbllee FFuunnccttiioonnss
Page Management currently consists a collection of functions:
ppaaggee__ooppeenn((aarrrraayy((
This function is to be called with an array of page
features/classname pairs. Valid features are at the moment:
sseessss
This page makes use of session variables.
aauutthh
This page uses session authentication. If you specify the
auth feature, you MUST specify the sess feature, also.
ppeerrmm
This page is protected by permissions and only accessible to
authenticated users with matching rights. If you specify the
perm feature, you MUST specify the auth and sess features,
also.
uusseerr
This page makes use of user variables. If you specify the
user feature, you MUST specify the auth and sess features,
also.
Each feature specifies the name of the class that implements
that feature, for example
________________________________________________________________
page_open(array("sess" => "Shop_Session"));
________________________________________________________________
The function creates an instance of Shop_Session as $sess and
initializes it. It also checks feature dependencies. Note that you
are expected to provide an implementation of the class
Shop_Session. This is usually done in local.inc and usually you do
so by extending the provided Session class.
Examples on how to do this is given in the documentation below when
the classes are introduced.
ppaaggee__cclloossee(())
At the end of your page (after all results have been calculated)
you have to call page_close(). This will save all page state,
session and user variables into database. Changes to session or
user variables after page_close() has been called are not
recorded. Currently it is allowed to call page_close() multiple
times on a single page (not guaranteed for future versions!).
Each time session state will be saved.
_N_o_t_e_: This is going to change. When we introduce record locking,
it is important that you call page_close() only once per page,
because that will implicitly unlock your session record. Also,
it is important that you call page_close() as early as possible
on a page so that the locking time is kept minimal.
sseessss__llooaadd((aarrrraayy((
_A_d_v_a_n_c_e_d _f_e_a_t_u_r_e. Some applications have need to manually load
data belonging to one or multiple session classes. @@TODO
sseessss__ssaavvee((aarrrraayy((
_A_d_v_a_n_c_e_d _f_e_a_t_u_r_e. @@TODO
33..22..22.. EExxaammppllee
______________________________________________________________________
"Shop_Session"));
$sess->register("s"); // See "Session" below for explanation.
?>
______________________________________________________________________
33..22..33.. TThhee ""ccaarrtt"" ffeeaattuurree iiss ggoonnee
There used to be a feature "cart" for page_open() in versions of
PHPLIB up to release-5. The cart has been removed from the core
functionality of PHPLIB to keep the library small, maintainable and
structured. Consequently the "cart" feature is gone.
The Cart class is still present and exists as an extended feature. You
have to include and instantiate your cart manually on that pages that
use it, though. See the Cart class for more information.
33..33.. CCTT__SSqqll
The Session class used to contain a bit of SQL to read and write
session data from and to a database. To make sessions database
independent, this SQL has been isolated and put in a separate class,
CT_Sql. Session now makes all storage accesses through a container
class, which may or may not be an SQL container.
33..33..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
33..33..22.. EExxaammppllee
Use a subclass to provide the appropriate parameters to your
container. Usually your subclass looks like this:
______________________________________________________________________
class My_Sql extends CT_Sql {
var $classname = "My_Sql";
var $database_table = "active_sessions";
var $database_class = "DB_Session";
}
______________________________________________________________________
You can then use My_Sql in class Session. Reference it by putting
"My_Sql" in the "that_class" variable.
33..44.. CCTT__SSpplliitt__SSqqll
The Session class used to contain a bit of SQL to read and write
session data from and to a database. To make sessions database
independent, Session now makes all storage accesses through a
container class. The CT_split_sql container is very similar to CT_Sql
container, with the difference that if serialized data exceeds a
specified amount of bytes, multiple rows will be used to memorized the
entire field.
This class is NOT compatible with CT_Sql class, since table layout is
different and column names are different in order to avoid reserved
words in various database implementation. This uses a DB_Sql like
class so you can access all supported databases with this container.
33..44..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
33..44..22.. EExxaammppllee
Use a subclass to provide the appropriate parameters to your
container. Usually your subclass looks like this:
______________________________________________________________________
class My_Sql extends CT_Split_Sql {
var $classname = "My_Sql";
var $database_table = "active_sessions_split";
var $database_class = "DB_Session";
var $split_length = 4096;
}
______________________________________________________________________
You can then use My_Sql in class Session. Reference it by putting
"My_Sql" in the "that_class" variable.
33..55.. CCTT__SShhmm
The Session class used to contain a bit of SQL to read and write
session data from and to a database. To make sessions database
independent, Session now makes all storage accesses through a
container class. To let Session use shared memory as container, you
use CT_Shm.
33..55..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
33..55..22.. EExxaammppllee
Use a subclass to provide the appropriate parameters to your
container. Usually your subclass looks like this:
______________________________________________________________________
class My_Shm extends CT_Shm {
var $classname = "My_Shm";
var $max_sessions = 500;
var $shm_key = 0x1234232;
var $shm_size = 64000;
}
______________________________________________________________________
You can then use My_Shm in class Session. Reference it by putting
"My_Shm" in the "that_class" variable.
33..66.. CCTT__DDbbmm
The Session class used to contain a bit of SQL to read and write
session data from and to a database. To make sessions database
independent, Session now makes all storage accesses through a
container class. To let Session use a DBM database file as a
container, you use CT_Dbm.
33..66..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
33..66..22.. EExxaammppllee
Use a subclass to provide the appropriate parameters to your
container. Usually your subclass looks like this:
______________________________________________________________________
class My_Dbm extends CT_Dbm {
var $dbm_file = "data/session.dbm";
}
______________________________________________________________________
You can then use My_Dbm in class Session. Reference it by putting
"My_Dbm" in the "that_class" variable.
33..77.. CCTT__LLddaapp
The Session class used to contain a bit of SQL to read and write
session data from and to a database. To make sessions database
independent, Session now makes all storage accesses through a
container class. To let Session use a LDAP database as a container,
you use CT_Ldap.
33..77..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
33..77..22.. EExxaammppllee
Use a subclass to provide the appropriate parameters to your
container. Usually your subclass looks like this:
______________________________________________________________________
class My_Ldap extends CT_Ldap {
var $classname = "My_Ldap";
var $ldap_host = "localhost";
var $ldap_port = 389;
var $basedn = "dc=your-domain, dc=com";
var $rootdn = "cn=root, dc=your-domain, dc=com";
var $rootpw = "secret";
var $objclass = "phplibdata";
}
______________________________________________________________________
You can then use My_Ldap in class Session. Reference it by putting
"My_Ldap" in the "that_class" variable.
33..88.. SSeessssiioonn
The session class keeps a list of global variable names and provides a
set of functions to load and save these variables from and to a data
storage container (we will call it container for shortness). The named
variables may be scalar variables (strings, integers and floats) or
arrays. Objects are handled as well, provided they implement two
instance variables naming their class and enumerating their
(persistent) slots.
33..88..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
Internal instance variables.
33..88..22.. IInnssttaannccee mmeetthhooddss
33..88..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
rreeggiisstteerr(($$vvaarrnnaammee))
Registers a global variable name as a session variable. The name
may identify a scalar variable, an array or an object. If an
object is to be made persistent, it must have two instance
variables:
ccllaassssnnaammee
A string with the name of the objects class.
ppeerrssiisstteenntt__sslloottss
An array with the names of all object slots to save.
uunnrreeggiisstteerr(($$vvaarrnnaammee))
Unregisters a global variable name as a session variable. The
variable is not deleted, but its value will be lost at the end
of a page. It is no longer saved to the database.
iiss__rreeggiisstteerreedd(($$vvaarrnnaammee))
Returns true if the variable named $varname is registered with
the session, false otherwise.
ddeelleettee(())
Destroy the current session and put_id() the current session id.
After delete() has been executed, all session data has been
removed from the database. Also, the session object is unusable
on this page. Consequently, page_close() may not be called for
this session. Session variables are still available on this
page, even after the delete(), but will be lost on the following
pages.
In cookie mode, it is possible to page_open() a new session
after delete() has been called, if no HTML has been output so
far so that the new cookie can be set. If you do this, you can
also re-register some of the previous session variables and can
call page_close() for the new session. This allows you to
change the session on the fly and selectively carry over session
data from the previous session.
uurrll(($$uurrll))
Return an URL referencing the current session. If in get mode,
the current session id is attached to this URL, else the URL is
returned unmodified.
ppuurrll(($$uurrll))
A shorthand for print $this->url($url);
sseellff__uurrll(())
Return an URL referencing the current page, including PHP_SELF
and QUERY_STRING information. If in get mode, the session id is
included.
ppsseellff__uurrll(())
A shorthand for print $this->self_url().
hhiiddddeenn__sseessssiioonn(())
Adds a hidden form element containing the session name and id.
aadddd__qquueerryy(($$qqaarrrraayy))
Return string to be appended to the current URL for parameters
in GET query format. Intended usage is like this:
___________________________________________________________________
"yes"))
?>"> Reload and log in?
___________________________________________________________________
ppaadddd__qquueerryy(($$qqaarrrraayy))
A shorthand for print $this-> add_query($qarray).
rreeiimmppoorrtt__ggeett__vvaarrss(())
When a FORM variable is made persistent, that form variable is
imported into PHP, then page_open() is being called and the new
variable value is overwritten from the database. The FORM value
is lost.
If you had enabled track_vars and were accessing HTTP_GET_VARS
directly, which is recommended, this were not a problem. Some
legacy scripts rely on persistent FORM input variables, though.
These scripts may call the appropriate reimport_x_vars()
functions. These functions will re-read the tracked variable
arrays and reinitialize the appropriate global variables after
session variables have been restored.
Use of this function is discouraged.
rreeiimmppoorrtt__ppoosstt__vvaarrss(())
See reimport_get_vars().
rreeiimmppoorrtt__ccooookkiiee__vvaarrss(())
See reimport_get_vars().
sseett__ccoonnttaaiinneerr(())
You shall not call this function directly. It is called back by
the start() function of Session() during initializiation. It is
documented so that you can override its implementation in your
subclass of Session if you know what you are doing.
This function creates and starts the container class used by
this instance of session.
sseett__ttookkeennnnaammee(())
You shall not call this function directly. It is called back by
the start() function of Session() during initializiation. It is
documented so that you can override its implementation in your
subclass of Session if you know what you are doing.
This function determines and sets the internal session name.
rreelleeaassee__ttookkeenn(())
You shall not call this function directly. It is called back by
the start() function of Session() during initializiation. It is
documented so that you can override its implementation in your
subclass of Session if you know what you are doing.
This function determines the current method of session
propagation and determines if a new session token has to be
generated.
ppuutt__hheeaaddeerrss(())
You shall not call this function directly. It is called back by
the start() function of Session() during initializiation. It is
documented so that you can override its implementation in your
subclass of Session if you know what you are doing.
This function determines which header lines are to be generated
by the session, including cache control headers.
33..88..22..22.. IInntteerrnnaall iinnssttaannccee mmeetthhooddss
ggeett__iidd(())
See get_id().
ggeett__iidd(($$iidd__ttoo__uussee))
get_id() is used internally to determine a session identifier.
Currently, a session identifier is a hex number of 32 characters
(128 bits) and it is generated by md5(uniqid($this->magic)) to
make it hard to guess.
get_id() may be called with an optional session id to use as a
parameter. This is useful if you want to change a session id
without breaking the session (taking over an old, left over
session).
get_id() can be overwritten by a subclass, if you want a
different system to create session ids. For example, some
applications want to use a constant session id that is not
propagated to the client to use a shared pool of persistent
variables (a guestbook for example). These applications need
locking (to be implemented soon).
ppuutt__iidd(())
put_id() is used internally to "unuse" a session it. At the
moment it deletes the client side cookie and deletes
$HTTP_COOKIE_VAR[$this->name] for that cookie. The variable
${$this->name} is _n_o_t deleted.
sseerriiaalliizzee(($$pprreeffiixx,, &&$$ssttrr))
serialize() is used internally to append to str all PHP code
needed to reconstruct the variable named in prefix.
ffrreeeezzee(())
freeze() serializes all register()ed variables and writes the
resulting code into the database, tagged with the current
session id and the current session name.
tthhaaww(())
thaw() loads a set of freeze()ed variables for the current
session id and session name out of the database and recreates
them.
ggcc(())
The active_sessions table contains one row for each session.
That row is uniquely identified by the sid and name values (name
is the name of the session class that has written the row). Each
time that row is written, the column changed is updated with the
current time.
The gc() function deletes all rows that are older than gc_time
minutes and have a matching name field. For speed reasons, gc()
is not not called every time an update to active_sessions is
being made. Instead it is called randomly with a probability of
gc_probability.
rreeiimmppoorrtt__aannyy__vvaarrss(($$aarrrraayynnaammee))
Used to implement the three official reimport functions.
ssttaarrtt(())
Initialization function, to be called after object
instantiation. Calls get_id() to get the current session id,
creates a database connection, then calls thaw() to load all
session variables. Randomly activates gc(). Checks allowcache to
send proper headers to control browser caching.
33..88..33.. EExxaammppllee
Use a subclass to provide the appropriate parameters to your session.
Usually your subclass looks like this:
______________________________________________________________________
class My_Session extends Session {
var $classname = "My_Session"; ## Persistence support
var $mode = "cookie";
var $lifetime = 0; ## use session cookies
## which container to use
var $that_class = "Session_sql";
}
______________________________________________________________________
Remember that you have to provide a DB_Sql subclass with the
parameters needed to access your database.
Use the page management functions (see above) to use your session
subclass. The feature name for session management is sess; provide the
name of your session subclass as a parameter to the sess feature:
______________________________________________________________________
page_open(array("sess" => "My_Session"));
______________________________________________________________________
Use the register() instance method to register variables as
persistent. If $sess is your session object, use
______________________________________________________________________
$sess->register("s");
______________________________________________________________________
to make the global variable $s persistent. $s may be a scalar value,
an array or an object with persistence support slots.
Do not use the instance methods freeze() and thaw() directly, but use
the page management functions instead.
To have some pages cached and others not cached, use multiple
instances of the session object. For example, for those pages that
should be cached, use a session object instance like
______________________________________________________________________
class My_Cached_Session extends My_Session {
## pages that use this session instance are cached.
var $allowcache = "private";
}
______________________________________________________________________
Be careful when using the public cache option. Publically cached pages
may be accessible to unauthenticated users. The private cache option
prevents unauthenticated access, but is only functional in HTTP/1.1
browsers.
33..88..44.. UUssiinngg ""aauuttoo__iinniitt""
You may define $sess->auto_init to the name of an include file in your
extension of session. Per convention, the name setup.inc is being
used.
______________________________________________________________________
class My_Session extends Session {
var $classname = "My_Session";
var $magic = "Calvin+Hobbes";
var $mode = "cookie";
var $gc_probability = 5;
var $auto_init = "setup.inc"; // name of auto_init file.
}
______________________________________________________________________
Whenever a new session is established, that is, a user without a
session id connects to your application, the auto_init file is
included and executed exactly once. The file is executed from within
the context of the page_open() function, that is, _n_o_t within a global
context. To define or access global variables from the auto_init file,
you have to global them.
When auto_init is being executed, all features of your page already
exist and are available globally. That is, you can safely rely on the
existence of the $sess, $auth, $perm and $user variables, if your
application specifies them. _N_o_t_e that you cannot in general know
which particular page triggered the execution of auto_init, though. If
you have some pages that request authentication and others that don't,
you cannot rely on the presence of the $auth object in general, but
have to test for it with is_object($auth) before accessing it.
The auto_init file is the appropriate place to initialize and register
all your session variables. A sample setup.inc may look like this:
______________________________________________________________________
register("lang");
global $cur; // application currency
$cur = "EUR"; // Euro by default
$sess->register("cur");
global $cart;
$cart = new Shop_Cart; // Create a shopping cart object as defined in local.inc
$sess->register("cart"); // register it.
?>
______________________________________________________________________
_N_o_t_e_: If you don't use a fallback_mode and you get users that turn off
cookies, these users will force a new session each time they hit any
page of your application. Of course this will force inclusion and
execution of setup.inc for each page they visit, too. Nothing can be
done about this.
33..88..55.. UUnnrreeggiisstteerriinngg vvaarriiaabblleess aanndd ddeelleettiinngg sseessssiioonnss
To get rid of a persistent variable, call $sess->unregister() with the
name of that variable. The value of the formerly registered variable
is still available after the call to unregister, but the variable is
no longer persistent and will be lost at the end of the current page.
To get rid of all session related data including the session record in
the database, the current session id and the session cookie in the
users browser, call $sess->delete(). In shopping applications this is
commonly done when the user commits his order to get rid of the
current shopping cart and everything else. You may want to remember
selected information about that user, though, as shown below.
______________________________________________________________________
"Shop_Session"));
// send order as mail
mail_order($shopowner, $user, $cart);
// delete the current session
$sess->delete();
// now get a new session id, but retain the users
// address and name:
page_open(array("sess" => "Shop_Session")); // will force auto_init again!
$sess->register("user"); // could be done in auto_init as well
?>
______________________________________________________________________
33..88..66.. RReeaaddiinngg aanndd uunnddeerrssttaannddiinngg sseessssiioonn ddaattaa ffoorr ddeebbuuggggiinngg
When debugging PHPLIB applications, it is often useful to be able to
read and understand the contents of the active_sessions table. Each
session is represented by a single line in this table. The primary key
to this table is the pair name and sid. name is the content of
$this->name and is usually the classname of your session class. sid is
the content of $this->id and is usually the MD5 hash of a uniqid and
some magic string.
By choosing a pair, it is possible for PHPLIB to have more than one
session type (for example, session and user data, see the User class
below) per application and store all this data in a single table. If
you are debugging a session class, for example Example_Session, only
records where name = "Example_Session" are of interest to you.
Determine the current session id of your Example_Session by printing
$sess->id and select the record with that name and sid from the
database.
The changed field indicates when this record has been updated the last
time. It is a 14 character (Y2K compliant) string of the format
YYYYMMDDhhmmss. Ordering by changed desc will show you the most
current session records first (the MySQL "limit" clause may come in
handy here).
The val column of a session record contains a PHP program that can be
safely fed to stripslashes() first and eval() after that. The PHP
program consists entirely of assignments and contains all instructions
necessary to recreate the persistent variables. The structure and
order of instructions within this program is always the same.
First item is always an assignment to $this->in. If set to 1,
auto_init has been executed by this session. If _n_o_t set to 1,
auto_init has not been executed, yet. This may be because no
auto_init file is defined for that session.
After that comes code like this: $this->pt = array(); followed by a
bunch of assignments like $this->pt["somestring"] = 1;. Each
somestring is the name of a registered variable. Variable
registrations are persistent themselves and are saved with the
$this->pt array. Even if the variable in question is not set, it may
be registered and stays so until it is unregistered or the session is
deleted. Check the contents of the pt array is you want to see which
variables are currently registered with your session.
Finally, the actual contents of your variables are saved. This is
always done by accessing the $GLOBALS array and always by enumerating
the scalar values that make up the persistent variable. For a scalar,
you will see code like $GLOBALS[somevar] = "value";.
For an array, first $GLOBALS[someary] = array(); is generated. Then
the scalars that make up the array, if any, are written out,
generating code that looks like $GLOBALS[someary][index] = "value".
And for objects, code to create an object instance is saved:
$GLOBALS[someobj] = new Classname;. "Classname" is taken from the
objects $classname slot, which _m_u_s_t be present and accurate. Then the
scalars that are to be saved are written out, according to the
contents of the objects persistent_slots array:
$GLOBALS[someobj]->slot = "value"; is written.
If you want to see what values have been saved to the database, you
just have to look at the $GLOBALS assignments for that session.
33..88..77.. HHooww ""sseerriiaalliizzee(())"" ooppeerraatteess
The following information is applicable only to library developers,
that is, programmers that want to change the internal workings of
PHPLIB. You may safely skip this section; some information here
requires advanced understanding of the PHP language.
The heart of the session class is the serialize() internal function.
This function takes an expression called prefix and generates PHP code
that will assign the value of that expression to the expression when
executed. For example, if the expression is $GLOBALS["a"] and the
global variable $a has the value 17, then serialize will create the
PHP program $GLOBALS["a"] = "17";. To save memory, serialize()
operates on a reference parameter $str, where is will append the code
generated.
First thing serialize() does is to determine the type of the current
expression using the PHP gettype() function. The current type is
stored in $t. The type of the expression may indicate either a scalar
value (integer number, float number or string), an array or an object.
Scalar values are the easiest to handle: serialize() just evaluates
the current expression and remembers the result value in $l. An
assignment is generated that will assign the current value to the
current expression. Since the current value may be a string and that
string may contain bad characters (any of backslash, double quotes or
dollar sign), these characters are backslashed. We are done,
serialize() ends here for scalars.
In the case of $t indicating an array, code is generated to create an
empty array (expression = array();). Then the keys of current
expression are enumerated and for each key serialize() is called
recursively with the current key appended to the expression. That will
append code for each array slot.
Should $t indicate an object, code is generated to create that object
(expression = new Classname;). Since one cannot find out the name of
the class of an object for arbitrary objects in PHP, objects handled
by serialize() must have a slot named classname. The object handler
will then enumerate the contents of the objects slot persistent_slots
and call serialize() recursively for each of these slots with the
appropriate prefix.
Since many of the expressions used in serialize() require variable
variable names or even variable code, eval() is used liberally.
Unfortunately, this makes the code hard to read.
33..99.. AAuutthh
Authentication management can be used to authenticate a session, that
is, to identify the user at the client side of the session.
Authentication is done inline, with HTML forms, _n_o_t with HTTP
authentication (that's the browser popup you get when you hit a page
protected with htaccess). Inline authentication has several advantages
over HTTP authentication:
· It can be undone: A session can be un-authenticated, the user can
"log out".
· It can expire: A session can automatically be un-authenticated
after a given idle time.
· It can be customized: You are not limited to user/password pairs.
Instead you could use a customer number, operator id and a password
to log in. Also, you have full control over the login screen, which
is a normal HTML page with logos, help and forms as you see fit.
· It is database based. Authentication is being done against a
database of your design, not a htpasswd text file.
· It is per page. You decide on a per-page basis which pages are
authenticated and which aren't.
· It can be user authenticating and optionally self registering. In
_r_e_g_i_s_t_r_a_t_i_o_n mode, a user without a valid login is encouraged to
register and an account is created for this user.
· It works with CGI PHP. HTTP authentication is available only in
mod_php.
· It is integrated with a permission checking scheme.
33..99..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
Internal instance variables.
33..99..22.. IInnssttaannccee mmeetthhooddss
33..99..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
uurrll(())
A function that can be used in auth_loginform()a and
auth_registerform. It returns the appropriate "action="
attribute to the form tag.
ppuurrll(())
A function that can be used in auth_loginform()a and
auth_registerform. It prints the appropriate "action=" attribute
to the form tag.
llooggiinn__iiff(($$tt))
A function that can be used to change the current user identity.
See the section and example on using default authentication
below.
uunnaauutthh(($$nnoobbooddyy == ffaallssee))
This function destroys the authentication information in
$this->auth, forcing the user to relogin the next time a
protected page is being loaded.
$this->auth["uname"] is being kept, so that the correct username
is available as a default.
Since V6: To give the user the credentials of `nobody', pass
true as the first parameter to unauth. This will also change
$this->auth["uname"].
Since V7.2: Passing $nobody to this method is deprecated.
llooggoouutt(($$nnoobbooddyy == $$tthhiiss-->>nnoobbooddyy))
This function destroy all authentication information in
$this->auth, forcing the user to relogin the next time a
protected page is being loaded.
Most applications want to use $this->unauth() instead.
Since V6: To give the user the credentials of `nobody', pass
true as the first parameter to logout. This defaults to the
value you set in the class definition ($nobody). logout() will
call unauth() (passing $nobody), so the behaviour is identical
(except logout() will always clear $this->auth["uname"] and
unregister the auth class).
Since V7.2: Passing $nobody to this method is deprecated.
iiss__aauutthheennttiiccaatteedd(())
Will return false, if the current authentication is invalid or
expired. Will return the authenticated uid otherwise.
aauutthh__pprreeaauutthh(())
This function can be overridden in a subclass to Auth. It is
being called as the very first step in the authentication
process and has the opportunity to authenticate the user without
a loginform being displayed (by deriving all necessary
information telepathically, or by using cookies, or divining the
user identities from the incestines of a dead squirrel).
If it returns a UID value, the user is authenticated and neither
auth_loginform() nor auth_validatelogin() are called. If it
returns false, all goes on as usual.
aauutthh__llooggiinnffoorrmm(())
This function must be overridden by a subclass to Auth. It
should output HTML that creates a login screen for the user. We
recommend that you use an include() statement to include your
HTML file.
aauutthh__vvaalliiddaatteellooggiinn(())
This function is called when the user submits the login form
created by auth_loginform(). It must validate the user input.
If the user authenticated successfully, it must set up several
fields within the $auth[] instance variable:
must contain the user id associated with that user.
must contain the user name as entered by the user.
must not be tampered with (field is maintained by start(),
contains the time when the login expires).
if you want to use the permission feature, you must store the
permissions of the validated user here. (Hint: due to a name
conflict with sybase, "perm" is called "perms" in all the
databases tables. Look for this small difference!)
See the example below for more information.
aauutthh__rreeffrreesshhllooggiinn(())
This function is called every refresh minutes. It must refresh
the authentication informations stored in auth array by
auth_validatelogin() method. It is not called if the user is
logged in as nobody.
It must return true on success, false otherwise (i.e.: the
userid is no longer valid).
aauutthh__rreeggiisstteerrffoorrmm(())
See auth_doregister().
aauutthh__ddoorreeggiisstteerr(())
These functions mirror auth_loginform() and auth_validatelogin()
in registration mode.
33..99..22..22.. IInntteerrnnaall iinnssttaannccee mmeetthhooddss
ssttaarrtt(())
Initialization function, does the authentication. If we are in
log (login) mode, auth_loginform() is called to draw a login
screen. When the login screen is submitted back,
auth_validatelogin() is called to validate the login. If the
validation was successful, the actual page content is shown,
otherwise we're back at auth_loginform().
In reg mode, auth_registerform() is called to draw a
registration form. When the registration form is submitted back,
auth_doregister() is called to register the user and to validate
the session. If registration was successful, the actual page
content is shown, otherwise we're back at auth_registerform().
33..99..33.. EExxaammppllee
Use a subclass of Auth to provide parameters for your authentication
class and to implement your own auth_* functions.
______________________________________________________________________
class My_Auth extends Auth {
var $classname = "My_Auth"; # Object serialization support
var $lifetime = 15;
## DB_Sql subclass and database table to use
var $database_class = "DB_Session";
var $database_table = "auth_user";
## Some magic value to make our uids harder to guess.
var $magic = "Abracadabra";
## Use an own login form
function auth_loginform() {
global $sess;
include("loginform.ihtml");
}
function auth_validatelogin() {
global $username, $password; ## form variables from loginform.ihtml
## If authentication fails, loginform.html will
## find $this->auth["uname"] set and use it.
$this->auth["uname"]=$username;
## Value to return in case auth fails.
$uid = false;
## Check the database for this user and password pair.
$query = sprintf(
"select * from %s where username = '%s' and password = '%s'",
$this->database_table,
addslashes($username),
addslashes($password)
);
$this->db->query($query);
## If we found a matching user, grab the uid and permissions...
while($this->db->next_record()) {
## Required.
$uid = $this->db->f("uid");
## Optional, for the perm feature.
$this->auth["perm"] = $this->db->f("perms");
## if you use perm feature be aware, that the db-field in our
## example table is called "perms" due to a name conflict with sybase
}
return $uid;
}
}
______________________________________________________________________
Your loginform.ihtml contains HTML and PHP code to draw a login form.
$this->auth["uname"] will be empty on the first login attempt and set
on all further login attempts. You can use this to detect repeated
login attempts and display an appropriate error message. You must
print the result of $this->url() to create your forms action
attribute.
See the provided loginform.ihtml for an example.
Use the page management functions (see above) to use your
authentication subclass. The feature name for authentication
management is auth; provide the name of your Auth subclass as a
parameter to the auth feature. The auth feature requires the sess
feature:
______________________________________________________________________
page_open(array("sess" => "My_Session", "auth" => "My_Auth"));
______________________________________________________________________
33..99..44.. UUssiinngg ddeeffaauulltt aauutthheennttiiccaattiioonn
Many applications want to use $auth and $perm objects to protect
functionality on a page, but do want to make the unprotected part of
this page available to users with no account. This presents a kind of
dilemma, because you need $auth and $perm objects to protect
functionality on a page, but you don't want a login screen to appear
by default.
Default authentication solves this dilemma by providing a special uid
and uname "nobody", which is guaranteed to fail every permission
check. If you set the nobody flag, $auth will not create a login
screen to force a user to authenticate, but will authenticate the user
silently as nobody. The application must offer a login button or other
facility for users with accounts to change from that id to their real
user id.
To use default authentication, create a subclass of My_Auth as shown
above with the nobody flag set (_N_o_t_e_: No need to extend in two steps.
The only important thing here is that the nobody flag is set.)
______________________________________________________________________
class My_Default_Auth extends My_Auth {
var $classname = "My_Default_Auth";
var $nobody = true;
}
______________________________________________________________________
To create a page that uses default authentication, use the page
management functions. Check for relogin requests with the login_if()
function. Create a relogin link on your page.
______________________________________________________________________
"My_Session", "auth" => "My_Default_Auth"));
$auth->login_if($again);
if ($auth->auth["uid"] == "nobody"):
?>
">Relogin
to this page.
______________________________________________________________________
33..99..55.. UUssiinngg CChhaalllleennggee--RReessppoonnssee AAuutthheennttiiccaattiioonn
As distributed, local.inc contains an example class named
Example_Challenge_Auth, which uses a Challenge-Response authentication
scheme. If the client browser supports Javascript, this login screen
does not transmit passwords in clear over the network. If the client
does not support Javascript, login is still possible, but passwords
are transmitted in clear, as regular Example_Auth always does.
Example_Challenge_Auth is there to demonstrate advanced usage of PHP
and Javascript and to show off the flexibility of the library base
classes: The Challenge-Response authentication scheme has been
implemented completely and naturally in local.inc by subclassing Auth
with no alteration of library code.
Example_Challenge_Auth includes crloginform.ihtml. It also requires
that the file md5.js is present in the document root directory of your
web server. That file contains an implementation of the MD5 message
digest algorithm done by Henri Torgemane. The basic idea behind this
authentication scheme is simple: $auth->auth_loginform() creates a
challenge value which is incorporated into this form. When the user
tries to submit the form, MD5("username:password:challenge") is
calculated and filled into the reply field. The password field is
erased. The server can calculate the expected reply from the username
received, the password in the database and the challenge, which it
knows. It can compare the expected reply to the actual reply value. If
they match, the user is authenticated.
If the reply field is empty and password is set, the server knows that
the client cannot do Javascript. The user can still be authenticated,
but the password is visible on the network.
The class is a dropin-replacement for Example_Auth.
33..99..66.. TThhee ccoommpplleettee gguuiiddee ttoo aauutthheennttiiccaattiioonn aanndd uusseerr vvaarriiaabblleess
This feature has originally been written for the PHPLIB mailing list
by Kristian Köhntopp and was included into the documentation later.
33..99..66..11.. HHooww iiss tthhee AAuutthh ccllaassss uusseedd uussuuaallllyy??
Usually, you write code like this into the top of the page you want to
protect:
______________________________________________________________________
"My_Session",
"auth" => "My_Auth"));
?>
______________________________________________________________________
33..99..66..22.. HHooww ddooeess $$aauutthh wwoorrkk iinntteerrnnaallllyy??
When you access this page, the call to page_open() is being made as
the first thing on that page. page_open() creates an instance of
My_Auth named $auth and starts it. $auth then detects that you are
not authenticated (how it does, I will explain below) and displays
loginform.ihtml. $auth then exits the interpreter, so that is never being executed or displayed.
The user now sits in front of a loginform.ihtml screen, which is shown
under the URL of the page the user originally tried to access. The
loginform has an action URL, which just points back to itself.
When the user filled out the loginform and submits it, the very same
URL is requested and the above page_open() is reexecuted, but this
time a username and a password are submitted. When the $auth object is
created and started, it detects these parameters and validates them,
resulting in either a NULL value or a valid user id. If the validation
failed, creating an empty user id, the loginform is displayed again
and the interpreter exits. Again is not
executed.
If a UID is returned, that UID and a timestamp are being made
persistent in that session and $auth returns control to page_open().
When page_open() finishes, which it may or may not do, depending on
the presence and result of an optional $perm check, is being executed or shown.
Later calls to other pages or the same page check for the presence of
the UID and the timestamp in the sessions data. If the UID is present
and the timestamp is still valid, the UID is retained and the
timestamp is refreshed. On page_close() both are written back to the
user database (Note: Authenticated pages REQUIRE that you page_close()
them, even when you access them read-only or the timestamp will not be
refreshed).
If the UID is not present ($auth->logout() or $auth->unauth() have
been called, for example) or the timestamp has expired, $auth will
again intercept page display and draw the loginform.
The only way to get into a page with an $auth object on it is to have
a UID and a valid timestamp in your session data (Note: This is true
even for default authentication. These create a dummy UID and
timestamp in your session data).
33..99..66..33.. HHooww ddoo $$sseessss aanndd $$aauutthh iinntteerraacctt??
Your browser has a session cookie, named after your session class.
This is the only thing that is ever shipped between your browser and
PHPLIB, as far as core functionality is concerned. The session cookie
value is used as a reference into active_sessions, to retrieve PHPLIB
generated PHP code, which is then eval()ed and recreates your session
variables within page_open().
Part of the $auth object now makes itself persistent and is retrieved
when the $sess part of page_open() is being executed. This is just
before the $auth part of page_open() gets its turn, so that $auth can
rely on its persistent data being present when it is being called.
From the PHPLIB source you all know that $auth has only one persistent
slot, called $auth->auth[], of type hash. This hash contains the slots
uid, exp and uname. $auth->auth["uid"] is the currently authenticated
user id, $auth->auth["exp"] is the currently active expiration
timestamp (Unix time_t format) for that uid. $auth->auth["uname"] is
completely irrelevant as far as the regular PHPLIB Auth class is
concerned. It is relevant in the context of the supplied default Auth
subclass Example_Auth, though.
So a session is authenticated, if it contains $auth->auth["uid"] !=
false and time() < $auth->auth["exp"].
33..99..66..44.. WWhheerree iiss tthhee bbeeeeff??
The original Auth class as included in PHPLIB makes no assumptions at
all on how a loginform looks or how and where uids come from. There is
no code at all in Auth that ever checks anything but the above two
conditions. It is your responsibility to modifiy a subclass of Auth in
a way that these conditions can ever be met.
Auth helps you in doing this by calling its own function
$auth->auth_loginform() when it wants to draw a loginform.
Unfortunately this function is empty in Auth itself, so you have to
provide an implementation for that. The suggested standard
implementation in local.incs Auth subclass Example_Auth is
______________________________________________________________________
function auth_loginform() {
include("loginform.ihtml");
}
______________________________________________________________________
and you put your code into that file. We also provide sample code for
that file, but you are not limited to that code and may write a
loginform.ihtml as it meets your needs.
When the loginform has been filled in and submitted back by the user,
Auth calls $auth->auth_validatelogin(). Again, this function is empty
in Auth itself and so Auth by itself will never function correctly.
You have to subclass Auth and provide your own implementation of
$auth->auth_validatelogin() in local.inc to make it work.
What you actually do in that function is completely irrelevant to Auth
itself. It only exspects that you either return false, if the user-
supplied authentication data was invalid, or a user id, if the user
could be validated. Auth then takes care to create the appropriate
entries ($auth->auth["uid"] and $auth->auth["exp"]) in the session
record.
33..99..66..55.. II ssttiillll ddoo nnoott uunnddeerrssttaanndd!! WWhhaatt aamm II ssuuppppoosseedd ttoo ccooddee??
You write your code into local.inc, after you have removed the classes
Example_Auth, Example_Default_Auth and Example_Challenge_Auth from
that file (keep a copy around, just for reference).
You code a class called My_Auth and you use that name later in your
calls to page_open as an argument to the auth feature, as show at the
start of this message. Follow the standard rules for deriving
persistent classes in PHPLIB when you create your code, that is, do it
like this:
______________________________________________________________________
class My_Auth extends Auth {
var $classname = "My_Auth";
// we inherit $persistent_slots and do not need to modify it.
// later code is inserted here
}
______________________________________________________________________
Now configure the lifetime of the authentication, that is, how many
minutes in the future shall the current value of $auth->auth["exp"]
be? Also, name a database connector class and name the table that you
will be using to check usernames and passwords.
______________________________________________________________________
// insert this code as indicated above.
var $lifetime = 15;
var $database_class = "DB_Example";
var $database_table = "my_special_user_table";
// later code is inserted here
______________________________________________________________________
Okay, now we have a basic implementation of My_Auth that is only
lacking the required functions auth_loginform() and
auth_validatelogin(). Our implementation of auth_loginform() will have
access to all $sess variables by globaling $sess into our context
(because these can come in handy) and to all $auth variables (via
$this).
______________________________________________________________________
function auth_loginform() {
global $sess;
include("loginform.ihtml");
}
______________________________________________________________________
The loginform is free to do whatever it damn well pleases to create a
form for the user to supply the needed values for authentication. It
has access to anything $sess and anything $this related.
The loginform will display some input fields for the user, for example
a given name, a surname and a password. When the form is submitted
back, auth_validatelogin() is being called. The form values are global
variables (or $HTTP_x_VARS[]) and must be imported into
$auth->auth_validatelogin(). Then, $auth->auth_validatelogin() is free
to do whatever it must do to produce a unique identifier for that user
(or return false).
Suppose you created input fields named given_name, surname and
password. So go ahead, global $given_name, $surname and $password and
set $uid to false. Then create the SQL needed to access you user table
and retrieve the user record from your database as indicated by
$given_name and $surname and $password.
The query may succeed, if a record with matching $given_name, $surname
and $password is present. In that case return the uid, which uniquely
identifies exactly that (given_name, surname) pair. Else return false.
In code:
______________________________________________________________________
function auth_validatelogin() {
// import authentication data
global $given_name, $surname, $password;
$uid = false;
$query = sprintf("select uid
from %s
where given_name = '%s'
and surname = '%s'
and password = '%s'",
$this->database_table,
$given_name, $surname, $password);
// we really should use addslashes() here,
// or have magic_quotes active.
// $auth->db is our DB_Example database connection
$this->db->query($query);
// now check for any results
while($this->db->next_record()) {
$uid = $this->db->f("uid");
}
// either $uid is false now (no results)
// or set to the last retrieved value from the uid
// column.
// Anyway we are set now and can return control
return $uid;
}
______________________________________________________________________
Okay, that's all and useable now. There is room for some improvements,
though: First we did not retrieve permission data, so this will not
work, if we want to use the perm feature as well.
This is easily changed: Modify the query to select uid, perms instead
of select uid alone. Of course, you may call your perm column whatever
you like, just adapt the SQL accordingly. Also, add a line after the
$uid assignment so that the code looks like this:
______________________________________________________________________
$uid = $this->db->f("uid");
$this->auth["perm"] = $this->db->f("perms");
______________________________________________________________________
This will store the retrived perms value under the key perm within the
$auth->auth[] array. It will be kept around in that place in case
$perm is called and starts looking for the current permissions of that
user.
Another possible improvement becomes apparent when you try to login
and fail to do so correctly: auth_validatelogin() returns false and
you hit the loginform again. Empty loginform that is, because we did
not remember what you typed into the given_name and surname fields
before. If we remembered what you typed, we could easily supply these
values back to you so that you can correct them. We would also be able
to detect if this is a second, third, ... attempt to login and display
an appropriate error message somewhere in that loginform to inform the
user of his or her typo. A convenient place to store these values is
the $auth->auth array, which is persistent anyway.
Standard Example_Auth uses the field $auth->auth["uname"] to store
that value, but you may use any field and as many fields as you like
as long as you make sure not to clash with any of the three officially
used fields, uid, exp, and perm.
Do not try to turn the global variables $given_name and $surname into
persistent variables by calling $sess->register("given_name") and
$sess->register("surname")! Remember: These are form variables! Never
ever make form variables persistent and never ever trust unvalidated
user supplied from the Internet!
So add the folling code just below the "global" line:
______________________________________________________________________
$this->auth["gname"] = $given_name;
$this->auth["sname"] = $surname;
______________________________________________________________________
and check for these two variables in loginform.ihtml at the
appropriate places.
33..99..66..66.. OOkk,, II ddiidd tthhaatt aanndd iitt wwoorrkkss.. II eevveenn uunnddeerrssttoooodd iitt.. NNooww,, wwhhaatt
eexxaaccttllyy iiss tthhaatt uuiidd uusseedd ffoorr??
It is simply a token to indicate that the user is authenticated. We
use a different token for each user, so that we can decide which user
we are currently dealing with. You can think of the uid as a primary
key for your auth_user table (or whatever it is being called in your
current application). The ( given_name, surname ) tuple would also be
a possible primary key, albeit a compound one. It is the external,
human-readable (and probably sometimes very long) representation of
the internal uid. The password field is functionally dependent on
either of both key candidates.
The internal user id should never be presented to the user; the (
given_name, surname ) pair is much more natural to handle for the user
and easier to remember (A user who does not remember his or her name
would probably not be in a state of mind to operate the rest of the
application anyway :-).
The internal user id should always be used to identify a user
internally within an application, though. That is, because the uid is
of a fixed length and has a known form and structure, so you can make
assumptions. A given_name or surname may be of any length and may
contain about any character, so you probably do not want to use this
as a user-reference internally.
33..99..66..77.. BBuutt iiss tthhee uuiidd uusseedd iinntteerrnnaallllyy bbyy PPHHPPLLIIBB??
Yes, if you make use of the user feature of page_open(), that is, if
you create user variables.
The User class is actually a subclass of Session. That is, user
variables are just like session variables. They are even stored in
active_sessions. The only difference is that the session has a
different name (it is called Example_User instead of Example_Session,
if you use the classes and names supplied in local.inc).
And in Example_User, the user id of the authenticated user becomes the
session id in the active_sessions table. That is the reason why we
recommend md5(uniqid("abracadabra")) style uids.
33..1100.. PPeerrmm
Permission management relies on an authenticated session. It
associates a set of required permissions with a page. The actual page
content is only visible to users with ALL matching permissions; all
other users are shown a screen of your design.
33..1100..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
33..1100..22.. IInnssttaannccee mmeetthhooddss
33..1100..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
cchheecckk(($$rreeqquuiirreedd))
Checks that the currently authenticated user has all the rights
that are specified in required. If not, perm_invalid() is
called.
If one or more of the required rights or user rights are invalid
(not to be found in the permissions hash), perm_invalid() is
called as well.
hhaavvee__ppeerrmm(($$rreeqquuiirreedd))
Similar to check() in usage, only that it doesn't halt the
session if the user doesn't have the appropriate rights: This
function returns true, if the user has the required rights,
false otherwise.
ppeerrmm__sseell(($$nnaammee,, $$ccuurrrreenntt ==
This function returns a SELECT-tag with the given name. Within
this tag, all available permission values from
$perm->permissions are contained as OPTION tags.
If you supply a value for current, the permission value that
matches current is SELECTED. If you supply a value for class,
the tags are marked with that CSS stylesheet class.
33..1100..22..22.. IInntteerrnnaall iinnssttaannccee mmeetthhooddss
ppeerrmmssuumm(($$rriigghhttss))
Logically or's all the rights and returns a pair (valid,
or_result). If valid is true, an or_result is provided. If valid
is false, the or_result is undefined and one or more of the
rights do not exist at all. This is a severe error and the
application should be halted at once.
ppeerrmm__iinnvvaalliidd(($$ddooeess__hhaavvee,, $$mmuusstt__hhaavvee))
Called in case of an access violation. does_have is a string
listing the rights the user actually has. must_have are the
rights the page requires.
33..1100..33.. EExxaammppllee
Use a subclass of Perm to provide parameters for your permission class
and to implement your own perm_invalid function.
______________________________________________________________________
class My_Perm extends Perm {
var $classname = "My_Perm";
var $permissions = array (
"user" => 1,
"author" => 2,
"editor" => 4,
"moderator" => 8,
"admin" => 16
);
function perm_invalid($does_have, $must_have) {
global $perm, $auth, $sess;
include("perminvalid.ihtml");
}
}
______________________________________________________________________
Use the page management functions (see above) to use your permission
subclass. The feature name for permission management is perm; provide
the name of your Perm subclass as a parameter to the perm feature. The
perm feature requires the sess feature and the auth feature:
______________________________________________________________________
page_open(array("sess" => "My_Session", "auth" => "My_Auth", "perm" => "My_Perm"));
______________________________________________________________________
Use the check() instance method to protect your page:
______________________________________________________________________
$perm->check("admin"); ## This page is for users with admin rights only.
______________________________________________________________________
Use have_perm() to create protected functionality on a page:
______________________________________________________________________
have_perm("admin")):
?>
Admin only functionality
______________________________________________________________________
33..1100..44.. HHooww ppeerrmmiissssiioonnss wwoorrkk
Your subclass of Perm defines an array $permissions, which translates
permission names into bit patterns. For example, the definition of
Example_Perm in the distributed local.inc defines the names user,
author, editor, supervisor and admin, all of which translate into a
bit pattern with a single bit set.
A user may be assigned any number of permissions as a comma separated
list of permission names (no spaces!) in the perms column of the
auth_user table. The effective permissions of the user are determined
by logically OR'ing the bit patterns of these permissions.
A page may require any permissions as a comma separated list of
permission names (again no spaces!) with the $perm->check() function.
The required permissions are again determined by logically OR'ing the
bit patterns of these permissions. Similarly, a page function may be
protected by requiring permissions with $perm->check().
Access is granted to a protected page or a protected page function, if
the effective permissions of the authenticated user have all the
required bits set, that is: If the effective permissions of the user
logically AND'ed with the required permissions are equal to the
required permissions.
With the permission names as defined in Example_Perm from the
distribution, a user kris may be defined with admin permission in the
auth_user table. A page that requires admin,user permission with
$perm->check("user,admin") is inaccessible to this user. This is how
it is calculated:
______________________________________________________________________
Effective Permissions of User: admin
translates into: 16
Required Permissions of Page : user,admin
translates into: 1 OR 16 == 17
Permission Check:
Effective Permissions 16
AND Required Permissions 17
ARE 16 & 17 = 16
MUST BE Required Permissions 17 -> access denied
______________________________________________________________________
The example permissions as defined in Example_Perm from the
distribution are called _a_t_o_m_i_c permissions, because each of them has
only a single bit set. Atomic permissions are the simplest of all
schemes, because they allow for easy permission checks: To access a
page protected with user,admin, you need to have at least user,admin
rights in your auth_user table.
Another common scheme used in permission definitions are inclusive
permissions. In this scheme, each permission definition has all bits
of its predecessor set plus one addition bit. For example
______________________________________________________________________
class Inclusive_Perm extends Perm {
var $classname = "Inclusive_Perm";
var $permissions = array(
"user" => 1,
"author" => 3,
"editor" => 7,
"supervisor" => 15,
"admin" => 31
);
}
______________________________________________________________________
defines a set of inclusive permissions. In this example, a user kris
with admin permissions can easily access a page protected with editor
permissions. This is how it is calculated:
______________________________________________________________________
Effective Permissions of User: admin
translates into: 31
Required Permissions of Page : editor
translates into: 7
Permission Check:
Effective Permissions 31
AND Required Permissions 7
ARE 31 & 7 = 7
MUST BE Required Permissions 7 -> access granted
______________________________________________________________________
Inclusive Permissions are easy to deal with, too, because a user with
a _h_i_g_h_e_r access level may access all pages or page functions with a
_l_o_w_e_r access level.
Due to limitations of your machines integer size you can only define
up to 31 permission levels.
33..1111.. UUsseerr
The user class is an extension (a subclass) of the Session class. It
keeps a list of global variable names and provides a set of functions
to load and save these variables from and to a database. The same
restrictions as for session variables apply to user variables.
Unlike session variables, user variables are not lost when the user
stops and restarts the browser or moves to a different workplace (the
session id is then lost and consequently all session variables are
lost, since they are bound to the session id).
User variables require that the user logs in, because they depend on
the availability of a User id to bind variables to this id. Thus, User
is dependent on Auth.
The User class is an extension of the Session class. It has all
instance variables and instance methods of Session, only that some are
implemented different. This documentation only describes these
differences.
Note that Session and User can successfully share a single
active_sessions table in a database due to the different values in the
name column.
33..1111..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
Internal instance variables.
33..1111..22.. IInnssttaannccee mmeetthhooddss
33..1111..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
rreeggiisstteerr(($$vvaarrnnaammee))
Works as expected.
uunnrreeggiisstteerr(($$vvaarrnnaammee))
Works as expected.
ddeelleettee(())
Works as expected.
uurrll(($$uurrll))
Not useful with User.
ppuurrll(($$uurrll))
Not useful with User.
sseellff__uurrll(())
Not useful with User.
ppsseellff__uurrll(())
Not useful with User.
rreeiimmppoorrtt__ggeett__vvaarrss(())
Works as expected.
rreeiimmppoorrtt__ppoosstt__vvaarrss(())
Works as expected.
rreeiimmppoorrtt__ccooookkiiee__vvaarrss(())
Works as expected.
33..1111..22..22.. IInntteerrnnaall iinnssttaannccee mmeetthhooddss
ggeett__iidd(())
This is only a stub implementation that depends on the user id
provided by the page management functions. The page management
functions will use $auth->auth["uid"], which is set up by Auth.
ppuutt__iidd(())
Empty. Not useful with User.
sseerriiaalliizzee(($$pprreeffiixx,, &&$$ssttrr))
Works as expected.
ffrreeeezzee(())
Works as expected.
tthhaaww(())
Works as expected.
ggcc(())
Works as expected. You do not want to use it, though.
rreeiimmppoorrtt__aannyy__vvaarrss(($$aarrrraayynnaammee))
Works as expected.
ssttaarrtt(())
Initialization function, to be called after object
instantiation. Calls get_id() to get the current session id,
creates a database connection, then calls thaw() to load all
session variables. _N_o_t_e_: gc() activation is commented out!
Remove the comments if you really want gc with User variables.
33..1111..33.. EExxaammppllee
Use a subclass to provide the appropriate parameters to your user
variables. Usually your subclass looks like this:
______________________________________________________________________
class My_User extends User {
var $classname = "My_User"; ## Persistence support
var $that_class = "CT_Sql";
}
______________________________________________________________________
Remember that you have to provide a DB_Sql subclass with the
parameters needed to access your database.
Use the page management functions (see above) to use your User
subclass. The feature name for user variables is user; provide the
name of your User subclass as a parameter to the user feature:
______________________________________________________________________
page_open(array("sess" => "My_Session", "auth" => "My_Auth", "user" => "My_User"));
______________________________________________________________________
Use the register() instance method to register variables as
persistent. If $user is your user object, use
______________________________________________________________________
$user->register("u");
______________________________________________________________________
to make the global variable $u persistent. $u may be a scalar value,
an array or an object with persistence support slots.
Do not use the instance methods freeze() and thaw() directly, but use
the page management functions instead.
_N_o_t_e_: Using default authentication and user variables is going to be a
problem, because currently User does not do any locking. This is,
because the DB_Sql has currently no portable locking mechanism.
44.. EExxtteennddeedd ffuunnccttiioonnaalliittyy
The section on extended functionality covers non-GUI classes that
provide often needed application functions without a user interface.
Some extended classes depend on core functionality, some contain
independent classes.
Extended classes are treated differently from core classes in that
their code is not automatically included by prepend.php3. You have to
include the class definition manually where needed or you modify
prepend.php3.
44..11.. CCaarrtt
The Cart class is programmatically independent, but makes sense only
if its instances are made persistent in some way. The Cart class
automatically registers itself as a session variable in its start()
function.
Cart implements a shopping cart. At the moment, items within the
shopping cart are independent of each other; the cart can only hold
simple things. Support for compound articles that require other
articles to function and provide a base for dependent articles is to
be added at a future time.
An example of a simple article is any article with no options, for
example an apple or a book. Common examples for compound articles are
a pizza (which requires a foundation in either American or Italian
style, a selection of toppings, and cheese, to function correctly) and
a computer system (which requires a housing, a motherboard, RAM, a
video card, etc to function correctly).
_N_o_t_e_: Cart was a core class up to _r_e_l_e_a_s_e_-_5. If your applications uses
the Cart class, you _m_u_s_t manually add the statement
include("cart.inc") to your prepend.php3 file where indicated in that
file.
_N_o_t_e_: The page management functions do no longer support the feature
cart to set up and start the cart class. It is recommended that you
use Session's auto_init feature instead to start your cart
automatically or that you manually set up your cart.
44..11..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
44..11..22.. IInnssttaannccee mmeetthhooddss
44..11..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
cchheecckk(($$aarrtt))
Checks that an item with the given article number $art is in the
cart. Returns an array of a boolean value and an integer number.
If the boolean is true, there are number many articles of that
article number in the cart.
rreesseett(())
Deletes all items in current cart, resetting $this->currentItem
to 1. Always returns true.
nnuumm__iitteemmss(())
Returns the number of articles in the current shopping cart, or
false if cart is empty. For compatibility reasons, this function
is available as tot_arts as well (but will print a warning if
used by this name).
aadddd__iitteemm(($$aarrtt,, $$nnuumm))
Add $num many articles of article number $art to the current
shopping cart. Returns the position number of $art in the
shopping cart.
rreemmoovvee__iitteemm
Remove $num many articles of article number $art from the
shopping cart, if there are at least that many articles in the
cart. Returns the position number of $art in the shopping cart
or false, if there weren't enough $art to remove them from the
cart. If the function does return false, the cart has not been
modified.
sseett__iitteemm
Set the quantity of article number $art in the shopping cart to
exactly $num. If $num is set to zero, article is removed from
cart. Returns the position number of $art in the shopping cart.
sshhooww__aallll(())
If the shopping cart is empty, it will call show_empty_cart()
once and then return.
Calls show_item_open() once at the beginning of a shopping cart
listing. Then calls show_item() once for each item in the
shopping cart. Calls show_item_close() once at the end of a
shopping cart listing.
sshhooww__iitteemm(($$aarrtt,, $$nnuumm))
This function should be provided by the user. It renders the
HTML to display a single item from the cart. $art is the article
number of the item and there are $num of these in the cart.
sshhooww__ccaarrtt__ooppeenn(())
This function should be provided by the user. It renders the
prologue HTML to display a shopping cart listing.
sshhooww__ccaarrtt__cclloossee(())
This function should be provided by the user. It renders the
epilogue HTML to display a shopping cart listing.
sshhooww__eemmppttyy__ccaarrtt
This function should be provided by the user. It should render
an appropriate message to symolize an empty cart.
44..11..33.. EExxaammppllee
Use a subclass of Cart to provide an implementation of show_item().
______________________________________________________________________
class My_Cart extends Cart {
var $classname = "My_Cart";
// Look up article numbers...
var $database_class = "DB_Article";
var $database_table = "articles";
var $db;
var $sum = 0;
function show_cart_open() {
printf("\n");
$this->sum = 0;
}
function show_cart_close() {
printf("
\n");
printf("That's a total of %s.\n", $this->sum);
}
function show_item($art, $num) {
if (!is_object($this->db)) {
$class = $this->database_class;
$this->db = new $class;
}
$query = sprintf("select * from %s where artid = '%s'",
$this->database_table,
$art);
$this->db->query($query);
while($this->db->next_record()) {
printf(" \n %s | \n",
$this->db->Record["name"]);
printf(" %s | \n",
$this->db->Record["price"]);
printf(" %s | \n",
$num);
$rowsum = $num * $this->db->Record["price"];
$this->sum += $rowsum;
printf(" %s | \n",
$rowsum);
printf("
\n");
}
}
}
______________________________________________________________________
To use a cart, create an instance of your Cart subclass and call
start(). This will automatically register cart.
It is recommended that you set in your Session subclass in local.inc
the slot $auto_init to the value setup.inc and create an include file
of that name which contains the following code:
______________________________________________________________________
global $cart; ## $cart is a global variable.
$cart = new My_Cart; ## Make a My_Cart instance named $cart
$cart->start(); ## and have it register itself.
______________________________________________________________________
Use add_item() and remove_item to work with your Cart:
______________________________________________________________________
$cart->add_item("101", 2); ## Add two pieces of "101"
$cart->remove_item("101", 1); ## Drop one piece of "101"
______________________________________________________________________
Use show_all() to display the contents of your cart.
______________________________________________________________________
$cart->show_all(); ## What's in a cart, anyway?
______________________________________________________________________
44..11..44.. OOnn uussiinngg CCaarrtt
To make use of the Cart class, you need to define a new table in your
database that lists all articles you shop should sell. With PHPLIB
and MySQL we recommend that you create a new instance of PHPLIB for
each virtual web server and a new database for each customer. This
database should hold the active_sessions and auth_user tables as well
as all application specific tables like for example the article list.
In other words, with MySQL we strongly discourage that you use PHPLIB
and the MySQL directive use _d_a_t_a_b_a_s_e___n_a_m_e together. There is no
support if you do (there is no support if you do as we say, too,
because PHPLIB is an open source product you are using on your own
risk, but ...).
So let us assume you define a very simple new table articles with a
structure like this:
______________________________________________________________________
#
# Table structure for table 'articles'
#
CREATE TABLE articles (
name text,
price float(8,2),
artid int(11) DEFAULT '0' NOT NULL auto_increment,
PRIMARY KEY (artid)
);
______________________________________________________________________
This table has an article number called artid, and for each artid
there is an article description name and a price. You may extend this
minimal definition for your purposes by adding article groups, BLOBs
with article images and more, but this will suffice for our example
purposes.
Populate this table with some products that suit your taste.
The next step is to teach PHPLIB about the cart class. Three steps are
necessary to do so:
· the Cart class has to be included on every page. Even on that pages
that do not make use of the Cart class.
On that pages that use Cart, a cart subclass is instantiated and
saved. On all subsequent pages, that Cart object is recreated and
to be able to recreate the Cart object, PHP must know what a Cart
object is. Since you cannot know which pages a user loads after he
has put the first item into the Cart, we need to have a definition
for Cart on _a_l_l pages.
The proper place to include the Cart definition from cart.inc is
consequently prepend.php3. Edit prepend.php3 and
require("cart.inc") as indicated by the comments in that file.
· a subclass of Cart has to be created to suit your tastes.
Your subclass of Cart will be called Example_Cart in this example.
You may actually name it as you like, but you have to be
consistent.
The definition of Example_Cart goes into local.inc anywhere below
your definition for Example_Session. It looks like this
______________________________________________________________________
class Example_Cart extends Cart {
var $classname = "Example_Cart";
}
______________________________________________________________________
and we will add additional code later in this example. That additional
code will teach your shopping cart about the database table that holds
your articles and so on.
· finally, you need to create an instance of your shopping cart class
so that you have an object that actually holds the articles
selected by the user.
We will use a very nifty feature of PHPLIB to create that object
instance: If you set up PHPLIB properly, it is able to load and
execute an include file every time a session is being created. We
call this feature auto_init, after the instance variable of Session
that controls it.
Go into local.inc and edit your subclass of Session. You will have
some code like
______________________________________________________________________
class Example_Session extends Session {
var $classname = "Example_Session";
...
}
______________________________________________________________________
in your local.inc. Add a line like
______________________________________________________________________
var $auto_init = "setup.inc",
______________________________________________________________________
to your definition of Example_Session and create a file setup.inc in
the same directory that holds your local.inc. Whatever code is in
this file will be executed every time we create a new session. The
code is being executed after your $sess, $auth and $perm objects are
loaded and initialized, but does run from within a function context.
You have to global everything you define to export it from that func
tion context.
In setup.inc, create a global instance of Example_Cart named $cart and
register that variable with PHPLIB:
______________________________________________________________________
register("cart");
?>
______________________________________________________________________
Now you have a $cart object available by default on every page that
uses PHPLIB. That object is created automatically at session startup,
is carried from page to page by PHPLIBs session management and is
destroyed by the garbage collection that reaps session records. You do
not have to worry anymore about that cart, but simply use it anytime
between page_open() and page_close(). PHPLIB does the rest for you.
The Cart class is actually dead stupid. It maintains an array
$cart->item[] that holds records about what the user bought. Each
$cart->item[$x] consists of a $cart->item[$x]["art"], which is the
article number of an item the user wants to buy and of a
$cart->item[$x]["num"], which is the # of items with that article
number that are wanted. $cart->currentItem is the next $x to use for
articles added to $cart->item[].
You add articles to the shopping cart with
______________________________________________________________________
$x = $cart->add_item($art, $num)
______________________________________________________________________
This will add $num items with the article number $art to your cart
contents. If you already have an item with that article number in your
cart, the count for that article is increased by $num. Otherwise a new
article entry is being created and set to $num. The function does
return the $x index into the $cart->item[] array for that article.
To remove an item from the shopping cart, code
______________________________________________________________________
$x = $cart->remove_item($art, $num)
______________________________________________________________________
This will remove $num items with the article number $art from your
cart, if there are that many items in your shopping cart. If you do
not have the $art in your cart or there are not $num many $art in your
cart, the function will return false and not remove anything from the
cart. Otherwise, $num articles with article number $art are taken out
of the cart and if the count for that article drops to zero while
doing this, we even unset the array element.
You may check how many articles with a given article number are in the
cart:
______________________________________________________________________
list($have, $num) = $cart->check($art)
______________________________________________________________________
The check function does return a two-element array. The first element
$have is true, if we have the wanted article in the cart. If $have is
true, $num holds the number of articles with that number in the cart,
otherwise $num is undefined (actually, it is 0, but you must not rely
on that).
Finally, we have a function
______________________________________________________________________
$cart->show_all()
______________________________________________________________________
which you may call to walk your shopping cart and have Example_Cart to
generate a list of articles in your cart. That function will first
call $cart->show_cart_open(), for which you may provide code in your
subclass. It will then call $cart->show_item($art, $num) for each item
in the cart. We have a stupid default implementation for that function
in Cart, but you may provide more sophisticated code in Example_Cart
for that, too. Finally, at the end of your cart listing,
$cart->show_cart_close() is being called, which again may be code of
yours.
The example in the previous section shows a more sophisticated
implementation of a Cart subclass. That implementation uses
show_cart_open() to create an opening table tag (formatted with a CSS
class) and sets a counter $cart->sum to zero.
In show_cart_close(), the table is being closed and the $cart->sum
counter is printed.
As you might have guessed, show_item($art, $num) queries the database
for each article number, retrieves the article description and prices
and finally sums up all prices, taking the number of articles per
article into consideration. It also generates table rows, printing a
nice receipt for the customer.
44..22.. TTeemmppllaattee
_N_o_t_e_: If you think that this is like FastTemplates, read carefully. It
isn't.
The template class allows you to keep your HTML code in some external
files which are completely free of PHP code, but contain replacement
fields. The class provides you with functions which can fill in the
replacement fields with arbitrary strings. These strings can become
very large, e.g. entire tables.
44..22..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
Internal instance variables.
44..22..22.. IInnssttaannccee mmeetthhooddss
44..22..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
TTeemmppllaattee(($$rroooott ==
Constructor. May be called with two optional parameters. The first
parameter sets the template directory (see set_root(), the second
parameter sets the policy regarding handling of unknown variables.
sseett__rroooott(($$rroooott))
The function checks that $root is a valid directory and sets this
directory as the base directory where templates are being stored.
sseett__uunnkknnoowwnnss(($$uunnkknnoowwnnss ==
The function sets the policy for dealing with unresolved variable
names. Must be either "remove", "comment" or "keep". If set to
"keep", those are left untouched. If set to "comment", unresolved
variable names are transformed into HTML comments reporting the
error. If set to "remove", unresolved variable names are silently
removed (the default).
sseett__ffiillee(($$hhaannddllee,, $$ffiilleennaammee ==
The function defines a filename for the initial value of a
variable. It may be called with either a $handle/$filename pair or
with a hash of $handle/$filename pairs. The files are not
referenced yet, but only when needed.
sseett__bblloocckk(($$ppaarreenntt,, $$hhaannddllee,, $$nnaammee ==
A variable $parent may contain a variable block named by $handle.
The function removes that block from $parent and replaces it with a
variable reference named $name. If $name is omitted, it is assumed
to be the same as $handle.
sseett__vvaarr(($$vvaarrnnaammee,, $$vvaalluuee ==
The functions sets the inital value of a variable. It may be called
with either a $varname/$value pair or with a hash of
$varname/$value pairs.
ssuubbsstt(($$hhaannddllee))
The function returns the value of the variable named $handle, with
all defined variable values filled in. The resulting string is not
"finished", that is, the unresolved variable name policy has not
been applied yet.
ppssuubbsstt(($$hhaannddllee))
This is a shorthand for print $this->subst($handle).
ppaarrssee(($$ttaarrggeett,, $$hhaannddllee,, $$aappppeenndd == ffaallssee))
The function substitutes the values of all defined variables in the
variable named $handle and stores or appends the result in the
variable named $target.
If $handle is an array of variable names, $append is ignored. The
variables named by $handle are being sequentially substituted and
the result of each substitution step is stored in $target. The
resulting substitution is available in the variable named by
$target, as is each intermediate step for the next $handle in
sequence.
ppppaarrssee(($$ttaarrggeett,, $$hhaannddllee,, $$aappppeenndd == ffaallssee))
A shorthand for print $this->parse(...).
ggeett__vvaarrss(())
Returns a hash of all defined values, keyed by their names.
ggeett__vvaarr(($$vvaarrnnaammee))
Returns the value of the variable named by $varname. If $varname
references a file and that file has not been loaded, yet, the
variable will be reported as empty.
When called with an array of variable names, an hash of values,
keyed by their names, will be returned.
ggeett__uunnddeeffiinneedd(($$hhaannddllee))
The function will return a hash of unresolved variable names in
$handle, keyed by their names (that is, the hash has the form
$a[$name] = $name).
ffiinniisshh(($$ssttrr))
The function will returned the finished version of $str, that is,
the policy regarding unresolved variable names will be applied to
$str.
pp(($$vvaarrnnaammee))
The function will print the finished version of the value of the
variable named by $varname.
ggeett(($$vvaarrnnaammee))
The function will return the finished version of the value of the
variable named by $varname.
hhaallttmmssgg(($$mmssgg))
This function can be overridden by your subclass of Template. It
will be called with an error message to print.
44..22..22..22.. IInntteerrnnaall iinnssttaannccee mmeetthhooddss
ffiilleennaammee(($$ffiilleennaammee))
When called with a relative pathname, this function will return
the pathname with $this->root prepended. Absolute pathnames are
taken unchanged.
The resulting filename must exist, or an error is generated.
vvaarrnnaammee(($$vvaarrnnaammee))
The function will construct a variable name regexp for a given
variable name.
llooaaddffiillee(($$hhaannddllee))
If a variable is undefined or empty and is backed by a filename,
the backing file will be loaded and the files contents will be
assigned as the variables value.
hhaalltt(($$mmssgg))
This function is called whenever an error occurs and will handle
the error according to the policy defined in
$this->halt_on_error.
44..22..33.. EExxaammppllee
The class manages a set of variables which are text strings. These
strings may contain references to other variables in the form of
"{variable}". When parsed or substituted, a variable reference is
being replaced by the value of that variable.
A variable value may be defined manually by calling set_var("name",
"value"); or it may be defined from a file by calling set_file("name",
"filename.ihtml");. In the latter case, the contents of the file are
being loaded when needed (as late as possible) and set as the value of
that variable.
A third way to define a variable value is to call set_block("parent",
"block", "name");. In this case, the variable named parent is being
searched for a block that starts with and ends
with . This string is removed from the variable
parent and assigned to the variable named block. In parent, a variable
reference to name is placed instead. If the optional parameter "name"
is left out, "block" is being used instead.
Use Template direcly or define a subclass of Template as needed.
Define a template file named page.ihtml as follows:
______________________________________________________________________
{PAGETITLE}
{PAGETITLE} |
{OUT} |
Content |
______________________________________________________________________
This file contains a reference to the variable pagetitle and a
reference to the variable named out. Another template file, named
box.ihtml, contains a block named row with three variable references
{TITLE}, {NUM} and {BIGNUM}:
______________________________________________________________________
______________________________________________________________________
The following php3 file demonstrates how to use these templates:
______________________________________________________________________
set_file(array(
"page" => "page.ihtml",
"box" => "box.ihtml"));
# extract the block named "row" from "box", creating a
# reference to {rows} in "box".
$t->set_block("box", "row", "rows");
# define the variables TITLE and PAGETITLE
$t->set_var(array("TITLE" => "Testpage",
"PAGETITLE" => "hugo"));
# define NUM and BIGNUM, then append "row" to "rows"...
for ($i=1; $i<=3; $i++) {
$n = $i;
$nn = $i*10;
$t->set_var(array("NUM" => $n, "BIGNUM" => $nn));
$t->parse("rows", "row", true);
}
# build out from box, then build out from page...
$t->parse("out", array("box", "page"));
# finish out and print it.
$t->p("out");
?>
get_undefined("rows"));
?>
______________________________________________________________________
55.. HHTTMMLL WWiiddggeettss CCllaasssseess
55..11.. SSqqll__QQuueerryy
Sql_Query will generate a query form for simple table queries: A list
of field names, comparision operators and input fields is presented.
The user may search for any values in any of the presented columns
using SQL standard operators. Multiple query conditions are possible
and these conditions can be joined using AND and OR operations.
The number of query conditions can be made variable. If so, the user
may shrink and grow the query widget using the appropriate buttons.
All button labels and other messages of the interface are variable and
held in language dictionaries. Currently, _d_e and _e_n dictionaries are
provided.
55..11..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
Internal instance variables.
55..11..22.. IInnssttaannccee mmeetthhooddss
55..11..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
ssttaarrtt(())
Initialization function. Currently empty.
ffoorrmm(($$bbaassee,, $$ooppttiioonn,, $$ccllaassss,, $$ttaarrggeett))
The function will generate and return HTML for the SQL Query
selection form. All variables in the form will start with the
prefix $base and have numeric indices appended after an
underline character. It is possible to have multiple Sql_Query
instances on a single page, if they use different base
characters.
The function must know the field names of the SQL table that is
to be queried. $option can be either a simple array of these
field names ($translate set empty) or a hash field name to long
name ($translate set to on).
All tags in the generated form are tagged with a CSS stylesheet
class, if $class is set to a CSS classname. $class is optional
and if it is left empty, no class attributes are generated.
$target is the URL of the SQL Query form target. It is optional
and if it is left empty, a self referencing form is generated
(recommended).
The function returns a string containing the HTML to render the
SQL Query selection form.
wwhheerree(($$bbaassee,, $$iinnccrr))
When the form() generated page is submitted, a lot of parameters
have to be evaluated and transformed into a SQL _w_h_e_r_e condition
matching the user selections. The where() function takes care of
all this; it just needs to be told which $base prefix has been
used in the form() call.
The $incr parameter is optional and determines how many query
condition rows are added or subtracted when the "More" and
"Fewer" buttons are used. The default value is 1.
The function returns a string which can be successfully used
behind a "where" keyword in a SQL query.
55..11..22..22.. IInntteerrnnaall iinnssttaannccee mmeetthhooddss
ppllaaiinn__wwhheerree(($$bbaassee))
This function does all the work for where(), but does not resize
the query condition window.
55..11..33.. EExxaammppllee
The Sql_Query class can be used directly. It is more useful when made
persistent, so it is recommended that you add the line
require("sqlquery.inc") to your prepend.php3 file where indicated in
that file.
See the Table class in this section for a nice method to display and
format query results. See the DB_Sql class (a core class) for a nice
method to connect to databases.
The following code fragment is quite large, but contains a complete
and working example using the Sql_Query, DB_Sql and Table classes to
query a database table.
______________________________________________________________________
"Example_Session"));
$db = new DB_Example; // That's a DB_Sql subclass.
$t = new Table; // For formatting results
$t->heading = "on"; // We want table headings..
?>
Testseite
Testpage
"Login Name",
"password" => "Password",
"perms" => "Permissions"
);
// When we hit this page the first time,
// there is no $q.
if (!isset($q)) {
$q = new Sql_Query; // We make one
$q->conditions = 1; // ... with a single condition (at first)
$q->translate = "on"; // ... column names are to be translated
$q->container = "on"; // ... with a nice container table
$q->variable = "on"; // ... # of conditions is variable
$q->lang = "en"; // ... in English, please
$sess->register("q"); // and don't forget this!
}
// When we hit that page a second time, the array named
// by $base will be set and we must generate the $query.
// Ah, and don't set $base to "q" when $q is your Sql_Query
// object... :-)
if (isset($x)) {
$query = $q->where("x", 1);
}
// In any case we must display that form now. Note that the
// "x" here and in the call to $q->where must match.
// Tag everything as a CSS "query" class.
printf($q->form("x", $field, "query));
printf("
");
// Do we have a valid query string?
if ($query) {
// Show that condition
printf("Query Condition = %s
\n", $query);
// Do that query
$db->query("select * from auth_user where ". $query);
// Dump the results (tagged as CSS class test)
printf("Query Results = %s
\n", $db->num_rows());
$t->show_result($db, "test");
}
page_close();
?>
______________________________________________________________________
55..22.. TTaabbllee aanndd CCSSVV__TTaabbllee
The Table class is a neat way to format two-dimensional associative
arrays of data or the results of a database query into a table. Table
and its subclasses allow you to simply pass them either an array or a
query result and they spit out the proper HTML for a table containing
all the values. Table has some primitive filtering capabilities making
it useful even without subclassing, but for the full power of Table
you have to write your own subclass.
When used with the check option, it is assumed that the table is part
of a HTML FORM element. Code is generated to create an INPUT
TYPE=CHECKBOX element before each table row. The checkboxes will form
an array indexed by row number. The name of the array will whatever
you set the check instance variable to.
Exactly one of two types of possible column filtering take place when
each table row is generated. If the fields instance variable is set,
only the columns keyed by the named fields in that array are shown in
that order. That is, if you fill in the fields instance variable with
array("a", "c", "e"), only the columns a, c and e become part of the
generated table.
If fields has not been set, all data columns are traversed with each()
and all columns whose names match the regexp in filter are shown in
the table. By default, this regular expression lets through all column
names that start with an alphabetic character and continue with either
alphanumeric characters or "_" (underscore). This default has been
chosen, because the DB_Sql database class uses mysql_fetch_array()
internally to get data from the database and this function returns all
columns twice with both a numeric index and proper column names. The
default filter will make all data show up only once and with proper
column names.
Additionally, the map_cols instance variable provides column name
remapping. If map_cols is set, it will remap the name of the found
column with a new name.
For instance, a table with the following columns, fname, lname, and
mydate can be remapped to First Name, Last Name, and Date using the
following code (where $t is your instantiated Table class object):
______________________________________________________________________
$t->map_cols = array("fname" => "First Name",
"lname" => "Last Name",
"mydate" => "Date");
______________________________________________________________________
The map_cols instance variable also allows you to map column names to
different languages using this same technique.
For derived classes, the instance variable add_extra has been added.
If this variable is set, then the functions
table_heading_row_add_extra() and table_row_add_extra() are called. In
the Table class, these functions do nothing, but in derived classes
override these functions to provide additional functionality that may
be needed. For instance, hyperlinks to provide edit, delete, or view
capabilities for that row could be easily added into these functions
(in your derived Table class) allowing greater customization.
A subclass of Table, CSV_Table, is being provided to allow to create
CSV representations of your data with minimal effort. CSV (comma
separated values) can be imported by MySQL's LOAD DATA INFILE
statement and many spreadsheet import functions.
The Table class now provides both high-level, mid-level and low-level
functions through modularization. This allows programmers to use
either the simplified high-level functionality or, depending on the
degree of complexity needed, the power of the mid- or low-level
functions. Every effort to maintain backwards compatibility has been
applied. However, it would be a good idea to become familiar with the
new functions if you use the Table class extensively. Typically, the
high- and mid-level support functions begin with show_ while the low-
level functions do not.
55..22..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
55..22..22.. IInnssttaannccee mmeetthhooddss
55..22..22..11.. HHiigghh--lleevveell iinnssttaannccee mmeetthhooddss
sshhooww(($$aarryy,, $$ccllaassss ==
Will format and print the two dimensional array (or hash) $ary
as a table according to the filtering rules explained above. If
$class is set, each HTML element will be tagged as belonging to
the named class; this is useful with cascading style sheets.
sshhooww__ppaaggee(($$aarryy,, $$ssttaarrtt,, $$nnuumm,, $$ccllaassss ==
Just as show(), but will show only num elements starting at
start.
sshhooww__rreessuulltt(($$ddbb,, $$ccllaassss ==
Will format and print the result set of $db. $db is exspected to
be a subclass of DB_Sql that has just been sent a query. Table
will grab all available results from the result set of that
query by calling $db->next_record() repeatedly and format them
into a table.
sshhooww__rreessuulltt__ppaaggee(($$ddbb,, $$ssttaarrtt,, $$nnuumm,, $$ccllaassss ==
Just as show_result(), but will show only num elements starting
at start.
55..22..22..22.. MMiidd--lleevveell iinnssttaannccee mmeetthhooddss
sshhooww__ttaabbllee__rroowwss(($$aarryy,, $$ccllaassss==
Walks the passed array displaying each row of data as an HTML
table row.
sshhooww__ttaabbllee__rroowwss__rreessuulltt(($$ddbb,, $$ccllaassss==
Walks the passed database object displaying each record as an
HTML table row.
sshhooww__ttaabbllee__ppaaggee__rroowwss(($$aarryy,, $$ssttaarrtt,, $$nnuumm,, $$ccllaassss==
Walks the passed array displaying each row of data as an HTML
table row. However, data does not start displaying until $start
element and end after $num rows.
sshhooww__ttaabbllee__ppaaggee__rroowwss__rreessuulltt(($$ddbb,, $$ssttaarrtt,, $$nnuumm,, $$ccllaassss==
Walks the passed database object displaying each record as an
HTML table row. However, data does not start displaying until
$start record and ends after $num records have been displayed.
sshhooww__ttaabbllee__hheeaaddiinngg__rrooww(($$aarryy,, $$ccllaassss==
Uses the passed array to create an HTML header row.
sshhooww__ttaabbllee__hheeaaddiinngg__rrooww__rreessuulltt(($$ddbb,, $$ccllaassss==
Uses the passed database object to create an HTML header row.
sshhooww__ttaabbllee__hheeaaddiinngg__cceellllss(($$ddaattaa,, $$ccllaassss==
Walks the passed array and displays each item in an HTML table
header cell.
sshhooww__ttaabbllee__cceellllss(($$rrooww,, $$rrooww__kkeeyy,, $$ddaattaa,, $$ccllaassss==
Walks the passed array and displays each item in an HTML table
cell.
55..22..22..33.. LLooww--lleevveell iinnssttaannccee mmeetthhooddss
ttaabbllee__ooppeenn(($$ccllaassss ==
This function can be overridden by a subclass of Table. It is
called as the very first step in table creation and should
output HTML that opens a table (for example
printf("\n", $class?" class=$class":"");).
ttaabbllee__cclloossee(())
This function can be overridden by a subclass of Table. It is
called as the very last step in table creation and should output
HTML that closes a table (for example printf("\n");/).
sseelleecctt__ccoollnnaammeess(($$ddaattaa))
Internal function to generate a list of column names.
ttaabbllee__hheeaaddiinngg__rrooww(($$ddaattaa,, $$ccllaassss ==
Internal driver function to generate a table heading row.
ttaabbllee__hheeaaddiinngg__cceellll(($$ccooll,, $$vvaall,, $$ccllaassss))
This function can be overridden by a subclass of Table. It is
called each time a table heading cell is to be generated.
$col is the current column number, $val is the name of the
column. $class is the HTML CSS class of the element that is to
be generated.
ttaabbllee__hheeaaddiinngg__cceellll__ooppeenn(($$ccllaassss==
Starts a header cell.
ttaabbllee__hheeaaddiinngg__cceellll__cclloossee(($$ccllaassss==
Ends a header cell.
ttaabbllee__hheeaaddiinngg__rrooww__aadddd__eexxttrraa(($$ddaattaa,, $$ccllaassss==
Virtual function for derived classes. This function is called
after all header cells have been created. It allows the
programmer to add additional HTML code to the header row before
it is closed.
ttaabbllee__rrooww(($$ddaattaa,, $$ccllaassss ==
Internal driver function to generate a table row.
ttaabbllee__rrooww__ooppeenn(($$rrooww,, $$ddaattaa,, $$ccllaassss ==
This function can be overridden by a subclass of Table. It is
called as the very first step in row creation and should output
HTML that opens a table row.
$row is the current row number. $data is a hash of column
name/value pairs for that row and $class is an optional HTML CSS
class name for all generated elements.
ttaabbllee__rrooww__cclloossee(())
This function can be overridden by a subclass of Table. It is
called as the very last step in row creation and should output
HTML that closes a table row.
ttaabbllee__cceellll(($$rrooww,, $$cceellll,, $$kkeeyy,, $$vvaall,, $$ccllaassss))
This function can be overridden by a subclass of Table. It is
called each time a table cell is to be generated.
$row is the current row number, $cell is the current cell
number. $key is the current column name, $val is the value of
the cell. $class is the HTML CSS class of the element that is to
be generated.
ttaabbllee__cceellll__ooppeenn(($$ccllaassss==
Starts a cell.
ttaabbllee__cceellll__cclloossee(($$ccllaassss==
Ends a cell.
sseett__cchheecckkbbooxx__hheeaaddiinngg(($$ccllaassss==
This function creates an empty header cell to coincide with the
checkbox option for that column.
ttaabbllee__cchheecckkbbooxx__cceellll(($$rrooww,, $$rrooww__kkeeyy,, $$ddaattaa,, $$ccllaassss==
Outputs HTML code to display a checkbox. This function runs if
the member variable $check has been set. $check should be set to
some key within the $data array (ex: if $data["myKey"], then set
$check="myKey").
sseett__cchheecckkbbooxx(($$rrooww,, $$rrooww__kkeeyy,, $$ddaattaa,, $$ccllaassss==
Creates an HTML checkbox based on the passed data, only if the
instance variable $check is set.
55..22..33.. EExxaammppllee
Table is not automatically included or prepended into each page.
Include the table class into the pages that are to use Table. Then
create an instance of Table:
______________________________________________________________________
heading = "on";
______________________________________________________________________
Now create a two dimensional array or prepare a database query and
have table print it.
______________________________________________________________________
// Create a database object
$db = new DB_Session;
// create a twodim array called $tab
$tab = $db->metadata("active_sessions");
// print that array
$t->show($tab, "metadata");
// prepare a database query
$db->query("select * from active_sessions");
// print that result
$t->show_result($db, "data");
______________________________________________________________________
55..33.. FFoorrmm
The form class (sometimes called OOH Forms) is a convenience library
for dealing with html forms. It provides Javascript and server-side
form validation, and is customizable and extensible.
55..33..11.. UUssiinngg OOOOHH FFoorrmmss
The OOH Forms library consists of five files: oohforms.inc
of_checkbox.inc of_radio.inc of_select.inc of_text.inc
of_textarea.inc. oohforms.inc automatically includes the others. You
may wish to modify this so you can manually include the files for just
the form elements you use. Or you may wish to cut and paste the
contents of the element files into oohforms.inc to save the overhead
of multiple includes. Determining the appropriate configuration of
the files for your site is left an exercise for the reader; for most
purposes require("oohforms.inc") will suffice.
In general, the structure of a page that uses oohforms is as follows:
______________________________________________________________________
require("oohforms.inc"); // include the library
$f = new form; // create a form object
$f->add_element(...); // set up form elements
$f->add_element(...);
$f->add_element(...);
if ($submitname) // Is there data to process?
if ($err = $f->validate()) { // Is the data valid?
echo $err; // No; Display error
$f->load_defaults(); // Load form with submitted data
else {
/* Process data */ // Data ok; Do something with it
}
$f->start(...); // Start displaying form
$f->show_element(...); // Show elements
$f->show_element(...);
$f->show_element(...);
$->finish(); // Finish form
______________________________________________________________________
There are obviously many variations on this theme, but that covers the
basics. Specific methods are documented below.
ssttaarrtt(($$jjvvssnnaammee,,$$mmeetthhoodd,,$$aaccttiioonn,, $$ttaarrggeett))
Outputs the initial tag, then the Javascript validation function (if
necessary). $after and $before are both optional; if either is
a nonempty string it is output as additional Javascript to be
run on submission of the form, either before or after the
validation code. Though the order of the arguments may seem
counterintuitive, it makes the common case easier to type; in
general you'll want to wait until after the validation has taken
place to do anything fancy with Javascript. Note that unlike
with validation, OOH Forms has no way of giving you server side
functionality equivalent to the Javascript you use here.
aadddd__eelleemmeenntt(($$eelleemmeenntt))
add_element is used to define the attributes of a particular
form element so that the other class methods can use and
manipulate it properly. add_element takes exactly one argument:
an associate array whose key value pairs are used to define the
form element type and it's various attributes. Some of these
attributes correspond to html attributes, while others are
needed for the value added features of oohforms. The attributes
and the syntax and semantics of the values they take are
documented below; note that not all element types use all of the
attributes
ttyyppee
The type of element this is; can be "submit", "hidden",
"text", "textarea", "select", "radio", "checkbox", or "file".
nnaammee
A string naming this element. This name will be used as an
argument to other methods and will be the name="" value in
the generated html (and hence the variable name in PHP). DDoo
nnoott append [] to the name if you want an array valued
element; set the multiple attribute instead.
vvaalluuee
The default value of this form element. If the form element
has the multiple attribute set, value can be an array. If
the this is a select element, value can refer to either the
textual name (label in the options array) or the submission
value (value in options).
mmuullttiippllee
A flag to tell oohforms to assume this element is array
valued. The use of this flag is most straightforward with
select elements, but it can be use with text and checkbox
elements as well. See the show_element documentation for
more information about how oohforms deals with such elements.
eexxttrraahhttmmll
Extra html code that is inserted verbatim into the opening
form tag. For select elements, the extra html goes into the
select tag, not the option tags.
ssiizzee
For text elements, used to set the html size attribute that
gives the width in characters of the text entry box. For
select elements, the size (number of options visible at once)
of the selection box. Validation is only performed on select
elements if size is set to 1, since select validation doesn't
make much sense if you can see multiple options at once. For
file elements, the maximum size file upload to accept.
ppaassss
If set for a text element, renders the html as a password
element, i.e. entry displays as asterisks.
ssrrcc
If set for a submit element, convert to an image element and
use the value of src as the source URL for the image.
mmaaxxlleennggtthh
Used verbatim as the maxlength html attribute in text
elements.
mmiinnlleennggtthh
If length_e is set, this is the minimum length text element
entry accepted by the validator.
lleennggtthh__ee
If set, validate the text element to assure it has at least
minlength characters. The value of length_e is the error
string to be used in the event of failed validation.
vvaalliidd__ee
If set, perform validation on a text, radio, or select
element. For a text element, validation assures a match with
valid_ regex. radio element validation assures that one of
the options in the group has been chosen. select validation
only works for select elements with multiple unset and size
equal to 1; the validator will not accept the first option of
accept menu, assuming that it is some sort of prompt (e.g.
"Please select an item"). In all cases, the value of valid_e
is the error string used for failed validations.
vvaalliidd__rreeggeexx
Regular expression used to validate entry into a test field
if valid_e is set. Note that if you must use ^...$ if you
want the regex to match the whole entry.
iiccaassee
If set, regex matching is case insensitive.
cchheecckkeedd
Only used for a checkbox element that does not have multiple
set. If checked is set, the element will display as checked.
rroowwss
Used verbatim as the rows= element in a textarea element.
ccoollss
Used verbatim as the cols= element in a textarea element.
wwrraapp
Used verbatim as the wrap= element in a textarea element.
ooppttiioonnss
Array of options to be displayed in a select element. If the
elements of the array are simple values (strings or numbers),
they are simply displayed verbatim and used as the value for
that particular option. The elements may themselves be
associate arrays with keys "label" and "value". In that
case, the value of "label" is displayed and the value of
"value" is used on submission.
Examples:
___________________________________________________________________
$f->add_element(array("type"=>"text",
"name"=>"foo",
"valid_regex"=>"^[a-z]*$",
"valid_e"=>"Letters only",
"icase"=>1,
"value"=>"bar"));
$f->add_element(array("type"=>"checkbox",
"name"=>"compress",
"multiple"=>1));
$f->add_element(array("type"=>"textarea",
"name"=>"comment",
"rows"=>6,
"cols"=>40,
"value"=>""));
$o = array(array("label"=>"Please Select","value"=>0),
array("label"=>"Apple","value"=>1),
array("label"=>"Orange","value"=>2),
array("label"=>"Pear","value"=>3),
array("label"=>"Grape","value"=>4));
$f->add_element(array("type"=>"select",
"name"=>"menu",
"options"=>$o,
"size"=>1,
"valid_e"=>"Please select a fruit",
"value"=>"apple"));
___________________________________________________________________
sshhooww__eelleemmeenntt(($$nnaammee,,$$vvaalluuee))
Outputs the form element named $name. Usually, the second
argument is not used. It is always necessary for radio elements
and checkbox elements with the multiple attribute set, since
many of these may have the same name. It also must be used for
submit elements to label the submission button; the value
attribute is not used for submit elements. For other elements
that may be array valued (notably text elements) multiple calls
to show_element will show successive values.
llooaadd__ddeeffaauullttss(($$eelleemmeenntt__lliisstt))
Sets the default value of form elements to the value of the PHP
variables with the same name. This is generally used to
redisplay a form with the same values which were submitted. The
argument is optional; if given it is an array of element names;
only these elements ares affected.
vvaalliiddaattee(($$rreessuulltt,,$$eelleemmeenntt__lliisstt))
Validates a form submission. If all of the elements are valid,
return $result, otherwise return the relevant error message (the
valid_e or length_e attribute of some form element). $result
defaults to false. The second argument is also optional; it is
an array of names of elements to validate.
ffrreeeezzee(($$eelleemmeenntt__lliisstt))
Freezes the form elements whose names are given in the array
passed as the argument. If no argument is given, freeze all of
the elements. Frozen elements are rendered as plain, static
html rather than form widgets. This static rendering is
accompanied by appropriate hidden elements to simulate the
affect of using the unfrozen version of the element.
55..33..22.. CCuussttoommiizziinngg OOOOHH FFoorrmmss
Since OOH Forms is object oriented, it can be easily customized by
extending the classes that define the element types. In general, you
must make sure your derived class has a constructor and you may
override any of the self_* functions of of_element. The source for
the existing elements is the best documentation for how to do this
properly, but a few general notes follow.
sseellff__sshhooww(($$vvaall,,$$wwhhiicchh))
Display an instance of this element unfrozen. $val is the
$value argument of show_element if there was one; $which can be
used as an index for array valued elements; it is equal to the
number of times show_element has been called for this element
previously. This function must return the number of hidden tags
output.
sseellff__sshhooww__ffrroozzeenn(($$vvaall,,$$wwhhiicchh))
Display an instance of this element frozen. In addition to the
html to show the frozen element, you must output tags for hidden
fields to duplicate the effect of submitting an unfrozen element
of this type. The function must return the number of hidden
tags output;
sseellff__vvaalliiddaattee(($$vvaall))
Validate $val for this element. If it is valid, return false,
otherwise return the relevant error string.
sseellff__pprriinntt__jjss(($$nnddxx__aarrrraayy))
Print out Javascript code to validate this element. $ndx_array
is an array of the indices of the elements to be validated as
used in the Javascript form.element[] array. This is needed
since the extra [] in array valued element names confuses
Javascript.
sseellff__llooaadd__ddeeffaauullttss(($$vvaall))
Set the default value for this element to $val. Usually the
default definition of this function, which just copies $val to
$this->value is all that is needed, but there may be special
cases that need to do something else. See the implementation of
the checkbox element for an example.
55..44.. ttppll__ffoorrmm
The tpl_form class is intended to provide a general framework for HTML
form deployment. It heavily depends on OOH Forms library, so it is
required that you read and understand the relative documentation.
The main idea is that you create a black box by sub-classing tpl_form,
provide some HTML code mixed with OOH Forms calls to actually render
the form itself. Your application will then use that black box to
obtain some input from the user. Your application doesn't have to know
how to handle user input, nor how to validate input data, since
internal methods will take care of that.
This approach is very similar (I think) to OOH Forms one, only at a
higher level. OOH Forms elements have no way to communicate with each
other, and are only able to perform "simple" checks on data integrity,
while tpl_form adds a consistent interface for complex data evaluation
and processing.
Furthermore, the get_default_values and set_default_values methods can
be used to maintain user input between sessions, without worrying
about serialization of form variables (a BAD THING(tm)), using an hash
array containing field names and values.
You'll note that an array is used to share data with the application.
You may object this is kinda futile, since all user input data may be
found in global vars and HTTP_POST or HTTP_GET global hashes. This is
true, and in the general case you'll pass back and forth an empty
array. The values variable is intended for use in very complex data-
entry setup, where a form behaviour may depend on previous data
entered by the user. In this case, if all forms cooperate reading and
writing to values hash array, final result may be constructed step by
step across multiple HTML pages.
55..44..11.. IInnssttaannccee vvaarriiaabblleess
Internal instance variables.
55..44..22.. IInnssttaannccee mmeetthhooddss
55..44..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
iinniitt(($$vvaalluueess))
This is a sort of a constructor for the class. $values is an
hash array intended to store form values to be passed back to
the application via get_values method.
ggeett__ddeeffaauulltt__vvaalluueess(())
Returns an array containing all data submitted by the user for
the form. This array is intended to be passed to
set_defaults_values some time later.
sseett__ddeeffaauulltt__vvaalluueess(($$ffvv))
Restore form defaults from an array as returned by
get_default_values.
ddiissppllaayy(())
Actually display form fields. This method should not be
overridden in descendants. User should instead provide a file
named as the derived class and with ".ihtml" extension which
will be automatically included.
ggeett__vvaalluueess(())
This method should not be overridden. It is intended as the main
interface between the application and the form. Once the form
has been properly derived to suit designer's needs, application
calls get_values and receives back the array passed to init,
eventually modified by process_input method, or false if user
input is invalid. In that latter case, the application should
call display to (re)present the form to the user, eventually
filled with proper default values.
cclleeaarr(())
Sort of a "destructor". There should no real need to call it,
except maybe freeing some memory. May be called from the
application, otherwise is not executed. Returns true.
55..44..22..22.. IInntteerrnnaall iinnssttaannccee mmeetthhooddss
sseettuupp(())
Init the Form object, which will contain all fields info. The
hidden field form_name, automatically added by this routine, is
used by other methods to determine if form has already been
submitted by the user. You shouldn't override this in
descendants, use setup_fields instead. Returns true.
sseettuupp__ffiieellddss(())
Override this method in order to provide form fields definition
that suit your needs.
vvaalliiddaattee(())
Validates user input. This method should not be overridden in
descendants. See validate_input instead. Returns false on error
and sets error variable accordingly.
vvaalliiddaattee__iinnppuutt(())
This method should be overridden in descendants, in order to
provided complex validation methods (i.e. field2 should not be
empty IF field1 == "other"). Should return false on error and
set error variable with a sensible error message.
pprroocceessss(())
Process user data. This method should not be overridden by
descendants. See process_input and process_default instead.
Returns true on success, false otherwise.
pprroocceessss__iinnppuutt(())
This method should be overridden in descendants. It is executed
after validation has taken place. The data passed to the form
could be used to fill values array.
pprroocceessss__ddeeffaauulltt(())
This method should be overridden in descendants. It is executed
when form validation fails or before presenting the form for the
first time. Should be used to bypass form displaying if data can
be extracted from previous actions, divination, penguin fly
watching or whatever.
55..44..33.. EExxaammppllee
Suppose you have a form that the user should fill with her (eheh) name
and e-mail. You want to check wether this e-mail is valid, or your
blind date setup is lost. A... er... simple regular expression for
validating syntactically the e-mail is presented in the example code
below.
______________________________________________________________________
$this->form_data->add_element(array(
"type"=>"text",
"name"=>"email",
"valid_e"=>"Syntax error in E-Mail address.",
"valid_regex"=>"^([-a-zA-Z0-9.]+@[-a-zA-Z0-9]+(\.[-a-zA-Z0-9]+)+)*$"
));
______________________________________________________________________
Now, this piece of code should do the job, but since you're feeling
very paranoid today, you'd also like to validate the host name part of
the address with DNS. So, you put together some code which takes an
hostname in input and reports true on valid hostname, false otherwise
(HINT: on PHP Code Exchange you should find a procedure for "active"
email validation).
Now that you have your shining new code, you can check the address.
The user fills out the form, you parse user input, no syntax errors,
time to call your mycheckhost from the application. If the function is
ok update your database, else load defaults into the form, display
again, close the page, goodbye.
I've done something similar for MANY forms, some of them with very
complex validation procedures, and I found that was too easy producing
very bad and unreadable code (well, I actually realized that the first
time I had to change some logic in data validation...).
tpl_form should provide a solid framework to build your forms with,
and all the code will be self-contained and separated from main
application logic. Hope you'll like it.
Time to see some code. First of all, class declaration, sub-classing
tpl_form:
______________________________________________________________________
class myform extends tpl_form {
var $classname = "myform";
function setup_fields() {
$this->form_data->add_element(array(
"name"=>"email",
..., // See previous code snippet
));
$this->form_data->add_element(array(
"name"=>"submit",
"type"=>"submit",
"value"=>"submit"
));
}
function validate_input() {
global $email;
list($uname, $hostname) = split("@", $email);
if (! mycheckhost($hostname)) {
$this->error = sprintf("Sorry, unknown host %s, try again.", $hostname);
return false;
}
// Additional checks here...
return true;
}
}
______________________________________________________________________
You shuld provide a file myform.ihtml with HTML and php code to render
the form. Minimalistic example:
______________________________________________________________________
form_data->start_form($this->classname, "POST", $sess->self_url(), "");
printf("%s
\n", $this->error);
$this->form_data->show_element("email");
printf("
\n");
$this->form_data->show_element("submit");
$this->form_data->finish();
?>
______________________________________________________________________
Your tpl_form class is complete, and will only need a little work on
the artistic side... 8-) To use your brand new class, include class
definition code into your application, then...
______________________________________________________________________
$mf = new myform;
$mf->init(array()); // This is not strictly required, AT PRESENT,
// but I recommend it
if ($rv = $mf->getdata()) {
$mf->clear(); // This is not strictly required, anyway it should free
// some memory...
global $email;
// process your data at will
} else {
$mf->display();
}
______________________________________________________________________
Hope this very little example does help in understanding the real
power of tpl_form class, at least in terms of rapid designing and code
partitioning.
55..55.. TTrreeee
The Tree class can render tree structures such as directory
hierarchies and menu structures as HTML. The structure must be given
to Tree as an nested array of arrays of arbitrary depth.
The idea of Tree is, that there are several mathematical models a tree
could be viewed: One model is a data structure like nested arrays or a
pointer structure from where you can print multidimensional graphics
and can do other neat things like deleting one part of the tree or
inserting a whole subtree. But you can also imagine a tree as a one
dimensional string or as a sequence of function calls (which is nearly
the same in the mathematical sense).
To generate HTML-code from a tree-structure it is like this: You need
at the end a one-dimensional string, which tells the browser what to
do. The Tree class assists you in generating this string in this way,
that it will go through the whole tree and call several functions on
every stage trough the way. It will be your task to change the
functions, so that a nice layout will be generated.
55..55..11.. IInnssttaannccee vvaarriiaabblleess
Accessible instance variables.
55..55..22.. IInnssttaannccee mmeetthhooddss
55..55..22..11.. AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss
bbuuiilldd__ttrreeee(())
This function is completely user driven! You have to create an
array with the structure described below. See the example for
details.
Don't be shy to create some own functions which are called by
build_tree() - e.g. for recursive calls.
ggoo__ttrroouugghh__ttrreeee(($$kkeeyy==
This is the most important function of this class. It will call
the output functions in the right order with the correct
parameters.
All variables are optional. The parameters are perhaps useful,
if you want to display only partial trees, but this is not
supported now.
ppaatthh__ttoo__iinnddeexx ((&&$$ppaatthh,,$$kkeeyy==
This function is mostly used internally, but could be useful for
you to generate $this->tree. This function generates a PHP3
associate array-index string from a path, which is also a string
but truncated by $this->delimiter. If $key is given, it will be
added to $path (minds empty path and so on).
Example:
___________________________________________________________________
$t->delimiter="/";
$path= "usr/local/lib";
## $path must be given as a var, because it is called by reference!
$bla = $t->path_to_index($path,"etc");
## $path is now "usr/local/lib/etc"
## $bla is now ["usr"]["local"]["lib"]["etc"]
___________________________________________________________________
ppaatthh__ttoo__ppaarreenntt ((&&$$ppaatthh))
This function isn't used internally, but could be useful for you
during generating the output tree. It will remove one from the
depth of the path.
Example:
___________________________________________________________________
$t->delimiter="/";
$path= "usr/local/lib";
$bla = $t->path_to_parent($path);
## $path is now "usr/local"
## $bla is now ["usr"]["local"]
___________________________________________________________________
ppaatthh__aadddd (($$ppaatthh,,$$kkeeyy))
This function is the 'non-call-by-reference-version' of
path_to_index. It will add the $key to the path and return it.
ppaatthh__ssuubb (($$ppaatthh))
This function is the 'non-call-by-reference-version' of
path_to_parent. It will find the parent of path and return it.
ppaatthh__iinnddeexx (($$ppaatthh))
This function is the 'non-call-by-reference-version' of
path_to_index(). It will return the associate key to the tree
described by path.
ssttaarrttttrreeee (())
This function is called by go_trough_tree() at the beginning of
the output of a tree.
All *tree-functions are called by go_trough_tree(), but it's
your turn, to give them a nice layout. I think it is possible to
generate nearly every kind of tree-layout with this. Have a look
at the variables: E.g. $depth makes it possible to handle every
"level" in another manner.
ggrroowwttrreeee (($$kkeeyy,,$$vvaalluuee,,$$ppaatthh,,$$ddeepptthh,,$$ccoouunntt,,$$ppccoouunntt))
This function is called by go_trough_tree() at the beginning of
the output of a tree.
It is called every time, when go_trough_tree() will call itself
recursively. You could also say it is called, when the current
item has a successor.
lleeaaffttrreeee (($$kkeeyy,,$$vvaalluuee,,$$ppaatthh,,$$ddeepptthh,,$$ccoouunntt,,$$ppccoouunntt))
This function is called, when the current item has _n_o successor.
sshhrriinnkkttrreeee (($$kkeeyy,,$$ddeepptthh))
This function is the "opposite" of growtree(). It is called
every time, when the current item was the last item in this sub-
list.
eennddttrreeee(())
Called when leaving tree.
55..55..33.. TThhee TTrreeee AArrrraayy
As said above, before you call go_trough_tree(), first $tree must be
generated.
$tree consists of nested arrays of arbitrary depth. An example:
______________________________________________________________________
$t= new Tree;
$t->tree = array(
"usr" => array(
0 => "allowed",
"lib" => "forbidden",
"local" => "allowed",
"bin" => "forbidden",
"etc" => array(
0 => "allowed",
"hosts" => "forbidden",
"mailcap"=> "allowed"
),
"var" => "allowed",
"tmp" => "allowed"
),
"root" =>"forbidden"
);
$t->go_through_tree();
print $t->outp;
______________________________________________________________________
This is a completely recursive structure and I think, it is clear, how
to create it with a recursive call of a function. If not, see the
example below.
One little quirk has to be explained, because it is a little bit
confusing: the array name 0 (zero) is used for the value of the parent
element. As shown in the example, an element with children (for
example "etc") cannot have attributes (such as "allowed"). Instead
the value of this element is stored in a pseudo-child named 0. If this
element is not present, it will have the value "Array" (perhaps
something that should be changed).
The output of this example if you don't change the output-functions
will look like this:
______________________________________________________________________
/
^---- usr->'allowed' : 'usr' (1) [1/2]
| ^---- lib->'forbidden' : 'usr^lib' (2) [2/7]
| O---- local->'allowed' : 'usr^local' (2) [3/7]
| O---- bin->'forbidden' : 'usr^bin' (2) [4/7]
| O---- etc->'allowed' : 'usr^etc' (2) [5/7]
| | ^---- hosts->'forbidden' : 'usr^etc^hosts' (3) [2/3]
| | \--- mailcap->'allowed' : 'usr^etc^mailcap' (3) [3/3]
| O---- var->'allowed' : 'usr^var' (2) [6/7]
| \--- tmp->'allowed' : 'usr^tmp' (2) [7/7]
\--- root->'forbidden' : 'root' (1) [2/2]
______________________________________________________________________
Looks a bit confusing. From left to right the fields are
· The _i_n_d_e_x_-_n_a_m_e of the current field
· The _v_a_l_u_e of this field
· The _f_u_l_l _p_a_t_h to this field (see path_to_*-functions)
· The current _d_e_p_t_h or _l_e_v_e_l
· The current _e_l_e_m_e_n_t _n_u_m_b_e_r. See below to understand, why it will
begin sometimes with "2" in this example!
· The _n_u_m_b_e_r _o_f _e_l_e_m_e_n_t_s in the subtree at this depth
55..55..44.. EExxaammppllee
My example is just going trough the directory structure of your hard
disk.
The following code could read it:
______________________________________________________________________
class dir_Tree extends Tree {
var $classname = "dir_Tree";
var $delimiter="/";
var $tdat;
function build_tree ($path=".") {
$this->tree=$this->recurs_dir($path,0);
}
## This example code can read and output 1000 directory entries with
## many subdirs in about 20 seconds on my system (P200, 64 MB);
## 220 dir entries with a maximum depth of 4 are read in 2 seconds.
## This is ok. :)
function recurs_dir ($path,$depth) {
GLOBAL $flap_out;
$d=opendir($path);
while ( $name=readdir($d) ) {
$pathname=$path . $this->delimiter . $name;
if (is_dir($pathname) && !ereg("\.\.?",$pathname)) {
if (isset($flap_out[$pathname])) {
$array[$name]=$this->recurs_dir($pathname,$depth+1);
}
# ATTENTION: It is IMPORTANT fill the [0] array
# *after* filling the rest of the array!
$array[$name][0]=$pathname;
} else {
$array[$name]=$pathname;
}
}
closedir($d);
return($array);
}
#################################################
## FLAPPING IN and OUT
## This is used to create an array which includes
## all sub-paths which should be showed
##
function flapping ($path) {
GLOBAL $flap_out;
if ($path) {
if (is_dir($path)) {
if (isset($flap_out[$path])) {
unset($flap_out[$path]);
} else {
$flap_out[$path]=$name;
}
}
}
}
}
$t= new dir_Tree;
$t->flapping($val); ## $val is given by GET-method, see *tree()-functions
$t->build_tree();
$t->go_through_tree();
print $t->outp;
______________________________________________________________________
With this code it is very easy to flap in and out whole parts of the
tree. Send the path via GET-method and put this path in flapping().
The whole $flap_out-array must be persistent (e.g. via _s_e_s_s_i_o_n).
Perhaps you can program a garbage collection, which will look into
$flap_out and check for paths that already exist?
55..55..55.. KKnnoowwnn BBuuggss // TTiippss
There is one known bug: If a name of a subpath contains the
$delimiter-string. This cannot be solved correctly and you have to
look for it when you create the tree.
The same thing is with the value [0] (zero) of a sub-array. This
element is always used as the attribute of the parent element.
A good tip: when you build your tree recursively then the [0]-index
must be filled _a_f_t_e_r the subtree is returned from recursive call. See
in the example above what I mean. I think it's a PHP3 specialty.
Also it is possible that not every name could be inserted into the
associate index-field (Control-chars etc.), but this is untested.
55..66.. SSTTRRIINNGGSS22 ffuunnccttiioonn sseett
This is a set of functions, which are used very often by me.
They are so easy, that I now stop describing and simply insert the
code. Perhaps the next revision of this set I will replace it with a
better description:
______________________________________________________________________
num_rows(),
## "Found only one matching record",
## "Found %s records");
## Will output if num_rows() is 1: "Found only one matching record"
## 200: "Found 200 records"
##
## if $val is empty() or "" a blank string will be returned!
##
function o_1or2 ($val,$str1,$str2) {
if (isset($val) && $val) {
if (1==$val) {
return(sprintf($str1,$val));
} else {
return(sprintf($str2,$val));
}
} else {
return(false);
}
}
function p_1or2 ($val,$str1,$str2) {
print o_1or2 ($val,$str1,$str2);
}
##
## This is for the case, that you want to output something
## if $val is false e.g.
##
## p_0or1($faxnumber,"THERE IS NO FAXNUMBER","Faxnumber: %s");
##
function o_0or1 ($val,$str1,$str2) {
if (empty($val) || !$val) {
if (isset($val)) {
return(sprintf($str1,$val));
} else {
return($str1);
}
} else {
return(sprintf($str2,$val));
}
}
function p_0or1 ($val,$str1,$str2) {
print o_0or1 ($val,$str1,$str2);
}
##
## Replaces all blank-chars with
## This function is used, when you are not willing to let the browser
## break your lines an can be used instead of -Tag
## as very compatible replacement
##
## can also be replaced by a true whitespace which has in
## ISO-latin-1 the code 160
##
function o_nonbsp ($val) {
return(ereg_replace("[[:blank:]\n\r]"," ",$val));
}
function p_nonbsp ($val) {
print o_nonbsp($val);
}
?>
______________________________________________________________________
66.. AAcckknnoowwlleeddggmmeennttss
The initial idea on how to do serialization was contributed by KH Wild
to the php3 mailing list. It was limited to serializing arrays of at
most three dimensions, though. We worked on his solution, improving it
to arrays of arbitrary depth and later rewrote the function from
scratch, turning it upside down. Our new serialization code can now
handle any first order data type available to PHP and is easily
extensible. It is also encapsulated in a class, keeping the name space
clean. While we were at it, we made session cookies more secure by not
using uniquid() directly, but a md5() hash of uniqid().
Cameron Taggart and Guarneri Carmelo contributed ODBC support.
Szandor van Verseveld contributed PostgreSQL support. Scott McMullan
contributed some nice ideas for cleanup and is working on Sybase
support.
Sascha Schumann contributed much time developing and extending PHPLIB,
including but not limited to mSQL/Sybase support, general storage
container support, shared memory and LDAP support.
Alexander Aulbach submitted his Tree class. Jay Bloodworth contributed
his excellent OOH Forms library for form generation and input
validation.
A lot of people provided helpful hints and occasionally patches.
Please see the file CREDITS for a complete list of contributors,
testers and inspirations.