  PHP Base Library Documentation, Release phplib_7_2
  Boris Erdmann, boris@erdmann.com,         Kristian Khntopp,
  kk@netuse.de     and Sascha Schumann, sascha@schumann.cx
  $Id: documentation.sgml,v 1.9 1999/10/28 22:13:15 kk Exp $
  ____________________________________________________________

  Table of Contents



























































  1. Quick Start

     1.1 License
     1.2 Target Group and Prerequisites
     1.3 Quick Guide to Installation
     1.4 Using core features of PHPLIB
     1.5 Testing

  2. Overview and Installation

     2.1 Files, classes and functions
        2.1.1 Customization
        2.1.2 Core functionality
        2.1.3 Extended functionality
        2.1.4 HTML widgets
     2.2 Downloading and unpacking the distribution
     2.3 Requirements and things to check for
        2.3.1 Interpreter requirements
        2.3.2 Database requirements
        2.3.3 Name space requirements
        2.3.4 Year 2000 compliance statement
     2.4 Installation procedure
     2.5 Using
     2.6 PHPLIB with mod_php (Apache module)

  3. Core Functionality

     3.1 DB_Sql
        3.1.1 Instance variables
        3.1.2 Instance methods
           3.1.2.1 Accessible instance methods
           3.1.2.2 Internal instance methods
        3.1.3 Example
        3.1.4 Additional information about database connections
        3.1.5 Using
     3.2 Page Management
        3.2.1 Accessible Functions
        3.2.2 Example
        3.2.3 The "cart" feature is gone
     3.3 CT_Sql
        3.3.1 Instance variables
        3.3.2 Example
     3.4 CT_Split_Sql
        3.4.1 Instance variables
        3.4.2 Example
     3.5 CT_Shm
        3.5.1 Instance variables
        3.5.2 Example
     3.6 CT_Dbm
        3.6.1 Instance variables
        3.6.2 Example
     3.7 CT_Ldap
        3.7.1 Instance variables
        3.7.2 Example
     3.8 Session
        3.8.1 Instance variables
        3.8.2 Instance methods
           3.8.2.1 Accessible instance methods
           3.8.2.2 Internal instance methods
        3.8.3 Example
        3.8.4 Using "auto_init"
        3.8.5 Unregistering variables and deleting sessions
        3.8.6 Reading and understanding session data for debugging
        3.8.7 How "serialize()" operates
     3.9 Auth
        3.9.1 Instance variables
        3.9.2 Instance methods
           3.9.2.1 Accessible instance methods
           3.9.2.2 Internal instance methods
        3.9.3 Example
        3.9.4 Using default authentication
        3.9.5 Using Challenge-Response Authentication
        3.9.6 The complete guide to authentication and user variables
           3.9.6.1 How is the
           3.9.6.2 How does
           3.9.6.3 How do $sess and $auth interact?
           3.9.6.4 Where is the beef?
           3.9.6.5 I still do not understand! What am I supposed to code?
           3.9.6.6 Ok, I did that and it works. I even understood it. Now, what exactly is that uid used for?
           3.9.6.7 But is the uid used internally by PHPLIB?
     3.10 Perm
        3.10.1 Instance variables
        3.10.2 Instance methods
           3.10.2.1 Accessible instance methods
           3.10.2.2 Internal instance methods
        3.10.3 Example
        3.10.4 How permissions work
     3.11 User
        3.11.1 Instance variables
        3.11.2 Instance methods
           3.11.2.1 Accessible instance methods
           3.11.2.2 Internal instance methods
        3.11.3 Example

  4. Extended functionality

     4.1 Cart
        4.1.1 Instance variables
        4.1.2 Instance methods
           4.1.2.1 Accessible instance methods
        4.1.3 Example
        4.1.4 On using Cart
     4.2 Template
        4.2.1 Instance variables
        4.2.2 Instance methods
           4.2.2.1 Accessible instance methods
           4.2.2.2 Internal instance methods
        4.2.3 Example

  5. HTML Widgets Classes

     5.1 Sql_Query
        5.1.1 Instance variables
        5.1.2 Instance methods
           5.1.2.1 Accessible instance methods
           5.1.2.2 Internal instance methods
        5.1.3 Example
     5.2 Table and CSV_Table
        5.2.1 Instance variables
        5.2.2 Instance methods
           5.2.2.1 High-level instance methods
           5.2.2.2 Mid-level instance methods
           5.2.2.3 Low-level instance methods
        5.2.3 Example
     5.3 Menu
        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 tpl_form
        5.5.1 Instance variables
        5.5.2 Instance methods
           5.5.2.1 Accessible instance methods
           5.5.2.2 Internal instance methods
        5.5.3 Example
     5.6 Tree
        5.6.1 Instance variables
        5.6.2 Instance methods
           5.6.2.1 Accessible instance methods
        5.6.3 The Tree Array
        5.6.4 Example
        5.6.5 Known Bugs / Tips
     5.7 STRINGS2 function set

  6. Acknowledgments



  ______________________________________________________________________

  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.12 or newer for this release of the library) and access to a
  supported SQL database (currently, PHPLIB supports MySQL, PostgreSQL,
  mSQL, Oracle 7 and Oracle 8, Sybase, Microsoft SQL Server and ODBC
  databases). You need to be able to create and drop database tables in
  that database and your web server must be able to execute SELECT,
  INSERT, UPDATE and DELETE statements on these tables.

  Throughout this manual, we assume that you are using the MySQL
  database server. PHPLIB will run with any supported SQL server, but we
  are using MySQL in the development of PHPLIB.

  PHPLIB can be used in conjunction with the CGI version of PHP and with
  mod_php, integrated into Apache. Usage of the CGI version has an
  impact on overall speed, because you cannot take advantage of
  persistent database connection. We recommend the Apache module over
  the CGI version, although we personally use the CGI version for
  various reasons (easier to update and can be run with Apache suexec).

  PHP 4 is still in beta. We do not support deployment of this library
  with beta software.


  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
  are valid for mod_php as well, though. _V_E_R_Y _I_M_P_O_R_T_A_N_T _N_O_T_E_: This is a
  quick installation guide to get you started if you have an
  installation where you control the web server, PHP interpreter and
  database server completely. They are not suitable for a web hosting
  setup where you have only limited to no control over the installation.
  Refer to Chapter 2 of this documentation for the complete installation
  instructions and troubleshooting information.

  Before installing PHPLIB, get your web server up and running and have
  it executing files with the extension .php3. Check that with a simple
  <?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. Do not put the include
        directory below your web servers document root.


     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.

        If you do not have control over your php3.ini file, you did not
        read the _V_E_R_Y _I_M_P_O_R_T_A_N_T _N_O_T_E above.


     SStteepp 44
        Also check that track_vars are enabled. While you are at it, you
        might want to check sendmail_path, if you plan to send mail from
        your application. It has to be set to /usr/lib/sendmail -t on
        most UNIX systems to work.

        If you do not have control over your php3.ini file, you did not
        read the _V_E_R_Y _I_M_P_O_R_T_A_N_T _N_O_T_E above.


     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.

  We do have a support mailing list available under the address
  phplib@lists.netuse.de. To subscribe to the list, send the command
  subscribe to the address phplib-request@lists.netuse.de.


     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.12)? Are your database interfaces present in the output of
     phpinfo()? If not, recompile again.

     Can you access /test2.php3 under the URL /cgi/php/test2.php3 as
     well? If so, you did not compile your PHP interpreter with
     --enable-force-cgi-redirect.  PHPLIB will not work with this
     interpreter. Recompile with the switch being set.


     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 = pathname to directory with all the .inc files
          auto_prepend_file = path to prepend.php3
          track_vars = On
          ___________________________________________________________________




     If PHPLIB is included properly by your setup, the following page
     will execute without errors:



          ___________________________________________________________________
          <?php
          $db = new DB_Example;
          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 auth_user");

            $t = new Table;
            $t->heading = "on";
            $t->show_result($db);
          ?>
          ___________________________________________________________________




     When executing properly, this page will show you the user entry for
     kris, password test, permissions admin from the auth_user table. If
     this does not happen, your DB_Example definition in local.inc is
     broken.


     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 installation, verification and
  layout of PHPLIB: How to install PHPLIB? Which functionality and class
  definitions are contained in which files? How do you layout a web
  server with PHPLIB installed? Which installation options are available
  and how do these affect performance?


  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. You _N_E_V_E_R need to change any other file with
  PHPLIB. 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_sybase.inc, db_mssql.inc, db_oracle.inc or db_oci8.inc:"

        A database access class for your database server. PHPLIB depends
        on the presence of a SQL database server. Depending on the type
        of your database server, you have to select the appropriate
        include file. The file contains the definition of a class DB_Sql
        suitable for your database server.


        The class manages a database connection (connection setup is
        implicit) and result memory is managed automatically.


        An independent class.


     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.


     Template in template.inc:
        Manages templates and variable replacement. Templates can be
        stored in files. They are loaded on demand and variables are
        replaced in these files.


        An independent class. Requires that you add the statement
        require("template.inc") to prepend.php3 or that you include it
        manually on each page where you want to use it.


  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.


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

  The PHPLIB support mailing list is available should you run into
  problems with the library. To subscribe send the command subscribe to
  the mailing list subscription address.


  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.12 or higher installed. Alternatively
  mod_php can be used. Lower versions of PHP do not work at all: The
  session class uses the base64_encode() and base64_decode() functions
  which are known to be buggy in lower versions (up to 3.0.7) of the
  library. Also, the OOH Forms classes are using constructor syntax,
  which has been introduced into the PHP language in 3.0.5 and later
  versions. An issue with the $PHP_SELF variable and CGI PHP has been
  resolved with version 3.0.5 and later. Perl regular expression
  functions are being used in the Template class and these are not
  really avilable up to 3.0.12.

  _N_o_t_e_: If you are using CGI PHP, it _m_u_s_t have been compiled with the
  --enable-force-cgi-redirect switch for $PHP_SELF to have the correct
  value.

  Basically, if PHP_SELF is the exact local part of your $URL, all is
  well. If it instead contains the modified URL with /your cgi-bin/php
  prefixed, you have a buggy version of CGI PHP.  Either upgrade your
  version of PHP or replace all occurrences of $PHP_SELF with $PATH_INFO
  in PHPLIB.

  _N_o_t_e_: PHPLIB requires that you have track_vars compiled in and
  enabled.

  _N_o_t_e_: PHPLIB does not require short_open_tag to be enabled. The
  library always uses <?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 in the default
  setup for storage of session variables, but this can be circumvented
  by selection another storage container type at installation time.
  Currently, storage containers are available for SQL databases (the
  default), SQL databases with limited string length (ct_split_sql.inc),
  System V shared memory (requires a PHP interpreter with SYSVSHM and
  SYSVSEM support), LDAP servers (requires a PHP interpreter with LDAP
  support), flat files, and DBM files.

  Using SQL, currently MySQL is fully supported and PostgreSQL, mSQL,
  Sybase, Microsoft SQL Server, 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 you can easily write your
  own interface.

  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, CT_Sql, Session, Auth, Perm, User.
  Additionally, the classnames DB_Example, Example_CT_Sql,
  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

  mod_php note: The following instructions apply to the CGI version of
  PHP as well as to the module version. If you are working with mod_php,
  you must restart your web server to force a reload of the php3.ini
  file, though.

  If you are using mod_php, you have additional configuration options:
  See the section below on using PHPLIB mit mod_php.



     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/phplib.netuse.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/phplib.netuse.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.netuse.de and
        your CGI user is webuser and you are accessing the database
        myapp, do



          ___________________________________________________________________
          mysql -h database -u webuser myapp
          ___________________________________________________________________




     If it does not work, connect your database as administrator and
     create the proper mysql access permissions. Adapt and run
     create_database.mysql from the stuff subdirectory of the
     distribution to create the databases active_sessions and auth_user
     as well as the sample user kris with password test. Try again to
     connect like shown above. Can you do select * from active_sessions?
     and insert into active_sessions values ("1", "2", "3", "") as well
     as delete from active_sessions? Can you select * from auth_user?

     _N_o_t_e_: Additional database creation scripts are provided for several
     different databases in the stuff directory of the distribution.


     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/phplib.netuse.de/php/prepend.php3 to our
        php3.ini.

        Not all classes are included/required by prepend.php3, only core
        functionality files are: db_xxx.inc, ct_sql.inc, session.inc,
        auth.inc, perm.inc, user.inc, local.inc and page.inc. The
        library provides other, less essential classes that can be
        included manually on a page-by-page basis.  Some classes make
        themselves persistent, if used. These classes require that you
        include their definitions in the prepend.php3 file where
        indicated to function correctly.  See the usage instructions for
        these classes for details.

        Having done this, access /index.php3. The counter should
        increment when that page is being reloaded. Also, checking
        active_sessions in the database should reflect that session.


     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@lists.netuse.de. Do so by
        sending a mail body of subscribe to phplib-
        request@lists.netuse.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))


  Installing PHPLIB onto a web server that has PHP3 as a module
  (actually Apache) mainly differs in where you can set up runtime
  settings for PHP3 itself.  PHP3 can be compiled with a wealth of
  parameters (see the PHP section in phpinfo()), most of which can get
  overridden by the php3.ini file. The location of this file is shows as
  part of the output of phpinfo().

  With PHP3 as a module you have a wider choice on placing these
  settings: they are overridden, in this order, by what is defined in
  httpd.conf and in your per-directory .htaccess file. Directives in
  these files are identical to their php3.ini brothers, but are prefixed
  with php_ to avoid clashes with Apache configuration keywords. Also,
  as they are Apache configuration keywords, they have no equals ("=")
  sign in them. If x=y is a configuration directive from php3.ini, you
  should be using php3_x y within the Apache configuration instead. That
  is, you should prepend php3_ to the keyword and omit the equals sign.
  If you misspell a configuration directive, you will get an error 500
  from your webserver and find more details about the error in the
  logfile you configured with ErrorLog in your webserver setup.

  _E_x_a_m_p_l_e_: If below we talk about setting in your php3.ini the
  configuration



       ______________________________________________________________________
       include_path = "/bla"
       ______________________________________________________________________




  mod_php users may alternatively configure in their httpd.conf the
  following:





  ______________________________________________________________________
  <Directory /home/www/servers/phplib.netuse.de/pages>
  php3_include_path "/bla"
  </Directory>
  ______________________________________________________________________




  Of special interest to PHPLIB users are the following directives:



       ______________________________________________________________________
       ;;;;;;;;;;;;;;;;;
       ; Data Handling ;
       ;;;;;;;;;;;;;;;;;
       magic_quotes_gpc = Off    ; magic quotes for incoming
                                 ; GET/POST/Cookie data
       magic_quotes_runtime = Off; magic quotes for runtime-generated data,
                                 ; e.g. data from SQL, from exec(), etc.
       magic_quotes_sybase = Off ; Use Sybase-style magic quotes
                                 ; (escape ' with '' instead of \')
       track_vars = On           ; enable $PHP_GET_VARS[], $PHP_POST_VARS[]
                                 ; and $PHP_COOKIE_VARS[] arrays

       ; automatically add files before or after any PHP 3.0 document
       auto_prepend_file = (add path to prepend.php3 here)
       auto_append_file  =

       ;;;;;;;;;;;;;;;;;;;;;;;;;
       ; Paths and Directories ;
       ;;;;;;;;;;;;;;;;;;;;;;;;;
       include_path      = (add path to the directory with all .inc files)
       ______________________________________________________________________




  All of this comes very handy when you have multiple virtual hosts
  (e.g. you are an ISP). In this case you can comfortably place the php3
  directives in the <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



     DDBB__SSqqll(($$qquueerryy ==
        Constructor. When creating an instance, you may optionally
        supply a query string.



          ___________________________________________________________________
          $db = new DB_Sql_Subclass("select * from mytable");)
          ___________________________________________________________________





     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 (or false, if Halt_On_Error isn't
        "yes").


     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 Auto_Free is true,
        free_result() is called automatically before false is returned.
     nnuumm__rroowwss(()),, nnff(())
        Returns the number of rows returned by the current SELECT query.

        _N_o_t_e_: This information is not available in all database
        interfaces. Some of the more advanced databases begin to return
        query results asynchronously while the backend is still
        appending result rows. In such environments the complete size of
        the result set is never known.

        You should duplicate your WHERE clause of the query in such
        environments and ask for the COUNT(*). This will be less
        inefficient as it seems as the query path and query result have
        been cached by the database.


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


     hhaallttmmssgg(($$mmssgg))
        This function is called by halt() and will actually print the
        database error message. You may override this method in your
        subclass of DB_Sql and format the error message to be consistent
        with the layout of the rest of your application.  You may also
        add additional error handling such as informing the application
        operator by mail that a database error has occured.


     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 Auto_Free is true, seek() may not be useable, because
        the result set has already been free'ed when next_record() when
        behind the last record of the result set.

        _N_o_t_e_: Not all database interfaces provide a cursor that is
        capable of seeking. This function will be unavailable in such
        environments.


     lliinnkk__iidd(())
        This function will return the current link ID, as returned by
        the pconnect() executed internally by the database class.

        You should not need this information.


     qquueerryy__iidd(())
        This function will return the current result ID, as returned by
        the query() executed internally by the database class.

        You should not need this information.


     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.

        The data returned by metadata() is suitable for passing it to
        the Table class. If you specify the full parameter, an
        additional column meta is added, which is indexed by field name
        and returns the field number of that name. Also, a column
        num_fields is added, containing the width of the table.

        If $table is omitted, the function returns metadata on the
        result of the last executed query.  _N_o_t_e_: This is currently
        implemented only for the MySQL interface.  You are encouraged to
        implement this feature for other interfaces.

        _N_O_T_E_: At the moment, the PostgreSQL and ODBC interface only
        report the table, name and type data reliably. You are
        encouraged to fix this.


     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neexxttiidd(($$sseeqquueennccee__nnaammee))
        This function will return a sequence number from the sequence
        named by $sequence_name. This number is guaranteed to be
        obtained in an atomic manner and can be used as a primary key.


  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. Depending on the
        setting of Halt_On_Error, this method will call haltmsg() to
        report the error.


     ffrreeee(())
        Used internally by next_record() to free the result set, if so
        configured.


  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 haltmsg($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");
         }
       }
       ______________________________________________________________________




  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..11..55..  UUssiinngg nneexxttiidd(())


  The nextid() function can be used to obtain a sequence number which
  can be used as a primary key. The function manages an arbitrary number
  of named sequences, you have to provide the name of a sequence upon
  call.



       ______________________________________________________________________
       $db = new DB_Article;

       $artnr = $db->nextid("article_sequence");
       $query = sprintf("insert into articles ( artnr, ...) values ('%s', ...)",
          $artnr, ...);
       $db->query($query);

       reset($articles);
       while(list($itemnr, $itemdesc) = each($articles)) {
         $itemnr = $db->nextid("item_sequence");
         $query = sprintf("insert into items (artnr, itemnr, ...) values ('%s', '%s', ...)",
           $artnr, $itemnr, ...);
         $db->query($query);
       }
       ______________________________________________________________________








  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


  The Session class used to contain a bit of SQL to read and write
  session data from and to a database. To make sessions database
  independent, this SQL has been isolated and put in a separate class,
  CT_Sql. Session now makes all storage accesses through a container
  class, which may or may not be an SQL container.





  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Spplliitt__SSqqll


  The Session class used to contain a bit of SQL to read and write
  session data from and to a database. To make sessions database
  independent, Session now makes all storage accesses through a
  container class. The CT_split_sql container is very similar to CT_Sql
  container, with the difference that if serialized data exceeds a
  specified amount of bytes, multiple rows will be used to memorized the
  entire field.

  This class is NOT compatible with CT_Sql class, since table layout is
  different and column names are different in order to avoid reserved
  words in various database implementation. This uses a DB_Sql like
  class so you can access all supported databases with this container.


  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_Sql extends CT_Split_Sql {
               var $classname = "My_Sql";
               var $database_table = "active_sessions_split";
               var $database_class = "DB_Session";
               var $split_length = 4096;
       }
       ______________________________________________________________________




  You can then use My_Sql in class Session. Reference it by putting
  "My_Sql" in the "that_class" variable.



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


  The Session class used to contain a bit of SQL to read and write
  session data from and to a database. To make sessions database
  independent, Session now makes all storage accesses through a
  container class. To let Session use shared memory as container, you
  use CT_Shm.


  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_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..66..  CCTT__DDbbmm


  The Session class used to contain a bit of SQL to read and write
  session data from and to a database. To make sessions database
  independent, Session now makes all storage accesses through a
  container class. To let Session use a DBM database file as a
  container, you use CT_Dbm.


  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..  CCTT__LLddaapp


  The Session class used to contain a bit of SQL to read and write
  session data from and to a database. To make sessions database
  independent, Session now makes all storage accesses through a
  container class. To let Session use a LDAP database as a container,
  you use CT_Ldap.


  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.


  33..77..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..88..  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..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



     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.


     iiss__rreeggiisstteerreedd(($$vvaarrnnaammee))
        Returns true if the variable named $varname is registered with
        the session, false otherwise.


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


     hhiiddddeenn__sseessssiioonn(())
        Prints a hidden form element containing the session name and id.
        This function will print the form element unconditionally, which
        is useful to propagate a session id from one web server to
        another, if both share the same stable storage for session data.
        This is useful to share a session id between a HTTPS and HTTP
        server or between several servers in different domains.



     ggeett__hhiiddddeenn__sseessssiioonn(())
        Returns a hidden element containing the session name and id.
        This function will return the form element unconditionally,
        which is useful to propagate a session id from one web server to
        another, if both share the same stable storage for session data.
        This is useful to share a session id between a HTTPS and HTTP
        server or between several servers in different domains.


     hhiiddddeenn__iidd(())
        Adds a hidden form element containing the session name and id,
        if currently in get-mode. Else the function prints nothing.
        This function can be used to generate a form element which will
        propagate the session id in a POST form.


     ggeett__hhiiddddeenn__sseessssiioonn(())
        Returns a hidden element containing the session name and id, if
        currently in get-mode. Else the functions returns the empty
        string. This function can be used to get a string with a form
        element which can propagate the session id in a POST form.



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


     sseett__ccoonnttaaiinneerr(())
        You shall not call this function directly. It is called back by
        the start() function of Session() during initializiation.  It is
        documented so that you can override its implementation in your
        subclass of Session if you know what you are doing.

        This function creates and starts the container class used by
        this instance of session.


     sseett__ttookkeennnnaammee(())
        You shall not call this function directly. It is called back by
        the start() function of Session() during initializiation.  It is
        documented so that you can override its implementation in your
        subclass of Session if you know what you are doing.

        This function determines and sets the internal session name.


     rreelleeaassee__ttookkeenn(())
        You shall not call this function directly. It is called back by
        the start() function of Session() during initializiation.  It is
        documented so that you can override its implementation in your
        subclass of Session if you know what you are doing.

        This function determines the current method of session
        propagation and determines if a new session token has to be
        generated.


     ppuutt__hheeaaddeerrss(())
        You shall not call this function directly. It is called back by
        the start() function of Session() during initializiation.  It is
        documented so that you can override its implementation in your
        subclass of Session if you know what you are doing.

        This function determines which header lines are to be generated
        by the session, including cache control headers.


  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



     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..88..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..88..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..88..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..88..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..88..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..99..  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:


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

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

    It can be customized: You are not limited to user/password pairs.
     Instead you could use a customer number, operator id and a password
     to log in. Also, you have full control over the login screen, which
     is a normal HTML page with logos, help and forms as you see fit.

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

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

    It can be user authenticating and optionally self registering. In
     _r_e_g_i_s_t_r_a_t_i_o_n mode, a user without a valid login is encouraged to
     register and an account is created for this user.

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

    It is integrated with a permission checking scheme.


  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.




                       Internal 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



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

        Since V7.2: Passing $nobody to this method is deprecated.


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

        Since V7.2: Passing $nobody to this method is deprecated.


     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__pprreeaauutthh(())
        This function can be overridden in a subclass to Auth. It is
        being called as the very first step in the authentication
        process and has the opportunity to authenticate the user without
        a loginform being displayed (by deriving all necessary
        information telepathically, or by using cookies, or divining the
        user identities from the incestines of a dead squirrel).

        If it returns a UID value, the user is authenticated and neither
        auth_loginform() nor auth_validatelogin() are called. If it
        returns false, all goes on as usual.


     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effrreesshhllooggiinn(())
        This function is called every refresh minutes. It must refresh
        the authentication informations stored in auth array by
        auth_validatelogin() method. It is not called if the user is
        logged in as nobody.

        It must return true on success, false otherwise (i.e.: the
        userid is no longer valid).

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



     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..99..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..99..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..99..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..99..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..99..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",
           "perm" => "My_Perm")); // see Perm docs for specifics
       $perm->check("admin");            // see Perm docs
       ?>

       <!-- your code here -->

       <?php
       page_close()
       ?>
       ______________________________________________________________________





  33..99..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..99..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..99..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..99..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,
               addslashes($given_name), addslashes($surname),
               addslashes($password));

    // $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..99..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..99..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..1100..  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..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.


  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



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




     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..1100..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..1100..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111..  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11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111..22..  IInnssttaannccee mmeetthhooddss



  33..111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



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



     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111..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.


     nnuumm__iitteemmss(())
        Returns the number of articles in the current shopping cart, or
        false if cart is empty. For compatibility reasons, this function
        is available as tot_arts as well (but will print a warning if
        used by this name).


     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(())
        If the shopping cart is empty, it will call show_empty_cart()
        once and then return.

        Calls show_item_open() once at the beginning of a shopping cart
        listing. Then calls show_item() once for each item in the
        shopping cart. Calls show_item_close() once at the end of a
        shopping cart listing.


     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.


     sshhooww__eemmppttyy__ccaarrtt
        This function should be provided by the user. It should render
        an appropriate message to symolize an empty cart.


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


  Use a subclass of Cart to provide an implementation of show_item().





































  ______________________________________________________________________
  class My_Cart extends Cart {
    var $classname = "My_Cart";

    // Look up article numbers...
    var $database_class = "DB_Article";
    var $database_table = "articles";
    var $db;

    var $sum = 0;

    function show_cart_open() {
      printf("<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

  To make use of the Cart class, you need to define a new table in your
  database that lists all articles you shop should sell.  With PHPLIB
  and MySQL we recommend that you create a new instance of PHPLIB for
  each virtual web server and a new database for each customer. This
  database should hold the active_sessions and auth_user tables as well
  as all application specific tables like for example the article list.
  In other words, with MySQL we strongly discourage that you use PHPLIB
  and the MySQL directive use _d_a_t_a_b_a_s_e___n_a_m_e together. There is no
  support if you do (there is no support if you do as we say, too,
  because PHPLIB is an open source product you are using on your own
  risk, but ...).

  So let us assume you define a very simple new table articles with a
  structure like this:



       ______________________________________________________________________
       #
       # Table structure for table 'articles'
       #
       CREATE TABLE articles (
         name text,
         price float(8,2),
         artid int(11) DEFAULT '0' NOT NULL auto_increment,
         PRIMARY KEY (artid)
       );
       ______________________________________________________________________



  This table has an article number called artid, and for each artid
  there is an article description name and a price. You may extend this
  minimal definition for your purposes by adding article groups, BLOBs
  with article images and more, but this will suffice for our example
  purposes.

  Populate this table with some products that suit your taste.

  The next step is to teach PHPLIB about the cart class. Three steps are
  necessary to do so:


    the Cart class has to be included on every page. Even on that pages
     that do not make use of the Cart class.

     On that pages that use Cart, a cart subclass is instantiated and
     saved. On all subsequent pages, that Cart object is recreated and
     to be able to recreate the Cart object, PHP must know what a Cart
     object is. Since you cannot know which pages a user loads after he
     has put the first item into the Cart, we need to have a definition
     for Cart on _a_l_l pages.

     The proper place to include the Cart definition from cart.inc is
     consequently prepend.php3. Edit prepend.php3 and
     require("cart.inc") as indicated by the comments in that file.

    a subclass of Cart has to be created to suit your tastes.

     Your subclass of Cart will be called Example_Cart in this example.
     You may actually name it as you like, but you have to be
     consistent.

     The definition of Example_Cart goes into local.inc anywhere below
     your definition for Example_Session. It looks like this



       ______________________________________________________________________
       class Example_Cart extends Cart {
         var $classname = "Example_Cart";

       }
       ______________________________________________________________________





  and we will add additional code later in this example. That additional
  code will teach your shopping cart about the database table that holds
  your articles and so on.

    finally, you need to create an instance of your shopping cart class
     so that you have an object that actually holds the articles
     selected by the user.

     We will use a very nifty feature of PHPLIB to create that object
     instance: If you set up PHPLIB properly, it is able to load and
     execute an include file every time a session is being created. We
     call this feature auto_init, after the instance variable of Session
     that controls it.

     Go into local.inc and edit your subclass of Session. You will have
     some code like


  ______________________________________________________________________
  class Example_Session extends Session {
    var $classname = "Example_Session";

  ...
  }
  ______________________________________________________________________





  in your local.inc. Add a line like



       ______________________________________________________________________
         var $auto_init = "setup.inc",
       ______________________________________________________________________





  to your definition of Example_Session and create a file setup.inc in
  the same directory that holds your local.inc.  Whatever code is in
  this file will be executed every time we create a new session. The
  code is being executed after your $sess, $auth and $perm objects are
  loaded and initialized, but does run from within a function context.
  You have to global everything you define to export it from that func
  tion context.

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



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

  44..22..  TTeemmppllaattee

  _N_o_t_e_: If you think that this is like FastTemplates, read carefully. It
  isn't.



  The template class allows you to keep your HTML code in some external
  files which are completely free of PHP code, but contain replacement
  fields. The class provides you with functions which can fill in the
  replacement fields with arbitrary strings. These strings can become
  very large, e.g.  entire tables.



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


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



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



     TTeemmppllaattee(($$rroooott ==
     Constructor. May be called with two optional parameters. The first
     parameter sets the template directories (see set_root(), the second
     parameter sets the policy regarding handling of unknown variables.

     sseett__rroooott(($$rroooott))
     The function checks that $root is an array of valid directory and
     sets these directories as the base directories where templates are
     being loaded from.


     sseett__uunnkknnoowwnnss(($$uunnkknnoowwnnss ==
     The function sets the policy for dealing with unresolved variable
     names. Must be either "remove", "comment" or "keep". If set to
     "keep", those are left untouched. If set to "comment", unresolved
     variable names are transformed into HTML comments reporting the
     error. If set to "remove", unresolved variable names are silently
     removed (the default).


     sseett__ffiillee(($$vvaarrnnaammee,, $$ffiilleennaammee ==
     The function defines a filename for the initial value of a
     variable. It may be called with either a $varname/$filename pair or
     with a hash of $varname/$filename pairs. The files are not loaded
     until they are needed.


     sseett__bblloocckk(($$ppaarreenntt,, $$vvaarrnnaammee,, $$nnaammee ==
     A variable $parent may contain a variable block named by $varname.
     The function removes that block from $parent and replaces it with a
     variable reference named $name. If $name is omitted, it is assumed
     to be the same as $varname.


     sseett__vvaarr(($$vvaarrnnaammee,, $$vvaalluuee ==
     The functions sets the inital value of a variable. It may be called
     with either a $varname/$value pair or with a hash of
     $varname/$value pairs.


     ssuubbsstt(($$vvaarrnnaammee))
     The function returns the value of the variable named $varname, with
     all defined variable values filled in. The resulting string is not
     "finished", that is, the unresolved variable name policy has not
     been applied yet.


     ppssuubbsstt(($$vvaarrnnaammee))
     This is a shorthand for print $this->subst($varname).


     ppaarrssee(($$ttaarrggeett,, $$vvaarrnnaammee,, $$aappppeenndd == ffaallssee))
     The function substitutes the values of all defined variables in the
     variable named $varname and stores or appends the result in the
     variable named $target.

     If $varname is an array of variable names, $append is ignored.  The
     variables named by $varname are being sequentially substituted and
     the result of each substitution step is stored in $target. The
     resulting substitution is available in the variable named by
     $target, as is each intermediate step for the next $varname in
     sequence.


     ppppaarrssee(($$ttaarrggeett,, $$vvaarrnnaammee,, $$aappppeenndd == ffaallssee))
     A shorthand for print $this->parse(...).


     ggeett__vvaarrss(())
     Returns a hash of all defined values, keyed by their names.

     ggeett__vvaarr(($$vvaarrnnaammee))
     Returns the value of the variable named by $varname. If $varname
     references a file and that file has not been loaded, yet, the
     variable will be reported as empty.

     When called with an array of variable names, an hash of values,
     keyed by their names, will be returned.


     ggeett__uunnddeeffiinneedd(($$vvaarrnnaammee))
     The function will return a hash of unresolved variable names in
     $varname, keyed by their names (that is, the hash has the form
     $a[$name] = $name).


     ffiinniisshh(($$ssttrr))
     The function will returned the finished version of $str, that is,
     the policy regarding unresolved variable names will be applied to
     $str.


     pp(($$vvaarrnnaammee))
     The function will print the finished version of the value of the
     variable named by $varname.


     ggeett(($$vvaarrnnaammee))
     The function will return the finished version of the value of the
     variable named by $varname.


     hhaallttmmssgg(($$mmssgg))
     This function can be overridden by your subclass of Template. It
     will be called with an error message to print.


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


     ffiilleennaammee(($$ffiilleennaammee))
        When called with a relative pathname, this function will return
        the pathname with the appropriate directory from $this->root
        prepended. Absolute pathnames are taken unchanged.

        The resulting filename must exist in any base directory, or an
        error is generated.


     vvaarrnnaammee(($$vvaarrnnaammee))
        The function will construct a variable name regexp for a given
        variable name.


     llooaaddffiillee(($$vvaarrnnaammee))
        If a variable is undefined or empty and is backed by a filename,
        the backing file will be loaded and the files contents will be
        assigned as the variables value.


     hhaalltt(($$mmssgg))
        This function is called whenever an error occurs and will handle
        the error according to the policy defined in
        $this->halt_on_error.



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

  The class manages a set of variables which are text strings.  These
  strings may contain references to other variables in the form of
  "{variable}". When parsed or substituted, a variable reference is
  being replaced by the value of that variable. For example, if you



       ______________________________________________________________________
       <?php
         $t = new Template;

         $t->set_var("a", "defined as hugo");
         $t->set_var("b", "the value of a is {a}");

         print $t->subst("b")
       ______________________________________________________________________




  will print the value of a is defined as hugo.

  A variable value may be defined manually by calling set_var("name",
  "value"); or it may be defined from a file by calling set_file("name",
  "filename.ihtml");. In the latter case, the contents of the file are
  being loaded when needed (as late as possible) and set as the value of
  that variable.

  A third way to define a variable value is to call set_block("parent",
  "block", "name");. In this case, the variable named parent is being
  searched for a block that starts with <!-- BEGIN block --> and ends
  with <!-- END block -->. This string is removed from the variable
  parent and assigned to the variable named block. In parent, a variable
  reference to name is placed instead. If the optional parameter "name"
  is left out, "block" is being used instead.

  For example, if you



       ______________________________________________________________________
       <?php
         $t = new Template;

         $t->set_var("a", "front matter

         <!-- BEGIN b -->
         this is block b
         <!-- END b -->

         end matter");
         $t->set_block("a", "b", "bb");
       ?>
       ______________________________________________________________________




  this will define the variable a as front matter {bb} end matter and
  the variable b as this is block b. All of this will become more clear
  when you set the instance variable debug to 7 in the following example
  and trace the variable accesses.


  Use Template direcly or define a subclass of Template as needed.

  Define a template file named page.ihtml as follows:



       ______________________________________________________________________
       <html>
        <head><title>{PAGETITLE}</title></head>
        <body bgcolor="#ffffff">
        <table border=1 cellpadding=4 cellspacing=0 bgcolor="#eeeeee">
         <tr>
          <td colspan=2><h1>{PAGETITLE}</h1></td>
         </tr>
         <tr>
          <td>{OUT}</td>
          <td>Content<br>{UNDEFINED_VARIABLE}</td>
         </tr>
        </table>
        </body>
       </html>
       ______________________________________________________________________




  This file contains a reference to the variable PAGETITLE and a
  reference to the variable named OUT. Another reference to the variable
  named UNDEFINED_VARIABLE is never resolved. Another template file,
  named box.ihtml, contains a block named row with three variable
  references {TITLE}, {NUM} and {BIGNUM}:



       ______________________________________________________________________
       <!-- start box.ihtml -->
       <table border=1 bgcolor="#cccccc" cellpadding=4 cellspacing=0>
        <tr>
         <td colspan=2><b>{TITLE}</b></td>
        </tr>
         <!-- BEGIN row -->
         <tr>
          <td>{NUM}</td>
          <td>{BIGNUM}
         </tr>
         <!-- END row -->
       </table>
       <!-- end box.ihtml -->
       ______________________________________________________________________




  The following php3 file demonstrates how to use these templates:












  ______________________________________________________________________
  <?php
    /* include the Template class */
    include("template.inc");

    /* create an instance of Template with desired parameters */
    $t = new Template("/home/kris/www/test.koehntopp.de/pages/template", "keep");
    /* $t->debug = 7; */ /* activate for full debugging */

    /* define two variables from files */
    $t->set_file(array(
       "page" => "page.ihtml",
       "box"  => "box.ihtml"));

    /* define a variable contained in another */
    $t->set_block("box", "row", "rows");

    /* define two variables manually */
    $t->set_var(array("TITLE"     => "Testseite",
                      "PAGETITLE" => "hugo"));

    for ($i=1; $i<=3; $i++) {
      $n  = $i;
      $nn = $i*10;

      /* define values for NUM and BIGNUM */
      $t->set_var(array("NUM" => $n, "BIGNUM" => $nn));

      /* replace NUM and BIGNUM in row and
       * append the result to rows */
      $t->parse("rows", "row", true);
    }

    /* replace all variables in box, result in OUT,
     * then replace all variables in page, result in OUT */
    $t->parse("OUT", array("box", "page"));

    /* print OUT */
    $t->p("OUT");
  ?>
  <hr>
  Undefined variables in OUT:
  <?php
    print @implode(", ", $t->get_undefined("OUT"));
   ?>
  ______________________________________________________________________







  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..  SSqqll__QQuueerryy

  Sql_Query will generate a query form for simple table queries: A list
  of field names, comparision operators and input fields is presented.
  The user may search for any values in any of the presented columns
  using SQL standard operators. Multiple query conditions are possible
  and these conditions can be joined using AND and OR operations.

  The number of query conditions can be made variable. If so, the user
  may shrink and grow the query widget using the appropriate buttons.

  All button labels and other messages of the interface are variable and
  held in language dictionaries. Currently, _d_e and _e_n dictionaries are
  provided.


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

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


     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..11..33..  EExxaammppllee


  The Sql_Query class can be used directly. It is more useful when made
  persistent, so it is recommended that you add the line
  require("sqlquery.inc") to your prepend.php3 file where indicated in
  that file.

  See the Table class in this section for a nice method to display and
  format query results. See the DB_Sql class (a core class) for a nice
  method to connect to databases.

  The following code fragment is quite large, but contains a complete
  and working example using the Sql_Query, DB_Sql and Table classes to
  query a database table.


































  ______________________________________________________________________
  <?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..22..  TTaabbllee aanndd CCSSVV__TTaabbllee


  The Table class is a neat way to format two-dimensional associative
  arrays of data or the results of a database query into a table. Table
  and its subclasses allow you to simply pass them either an array or a
  query result and they spit out the proper HTML for a table containing
  all the values. Table has some primitive filtering capabilities making
  it useful even without subclassing, but for the full power of Table
  you have to write your own subclass.


  When used with the check option, it is assumed that the table is part
  of a HTML FORM element. Code is generated to create an INPUT
  TYPE=CHECKBOX element before each table row. The checkboxes will form
  an array indexed by row number. The name of the array will whatever
  you set the check instance variable to.


  Exactly one of two types of possible column filtering take place when
  each table row is generated. If the fields instance variable is set,
  only the columns keyed by the named fields in that array are shown in
  that order. That is, if you fill in the fields instance variable with
  array("a", "c", "e"), only the columns a, c and e become part of the
  generated table.


  If fields has not been set, all data columns are traversed with each()
  and all columns whose names match the regexp in filter are shown in
  the table. By default, this regular expression lets through all column
  names that start with an alphabetic character and continue with either
  alphanumeric characters or "_" (underscore). This default has been
  chosen, because the DB_Sql database class uses mysql_fetch_array()
  internally to get data from the database and this function returns all
  columns twice with both a numeric index and proper column names. The
  default filter will make all data show up only once and with proper
  column names.


  Additionally, the map_cols instance variable provides column name
  remapping. If map_cols is set, it will remap the name of the found
  column with a new name.


  For instance, a table with the following columns, fname, lname, and
  mydate can be remapped to First Name, Last Name, and Date using the
  following code (where $t is your instantiated Table class object):



       ______________________________________________________________________
       $t->map_cols = array("fname"  => "First Name",
                            "lname"  => "Last Name",
                            "mydate" => "Date");
       ______________________________________________________________________





  The map_cols instance variable also allows you to map column names to
  different languages using this same technique.


  For derived classes, the instance variable add_extra has been added.
  If this variable is set, then the functions
  table_heading_row_add_extra() and table_row_add_extra() are called. In
  the Table class, these functions do nothing, but in derived classes
  override these functions to provide additional functionality that may
  be needed. For instance, hyperlinks to provide edit, delete, or view
  capabilities for that row could be easily added into these functions
  (in your derived Table class) allowing greater customization.

  A subclass of Table, CSV_Table, is being provided to allow to create
  CSV representations of your data with minimal effort. CSV (comma
  separated values) can be imported by MySQL's LOAD DATA INFILE
  statement and many spreadsheet import functions.


  The Table class now provides both high-level, mid-level and low-level
  functions through modularization. This allows programmers to use
  either the simplified high-level functionality or, depending on the
  degree of complexity needed, the power of the mid- or low-level
  functions. Every effort to maintain backwards compatibility has been
  applied. However, it would be a good idea to become familiar with the
  new functions if you use the Table class extensively. Typically, the
  high- and mid-level support functions begin with show_ while the low-
  level functions do not.



  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.


  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..  HHiigghh--lleevveell 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..22..22..22..  MMiidd--lleevveell iinnssttaannccee mmeetthhooddss



     sshhooww__ttaabbllee__rroowwss(($$aarryy,, $$ccllaassss==
        Walks the passed array displaying each row of data as an HTML
        table row.


     sshhooww__ttaabbllee__rroowwss__rreessuulltt(($$ddbb,, $$ccllaassss==
        Walks the passed database object displaying each record as an
        HTML table row.


     sshhooww__ttaabbllee__ppaaggee__rroowwss(($$aarryy,, $$ssttaarrtt,, $$nnuumm,, $$ccllaassss==
        Walks the passed array displaying each row of data as an HTML
        table row. However, data does not start displaying until $start
        element and end after $num rows.


     sshhooww__ttaabbllee__ppaaggee__rroowwss__rreessuulltt(($$ddbb,, $$ssttaarrtt,, $$nnuumm,, $$ccllaassss==
        Walks the passed database object displaying each record as an
        HTML table row. However, data does not start displaying until
        $start record and ends after $num records have been displayed.


     sshhooww__ttaabbllee__hheeaaddiinngg__rrooww(($$aarryy,, $$ccllaassss==
        Uses the passed array to create an HTML header row.


     sshhooww__ttaabbllee__hheeaaddiinngg__rrooww__rreessuulltt(($$ddbb,, $$ccllaassss==
        Uses the passed database object to create an HTML header row.


     sshhooww__ttaabbllee__hheeaaddiinngg__cceellllss(($$ddaattaa,, $$ccllaassss==
        Walks the passed array and displays each item in an HTML table
        header cell.


     sshhooww__ttaabbllee__cceellllss(($$rrooww,, $$rrooww__kkeeyy,, $$ddaattaa,, $$ccllaassss==
        Walks the passed array and displays each item in an HTML table
        cell.




  55..22..22..33..  LLooww--lleevveell iinnssttaannccee mmeetthhooddss



     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");/).


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


     ttaabbllee__hheeaaddiinngg__cceellll__ooppeenn(($$ccllaassss==
        Starts a header cell.


     ttaabbllee__hheeaaddiinngg__cceellll__cclloossee(($$ccllaassss==
        Ends a header cell.


     ttaabbllee__hheeaaddiinngg__rrooww__aadddd__eexxttrraa(($$ddaattaa,, $$ccllaassss==
        Virtual function for derived classes. This function is called
        after all header cells have been created. It allows the
        programmer to add additional HTML code to the header row before
        it is closed.


     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__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__cceellll__ooppeenn(($$ccllaassss==
        Starts a cell.


     ttaabbllee__cceellll__cclloossee(($$ccllaassss==
        Ends a cell.


     sseett__cchheecckkbbooxx__hheeaaddiinngg(($$ccllaassss==
        This function creates an empty header cell to coincide with the
        checkbox option for that column.


     ttaabbllee__cchheecckkbbooxx__cceellll(($$rrooww,, $$rrooww__kkeeyy,, $$ddaattaa,, $$ccllaassss==
        Outputs HTML code to display a checkbox. This function runs if
        the member variable $check has been set. $check should be set to
        some key within the $data array (ex: if $data["myKey"], then set
        $check="myKey").


     sseett__cchheecckkbbooxx(($$rrooww,, $$rrooww__kkeeyy,, $$ddaattaa,, $$ccllaassss==
        Creates an HTML checkbox based on the passed data, only if the
        instance variable $check is set.



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

  Table is not automatically included or prepended into each page.
  Include the table class into the pages that are to use Table.  Then
  create an instance of Table:



       ______________________________________________________________________
       <?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..33..  MMeennuu

  Menu will generate a hierarchical menu of clickable items suitable as
  a navigation bar. Menu takes a tree definition of items as the basis
  for this navigation bar and knows which subtrees to fold, depending on
  the current position in the menu tree. Menu uses the current URL as
  presented in PHP_SELF to determine the current position in the menu
  tree automatically.

  Menu does not depend on a hierarchical organisation of files in URL-
  space to generate a menu hierarchy. The organisation of menu items and
  the organisation of files in URL-space are in fact completely
  independent and Menu uses a mapping hash to derive a menu position
  from an URL. In the following class documentation we'll say URL when
  we mean files the latter and menustring when we mean the former. In
  the context of the Menu class, URLs are always relative URLs starting
  at the root of the local servers URL space, as we'll see them in
  PHP_SELF. They may look like /menu/index.php3. A menustring is usually
  numeric and all components have the same length, if necessary with
  leading zeroes. It may look like /001/007, denoting an item in main
  menu 1, submenu 7.


  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.




                       Internal 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



     MMeennuu(())
        Constructor. Calls Menu::setup() internally.


     sshhooww(())
        A shorthand notation for print $this->get().


     ggeett(())
        This function will calculate the menu items visible from the
        current map position. The menu will be constructed by calling
        Menu::start_menu() first. For each visible menu item, Menu will
        check the current indentation level and the indentation level of
        the current menu cell. If the indentation level increases,
        Menu::shift_in($oldlevel, $level) is called once, if it
        decreases, Menu:shift_out($oldlevel, $level) is called once.

        After that, Menu::get_cell($number, $level) is called once. The
        number is an index into the visible array.

        After all menu cells have been drawn, Menu::end_menu() will be
        called once.


     ggeett__cceellll(($$nn,, $$lleevveell))

        You are expected to implement this function yourself. It should
        render a single menu item. You may use the visible and item
        arrays for that purpose: $m = $this->visible[$n] will return a
        menu string and $attr = $this->item[$m] is a hash of attributes
        for that menu string. $hilite = ($this->visible[$n] ==
        $this->map) is true for the current menu item, which should be
        rendered in way to stand out from the rest of the menu items.


     sseettuupp(())

        This function initializes the internal arrays of Menu and should
        be called once from the constructor. It actually is the
        constructor, but language stupidity has it that PHP3 constructor
        names vary with class names, which means that you have to write
        a new constructor for each subclass and call this function
        manually.



  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


     nnoorrmmaalliizzee__ppooss(($$ppooss))
        This function looks at the current URL in $PHP_SELF and tried to
        translate this into a menustring. If the URL matches a
        menustring directly, this is easy.

        If not, the current URL will be sequentially shortened by
        applying the dirname PHP function to it until it matches.  This
        allows you to create a single menu item for all files in a
        directory.


     sspplliitt__ppaatthh(($$pp))
        This function is used in the construction of the set of visible
        menu items. Given a menustring or a pathname, it constructs a
        series of pathnames which converge elementwise against the given
        pathname. That is, given the menustring /3/2, this function will
        return an array with the elements "" (the empty string), /3 and
        /3/2.


     ffiinndd__vviissiibbllee(($$rr))
        This function calculates the actual set of visible URLs given a
        series of converging pathnames. It will include the set of
        children of each of these pathnames in the visible set, then
        sort this set numerically.



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


  To use Menu, you must enable the require statement for menu.inc in
  prepend.php3. To use Menu_Button, you must enable the require
  statement for menu.inc and menu_button.inc in prepend.php3.

  Use a subclass of either Menu or Menu_Button to create a menu. Define
  a class Example_Menu in your local.inc file with a number of menu
  items in it. Do not forget to build a constructor.



       ______________________________________________________________________
       class Example_Menu extends Menu {
         # Map of PHP_SELF URL strings to menu positions
         var $urlmap = array(
           "/menu/index.php3"   => "",
           "/menu/item1.php3"   => "/1",
           "/menu/item11.php3"  => "/1/1",
           "/menu/item12.php3"  => "/1/2",
           "/menu/item13.php3"  => "/1/3",
           "/menu/item2.php3"   => "/2",
           "/menu/item21.php3"  => "/2/1",
           "/menu/item22.php3"  => "/2/2",
           "/menu/item221.php3" => "/2/2/1",
           "/menu/item222.php3" => "/2/2/2",
           "/menu/item23.php3"  => "/2/3",
           "/menu/item24.php3"  => "/2/4"
         );

         # Information about each menu item
         var $item = array(
           ""      => array("title" => "Main"),
           "/1"    => array("title" => "Text 1"),
           "/1/1"  => array("title" => "Text 1.1"),
           "/1/2"  => array("title" => "Text 1.2"),
           "/1/3"  => array("title" => "Text 1.3"),
           "/2"    => array("title" => "Text 2"),
           "/2/1"  => array("title" => "Text 2.1"),
           "/2/2"  => array("title" => "Text 2.2"),
           "/2/2/1"=> array("title" => "Text 2.2.1"),
           "/2/2/2"=> array("title" => "Text 2.2.2"),
           "/2/3"  => array("title" => "Text 2.3"),
           "/2/4"  => array("title" => "Text 2.4")
         );

         function Example_Menu() {
           $this->setup();
         }
       }
       ______________________________________________________________________



  In each of your files mentioned in the above urlmap, create an
  instance of Example_Menu and call the show() method of that instance.



       ______________________________________________________________________
       <?php
         $m = new Example_Menu;
        ?><html>
       <head>
        <title>Page with a menu</title>
       </head>

       <body bgcolor="#ffffff">
       <table border=1 bgcolor="#eeeeee" cellspacing=0 cellpadding=4>
         <tr>
          <td colspan=2 valign=top align=center>
           <h1>Page with a menu</h1>
          </td>
         </tr>
         <tr>
          <td align=left valign=top><?php $m->show() ?></td>
          <td align=left valign=top>Content</td>
         </tr>
        </table>
       </body>
       </html>
       ______________________________________________________________________






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

  The form class (sometimes called OOH Forms) is a convenience library
  for dealing with html forms.  It provides Javascript and server-side
  form validation, and is customizable and extensible.


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

  The OOH Forms library consists of five files: oohforms.inc
  of_checkbox.inc of_radio.inc of_select.inc of_text.inc
  of_textarea.inc.  oohforms.inc automatically includes the others.  You
  may wish to modify this so you can manually include the files for just
  the form elements you use.  Or you may wish to cut and paste the
  contents of the element files into oohforms.inc to save the overhead
  of multiple includes.  Determining the appropriate configuration of
  the files for your site is left an exercise for the reader; for most
  purposes require("oohforms.inc") will suffice.


  In general, the structure of a page that uses oohforms is as follows:











  ______________________________________________________________________
  require("oohforms.inc");         // include the library

  $f = new form;                   // create a form object

  $f->add_element(...);     // set up form elements
  $f->add_element(...);
  $f->add_element(...);

  if ($submitname)                 // Is there data to process?
    if ($err = $f->validate()) {   // Is the data valid?
      echo $err;                   // No; Display error
      $f->load_defaults();  // Load form with submitted data
    else {
      /* Process data */           // Data ok; Do something with it
    }

  $f->start(...);                  // Start displaying form
  $f->show_element(...);    // Show elements
  $f->show_element(...);
  $f->show_element(...);
  $->finish();                     // Finish form
  ______________________________________________________________________





  There are obviously many variations on this theme, but that covers the
  basics.  Specific methods are documented below.



     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

  Since OOH Forms is object oriented, it can be easily customized by
  extending the classes that define the element types.  In general, you
  must make sure your derived class has a constructor and you may
  override any of the self_* functions of of_element.  The source for
  the existing elements is the best documentation for how to do this
  properly, but a few general notes follow.



     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tppll__ffoorrmm

  The tpl_form class is intended to provide a general framework for HTML
  form deployment. It heavily depends on OOH Forms library, so it is
  required that you read and understand the relative documentation.


  The main idea is that you create a black box by sub-classing tpl_form,
  provide some HTML code mixed with OOH Forms calls to actually render
  the form itself. Your application will then use that black box to
  obtain some input from the user. Your application doesn't have to know
  how to handle user input, nor how to validate input data, since
  internal methods will take care of that.



  This approach is very similar (I think) to OOH Forms one, only at a
  higher level. OOH Forms elements have no way to communicate with each
  other, and are only able to perform "simple" checks on data integrity,
  while tpl_form adds a consistent interface for complex data evaluation
  and processing.


  Furthermore, the get_default_values and set_default_values methods can
  be used to maintain user input between sessions, without worrying
  about serialization of form variables (a BAD THING(tm)), using an hash
  array containing field names and values.


  You'll note that an array is used to share data with the application.
  You may object this is kinda futile, since all user input data may be
  found in global vars and HTTP_POST or HTTP_GET global hashes.  This is
  true, and in the general case you'll pass back and forth an empty
  array. The values variable is intended for use in very complex data-
  entry setup, where a form behaviour may depend on previous data
  entered by the user. In this case, if all forms cooperate reading and
  writing to values hash array, final result may be constructed step by
  step across multiple HTML pages.


  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





                       Internal 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



     iinniitt(($$vvaalluueess))
        This is a sort of a constructor for the class. $values is an
        hash array intended to store form values to be passed back to
        the application via get_values method.


     ggeett__ddeeffaauulltt__vvaalluueess(())
        Returns an array containing all data submitted by the user for
        the form. This array is intended to be passed to
        set_defaults_values some time later.


     sseett__ddeeffaauulltt__vvaalluueess(($$ffvv))
        Restore form defaults from an array as returned by
        get_default_values.


     ddiissppllaayy(())
        Actually display form fields. This method should not be
        overridden in descendants. User should instead provide a file
        named as the derived class and with ".ihtml" extension which
        will be automatically included.



     ggeett__vvaalluueess(())
        This method should not be overridden. It is intended as the main
        interface between the application and the form. Once the form
        has been properly derived to suit designer's needs, application
        calls get_values and receives back the array passed to init,
        eventually modified by process_input method, or false if user
        input is invalid. In that latter case, the application should
        call display to (re)present the form to the user, eventually
        filled with proper default values.


     cclleeaarr(())
        Sort of a "destructor". There should no real need to call it,
        except maybe freeing some memory. May be called from the
        application, otherwise is not executed. Returns true.



  55..55..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ettuupp(())
        Init the Form object, which will contain all fields info. The
        hidden field form_name, automatically added by this routine, is
        used by other methods to determine if form has already been
        submitted by the user. You shouldn't override this in
        descendants, use setup_fields instead. Returns true.


     sseettuupp__ffiieellddss(())
        Override this method in order to provide form fields definition
        that suit your needs.


     vvaalliiddaattee(())
        Validates user input. This method should not be overridden in
        descendants. See validate_input instead. Returns false on error
        and sets error variable accordingly.


     vvaalliiddaattee__iinnppuutt(())
        This method should be overridden in descendants, in order to
        provided complex validation methods (i.e. field2 should not be
        empty IF field1 == "other").  Should return false on error and
        set error variable with a sensible error message.


     pprroocceessss(())
        Process user data. This method should not be overridden by
        descendants.  See process_input and process_default instead.
        Returns true on success, false otherwise.


     pprroocceessss__iinnppuutt(())
        This method should be overridden in descendants. It is executed
        after validation has taken place. The data passed to the form
        could be used to fill values array.


     pprroocceessss__ddeeffaauulltt(())
        This method should be overridden in descendants. It is executed
        when form validation fails or before presenting the form for the
        first time. Should be used to bypass form displaying if data can
        be extracted from previous actions, divination, penguin fly
        watching or whatever.

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

  Suppose you have a form that the user should fill with her (eheh) name
  and e-mail. You want to check wether this e-mail is valid, or your
  blind date setup is lost. A... er... simple regular expression for
  validating syntactically the e-mail is presented in the example code
  below.



       ______________________________________________________________________
        $this->form_data->add_element(array(
         "type"=>"text",
         "name"=>"email",
         "valid_e"=>"Syntax error in E-Mail address.",
         "valid_regex"=>"^([-a-zA-Z0-9.]+@[-a-zA-Z0-9]+(\.[-a-zA-Z0-9]+)+)*$"
        ));
       ______________________________________________________________________





  Now, this piece of code should do the job, but since you're feeling
  very paranoid today, you'd also like to validate the host name part of
  the address with DNS. So, you put together some code which takes an
  hostname in input and reports true on valid hostname, false otherwise
  (HINT: on PHP Code Exchange you should find a procedure for "active"
  email validation).


  Now that you have your shining new code, you can check the address.
  The user fills out the form, you parse user input, no syntax errors,
  time to call your mycheckhost from the application. If the function is
  ok update your database, else load defaults into the form, display
  again, close the page, goodbye.



  I've done something similar for MANY forms, some of them with very
  complex validation procedures, and I found that was too easy producing
  very bad and unreadable code (well, I actually realized that the first
  time I had to change some logic in data validation...).



  tpl_form should provide a solid framework to build your forms with,
  and all the code will be self-contained and separated from main
  application logic. Hope you'll like it.


  Time to see some code. First of all, class declaration, sub-classing
  tpl_form:













  ______________________________________________________________________
  class myform extends tpl_form {
    var $classname = "myform";

    function setup_fields() {
      $this->form_data->add_element(array(
       "name"=>"email",
       ..., // See previous code snippet
      ));
      $this->form_data->add_element(array(
       "name"=>"submit",
       "type"=>"submit",
       "value"=>"submit"
      ));
    }

    function validate_input() {
      global $email;
      list($uname, $hostname) = split("@", $email);
      if (! mycheckhost($hostname)) {
        $this->error = sprintf("Sorry, unknown host %s, try again.", $hostname);
        return false;
      }
      // Additional checks here...
      return true;
    }
  }
  ______________________________________________________________________





  You shuld provide a file myform.ihtml with HTML and php code to render
  the form. Minimalistic example:



       ______________________________________________________________________
       <html>
       <body>
       <?php
        $this->form_data->start_form($this->classname, "POST", $sess->self_url(), "");
        printf("%s<br>\n", $this->error);
        $this->form_data->show_element("email");
        printf("<br>\n");
        $this->form_data->show_element("submit");
        $this->form_data->finish();
       ?>
       </body>
       </html>
       ______________________________________________________________________





  Your tpl_form class is complete, and will only need a little work on
  the artistic side... 8-) To use your brand new class, include class
  definition code into your application, then...






  ______________________________________________________________________
  $mf = new myform;
  $mf->init(array()); // This is not strictly required, AT PRESENT,
                      // but I recommend it
  if ($rv = $mf->getdata()) {
    $mf->clear(); // This is not strictly required, anyway it should free
                  // some memory...
    global $email;
    // process your data at will
  } else {
    $mf->display();
  }
  ______________________________________________________________________





  Hope this very little example does help in understanding the real
  power of tpl_form class, at least in terms of rapid designing and code
  partitioning.


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

  The Tree class can render tree structures such as directory
  hierarchies and menu structures as HTML. The structure must be given
  to Tree as an nested array of arrays of arbitrary depth.



  The idea of Tree is, that there are several mathematical models a tree
  could be viewed: One model is a data structure like nested arrays or a
  pointer structure from where you can print multidimensional graphics
  and can do other neat things like deleting one part of the tree or
  inserting a whole subtree. But you can also imagine a tree as a one
  dimensional string or as a sequence of function calls (which is nearly
  the same in the mathematical sense).



  To generate HTML-code from a tree-structure it is like this: You need
  at the end a one-dimensional string, which tells the browser what to
  do. The Tree class assists you in generating this string in this way,
  that it will go through the whole tree and call several functions on
  every stage trough the way. It will be your task to change the
  functions, so that a nice layout will be generated.



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


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






  55..66..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..66..33..  TThhee TTrreeee AArrrraayy

  As said above, before you call go_trough_tree(), first $tree must be
  generated.

  $tree consists of nested arrays of arbitrary depth. An example:



       ______________________________________________________________________
       $t= new Tree;
       $t->tree = array(
                       "usr" => array(
                         0       => "allowed",
                         "lib"   => "forbidden",
                         "local" => "allowed",
                         "bin"   => "forbidden",
                         "etc"   => array(
                            0       => "allowed",
                           "hosts"  => "forbidden",
                           "mailcap"=> "allowed"
                         ),
                         "var"   => "allowed",
                         "tmp"   => "allowed"
                       ),
                       "root" =>"forbidden"
                     );
       $t->go_through_tree();
       print $t->outp;
       ______________________________________________________________________




  This is a completely recursive structure and I think, it is clear, how
  to create it with a recursive call of a function. If not, see the
  example below.


  One little quirk has to be explained, because it is a little bit
  confusing: the array name 0 (zero) is used for the value of the parent
  element. As shown in the example, an element with children (for
  example "etc") cannot have attributes (such as "allowed").  Instead
  the value of this element is stored in a pseudo-child named 0. If this
  element is not present, it will have the value "Array" (perhaps
  something that should be changed).


  The output of this example if you don't change the output-functions
  will look like this:
















  ______________________________________________________________________
  /
  ^---- usr->'allowed' : 'usr' (1) [1/2]
  |    ^---- lib->'forbidden' : 'usr^lib' (2) [2/7]
  |    O---- local->'allowed' : 'usr^local' (2) [3/7]
  |    O---- bin->'forbidden' : 'usr^bin' (2) [4/7]
  |    O---- etc->'allowed' : 'usr^etc' (2) [5/7]
  |    |    ^---- hosts->'forbidden' : 'usr^etc^hosts' (3) [2/3]
  |    |     \--- mailcap->'allowed' : 'usr^etc^mailcap' (3) [3/3]
  |    O---- var->'allowed' : 'usr^var' (2) [6/7]
  |     \--- tmp->'allowed' : 'usr^tmp' (2) [7/7]
   \--- root->'forbidden' : 'root' (1) [2/2]
  ______________________________________________________________________




  Looks a bit confusing. From left to right the fields are

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

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

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

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

    The current _e_l_e_m_e_n_t _n_u_m_b_e_r. See below to understand, why it will
     begin sometimes with "2" in this example!

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



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


  My example is just going trough the directory structure of your hard
  disk.


  The following code could read it:
























  ______________________________________________________________________
  class dir_Tree extends Tree {
       var $classname = "dir_Tree";
       var $delimiter="/";

       var $tdat;

       function build_tree ($path=".") {
           $this->tree=$this->recurs_dir($path,0);
       }

       ## This example code can read and output 1000 directory entries with
       ## many subdirs in about 20 seconds on my system (P200, 64 MB);
       ## 220 dir entries with a maximum depth of 4 are read in 2 seconds.
       ## This is ok. :)

       function recurs_dir ($path,$depth) {
       GLOBAL $flap_out;
           $d=opendir($path);

           while ( $name=readdir($d) ) {
               $pathname=$path . $this->delimiter . $name;
               if (is_dir($pathname) && !ereg("\.\.?",$pathname)) {
                   if (isset($flap_out[$pathname])) {
                       $array[$name]=$this->recurs_dir($pathname,$depth+1);
                   }
                   # ATTENTION: It is IMPORTANT fill the [0] array
                   # *after* filling the rest of the array!
                   $array[$name][0]=$pathname;
               } else {
                   $array[$name]=$pathname;
               }
           }
           closedir($d);
           return($array);

       }

       #################################################
       ## FLAPPING IN and OUT
       ## This is used to create an array which includes
       ## all sub-paths which should be showed
       ##

       function flapping ($path) {
       GLOBAL $flap_out;
           if ($path) {
               if (is_dir($path)) {
                   if (isset($flap_out[$path])) {
                       unset($flap_out[$path]);
                   } else {
                       $flap_out[$path]=$name;
                   }
               }
           }
       }
  }

  $t= new dir_Tree;
  $t->flapping($val); ## $val is given by GET-method, see *tree()-functions
  $t->build_tree();
  $t->go_through_tree();
  print $t->outp;
  ______________________________________________________________________


  With this code it is very easy to flap in and out whole parts of the
  tree. Send the path via GET-method and put this path in flapping().
  The whole $flap_out-array must be persistent (e.g. via _s_e_s_s_i_o_n).
  Perhaps you can program a garbage collection, which will look into
  $flap_out and check for paths that already exist?


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

  There is one known bug: If a name of a subpath contains the
  $delimiter-string. This cannot be solved correctly and you have to
  look for it when you create the tree.

  The same thing is with the value [0] (zero) of a sub-array. This
  element is always used as the attribute of the parent element.

  A good tip: when you build your tree recursively then the [0]-index
  must be filled _a_f_t_e_r the subtree is returned from recursive call. See
  in the example above what I mean. I think it's a PHP3 specialty.

  Also it is possible that not every name could be inserted into the
  associate index-field (Control-chars etc.), but this is untested.


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

  This is a set of functions, which are used very often by me.

  They are so easy, that I now stop describing and simply insert the
  code.  Perhaps the next revision of this set I will replace it with a
  better description:



































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


  The initial idea on how to do serialization was contributed by KH Wild
  to the php3 mailing list. It was limited to serializing arrays of at
  most three dimensions, though. We worked on his solution, improving it
  to arrays of arbitrary depth and later rewrote the function from
  scratch, turning it upside down. Our new serialization code can now
  handle any first order data type available to PHP and is easily
  extensible. It is also encapsulated in a class, keeping the name space
  clean. While we were at it, we made session cookies more secure by not
  using uniquid() directly, but a md5() hash of uniqid().

  Cameron Taggart and Guarneri Carmelo contributed ODBC support.
  Szandor van Verseveld contributed PostgreSQL support. Scott McMullan
  contributed some nice ideas for cleanup and is working on Sybase
  support.

  Sascha Schumann contributed much time developing and extending PHPLIB,
  including but not limited to mSQL/Sybase support, general storage
  container support, shared memory and LDAP support.

  Alexander Aulbach submitted his Tree class. Jay Bloodworth contributed
  his excellent OOH Forms library for form generation and input
  validation.

  A lot of people provided helpful hints and occasionally patches.
  Please see the file CREDITS for a complete list of contributors,
  testers and inspirations.

































































