  PHP Base Library Documentation, Release phplib_7
  Boris Erdmann, be@shonline.de,         Kristian Khntopp,
  kk@shonline.de     and Sascha Schumann, sascha@schumann.cx
  $Id: head.isgml,v 1.2 1999/07/07 20:43:43 sas 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.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_Shm
        3.4.1 Instance variables
        3.4.2 Example
     3.5 CT_Ldap
        3.5.1 Instance variables
        3.5.2 Example
     3.6 CT_Dbm
        3.6.1 Instance variables
        3.6.2 Example
     3.7 Session
        3.7.1 Instance variables
        3.7.2 Instance methods
           3.7.2.1 Accessible instance methods
           3.7.2.2 Internal instance methods
        3.7.3 Example
        3.7.4 Using "auto_init"
        3.7.5 Unregistering variables and deleting sessions
        3.7.6 Reading and understanding session data for debugging
        3.7.7 How "serialize()" operates
     3.8 Auth
        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 default authentication
        3.8.5 Using Challenge-Response Authentication
        3.8.6 The complete guide to authentication and user variables
           3.8.6.1 How is the
           3.8.6.2 How does
           3.8.6.3 How do $sess and $auth interact?
           3.8.6.4 Where is the beef?
           3.8.6.5 I still do not understand! What am I supposed to code?
           3.8.6.6 Ok, I did that and it works. I even understood it. Now, what exactly is that uid used for?
           3.8.6.7 But is the uid used internally by PHPLIB?
     3.9 Perm
        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 How permissions work
     3.10 User
        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

  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

  5. HTML Widgets Classes

     5.1 Graph_Line
        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 Sql_Query
        5.2.1 Instance variables
        5.2.2 Instance methods
           5.2.2.1 Accessible instance methods
           5.2.2.2 Internal instance methods
        5.2.3 Example
     5.3 Table and CSV_Table
        5.3.1 Instance variables
        5.3.2 Instance methods
           5.3.2.1 Accessible instance methods
           5.3.2.2 Internal instance methods
        5.3.3 Example
     5.4 Form
        5.4.1 Using OOH Forms
        5.4.2 Customizing OOH Forms
     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
  7. Example configuration



  ______________________________________________________________________

  11..  QQuuiicckk SSttaarrtt

  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.


  11..11..  LLiicceennssee


  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.



  11..22..  TTaarrggeett GGrroouupp aanndd PPrreerreeqquuiissiitteess


  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.5 or newer for this release of the library) and access to a
  supported SQL database (currently, PHPLIB supports MySQL, PostgreSQL,
  mSQL, Oracle 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).




  11..33..  QQuuiicckk GGuuiiddee ttoo IInnssttaallllaattiioonn


  These instructions apply to PHPLIB running with CGI PHP. Most of them
  is valid for mod_php as well, though.

  Before installing PHPLIB, get your web server up and running and have
  it executing files with the extension .php3. Check that with a simple
  <?php phpinfo() ?> 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.


     SStteepp 11
        Create an include directory named php parallel to your web
        servers document root directory.


     SStteepp 22
        Unpack your PHPLIB distribution. Move the contents of the php
        distribution directory into the php directory you just created.


     SStteepp 33
        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.


     SStteepp 44
        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.


     SStteepp 55
        cd into the php include directory. Edit local.inc.  In class
        DB_Example supply the appropriate parameters for your database
        connection.


     SStteepp 66
        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.


     SStteepp 77
        Move the contents of the pages directory and all its
        subdirectories into your document root directory.


     SStteepp 88
        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.


     SStteepp 99
        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.


     SStteepp 1100
        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.


  11..44..  UUssiinngg ccoorree ffeeaattuurreess ooff PPHHPPLLIIBB

  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.


     CCuussttoommiizziinngg tthhee llooggiinn ssccrreeeenn
        Edit loginform.ihtml in the include directory to suit your
        needs.


     CCuussttoommiizziinngg tthhee ppeerrmmiissssiioonn lleevveellss
        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.


     CCrreeaattiinngg NNeeww UUsseerrss
        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");.


     CCrreeaattiinngg aann uunnpprrootteecctteedd sseessssiioonn ppaaggee
        Begin that page with



          ___________________________________________________________________
          <?php page_open(array("sess" => "Example_Session")); ?>
          ___________________________________________________________________




     End that page with




     ___________________________________________________________________
     <?php page_close(); ?>
     ___________________________________________________________________





     CCrreeaattiinngg aa pprrootteecctteedd sseessssiioonn ppaaggee
        Begin that page with



          ___________________________________________________________________
          <?php
            page_open(
              array("sess" => "Example_Session",
                    "auth" => "Example_Auth",
                    "perm" => "Example_Perm"));
            $perm->check("desired protection");
          ?>
          ___________________________________________________________________




     and end that page with



          ___________________________________________________________________
          <?php page_close(); ?>
          ___________________________________________________________________





     CCrreeaattiinngg pprrootteecctteedd ffuunnccttiioonnaalliittyy
        Begin that page with



          ___________________________________________________________________
          <?php
            page_open(
              array("sess" => "Example_Session",
                    "auth" => "Example_Auth",
                    "perm" => "Example_Perm"));
          ?>
          ___________________________________________________________________




     and end that page with



          ___________________________________________________________________
          <?php page_close(); ?>
          ___________________________________________________________________




     Enclose the protected functionality in



          ___________________________________________________________________
          <?php
            if ($perm->have_perm("desired protection")):
          ?>
          Put protected HTML or PHP here
          <?php
            endif
          ?>
          ___________________________________________________________________




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


     GGeettttiinngg aa ggrriipp oonn PPHHPPLLIIBB
        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...


  11..55..  TTeessttiinngg


  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.


     CChheecckkiinngg tthhaatt tthhee wweebb sseerrvveerr iiss uupp aanndd rruunnnniinngg
        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.

     CChheecckkiinngg tthhaatt tthhee wweebb sseerrvveerr iiss eexxeeccuuttiinngg CCGGII pprrooggrraammss
        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.


     CChheecckkiinngg tthhaatt tthhee PPHHPP iinntteerrpprreetteerr iiss rruunnnniinngg ((AAssssuummiinngg CCGGII
        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 <?php phpinfo() ?> 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.5)?
     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.


     PPHHPP iinntteerrpprreetteerr ((AAssssuummiinngg mmoodd__pphhpp))

        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.


     CChheecckkiinngg PPHHPPLLIIBB iinncclluussiioonn
        Does you PHP include PHPLIB properly? Check your php3.ini file.
        It must include the following settings:



          ___________________________________________________________________
          include_path = <em/pathname to directory with all the .inc files/
          auto_prepend_file = <em/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:




     ___________________________________________________________________
     <?php
     $db = new DB_Sql;
     print "It works without error messages.<br>\n";
      ?>
     ___________________________________________________________________





     CChheecckkiinngg ddaattaabbaassee ccoonnnneeccttiivviittyy
        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:



          ___________________________________________________________________
          <?php
            include("table.inc"); // requires include_path to be functioning

            $db = new DB_Example;
            $db->query("select * from active_sessions");

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


     CChheecckkiinngg tthhaatt sseessssiioonnss wwoorrkk
        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.


     CChheecckkiinngg tthhaatt AAuutthheennttiiccaattiioonn wwoorrkkss
        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.


  22..  OOvveerrvviieeww aanndd IInnssttaallllaattiioonn


  The following sections discuss the layout of 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?


  22..11..  FFiilleess,, ccllaasssseess aanndd ffuunnccttiioonnss


  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. Details are outlined below.


  22..11..11..  CCuussttoommiizzaattiioonn

  The following three files are the only files from PHPLIB that require
  changes in normal PHPLIB applications.


     AApppplliiccaattiioonn ccoonnffiigguurraattiioonn iinn 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.


     AApppplliiccaattiioonn sseettuupp iinn 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.


     SSeelleeccttiioonn ooff aauuttoommaattiiccaallllyy llooaaddeedd ccllaasssseess iinn 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.


  22..11..22..  CCoorree ffuunnccttiioonnaalliittyy


  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.


     CCllaassss DB_Sql defined in exactly one of
        db_mysql.inc, db_msql.inc, db_pgsql.inc, db_odbc.inc,db_ora-
        cle.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.


     CCllaassss 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.


     CCllaassss 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.


     CCllaassss 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.


     CCllaassss 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.


     ffuunnccttiioonnss page_open() and page_close()
        defined in page.inc:" Setup and Shutdown functions, must be
        present on any session page.


        Depend on Session.


  22..11..33..  EExxtteennddeedd ffuunnccttiioonnaalliittyy


  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.


  22..11..44..  HHTTMMLL wwiiddggeettss


  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.


     Graph in graph.inc
        Create a linegraph gif image from some array data.


        An independent class.


     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.


  22..22..  DDoowwnnllooaaddiinngg aanndd uunnppaacckkiinngg tthhee ddiissttrriibbuuttiioonn

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


  22..33..  RReeqquuiirreemmeennttss aanndd tthhiinnggss ttoo cchheecckk ffoorr

  22..33..11..  IInntteerrpprreetteerr rreeqquuiirreemmeennttss


  The PHP base library requires a working web server with CGI capability
  and the CGI version of PHP 3.0.5 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 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.

  _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 <?php as the PHP command introducer.

  _N_o_t_e_: PHPLIB does not require magic_quotes_gpc to be enabled. The
  library always uses addslashes() when necessary.


  22..33..22..  DDaattaabbaassee rreeqquuiirreemmeennttss

  The PHP base library requires a database connection. Currently MySQL
  is fully supported and PostgreSQL, mSQL, ODBC and Oracle have limited
  support (the limitation is only relevant if you intend to access
  metadata information, i.e. table definitions and the like). Database
  interfaces are not difficult to write and Adabas-D and Sybase are
  likely to be supported in the future.

  You need a database server connection with select, insert, update and
  delete privileges from your CGI environment. You need create and drop
  privileges from an administrative account outside your CGI environment
  as well.

  PHPLIB core functionality requires two tables as part of your
  application table name space: active_sessions (select, insert, update
  and delete privilege required for the application user) and auth_user
  (select privilege required for the application user. insert, update
  and delete privilege required for the application user if user
  management is to be done from within the application).

  Extended functionality may require additional tables.


  22..33..33..  NNaammee ssppaaccee rreeqquuiirreemmeennttss


  PHPLIB tries to be as name space neutral as possible with its core
  features. Is issues no HTML by default and it occupies only few names
  in the global name space. These are the class names for the classes
  defined: DB_Sql, DB_SAM, Session, Auth, Perm, User. Additionally, the
  classnames DB_Example, Example_Session, Example_Auth,
  Example_Challenge_Auth, Example_Perm and Example_User are defined by
  the sample setup in local.inc, but these names can and shall be
  customized by the application developer. PHPLIB defines the function
  names page_open(), page_close, sess_load() and sess_save() for the
  page management functions. The global variable $_PHPLIB (a hash) is
  taken. Only if page_open() is being used, globals are defined by the
  library by default, but one global for each "feature" requested in the
  page_open() statement is taken. These are at most $sess, $user, $auth
  and $perm.

  Including extension functionality or HTML widgets may occupy
  additional classnames, function names or variables in the global name
  space.


  22..33..44..  YYeeaarr 22000000 ccoommpplliiaannccee ssttaatteemmeenntt


  PHPLIB uses date fields within the column changed in the table
  active_sessions in your database.  The changed field is used in
  garbage collection, that is, to clean out abandoned sessions. The date
  field is a 14 character field of the format YYYYMMDDhhmmss, that is,
  the date field has four digit years and will cope properly with the
  new millennium.

  PHPLIB sets cookies in the client browser. These cookies by default
  have session lifetime, that is, they do not expire but are not written
  to disk. Date calculations are not involved.

  It is possible to have PHPLIB set cookies with a limited lifetime by
  defining the $lifetime slot of the Session class.  If this is done, it
  depends on the date handling of the client browser and client
  operating system, if the result is Y2K compliant. There are known
  issues with longterm cookies and any browser on MS-DOS/Windows 3.11
  systems.

  PHPLIB does some date arithmetic internally that involves mktime() and
  date() functions of the PHP3 language and Unix time_t data types. The
  signed 32 bit Unix time_t data type counts seconds since 01-Jan-1970
  Midnight GMT and will overflow sometime in the year 2038.

  PHPLIB itself will function up to 2038 and longer, if the Unix time_t
  is being extended in time. PHPLIB does not protect you from date and
  Y2K issues in your PHPLIB application, the PHP3 interpreter, the
  server operating system oder server software, the client browser, the
  client operating system or other parts of your installation.


  22..44..  IInnssttaallllaattiioonn pprroocceedduurree



     LLiibbrraarryy SSeettuupp
        Create a directory php next to your cgi:



          ___________________________________________________________________
          /home/www/servers/poe.shonline.de/pages   <- document root
                                            cgi     <- php binary
                                            php     <- includes and prepends
          ___________________________________________________________________



     Make this php directory your php include directory: Put
     include_path    = /home/www/servers/poe.shonline.de/php into
     cgi/php3.ini. If you already have an include path defined in your
     setup, add the PHPLIB include path to the existing include path
     using the separator character applicable for your operating system
     (":" on UNIX, ";" on Windows).  Defining an include path will not
     actually include code on your pages; it only tells the PHP
     interpreter in which directories to look for files referenced in
     require() and include() statements.

     Into the php directory go all the files from the php directory of
     the distribution. Into documentroot, put all the files and
     directories from the pages directory of the distribution.

     Have documentation.txt handy and read it.


     DDaattaabbaassee aacccceessss wwiitthh MMyySSQQLL
        The following information applies to MySQL only. No information
        is provided for other database servers as of now. You are
        encouraged to copy this section, adapt it for your database
        server and submit it to the authors. It will be included in
        further releases of PHPLIB.

        Edit prepend.php3. Change the first require() statement to
        require("db_mysql.inc");. This will include the MySQL database
        interface (Interfaces for other databases are provided in
        db_<databasename>.inc. The require() statement has to be adapted
        to reflect this).

        Assuming your database server is named database.shonline.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.


     MMeerrggiinngg tthhee lliibbrraarryy wwiitthh yyoouurr PPHHPP ffiilleess
        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/poe.shonline.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, 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.


     SSuubbssccrriibbee ffoorr ssuuppppoorrtt
        Subscribe to the mailing list phplib@shonline.de. Do so by
        sending a mail body of subscribe to phplib-request@shonline.de
        and follow instructions. Share your experiences.


  22..55..  UUssiinngg iinncclluuddee(())  iinnsstteeaadd ooff aauuttoo__pprreeppeenndd__ffiillee==

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


  22..66..  PPHHPPLLIIBB wwiitthh mmoodd__pphhpp ((AAppaacchhee mmoodduullee))


  The following was contributed by Giancarlo Pinerolo on the PHPLIB
  mailing list.

  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 php.ini file, located by default, on Unix, in
  /usr/local/lib.

  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.

  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)
  ______________________________________________________________________




  When you write these directives in your per-directory .htaccess files
  and in httpd.conf, you have to prepend them with php3_ and omit the =
  sign.

  That is, the php3.ini statement include_path = /home/www/phplib
  becomes php3_include_path /home/www/phplib in an Apache configuration
  file.

  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 <VirtualHost> block or in an .htaccess file in the
  client directory.


  33..  CCoorree FFuunnccttiioonnaalliittyy

  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.


  33..11..  DDBB__SSqqll

  DB_Sql is used by CT_Sql and Auth to access a SQL database. You are
  encouraged to use it directly, too.





  33..11..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.




                       Internal instance variables.


  33..11..22..  IInnssttaannccee mmeetthhooddss



  33..11..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss



     qquueerryy(($$qquueerryy__ssttrriinngg))
        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.


     nneexxtt__rreeccoorrdd(())
        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 Autofree is true,
        free_result() is called automatically before false is returned.


     sseeeekk(($$ppooss))
        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 Autofree 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.


     mmeettaaddaattaa(($$ttaabbllee))
        $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.

        NOTE: At the moment, the PostgreSQL and ODBC interface only
        report the table, name and type data reliably. You are
        encouraged to fix this.


     ttaabbllee__nnaammeess(())
        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


     nnuumm__rroowwss(()),, nnff(())
        Returns the number of rows returned by the current SELECT query.


     aaffffeecctteedd__rroowwss(())
        Returns the number of rows affected by the current INSERT,
        UPDATE or DELETE query.


     nnuumm__ffiieellddss(())
        Returns the number of columns returned by the current query.


     nnpp(())
        Prints the number of rows returned by the current query.


     ff(($$ffiieelldd))
        Identical to accessing Record[$field].


     pp(($$ffiieelldd))
        Identical to printing Record[$field].


  33..11..22..22..  IInntteerrnnaall iinnssttaannccee mmeetthhooddss



     ccoonnnneecctt(())
        Used internally to generate a Link_ID, if necessary. Link
        creation is implicit, there is no need to call connect()
        manually, ever.


     hhaalltt(($$mmssgg))
        Used by query() if the initial database connection cannot be
        made or the target database does not exist. Prints an error
        message, then halts Script execution.


  33..11..33..  EExxaammppllee

  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 halt($msg) {
      printf("</td></table><b>Database error:</b> %s<br>\n", $msg);
      printf("<b>MySQL Error</b>: %s (%s)<br>\n",
        $this->Errno, $this->Error);
      printf("Please contact shopmaster@doma.in and report the ");
      printf("exact error message.<br>\n");
      die("Session halted.");
    }
  }
  ______________________________________________________________________




  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("<tr><td>%s</td><td>%s</td></tr>\n",
           $q->f("art_id"),
           $q->f("article"));
       }
       ______________________________________________________________________





  33..11..44..  AAddddiittiioonnaall iinnffoorrmmaattiioonn aabboouutt ddaattaabbaassee ccoonnnneeccttiioonnss


  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.


  33..22..  PPaaggee MMaannaaggeemmeenntt



  33..22..11..  AAcccceessssiibbllee FFuunnccttiioonnss


  Page Management currently consists a collection of functions:


     ppaaggee__ooppeenn((aarrrraayy((
        This function is to be called with an array of page
        features/classname pairs. Valid features are at the moment:


        sseessss
           This page makes use of session variables.


        aauutthh
           This page uses session authentication. If you specify the
           auth feature, you MUST specify the sess feature, also.


        ppeerrmm
           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.


        uusseerr
           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.


     ppaaggee__cclloossee(())

        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.


     sseessss__llooaadd((aarrrraayy((


        _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


     sseessss__ssaavvee((aarrrraayy((
        _A_d_v_a_n_c_e_d _f_e_a_t_u_r_e. @@TODO



  33..22..22..  EExxaammppllee




       ______________________________________________________________________
       <?php
         page_open(array("sess" => "Shop_Session"));
         $sess->register("s");  // See "Session" below for explanation.
        ?>
       <html>
       <h1><?php print ++$s ?></h1>
       </html>
       <?php page_close(); ?>
       ______________________________________________________________________





  33..22..33..  TThhee ""ccaarrtt"" ffeeaattuurree iiss ggoonnee


  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.


  33..33..  CCTT__SSqqll



  To let Session use a SQL database as a container, you use CT_Sql. This
  uses a DB_Sql like class so you can access all supported databases
  with this container.


  33..33..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.


  33..33..22..  EExxaammppllee


  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.


  33..44..  CCTT__SShhmm


  To let Session use shared memory as container, you use CT_Shm.


  33..44..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.


  33..44..22..  EExxaammppllee


  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.


  33..55..  CCTT__LLddaapp


  To let Session use a LDAP database as a container, you use CT_Ldap.


  33..55..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.


  33..55..22..  EExxaammppllee


  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.


  33..66..  CCTT__DDbbmm


  To let Session use a DBM database file as a container, you use CT_Dbm.




  33..66..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.


  33..66..22..  EExxaammppllee


  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.


  33..77..  SSeessssiioonn


  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.


  33..77..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.




                       Internal instance variables.


  33..77..22..  IInnssttaannccee mmeetthhooddss



  33..77..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss



     rreeggiisstteerr(($$vvaarrnnaammee))
        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:


        ccllaassssnnaammee
           A string with the name of the objects class.

         ppeerrssiisstteenntt__sslloottss
           An array with the names of all object slots to save.


     uunnrreeggiisstteerr(($$vvaarrnnaammee))
        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.


     ddeelleettee(())
        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.


     uurrll(($$uurrll))
        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.


     ppuurrll(($$uurrll))
        A shorthand for print $this->url($url);


     sseellff__uurrll(())
        Return an URL referencing the current page, including PHP_SELF
        and QUERY_STRING information.  If in get mode, the session id is
        included.


     ppsseellff__uurrll(())
        A shorthand for print $this->self_url().


     aadddd__qquueerryy(($$qqaarrrraayy))

        Return string to be appended to the current URL for parameters
        in GET query format. Intended usage is like this:





     ___________________________________________________________________

     <a href="<<?
     $sess->pself_url().$sess->padd_query(array("again"=>"yes"))
     ?>"> Reload</a> and log in?
     ___________________________________________________________________





     ppaadddd__qquueerryy(($$qqaarrrraayy))

        A shorthand for print $this-> add_query($qarray).


     rreeiimmppoorrtt__ggeett__vvaarrss(())

        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.


     rreeiimmppoorrtt__ppoosstt__vvaarrss(())
        See reimport_get_vars().


     rreeiimmppoorrtt__ccooookkiiee__vvaarrss(())
        See reimport_get_vars().


  33..77..22..22..  IInntteerrnnaall iinnssttaannccee mmeetthhooddss



     ggeett__iidd(())
        See get_id().


     ggeett__iidd(($$iidd__ttoo__uussee))
        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).


     ppuutt__iidd(())
        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.


     sseerriiaalliizzee(($$pprreeffiixx,, &&$$ssttrr))
        serialize() is used internally to append to str all PHP code
        needed to reconstruct the variable named in prefix.


     ffrreeeezzee(())
        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.


     tthhaaww(())
        thaw() loads a set of freeze()ed variables for the current
        session id and session name out of the database and recreates
        them.


     ggcc(())
        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.


     rreeiimmppoorrtt__aannyy__vvaarrss(($$aarrrraayynnaammee))
        Used to implement the three official reimport functions.


     ssttaarrtt(())
        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.




  33..77..33..  EExxaammppllee

  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.


  33..77..44..  UUssiinngg ""aauuttoo__iinniitt""


  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:



       ______________________________________________________________________
       <?php
       global $lang;   // application language
       $lang = "de";   // german by default
       $sess->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.


  33..77..55..  UUnnrreeggiisstteerriinngg vvaarriiaabblleess aanndd ddeelleettiinngg sseessssiioonnss

  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.



       ______________________________________________________________________
       <?php
         page_open(array("sess" => "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

       ?>
       ______________________________________________________________________





  33..77..66..  RReeaaddiinngg aanndd uunnddeerrssttaannddiinngg sseessssiioonn ddaattaa ffoorr ddeebbuuggggiinngg

  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.


  33..77..77..  HHooww ""sseerriiaalliizzee(())"" ooppeerraatteess


  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.


  33..88..  AAuutthh


  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:


  +o  It can be undone: A session can be un-authenticated, the user can
     "log out".

  +o  It can expire: A session can automatically be un-authenticated
     after a given idle time.

  +o  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.

  +o  It is database based. Authentication is being done against a
     database of your design, not a htpasswd text file.

  +o  It is per page. You decide on a per-page basis which pages are
     authenticated and which aren't.

  +o  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.

  +o  It works with CGI PHP. HTTP authentication is available only in
     mod_php.

  +o  It is integrated with a permission checking scheme.


  33..88..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.




                       Internal instance variables.


  33..88..22..  IInnssttaannccee mmeetthhooddss



  33..88..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss



     uurrll(())

        A function that can be used in auth_loginform()a and
        auth_registerform. It returns the appropriate "action="
        attribute to the form tag.


     ppuurrll(())

        A function that can be used in auth_loginform()a and
        auth_registerform. It prints the appropriate "action=" attribute
        to the form tag.


     llooggiinn__iiff(($$tt))
        A function that can be used to change the current user identity
        from nobody in using default authentication mode.  See the
        section and example on using default authentication below.


     uunnaauutthh(($$nnoobbooddyy == ffaallssee))
        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"].


     llooggoouutt(($$nnoobbooddyy == $$tthhiiss-->>nnoobbooddyy))
        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).


     iiss__aauutthheennttiiccaatteedd(())
        Will return false, if the current authentication is invalid or
        expired. Will return the authenticated uid otherwise.



     aauutthh__llooggiinnffoorrmm(())

        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.


     aauutthh__vvaalliiddaatteellooggiinn(())
        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.


     aauutthh__rreeggiisstteerrffoorrmm(())
        See auth_doregister().


     aauutthh__ddoorreeggiisstteerr(())
        These functions mirror auth_loginform() and auth_validatelogin()
        in registration mode.


  33..88..22..22..  IInntteerrnnaall iinnssttaannccee mmeetthhooddss



     ssttaarrtt(())

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


  33..88..33..  EExxaammppllee

  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"));
       ______________________________________________________________________





  33..88..44..  UUssiinngg ddeeffaauulltt aauutthheennttiiccaattiioonn

  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.




  ______________________________________________________________________
  <?php
    // using Default Authentication
    page_open(array("sess" => "My_Session", "auth" => "My_Default_Auth"));
    $auth->login_if($again);

    if ($auth->auth["uid"] == "nobody"):
  ?>
    <A HREF="<?php $sess-<purl("$PHP_SELF?again=yes") ?>">Relogin</A>
    to this page.
  <?php endif ?>
  ______________________________________________________________________





  33..88..55..  UUssiinngg CChhaalllleennggee--RReessppoonnssee AAuutthheennttiiccaattiioonn

  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.


  33..88..66..  TThhee ccoommpplleettee gguuiiddee ttoo aauutthheennttiiccaattiioonn aanndd uusseerr vvaarriiaabblleess


  This feature has originally been written for the PHPLIB mailing list
  by Kristian Khntopp and was included into the documentation later.


  33..88..66..11..  HHooww iiss tthhee AAuutthh  ccllaassss uusseedd uussuuaallllyy??


  Usually, you write code like this into the top of the page you want to
  protect:


       ______________________________________________________________________
       <?php
       page_open(array(
           "sess" => "My_Session",
           "auth" => "My_Auth"));
       ?>

       <!-- your code here -->

       <?php
       page_close()
       ?>
       ______________________________________________________________________





  33..88..66..22..  HHooww ddooeess $$aauutthh  wwoorrkk iinntteerrnnaallllyy??


  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 <!-- your
  code here --> 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 <!-- your code here --> 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, <!-- your code
  here --> 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).



  33..88..66..33..  HHooww ddoo $$sseessss aanndd $$aauutthh iinntteerraacctt??


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


  33..88..66..44..  WWhheerree iiss tthhee bbeeeeff??


  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.


  33..88..66..55..  II ssttiillll ddoo nnoott uunnddeerrssttaanndd!! WWhhaatt aamm II ssuuppppoosseedd ttoo ccooddee??



  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.


  33..88..66..66..  OOkk,, II ddiidd tthhaatt aanndd iitt wwoorrkkss.. II eevveenn uunnddeerrssttoooodd iitt.. NNooww,, wwhhaatt
  eexxaaccttllyy iiss tthhaatt uuiidd uusseedd ffoorr??


  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.


  33..88..66..77..  BBuutt iiss tthhee uuiidd uusseedd iinntteerrnnaallllyy bbyy PPHHPPLLIIBB??



  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.


  33..99..  PPeerrmm


  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.


  33..99..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.


  33..99..22..  IInnssttaannccee mmeetthhooddss



  33..99..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss



     cchheecckk(($$rreeqquuiirreedd))
        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.


     hhaavvee__ppeerrmm(($$rreeqquuiirreedd))
        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.


     ppeerrmm__sseell(($$nnaammee,, $$ccuurrrreenntt ==
        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.
  33..99..22..22..  IInntteerrnnaall iinnssttaannccee mmeetthhooddss




     ppeerrmmssuumm(($$rriigghhttss))
        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.


     ppeerrmm__iinnvvaalliidd(($$ddooeess__hhaavvee,, $$mmuusstt__hhaavvee))
        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.


  33..99..33..  EExxaammppllee


  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:



       ______________________________________________________________________
       <?php
         if ($perm->have_perm("admin")):
        ?>
         <h1>Admin only functionality</h1>
       <?php
         endif;
        ?>
       ______________________________________________________________________





  33..99..44..  HHooww ppeerrmmiissssiioonnss wwoorrkk



  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.



  33..1100..  UUsseerr


  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.



  33..1100..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.







                       Internal instance variables.
  33..1100..22..  IInnssttaannccee mmeetthhooddss



  33..1100..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss



     rreeggiisstteerr(($$vvaarrnnaammee))
        Works as expected.


     uunnrreeggiisstteerr(($$vvaarrnnaammee))
        Works as expected.


     ddeelleettee(())
        Works as expected.


     uurrll(($$uurrll))
        Not useful with User.


     ppuurrll(($$uurrll))
        Not useful with User.


     sseellff__uurrll(())
        Not useful with User.


     ppsseellff__uurrll(())
        Not useful with User.


     rreeiimmppoorrtt__ggeett__vvaarrss(())
        Works as expected.


     rreeiimmppoorrtt__ppoosstt__vvaarrss(())
        Works as expected.


     rreeiimmppoorrtt__ccooookkiiee__vvaarrss(())
        Works as expected.



  33..1100..22..22..  IInntteerrnnaall iinnssttaannccee mmeetthhooddss



     ggeett__iidd(())
        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.


     ppuutt__iidd(())
        Empty. Not useful with User.

     sseerriiaalliizzee(($$pprreeffiixx,, &&$$ssttrr))
        Works as expected.


     ffrreeeezzee(())
        Works as expected.


     tthhaaww(())
        Works as expected.


     ggcc(())
        Works as expected. You do not want to use it, though.


     rreeiimmppoorrtt__aannyy__vvaarrss(($$aarrrraayynnaammee))
        Works as expected.


     ssttaarrtt(())
        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.




  33..1100..33..  EExxaammppllee

  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.


  44..  EExxtteennddeedd ffuunnccttiioonnaalliittyy


  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.


  44..11..  CCaarrtt


  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.




  44..11..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.


  44..11..22..  IInnssttaannccee mmeetthhooddss



  44..11..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss




     cchheecckk(($$aarrtt))

        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.


     rreesseett(())

        Deletes all items in current cart, resetting $this->currentItem
        to 1. Always returns true.


     ttoott__aarrttss(())
        Returns the number of articles in the current shopping cart, or
        false if cart is empty.


     aadddd__iitteemm(($$aarrtt,, $$nnuumm))
        Add $num many articles of article number $art to the current
        shopping cart. Returns the position number of $art in the
        shopping cart.


     rreemmoovvee__iitteemm
        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.


     sseett__iitteemm
        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.

     sshhooww__aallll(())


        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.


        sshhooww__iitteemm(($$aarrtt,, $$nnuumm))
           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.


        sshhooww__ccaarrtt__ooppeenn(())
           This function should be provided by the user. It renders the
           prologue HTML to display a shopping cart listing.


        sshhooww__ccaarrtt__cclloossee(())
           This function should be provided by the user. It renders the
           epilogue HTML to display a shopping cart listing.


     44..11..33..  EExxaammppllee


     UUssee aa ssuubbccllaassss ooff 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("<table class=cart_table>\n");
     $this->sum = 0; }

     function show_cart_close() { printf("</table>\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(" <tr class=cart_row>\n
     <td class=cart_cell>%s</td>\n", $this->db->Record["name"]);
     printf("  <td class=cart_cell>%s</td>\n",
     $this->db->Record["price"]); printf("  <td
     class=cart_cell>%s</td>\n", $num); $rowsum = $num *
     $this->db->Record["price"]; $this->sum += $rowsum; printf("  <td
     class=cart_cell>%s</td>\n", $rowsum); printf(" </tr>\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?
          ___________________________________________________________________





     44..11..44..  OOnn uussiinngg CCaarrtt

     TToo mmaakkee uussee ooff tthhee CCaarrtt ccllaassss,, yyoouu nneeeedd ttoo ddeeffiinnee aa nneeww ttaabbllee iinn
     yyoouurr ddaattaabbaassee tthhaatt lliissttss aallll aarrttiicclleess yyoouu sshhoopp sshhoouulldd sseellll..  WWiitthh
     PPHHPPLLIIBB aanndd MMyySSQQLL wwee rreeccoommmmeenndd tthhaatt yyoouu ccrreeaattee aa nneeww iinnssttaannccee ooff
     PPHHPPLLIIBB ffoorr eeaacchh vviirrttuuaall wweebb sseerrvveerr aanndd aa nneeww ddaattaabbaassee ffoorr eeaacchh
     ccuussttoommeerr.. TThhiiss ddaattaabbaassee sshhoouulldd hhoolldd tthhee aaccttiivvee__sseessssiioonnss aanndd
     aauutthh__uusseerr ttaabblleess aass wweellll aass aallll aapppplliiccaattiioonn ssppeecciiffiicc ttaabblleess lliikkee
     ffoorr eexxaammppllee tthhee aarrttiiccllee lliisstt.. IInn ootthheerr wwoorrddss,, wwiitthh MMyySSQQLL wwee
     ssttrroonnggllyy ddiissccoouurraaggee tthhaatt yyoouu uussee PPHHPPLLIIBB aanndd tthhee MMyySSQQLL ddiirreeccttiivvee 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:


     +o  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.

     +o  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 addi-
     tional code will teach your shopping cart about the database table
     that holds your articles and so on.

     +o  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 function context.

     In setup.inc, create a global instance of Example_Cart named $cart
     and register that variable with PHPLIB:



          ___________________________________________________________________
          <?php
            global $cart;
            $cart = new Example_Cart;

            // $sess is already global
            $sess->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.


     55..  HHTTMMLL WWiiddggeettss CCllaasssseess

     55..11..  GGrraapphh__LLiinnee


     TThhee GGrraapphh__LLiinnee ccllaassss iiss aann iinnddeeppeennddeenntt ccllaassss tthhaatt ccaann bbee uusseedd ttoo
     ddrraaww ssiimmppllee lliinnee ggrraapphhss.. CCoonnssiiddeerr tthhiiss ccllaassss ttoo bbee eexxppeerriimmeennttaall,,
     tthhee iinntteerrffaaccee wwiillll cchhaannggee iinn ffuuttuurree vveerrssiioonnss.. IItt wwiillll aallssoo hhaavvee
     lleessss bbuuggss..


     55..11..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.




                       Internal instance variables.


     55..11..22..  IInnssttaannccee mmeetthhooddss



     55..11..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss




        ssttaarrtt(($$wwiiddtthh,, $$hheeiigghhtt,, $$ttiittllee,, $$yyllaabbeell,, $$xxllaabbeell))


           Start a new line graph with the given title, x- and y-Axis
           labels and of the given width and height. All parameters are
           optional from right to left. The default title, ylabel and
           xlabel values are empty (no labels are drawn), the default
           width and height are 500 and 200 pixels.


        yynnuumm__llaabbeellss(($$nnuummbbeerr))
           Label the y-axis of the result graph numerically with $number
           many labels. Text labels take precedence over numeric labels.


        xxnnuumm__llaabbeellss(($$nnuummbbeerr))
           Label the x-axis of the result graph numerically with $number
           many labels. Text labels take precedence over numeric labels.


        sseettyyllaabbeellss(($$llaabbeellss))
           $labels is an array of strings. These strings are used to
           label the y-axis. If this function is being used, numeric
           labels cannot be used on this axis.


        sseettxxllaabbeellss(($$llaabbeellss))
           $labels is an array of strings. These strings are used to
           label the x-axis. If this function is being used, numeric
           labels cannot be used on this axis.


        ggrriidd(($$xxnnuumm,, $$yynnuumm))
           Draw a grid of $xnum by $ynum lines.


        ccrreeaattee__ccoolloorr(($$nnaammee,, $$rr,, $$gg,, $$bb))
           Create a color named $name with the given RGB values in the
           color dictionary. The following default colors are in the
           color dictionary: "black" (0,0,0), "white" (255,255,255),
           "red" (255,0,0), "green" (0,255,0), "blue" (0,0,255),
           "yellow" (255,255,0), "magenta" (255,0,255), "cyan"
           (0,255,255).


        pplloott(($$ppooiinnttss,, $$ccoolloorr,, $$nnaammee))
           Plot the y-values in the array named points in the given
           color and create a matching legend entry with the given name.
           The name is optional; if it is missing, no legend entry is
           being created.  If no plot() ever created a legend entry, no
           legend is created, freeing additional space at the right
           border of the resulting plot. The color is optional; if it is
           missing, the foreground color is used.


        ddrraaww(())
           Use this function to emit the resulting image, content-type
           header and all.



     55..11..22..22..  IInntteerrnnaall iinnssttaannccee mmeetthhooddss


        ddrraaww__ttiittllee(($$ttiittllee))
           Draw the title $title and reserve an appropriate amount of
           border space for the title.


        ddrraaww__yyllaabbeell(($$llaabbeell))
           Draw the y-axis label and reserve an appropriate amount of
           border space for the label.

        ddrraaww__xxllaabbeell(($$llaabbeell))
           Draw the x-axis label and reserve an appropriate amount of
           border space for the label.


        mmaakkee__ccoolloorrss(())
           Create the default color dictionary with the eight standard
           colors: "black" (0,0,0), "white" (255,255,255), "red"
           (255,0,0), "green" (0,255,0), "blue" (0,0,255), "yellow"
           (255,255,0), "magenta" (255,0,255), "cyan" (0,255,255).


        ddrraaww__lliinneess(())
           This function does all the work.



     55..11..33..  EExxaammppllee


     UUssee GGrraapphh__LLiinnee ddiirreeccttllyy ttoo ddrraaww lliinnee ggrraapphhss.. TThhiiss eexxaammppllee wwiillll ddrraaww
     aa lliinnee ggrraapphh wwiitthh rraannddoomm nnuummbbeerrss..



          ___________________________________________________________________
          <?php
           include("graph.inc");

           srand(time());
           for ($i=0; $i<40; $i++) {
             $a[$i] = rand(0, 10);
             $b[$i] = rand(0, 10);
           }

           $g = new Graph_Line;

           $g->start(500, 200, "Random Graph"); // set it up
           $g->ynum_labels(10);                 // numeric and alpha labels
           $g->setxlabels(array("start", "middle", "end"));
           $g->grid(10, 10);                    // a grid

           $g->plot($a, "red", "a data");       // plot the data
           $g->plot($b, "blue", "b data");
           $g->draw();                          // create image

           ?>
          ___________________________________________________________________





     55..22..  SSqqll__QQuueerryy

     SSqqll__QQuueerryy wwiillll ggeenneerraattee aa qquueerryy ffoorrmm ffoorr ssiimmppllee ttaabbllee qquueerriieess:: AA
     lliisstt ooff ffiieelldd nnaammeess,, ccoommppaarriissiioonn ooppeerraattoorrss aanndd iinnppuutt ffiieellddss iiss
     pprreesseenntteedd.. TThhee uusseerr mmaayy sseeaarrcchh ffoorr aannyy vvaalluueess iinn aannyy ooff tthhee
     pprreesseenntteedd ccoolluummnnss uussiinngg SSQQLL ssttaannddaarrdd ooppeerraattoorrss.. MMuullttiippllee qquueerryy
     ccoonnddiittiioonnss aarree ppoossssiibbllee aanndd tthheessee ccoonnddiittiioonnss ccaann bbee jjooiinneedd uussiinngg
     AANNDD aanndd OORR ooppeerraattiioonnss..

     TThhee nnuummbbeerr ooff qquueerryy ccoonnddiittiioonnss ccaann bbee mmaaddee vvaarriiaabbllee.. IIff ssoo,, tthhee
     uusseerr mmaayy sshhrriinnkk aanndd ggrrooww tthhee qquueerryy wwiiddggeett uussiinngg tthhee aapppprroopprriiaattee
     bbuuttttoonnss..

     AAllll bbuuttttoonn llaabbeellss aanndd ootthheerr mmeessssaaggeess ooff tthhee iinntteerrffaaccee aarree vvaarriiaabbllee
     aanndd hheelldd iinn llaanngguuaaggee ddiiccttiioonnaarriieess.. CCuurrrreennttllyy,, _d_e aanndd _e_n
     ddiiccttiioonnaarriieess aarree pprroovviiddeedd..


     55..22..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.




                       Internal instance variables.


     55..22..22..  IInnssttaannccee mmeetthhooddss



     55..22..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss




        ssttaarrtt(())

           Initialization function. Currently empty.


        ffoorrmm(($$bbaassee,, $$ooppttiioonn,, $$ccllaassss,, $$ttaarrggeett))

           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.


        wwhheerree(($$bbaassee,, $$iinnccrr))

           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.



     55..22..22..22..  IInntteerrnnaall iinnssttaannccee mmeetthhooddss


        ppllaaiinn__wwhheerree(($$bbaassee))
           This function does all the work for where(), but does not
           resize the query condition window.


     55..22..33..  EExxaammppllee


     TThhee 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.


































     ___________________________________________________________________
     <?php
       // We require() sqlquery.inc and table.inc in prepend.inc
       // to make this example work!
       page_open(array("sess" => "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..
     ?>
     <html>
     <head><title>Testseite</title>
     <style type="text/css"><!--
     h1          { font-family: arial, helvetica, sans-serif; color: #d33e30 }
     table.test  { background-color: #eeeeee }
     th.test     { font-family: arial, helvetica, sans-serif  }
     td.test     { font-family: arial, helvetica, sans-serif }
     table.query { background-color: #cccccc }
     td.query    { font-face: arial, helvetica, sans-serif }
     --></style>
     </head>
     <body bgcolor="#ffffff">
     <h1>Testpage</h1>
     <?php
       // the following fields are selectable
       $field = array(
         "username"   => "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("<hr>");

       // Do we have a valid query string?
       if ($query) {
         // Show that condition
         printf("Query Condition = %s<br>\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<br>\n", $db->num_rows());
         $t->show_result($db, "test");
       }

       page_close();
     ?>
     </body>
     </html>
     ___________________________________________________________________






     55..33..  TTaabbllee aanndd CCSSVV__TTaabbllee


     TThhee TTaabbllee ccllaassss iiss aa nneeaatt wwaayy ttoo ffoorrmmaatt ttwwoo--ddiimmeennssiioonnaall aassssoocciiaattiivvee
     aarrrraayyss ooff ddaattaa oorr tthhee rreessuullttss ooff aa ddaattaabbaassee qquueerryy iinnttoo aa ttaabbllee..
     TTaabbllee aanndd iittss ssuubbccllaasssseess aallllooww yyoouu ttoo ssiimmppllyy ppaassss tthheemm eeiitthheerr aann
     aarrrraayy oorr aa qquueerryy rreessuulltt aanndd tthheeyy ssppiitt oouutt tthhee pprrooppeerr HHTTMMLL ffoorr aa
     ttaabbllee ccoonnttaaiinniinngg aallll tthhee vvaalluueess.. TTaabbllee hhaass ssoommee pprriimmiittiivvee ffiilltteerriinngg
     ccaappaabbiilliittiieess mmaakkiinngg iitt uusseeffuull eevveenn wwiitthhoouutt ssuubbccllaassssiinngg,, bbuutt ffoorr tthhee
     ffuullll ppoowweerr ooff TTaabbllee yyoouu hhaavvee ttoo wwrriittee yyoouurr oowwnn ssuubbccllaassss..


     WWhheenn uusseedd wwiitthh tthhee cchheecckk ooppttiioonn,, iitt iiss aassssuummeedd tthhaatt tthhee ttaabbllee iiss
     ppaarrtt ooff aa HHTTMMLL 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.


     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.



     55..33..11..  IInnssttaannccee vvaarriiaabblleess






                      Accessible instance variables.
     55..33..22..  IInnssttaannccee mmeetthhooddss



     55..33..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss




        sshhooww(($$aarryy,, $$ccllaassss ==

           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.


        sshhooww__ppaaggee(($$aarryy,, $$ssttaarrtt,, $$nnuumm,, $$ccllaassss ==
           Just as show(), but will show only num elements starting at
           start.


        sshhooww__rreessuulltt(($$ddbb,, $$ccllaassss ==
           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.


        sshhooww__rreessuulltt__ppaaggee(($$ddbb,, $$ssttaarrtt,, $$nnuumm,, $$ccllaassss ==

           Just as show_result(), but will show only num elements
           starting at start.



     55..33..22..22..  IInntteerrnnaall iinnssttaannccee mmeetthhooddss



        sseelleecctt__ccoollnnaammeess(($$ddaattaa))
           Internal function to generate a list of column names.


        ttaabbllee__hheeaaddiinngg__rrooww(($$ddaattaa,, $$ccllaassss ==
           Internal driver function to generate a table heading row.


        ttaabbllee__rrooww(($$ddaattaa,, $$ccllaassss ==
           Internal driver function to generate a table row.


        ttaabbllee__ooppeenn(($$ccllaassss ==
           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("<table%s>\n", $class?" class=$class":"");).



        ttaabbllee__cclloossee(())
           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("<table>\n");/).


        ttaabbllee__rrooww__ooppeenn(($$rrooww,, $$ddaattaa,, $$ccllaassss ==
           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.


        ttaabbllee__rrooww__cclloossee(())
           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.


        ttaabbllee__cceellll(($$rrooww,, $$cceellll,, $$kkeeyy,, $$vvaall,, $$ccllaassss))
           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.


        ttaabbllee__hheeaaddiinngg__cceellll(($$ccooll,, $$vvaall,, $$ccllaassss))
           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.


     55..33..33..  EExxaammppllee

     TTaabbllee iiss nnoott aauuttoommaattiiccaallllyy iinncclluuddeedd oorr pprreeppeennddeedd iinnttoo eeaacchh ppaaggee..
     IInncclluuddee tthhee ttaabbllee ccllaassss iinnttoo tthhee ppaaggeess tthhaatt aarree ttoo uussee TTaabbllee..  TThheenn
     ccrreeaattee aann iinnssttaannccee ooff TTaabbllee::



          ___________________________________________________________________
          <?php
            // Include Table
            require("table.inc");

            // make a Table instance
            $t = new Table;

            // We want table headings to be printed.
            $t->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");
          ___________________________________________________________________





     55..44..  FFoorrmm

     TThhee ffoorrmm ccllaassss ((ssoommeettiimmeess ccaalllleedd OOOOHH FFoorrmmss)) iiss aa ccoonnvveenniieennccee
     lliibbrraarryy ffoorr ddeeaalliinngg wwiitthh hhttmmll ffoorrmmss..  IItt pprroovviiddeess JJaavvaassccrriipptt aanndd
     sseerrvveerr--ssiiddee ffoorrmm vvaalliiddaattiioonn,, aanndd iiss ccuussttoommiizzaabbllee aanndd eexxtteennssiibbllee..


     55..44..11..  UUssiinngg OOOOHH FFoorrmmss

     TThhee OOOOHH FFoorrmmss lliibbrraarryy ccoonnssiissttss ooff ffiivvee ffiilleess:: 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.



        ssttaarrtt(($$jjvvssnnaammee,,$$mmeetthhoodd,,$$aaccttiioonn,, $$ttaarrggeett))
           Outputs the initial <form> tag and sets up some initial state
           needed by the class.  All of the arguments are optional,
           though you'll usually want to use at least one in order to
           get Javascript validation.  $jvsname is an arbitrary string
           used to link the Javascript to the form; if it is empty (the
           default), no Javascript validation is provided.  $method is
           the HTTP method used to submit the form; default is "POST".
           $action is the URL that receives the form submission; default
           is $PHP_SELF.  $target is the frame target for the form
           results; default is _self.


        ffiinniisshh(($$aafftteerr,,$$bbeeffoorree))
           Outputs the any hidden fields that were added to the form,
           the final </form> 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.


        aadddd__eelleemmeenntt(($$eelleemmeenntt))
           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



           ttyyppee
              The type of element this is; can be "submit", "hidden",
              "text", "textarea", "select", "radio", "checkbox", or
              "file".


           nnaammee
              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).  DDoo nnoott append [] to the name if you want an array
              valued element; set the multiple attribute instead.


           vvaalluuee
              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).


           mmuullttiippllee
              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.


           eexxttrraahhttmmll
              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.


           ssiizzee
              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.


           ppaassss
              If set for a text element, renders the html as a password
              element, i.e. entry displays as asterisks.


           ssrrcc
              If set for a submit element, convert to an image element
              and use the value of src as the source URL for the image.

           mmaaxxlleennggtthh
              Used verbatim as the maxlength html attribute in text
              elements.


           mmiinnlleennggtthh
              If length_e is set, this is the minimum length text
              element entry accepted by the validator.


           lleennggtthh__ee
              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.


           vvaalliidd__ee
              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.


           vvaalliidd__rreeggeexx
              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.


           iiccaassee
              If set, regex matching is case insensitive.


           cchheecckkeedd
              Only used for a checkbox element that does not have
              multiple set.  If checked is set, the element will display
              as checked.


           rroowwss
              Used verbatim as the rows= element in a textarea element.


           ccoollss
              Used verbatim as the cols= element in a textarea element.


           wwrraapp
              Used verbatim as the wrap= element in a textarea element.


           ooppttiioonnss
              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"));
             ________________________________________________________________





        sshhooww__eelleemmeenntt(($$nnaammee,,$$vvaalluuee))
           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.


        llooaadd__ddeeffaauullttss(($$eelleemmeenntt__lliisstt))
           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.


        vvaalliiddaattee(($$rreessuulltt,,$$eelleemmeenntt__lliisstt))
           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.



        ffrreeeezzee(($$eelleemmeenntt__lliisstt))
           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.



     55..44..22..  CCuussttoommiizziinngg OOOOHH FFoorrmmss

     SSiinnccee OOOOHH FFoorrmmss iiss oobbjjeecctt oorriieenntteedd,, iitt ccaann bbee eeaassiillyy ccuussttoommiizzeedd bbyy
     eexxtteennddiinngg tthhee ccllaasssseess tthhaatt ddeeffiinnee tthhee eelleemmeenntt ttyyppeess..  IInn ggeenneerraall,,
     yyoouu mmuusstt mmaakkee ssuurree yyoouurr ddeerriivveedd ccllaassss hhaass aa ccoonnssttrruuccttoorr aanndd yyoouu mmaayy
     oovveerrrriiddee aannyy ooff tthhee sseellff__** ffuunnccttiioonnss ooff of_element.  The source for
     the existing elements is the best documentation for how to do this
     properly, but a few general notes follow.



        sseellff__sshhooww(($$vvaall,,$$wwhhiicchh))
           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.


        sseellff__sshhooww__ffrroozzeenn(($$vvaall,,$$wwhhiicchh))
           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;


        sseellff__vvaalliiddaattee(($$vvaall))
           Validate $val for this element.  If it is valid, return
           false, otherwise return the relevant error string.


        sseellff__pprriinntt__jjss(($$nnddxx__aarrrraayy))
           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.


        sseellff__llooaadd__ddeeffaauullttss(($$vvaall))
           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.



     55..55..  TTrreeee

     TThhee TTrreeee ccllaassss ccaann rreennddeerr ttrreeee ssttrruuccttuurreess ssuucchh aass ddiirreeccttoorryy
     hhiieerraarrcchhiieess aanndd mmeennuu ssttrruuccttuurreess aass HHTTMMLL.. TThhee ssttrruuccttuurree mmuusstt bbee
     ggiivveenn ttoo TTrreeee aass aann nneesstteedd aarrrraayy ooff aarrrraayyss ooff aarrbbiittrraarryy ddeepptthh..

     TThhee iiddeeaa ooff TTrreeee iiss,, tthhaatt tthheerree aarree sseevveerraall mmaatthheemmaattiiccaall mmooddeellss aa
     ttrreeee ccoouulldd bbee vviieewweedd:: OOnnee mmooddeell iiss aa ddaattaa ssttrruuccttuurree lliikkee nneesstteedd
     aarrrraayyss oorr aa ppooiinntteerr ssttrruuccttuurree ffrroomm wwhheerree yyoouu ccaann pprriinntt
     mmuullttiiddiimmeennssiioonnaall ggrraapphhiiccss aanndd ccaann ddoo ootthheerr nneeaatt tthhiinnggss lliikkee
     ddeelleettiinngg oonnee ppaarrtt ooff tthhee ttrreeee oorr iinnsseerrttiinngg aa wwhhoollee ssuubbttrreeee.. BBuutt yyoouu
     ccaann aallssoo iimmaaggiinnee aa ttrreeee aass aa oonnee ddiimmeennssiioonnaall ssttrriinngg oorr aass aa
     sseeqquueennccee ooff ffuunnccttiioonn ccaallllss ((wwhhiicchh iiss nneeaarrllyy tthhee ssaammee iinn tthhee
     mmaatthheemmaattiiccaall sseennssee))..



     TToo ggeenneerraattee HHTTMMLL--ccooddee ffrroomm aa ttrreeee--ssttrruuccttuurree iitt iiss lliikkee tthhiiss:: YYoouu
     nneeeedd aatt tthhee eenndd aa oonnee--ddiimmeennssiioonnaall ssttrriinngg,, wwhhiicchh tteellllss tthhee bbrroowwsseerr
     wwhhaatt ttoo ddoo.. TThhee TTrreeee ccllaassss aassssiissttss yyoouu iinn ggeenneerraattiinngg tthhiiss ssttrriinngg iinn
     tthhiiss wwaayy,, tthhaatt iitt wwiillll ggoo tthhrroouugghh tthhee wwhhoollee ttrreeee aanndd ccaallll sseevveerraall
     ffuunnccttiioonnss oonn eevveerryy ssttaaggee ttrroouugghh tthhee wwaayy.. IItt wwiillll bbee yyoouurr ttaasskk ttoo
     cchhaannggee tthhee ffuunnccttiioonnss,, ssoo tthhaatt aa nniiccee llaayyoouutt wwiillll bbee ggeenneerraatteedd..



     55..55..11..  IInnssttaannccee vvaarriiaabblleess





                      Accessible instance variables.


     55..55..22..  IInnssttaannccee mmeetthhooddss



     55..55..22..11..  AAcccceessssiibbllee iinnssttaannccee mmeetthhooddss



        bbuuiilldd__ttrreeee(())
           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.


        ggoo__ttrroouugghh__ttrreeee(($$kkeeyy==

           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.


        ppaatthh__ttoo__iinnddeexx ((&&$$ppaatthh,,$$kkeeyy==
           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"]
             ________________________________________________________________





        ppaatthh__ttoo__ppaarreenntt ((&&$$ppaatthh))
           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"]
             ________________________________________________________________






        ppaatthh__aadddd (($$ppaatthh,,$$kkeeyy))
           This function is the 'non-call-by-reference-version' of
           path_to_index. It will add the $key to the path and return
           it.


        ppaatthh__ssuubb (($$ppaatthh))
           This function is the 'non-call-by-reference-version' of
           path_to_parent. It will find the parent of path and return
           it.


        ppaatthh__iinnddeexx (($$ppaatthh))
           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.




        ssttaarrttttrreeee (())
           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.

        ggrroowwttrreeee (($$kkeeyy,,$$vvaalluuee,,$$ppaatthh,,$$ddeepptthh,,$$ccoouunntt,,$$ppccoouunntt))
           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.



        lleeaaffttrreeee (($$kkeeyy,,$$vvaalluuee,,$$ppaatthh,,$$ddeepptthh,,$$ccoouunntt,,$$ppccoouunntt))
           This function is called, when the current item has _n_o
           successor.


        sshhrriinnkkttrreeee (($$kkeeyy,,$$ddeepptthh))
           This function is the "opposite" of growtree(). It is called
           every time, when the current item was the last item in this
           sub-list.


        eennddttrreeee(())
           Called when leaving tree.




     55..55..33..  TThhee TTrreeee AArrrraayy

     AAss ssaaiidd aabboovvee,, bbeeffoorree yyoouu ccaallll 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

     +o  The _i_n_d_e_x_-_n_a_m_e of the current field

     +o  The _v_a_l_u_e of this field

     +o  The _f_u_l_l _p_a_t_h to this field (see path_to_*-functions)

     +o  The current _d_e_p_t_h or _l_e_v_e_l

     +o  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!

     +o  The _n_u_m_b_e_r _o_f _e_l_e_m_e_n_t_s in the subtree at this depth



     55..55..44..  EExxaammppllee


     MMyy eexxaammppllee iiss jjuusstt ggooiinngg ttrroouugghh tthhee ddiirreeccttoorryy ssttrruuccttuurree ooff yyoouurr
     hhaarrdd ddiisskk..


     TThhee ffoolllloowwiinngg ccooddee ccoouulldd rreeaadd iitt::










     ___________________________________________________________________
     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?


     55..55..55..  KKnnoowwnn BBuuggss // TTiippss

     TThheerree iiss oonnee kknnoowwnn bbuugg:: IIff aa nnaammee ooff aa ssuubbppaatthh ccoonnttaaiinnss tthhee
     $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.


     55..66..  SSTTRRIINNGGSS22 ffuunnccttiioonn sseett

     TThhiiss iiss aa sseett ooff ffuunnccttiioonnss,, wwhhiicchh aarree uusseedd vveerryy oofftteenn bbyy mmee..

     TThheeyy aarree ssoo eeaassyy,, tthhaatt II nnooww ssttoopp ddeessccrriibbiinngg aanndd ssiimmppllyy iinnsseerrtt tthhee
     ccooddee..  PPeerrhhaappss tthhee nneexxtt rreevviissiioonn ooff tthhiiss sseett II wwiillll rreeppllaaccee iitt wwiitthh
     aa bbeetttteerr ddeessccrriippttiioonn::


































     ___________________________________________________________________
     <?php
     ##
     ## Strings2-Functions
     ##
     ## Copyright (c) 1998,1999 Alex 'SSilk' Aulbach
     ##
     ## These Functions are very practical and if I could program
     ## C a little bit better it will be placed directly in PHP3.
     ## But I can't... :-}
     ##


     ##
     ## Have you ever worried about such constructs like
     ##    echo ($faxnumber) ? sprintf("Fax: %s",$faxnumber) : "";
     ##
     ## This functionset could help you to replace those constructs by
     ##    p_iftrue($faxnumber,"Fax: %s");
     ## which is nearly the half of typing and looks more clear and solves
     ## an error if $faxnumber is unset.
     ##
     function o_iftrue ($val,$str) {
             if (isset($val) && $val) {
                     return(sprintf($str,$val));
             }
     }
     function p_iftrue ($val,$str) {
             print o_iftrue($val,$str);
     }

     ##
     ## Output "One or More"
     ##
     ## This function is good if you want to differ a output by number:
     ##  e.g.  o_1or2($q->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 <NOBR>-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);
     }
     ?>
     ___________________________________________________________________






     66..  AAcckknnoowwlleeddggmmeennttss


     TThhee iinniittiiaall iiddeeaa oonn hhooww ttoo ddoo sseerriiaalliizzaattiioonn wwaass ccoonnttrriibbuutteedd bbyy KKHH
     WWiilldd ttoo tthhee pphhpp33 mmaaiilliinngg lliisstt.. IItt wwaass lliimmiitteedd ttoo sseerriiaalliizziinngg aarrrraayyss
     ooff aatt mmoosstt tthhrreeee ddiimmeennssiioonnss,, tthhoouugghh.. WWee wwoorrkkeedd oonn hhiiss ssoolluuttiioonn,,
     iimmpprroovviinngg iitt ttoo aarrrraayyss ooff aarrbbiittrraarryy ddeepptthh aanndd llaatteerr rreewwrroottee tthhee
     ffuunnccttiioonn ffrroomm ssccrraattcchh,, ttuurrnniinngg iitt uuppssiiddee ddoowwnn.. OOuurr nneeww
     sseerriiaalliizzaattiioonn ccooddee ccaann nnooww hhaannddllee aannyy ffiirrsstt oorrddeerr ddaattaa ttyyppee
     aavvaaiillaabbllee ttoo PPHHPP aanndd iiss eeaassiillyy eexxtteennssiibbllee.. IItt iiss aallssoo eennccaappssuullaatteedd
     iinn aa ccllaassss,, kkeeeeppiinngg tthhee nnaammee ssppaaccee cclleeaann.. WWhhiillee wwee wweerree aatt iitt,, wwee
     mmaaddee sseessssiioonn ccooookkiieess mmoorree sseeccuurree bbyy nnoott uussiinngg uunniiqquuiidd(()) ddiirreeccttllyy,,
     bbuutt aa mmdd55(()) hhaasshh ooff uunniiqqiidd(())..

     CCaammeerroonn TTaaggggaarrtt aanndd GGuuaarrnneerrii CCaarrmmeelloo ccoonnttrriibbuutteedd OODDBBCC ssuuppppoorrtt..
     SSzzaannddoorr vvaann VVeerrsseevveelldd ccoonnttrriibbuutteedd PPoossttggrreeSSQQLL ssuuppppoorrtt.. SSccootttt
     MMccMMuullllaann ccoonnttrriibbuutteedd ssoommee nniiccee iiddeeaass ffoorr cclleeaannuupp aanndd iiss wwoorrkkiinngg oonn
     SSyybbaassee ssuuppppoorrtt..

     SSaasscchhaa SScchhuummaannnn ccoonnttrriibbuutteedd mmuucchh ttiimmee ddeevveellooppiinngg aanndd eexxtteennddiinngg
     PPHHPPLLIIBB,, iinncclluuddiinngg bbuutt nnoott lliimmiitteedd ttoo mmSSQQLL//SSyybbaassee ssuuppppoorrtt,, ggeenneerraall
     ssttoorraaggee ccoonnttaaiinneerr ssuuppppoorrtt,, sshhaarreedd mmeemmoorryy aanndd LLDDAAPP ssuuppppoorrtt..

     AAlleexxaannddeerr AAuullbbaacchh ssuubbmmiitttteedd hhiiss TTrreeee ccllaassss.. JJaayy BBllooooddwwoorrtthh
     ccoonnttrriibbuutteedd hhiiss eexxcceelllleenntt OOOOHH FFoorrmmss lliibbrraarryy ffoorr ffoorrmm ggeenneerraattiioonn aanndd
     iinnppuutt vvaalliiddaattiioonn..

     AA lloott ooff ppeeooppllee pprroovviiddeedd hheellppffuull hhiinnttss aanndd ooccccaassiioonnaallllyy ppaattcchheess..
     PPlleeaassee sseeee tthhee ffiillee CCRREEDDIITTSS ffoorr aa ccoommpplleettee lliisstt ooff ccoonnttrriibbuuttoorrss,,
     tteesstteerrss aanndd iinnssppiirraattiioonnss..


     77..  EExxaammppllee ccoonnffiigguurraattiioonn


     iinnddeexx..pphhpp33 iiss aa ssiimmppllee eexxaammppllee uussiinngg oonnllyy tthhee EExxaammppllee__SSeessssiioonn,,
     ccllaassss.. sshhoowwooffff..pphhpp33 iiss aa mmuucchh mmoorree ccoommpplleexx eexxaammppllee tthhaatt aallssoo mmaakkeess
     uussee ooff EExxaammppllee__AAuutthh aanndd EExxaammppllee__PPeerrmm aass wweellll aass tthhee TTaabbllee ccllaassss ttoo
     ccrreeaattee aa sseessssiioonn ppaaggee tthhaatt iiss aauutthheennttiiccaatteedd aanndd pprrootteecctteedd bbyy
     ppeerrmmiissssiioonnss.. TThhiiss eexxaammppllee nneeeeddss aa ccoorrrreecctt ddaattaabbaassee sseettuupp ttoo
     ffuunnccttiioonn..  ccrreeaattee__ddaattaabbaassee..ssqqll wwiillll ccrreeaattee tthhiiss ddaattaabbaassee sseettuupp ffoorr
     yyoouurr ddaattaabbaassee,, iiff yyoouu aarree uussiinngg MMyySSQQLL..

     llooccaall..iinncc iinncclluuddeess tthhee ddeeffiinniittiioonnss nneecceessssaarryy ttoo ccoonnnneecctt ttoo tthhaatt
     ddaattaabbaassee.. YYoouu MMUUSSTT aaddaapptt tthheessee ddeeffiinniittiioonnss ttoo mmaattcchh yyoouurr llooccaall
     sseettuupp ttoo mmaakkee tthhiiss wwoorrkk:: IItt sshhoouulldd bbee eennoouugghh ttoo aaddaapptt DDBB__EExxaammppllee,,
     tthhee vveerryy ffiirrsstt ccllaassss iinn llooccaall..iinncc ttoo mmaakkee iitt rruunn.. llooccaall..iinncc aallssoo
     iinncclluuddeess aa ccllaassss EExxaammppllee__CChhaalllleennggee__AAuutthh wwhhiicchh iiss uunnuusseedd ((aanndd ccaann
     rreeppllaaccee EExxaammppllee__AAuutthh)).. YYoouu mmaayy wwaanntt ttoo eeiitthheerr ddeelleettee tthhiiss ccllaassss oorr
     EExxaammppllee__AAuutthh,, iiff yyoouu aarree ggooiinngg ttoo uussee llooccaall..iinncc aass aa ssttaarrtt ffoorr yyoouurr
     oowwnn llooccaall..iinncc..


     CCllaassss EExxaammppllee__AAuutthh ddooeess iinncclluuddee llooggiinnffoorrmm..iihhttmmll ttoo ccrreeaattee tthhee llooggiinn
     ssccrreeeenn.. YYoouu mmaayy wwaanntt ttoo uussee tthhiiss ffiillee aass aa ssttaarrtt ffoorr yyoouurr oowwnn llooggiinn
     ssccrreeeennss..

     CCllaassss EExxaammppllee__CChhaalllleennggee__AAuutthh ddooeess iinncclluuddee ccrrllooggiinnffoorrmm..iihhttmmll ttoo
     ccrreeaattee tthhee llooggiinn ssccrreeeenn.. YYoouu mmaayy wwaanntt ttoo uussee tthhiiss ffiillee aass aa ssttaarrtt
     ffoorr yyoouurr oowwnn llooggiinn ssccrreeeennss,, iiff yyoouu uussee CChhaalllleennggee//RReessppoonnssee
     aauutthheennttiiccaattiioonn ((sseeee bbeellooww)).. ccrrllooggiinnffoorrmm..iihhttmmll iinn ttuurrnn rreeffeerreenncceess
     mmdd55..jjss,, aa JJaavvaassccrriipptt iimmpplleemmeennttaattiioonn ooff tthhee mmdd55 hhaasshh aallggoorriitthhmm..


     CCllaassss EExxaammppllee__CChhaalllleennggee__AAuutthh iiss aa mmooddiiffiieedd llooggiinn ssyysstteemm tthhaatt uusseess
     JJaavvaassccrriipptt aanndd aa CChhaalllleennggee//RReessppoonnssee mmeecchhaanniissmm ttoo aavvooiidd tthhaatt tthhee
     uusseerr ppaasssswwoorrdd eevveerr hhiittss tthhee wwiirree.. IItt iiss aattttaacckkaabbllee,, bbeeccaauussee iitt uusseess
     aa sseemmii--pprreeddiiccttaabbllee cchhaalllleennggee ((mmdd55((uunniiqqiidd(("""")))),, bbuutt mmoorree sseeccuurree tthhaann
     ppllaaiinntteexxtt ppaasssswwoorrddss..


























