Help - Search - Members - Calendar
Full Version: Member Registration And Login System
Zymic Webmaster Forums > Zymic Free Web Hosting > Tutorials
Pages: 1, 2, 3
evolution815
Hmm... ive found a couple of sites with guides on how to make a login

And well this seems to be the most handle but Might Lack in explaining the content your using.

As yeah i know what each thing does,

But some people might not know why your writing in, what does this mean


But hey, this is just just plain and simple which is good enough for me.



Are you going to add a guide on how to control the prevliges giving for your members, such as if you have registered then you can't view this webpage, or make a blog Etc...

oziyn
yo is any1 here gonna help me with my question???
im dieing here
ii have everything done but.........its got error on line 15 ffs
and the number of the line changes when i move the period to dif lines.ive tried on different lines and none of them work but when i take it off i get db connection error
and yes i have tried putting it at the end

plzzz help me ppls

uncled1023
ok, right after you password, you dont needd that period at the end of the line...

oziyn
it doesnt work at all then...><
uncled1023
ok, i see your problem. you put your password and username info in the wrong part...

CODE
<?php

//require the PEAR::DB classes.

require_once 'DB.php';

$db_engine = 'mysql';
$db_user = 'username';
$db_pass = 'password';
$db_host = 'localhost';
$db_name = 'database';

$datasource = $db_engine.'://'.
              $db_user.':'.
              $db_pass.'@'.
               $db_host.'/'.
                $db_name;

$db_object = DB::connect($datasource, TRUE);

/* assign database object in $db_object,

if the connection fails $db_object will contain

the error message. */

// If $db_object contains an error:

// error and exit.

if(DB::isError($db_object)) {
    die($db_object->getMessage());
}

$db_object->setFetchMode(DB_FETCHMODE_ASSOC);

// we write this later on, ignore for now.

include('check_login.php');

?>


the "username" should change to your username, and the "password" should change to your password, anbd the "database should change to your database.
GBE
I did it but when i try to register it says no such table. If you can fix this it would be greatly appreciated, to login to my sql manager i goto http://cpanel.0lx.net my username is 0lx_1725306 and my password is willo35207, htan goto control panel then sql databases. you should se a list clickm on the admin button under woospace. thanks
oziyn
ok first of all im about to try that. thank u very much for taking ur time to help me and second...
buddy....that wasnt smart dont giv ur user and pass to ppl just like that..
im not saying i wud....im a nice person and all but rly......reality is that there are some asswipes who will go on that and completely wipe all ur stuff wich wudnt be very cool...

just saying... wach out

peace

edit: omfg thank u so much it actually woked omfg i love u!!!!lol

thx bud

2nd edit: im gonna go get that second profile thing u added....
uncled1023
QUOTE(GBE @ Apr 3 2008, 11:36 PM) *
I did it but when i try to register it says no such table. If you can fix this it would be greatly appreciated, to login to my sql manager i goto http://cpanel.0lx.net my username is 0lx_1725306 and my password is willo35207, htan goto control panel then sql databases. you should se a list clickm on the admin button under woospace. thanks

ok, ill check it out, but remember NEVER to give out your username and password... send it in a pm or something.

ok, you never made the users table. all you have it a whoospace members table, and it doesnt have the fields in it. so first, make a new table and call it users, and add the correct fields.
bezz
This script is vulnerable to both mysql and XSS exploits.

GBE
What are the correct fields?
uncled1023
QUOTE(GBE @ Apr 7 2008, 10:40 AM) *
What are the correct fields?

you need to make a table with the following fields.
CODE
CREATE TABLE users (
id int(10) DEFAULT '0' NOT NULL auto_increment,
username varchar(40),
password varchar(50),
regdate varchar(20),
email varchar(100),
website varchar(150),
location varchar(150),
show_email int(2) DEFAULT '0',
last_login varchar(20),
PRIMARY KEY(id))
GBE
whenever i put that in it says INVALID INDEX TYPE, can you try to do it?
uncled1023
ok, send me your stats, and ill do it for ya... also what you want the table to be called.
GBE
Anything ill send u my stats in a min, thanks
uncled1023
no pro0b.
wozzym
this turned out to be a great help section too

It works for me still cool.gif
uncled1023
lol, i like helping others, help my knowledge in php grow.
smile.gif

lol, i like helping others, help my knowledge in php grow.
smile.gif
shiyaoshuya
thanks for this tutorial!

now i can protect or i can see who viewed my site.


thanks!
uncled1023
lol, no prob!
Steve Nash
Good Work Guys !
pikachuregion
It says invalid id could u help me with it and it is doing that for the pm system as well unsure.gif unsure.gif unsure.gif
uncled1023
um... did you set up the table correctly?
pikachuregion
QUOTE(uncled1023 @ May 7 2008, 10:44 PM) *
um... did you set up the table correctly?


i cant set it up at all angry.gif angry.gif angry.gif
uncled1023
ok, well, thats the problem.. open up phpminiadmin. select your database, and enter the following code into the query box.

CODE
CREATE TABLE users (
id int(10) NOT NULL auto_increment,
username varchar(40),
password varchar(50),
regdate varchar(20),
email varchar(100),
website varchar(150),
location varchar(150),
show_email int(2) DEFAULT '0',
last_login varchar(20),
PRIMARY KEY(id))


and for the PM system. enter in the following code into the box after you make the 'users' table.

CODE
CREATE TABLE `messages` (
`id` int(11) NOT NULL auto_increment,
`reciever` varchar(25) NOT NULL default '',
`sender` varchar(25) NOT NULL default '',
`subject` text NOT NULL,
`message` longtext NOT NULL,
`recieved` enum('1','0') default '0',
PRIMARY KEY (`id`)
)
RussellSprouts
So this is the one that works with the PM system? I think I'll try it... does it have a remember me feature?


EDIT: I am using Zymic webhosting. What is the local host supposed to be in db_connect.php?

EDIT2: I get
QUOTE
Error: Invalid default value for 'id'
when I do the database query.

EDIT (again): Fixed it.
pikachuregion
QUOTE(uncled1023 @ May 8 2008, 11:26 PM) *
ok, well, thats the problem.. open up phpminiadmin. select your database, and enter the following code into the query box.

CODE
CREATE TABLE users (
id int(10) NOT NULL auto_increment,
username varchar(40),
password varchar(50),
regdate varchar(20),
email varchar(100),
website varchar(150),
location varchar(150),
show_email int(2) DEFAULT '0',
last_login varchar(20),
PRIMARY KEY(id))


and for the PM system. enter in the following code into the box after you make the 'users' table.

CODE
CREATE TABLE `messages` (
`id` int(11) NOT NULL auto_increment,
`reciever` varchar(25) NOT NULL default '',
`sender` varchar(25) NOT NULL default '',
`subject` text NOT NULL,
`message` longtext NOT NULL,
`recieved` enum('1','0') default '0',
PRIMARY KEY (`id`)
)


Thanks Could U Email ME ANY other Tutorials you have and four that unique members page it says open up members.php, i dont have one of those wat is the code for that page
uncled1023
for your members.php page, just use whatever page you want the users to see. as long as its called members.php
RussellSprouts
CODE
Warning: preg_match() [function.preg-match]: Unknown modifier '/' in /www/zxq.net/g/a/l/galaxygame/htdocs/logintest/register.php on line 111

I get that when I register using this on my site. It still show the you are now logged in stuff, and gives a register link.
pikachuregion
QUOTE(uncled1023 @ May 9 2008, 08:43 PM) *
for your members.php page, just use whatever page you want the users to see. as long as its called members.php


Ok i have go it working and im registered and when i go to my indox and stuff it says i not logged in please help laugh.gif laugh.gif
Trippin7464
Yea i've been having a problem where an IE user can login, but as soon as they click any link (even home, or refresh) they get logged out (happens on two different computers, both IE7 and running Vista if it helps)
But it firefox 3 Beta 5 (haven't tried Firefox 2 since I haven't got it installed) it keeps me logged in just fine, (for days even, if I leave my browser open)

So it's working like it should in Firefox, but in Internet Explorer it will NOT keep a user logged in.
Have you run into this problem before?
I might just start over fresh and see if that helps if nobody has an answer.
uncled1023
QUOTE(Trippin7464 @ May 10 2008, 05:28 PM) *
Yea i've been having a problem where an IE user can login, but as soon as they click any link (even home, or refresh) they get logged out (happens on two different computers, both IE7 and running Vista if it helps)
But it firefox 3 Beta 5 (haven't tried Firefox 2 since I haven't got it installed) it keeps me logged in just fine, (for days even, if I leave my browser open)

So it's working like it should in Firefox, but in Internet Explorer it will NOT keep a user logged in.
Have you run into this problem before?
I might just start over fresh and see if that helps if nobody has an answer.



huh.... im running IE, and its working fine... try re-doing the check_login.php and make sure the if(logged_in == 0) { } is correct and stuff. cause the Logged_in is what tells if you are logged in. 1 for logged in, 0 for not logged in.

QUOTE(RussellSprouts @ May 10 2008, 03:35 PM) *
CODE
Warning: preg_match() [function.preg-match]: Unknown modifier '/' in /www/zxq.net/g/a/l/galaxygame/htdocs/logintest/register.php on line 111

I get that when I register using this on my site. It still show the you are now logged in stuff, and gives a register link.



ok, it looks like on your register page, on line 100, there is an extra "/" or one where ther should be. also check to make sure, if its a php function, there has to be a ; at the end of the line.

QUOTE(pikachuregion @ May 10 2008, 04:44 PM) *
Ok i have go it working and im registered and when i go to my indox and stuff it says i not logged in please help laugh.gif laugh.gif



ok, after you register, you have to login. if you logged in, look at the above suggestion.
pcdoc2010
does this work with just 1 page or the entire site?
pcdoc2010
QUOTE(ORiOn @ Mar 19 2008, 04:42 PM) *
cant create the table sad.gif
error:

CREATE TABLE users(
id int( 10 ) DEFAULT '0' NOT NULL AUTO_INCREMENT ,
username varchar( 40 ) ,
PASSWORD varchar( 50 ) ,
regdate varchar( 20 ) ,
email varchar( 100 ) ,
website varchar( 150 ) ,
location varchar( 150 ) ,
show_email int( 2 ) DEFAULT '0',
last_login varchar( 20 ) ,
PRIMARY KEY ( id )
)

Mensagens do MySQL : Documentação
#1067 - Invalid default value for 'id'
tanks for any help


is there any fix to this problem?
uncled1023 said
QUOTE
there is a problem. so i just manually installed it.

But I don't understand what that means.
uncled1023
ok, check the main post. i think i fixed it.
Shazham
When I log in, why does it have all of this extra stuff? It shows this.


Logged in

Welcome back Admin, you are logged in.

Warning: Unknown: write failed: No space left on device (28) in Unknown on line 0

Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php5) in Unknown on line 0
Trippin7464
QUOTE(Shazham @ Jun 2 2008, 12:26 AM) *
When I log in, why does it have all of this extra stuff? It shows this.


Logged in

Welcome back Admin, you are logged in.

Warning: Unknown: write failed: No space left on device (28) in Unknown on line 0

Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php5) in Unknown on line 0


The errors that your getting have nothing to do with this script. It is the Zymic servers. If you would read the news forum, you would know whats going on.
uncled1023
yea, trippin is right. the serers are packed, so it cant save anything else to your accoutn.
astroinus
Hi Thank you so much for the guide i have been looking for this for ages
I am new to php.
Is is possible to make a forgotten Password field?
uncled1023
um, you mean like a link to a new page where they enter their email and it sends them their password?
Banjo
This might help you http://www.techtuts.com/forums/index.php?showtopic=4118
iWorld
Any demos for this? I would like to see this in action first and see if I like it or not.
Thanks
Hunter X
When I try to view any page I get this error:
QUOTE
Warning: require_once(DB.php) [function.require-once]: failed to open stream: No such file or directory in /www/vndv.com/d/a/r/darkmatter/htdocs/db_connect.php on line 5

Fatal error: require_once() [function.require]: Failed opening required 'DB.php' (include_path='.:/usr/share/php') in /www/vndv.com/d/a/r/darkmatter/htdocs/db_connect.php on line 5

My site is at darkmatter.vndv.com, please instruct me on how to remedy this.

Thanks in advance.
LordofKaos94
in my page when i want to register apear me this error
CODE

Fatal error: Call to a member function query() on a non-object in /www/vndv.com/m/e/x/mexicangamers/htdocs/register.php on line 28

that is that
CODE
$name_check = $db_object->query($qry);
what i want to do??
help blink.gifMy web
Elquorya
Hi, Sorry im completley lost when it comes to databases.... and whatnot... What program is this tutorial made for? Im using Microsoft Access, and im compltley lost at how to use this tutorial on it...
rubsubphp

Thank you so much for the tutorial. It was really helpful and worked for me.

I would like to include forgot password and forgot username link along with this.

Ask them to enter the username and then we can ask them to create new password.
if forgotten their username, send email back with username.

Is it possible to provide code for doing reset password?

thanks in advance.

lavendalla
I think i got something wroung. i am just starting to learn PHP and havnt started with SQL. I always get an error. the error is "DB Error: not found" anyways, here is the code for my db_connect.php(i have all required files)
CODE
<?php

//require the PEAR:biggrin.gifB classes.

require_once 'DB.php';

$db_engine = 'mysql';
$db_user = 'lavendalla_lavendalla';
$db_pass = '**********';
$db_host = 'localhost';
$db_name = 'lavendalla_members';

$datasource = "mysql://$db_user:$db_pass@$db_host/$db_name";

$db_object = DB::connect($datasource, TRUE);

/* assign database object in $db_object,

if the connection fails $db_object will contain

the error message. */

// If $db_object contains an error:

// error and exit.

if(DB::isError($db_object)) {
die($db_object->getMessage());
}

$db_object->setFetchMode(DB_FETCHMODE_ASSOC);

// we write this later on, ignore for now.

include('check_login.php');

?>


Oops, if i would have put it like this
CODE
$datasource = "mysql://$db_user:$db_pass@$db_host/$db_name";
(or known anything about PHP blush.gif i would have gotten it right away
iCemaT
QUOTE(pcdoc2010 @ May 28 2008, 01:34 PM) *
is there any fix to this problem?
uncled1023 said
But I don't understand what that means.


Dude, you need to delete "Default '0' " from the id line, i did that and it imported it right wink.gif
iCemaT
About that with login, i get following errors:

CODE
Warning: require_once(DB.php) [function.require-once]: failed to open stream: No such file or directory in /www/vndv.com/i/c/e/icematproductions/htdocs/db_connect.php on line 5

Fatal error: require_once() [function.require]: Failed opening required 'DB.php' (include_path='.:/usr/share/php') in /www/vndv.com/i/c/e/icematproductions/htdocs/db_connect.php on line 5


Where is the DB.php file? unsure.gif

I did all your login tutorial right, i hope mellow.gif

And yes i am hosting at Zynic.com wink.gif
lavendalla
QUOTE(iCemaT @ Jul 16 2008, 01:05 PM) *
Where is the DB.php file? unsure.gif


I have no idea where its supposed to be, but i downloaded it and pear.php from the pear's homepage, just google it and download it.

Also, u have to have the DB.php and pear.php wherever u have the scripts

The source for both are below


DB.php
[codebox]<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
* Database independent query interface
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V.Cox <cox@idecnet.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: DB.php,v 1.88 2007/08/12 05:27:25 aharvey Exp $
* @link http://pear.php.net/package/DB
*/

/**
* Obtain the PEAR class so it can be extended from
*/
require_once 'PEAR.php';


// {{{ constants
// {{{ error codes

/**#@+
* One of PEAR DB's portable error codes.
* @see DB_common::errorCode(), DB::errorMessage()
*
* {@internal If you add an error code here, make sure you also add a textual
* version of it in DB::errorMessage().}}
*/

/**
* The code returned by many methods upon success
*/
define('DB_OK', 1);

/**
* Unkown error
*/
define('DB_ERROR', -1);

/**
* Syntax error
*/
define('DB_ERROR_SYNTAX', -2);

/**
* Tried to insert a duplicate value into a primary or unique index
*/
define('DB_ERROR_CONSTRAINT', -3);

/**
* An identifier in the query refers to a non-existant object
*/
define('DB_ERROR_NOT_FOUND', -4);

/**
* Tried to create a duplicate object
*/
define('DB_ERROR_ALREADY_EXISTS', -5);

/**
* The current driver does not support the action you attempted
*/
define('DB_ERROR_UNSUPPORTED', -6);

/**
* The number of parameters does not match the number of placeholders
*/
define('DB_ERROR_MISMATCH', -7);

/**
* A literal submitted did not match the data type expected
*/
define('DB_ERROR_INVALID', -8);

/**
* The current DBMS does not support the action you attempted
*/
define('DB_ERROR_NOT_CAPABLE', -9);

/**
* A literal submitted was too long so the end of it was removed
*/
define('DB_ERROR_TRUNCATED', -10);

/**
* A literal number submitted did not match the data type expected
*/
define('DB_ERROR_INVALID_NUMBER', -11);

/**
* A literal date submitted did not match the data type expected
*/
define('DB_ERROR_INVALID_DATE', -12);

/**
* Attempt to divide something by zero
*/
define('DB_ERROR_DIVZERO', -13);

/**
* A database needs to be selected
*/
define('DB_ERROR_NODBSELECTED', -14);

/**
* Could not create the object requested
*/
define('DB_ERROR_CANNOT_CREATE', -15);

/**
* Could not drop the database requested because it does not exist
*/
define('DB_ERROR_CANNOT_DROP', -17);

/**
* An identifier in the query refers to a non-existant table
*/
define('DB_ERROR_NOSUCHTABLE', -18);

/**
* An identifier in the query refers to a non-existant column
*/
define('DB_ERROR_NOSUCHFIELD', -19);

/**
* The data submitted to the method was inappropriate
*/
define('DB_ERROR_NEED_MORE_DATA', -20);

/**
* The attempt to lock the table failed
*/
define('DB_ERROR_NOT_LOCKED', -21);

/**
* The number of columns doesn't match the number of values
*/
define('DB_ERROR_VALUE_COUNT_ON_ROW', -22);

/**
* The DSN submitted has problems
*/
define('DB_ERROR_INVALID_DSN', -23);

/**
* Could not connect to the database
*/
define('DB_ERROR_CONNECT_FAILED', -24);

/**
* The PHP extension needed for this DBMS could not be found
*/
define('DB_ERROR_EXTENSION_NOT_FOUND',-25);

/**
* The present user has inadequate permissions to perform the task requestd
*/
define('DB_ERROR_ACCESS_VIOLATION', -26);

/**
* The database requested does not exist
*/
define('DB_ERROR_NOSUCHDB', -27);

/**
* Tried to insert a null value into a column that doesn't allow nulls
*/
define('DB_ERROR_CONSTRAINT_NOT_NULL',-29);
/**#@-*/


// }}}
// {{{ prepared statement-related


/**#@+
* Identifiers for the placeholders used in prepared statements.
* @see DB_common::prepare()
*/

/**
* Indicates a scalar (<kbd>?</kbd>) placeholder was used
*
* Quote and escape the value as necessary.
*/
define('DB_PARAM_SCALAR', 1);

/**
* Indicates an opaque (<kbd>&</kbd>) placeholder was used
*
* The value presented is a file name. Extract the contents of that file
* and place them in this column.
*/
define('DB_PARAM_OPAQUE', 2);

/**
* Indicates a misc (<kbd>!</kbd>) placeholder was used
*
* The value should not be quoted or escaped.
*/
define('DB_PARAM_MISC', 3);
/**#@-*/


// }}}
// {{{ binary data-related


/**#@+
* The different ways of returning binary data from queries.
*/

/**
* Sends the fetched data straight through to output
*/
define('DB_BINMODE_PASSTHRU', 1);

/**
* Lets you return data as usual
*/
define('DB_BINMODE_RETURN', 2);

/**
* Converts the data to hex format before returning it
*
* For example the string "123" would become "313233".
*/
define('DB_BINMODE_CONVERT', 3);
/**#@-*/


// }}}
// {{{ fetch modes


/**#@+
* Fetch Modes.
* @see DB_common::setFetchMode()
*/

/**
* Indicates the current default fetch mode should be used
* @see DB_common::$fetchmode
*/
define('DB_FETCHMODE_DEFAULT', 0);

/**
* Column data indexed by numbers, ordered from 0 and up
*/
define('DB_FETCHMODE_ORDERED', 1);

/**
* Column data indexed by column names
*/
define('DB_FETCHMODE_ASSOC', 2);

/**
* Column data as object properties
*/
define('DB_FETCHMODE_OBJECT', 3);

/**
* For multi-dimensional results, make the column name the first level
* of the array and put the row number in the second level of the array
*
* This is flipped from the normal behavior, which puts the row numbers
* in the first level of the array and the column names in the second level.
*/
define('DB_FETCHMODE_FLIPPED', 4);
/**#@-*/

/**#@+
* Old fetch modes. Left here for compatibility.
*/
define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);
define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC);
define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED);
/**#@-*/


// }}}
// {{{ tableInfo() && autoPrepare()-related


/**#@+
* The type of information to return from the tableInfo() method.
*
* Bitwised constants, so they can be combined using <kbd>|</kbd>
* and removed using <kbd>^</kbd>.
*
* @see DB_common::tableInfo()
*
* {@internal Since the TABLEINFO constants are bitwised, if more of them are
* added in the future, make sure to adjust DB_TABLEINFO_FULL accordingly.}}
*/
define('DB_TABLEINFO_ORDER', 1);
define('DB_TABLEINFO_ORDERTABLE', 2);
define('DB_TABLEINFO_FULL', 3);
/**#@-*/


/**#@+
* The type of query to create with the automatic query building methods.
* @see DB_common::autoPrepare(), DB_common::autoExecute()
*/
define('DB_AUTOQUERY_INSERT', 1);
define('DB_AUTOQUERY_UPDATE', 2);
/**#@-*/


// }}}
// {{{ portability modes


/**#@+
* Portability Modes.
*
* Bitwised constants, so they can be combined using <kbd>|</kbd>
* and removed using <kbd>^</kbd>.
*
* @see DB_common::setOption()
*
* {@internal Since the PORTABILITY constants are bitwised, if more of them are
* added in the future, make sure to adjust DB_PORTABILITY_ALL accordingly.}}
*/

/**
* Turn off all portability features
*/
define('DB_PORTABILITY_NONE', 0);

/**
* Convert names of tables and fields to lower case
* when using the get*(), fetch*() and tableInfo() methods
*/
define('DB_PORTABILITY_LOWERCASE', 1);

/**
* Right trim the data output by get*() and fetch*()
*/
define('DB_PORTABILITY_RTRIM', 2);

/**
* Force reporting the number of rows deleted
*/
define('DB_PORTABILITY_DELETE_COUNT', 4);

/**
* Enable hack that makes numRows() work in Oracle
*/
define('DB_PORTABILITY_NUMROWS', 8);

/**
* Makes certain error messages in certain drivers compatible
* with those from other DBMS's
*
* + mysql, mysqli: change unique/primary key constraints
* DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
*
* + odbc(access): MS's ODBC driver reports 'no such field' as code
* 07001, which means 'too few parameters.' When this option is on
* that code gets mapped to DB_ERROR_NOSUCHFIELD.
*/
define('DB_PORTABILITY_ERRORS', 16);

/**
* Convert null values to empty strings in data output by
* get*() and fetch*()
*/
define('DB_PORTABILITY_NULL_TO_EMPTY', 32);

/**
* Turn on all portability features
*/
define('DB_PORTABILITY_ALL', 63);
/**#@-*/

// }}}


// }}}
// {{{ class DB

/**
* Database independent query interface
*
* The main "DB" class is simply a container class with some static
* methods for creating DB objects as well as some utility functions
* common to all parts of DB.
*
* The object model of DB is as follows (indentation means inheritance):
* <pre>
* DB The main DB class. This is simply a utility class
* with some "static" methods for creating DB objects as
* well as common utility functions for other DB classes.
*
* DB_common The base for each DB implementation. Provides default
* | implementations (in OO lingo virtual methods) for
* | the actual DB implementations as well as a bunch of
* | query utility functions.
* |
* +-DB_mysql The DB implementation for MySQL. Inherits DB_common.
* When calling DB::factory or DB::connect for MySQL
* connections, the object returned is an instance of this
* class.
* </pre>
*
* @category Database
* @package DB
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V.Cox <cox@idecnet.com>
* @author Daniel Convissor <danielc@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.14RC1
* @link http://pear.php.net/package/DB
*/
class DB
{
// {{{ &factory()

/**
* Create a new DB object for the specified database type but don't
* connect to the database
*
* @param string $type the database type (eg "mysql")
* @param array $options an associative array of option names and values
*
* @return object a new DB object. A DB_Error object on failure.
*
* @see DB_common::setOption()
*/
function &factory($type, $options = false)
{
if (!is_array($options)) {
$options = array('persistent' => $options);
}

if (isset($options['debug']) && $options['debug'] >= 2) {
// expose php errors with sufficient debug level
include_once "DB/{$type}.php";
} else {
@include_once "DB/{$type}.php";
}

$classname = "DB_${type}";

if (!class_exists($classname)) {
$tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
"Unable to include the DB/{$type}.php"
. " file for '$dsn'",
'DB_Error', true);
return $tmp;
}

@$obj = new $classname;

foreach ($options as $option => $value) {
$test = $obj->setOption($option, $value);
if (DB::isError($test)) {
return $test;
}
}

return $obj;
}

// }}}
// {{{ &connect()

/**
* Create a new DB object including a connection to the specified database
*
* Example 1.
* <code>
* require_once 'DB.php';
*
* $dsn = 'pgsql://user:password@host/database';
* $options = array(
* 'debug' => 2,
* 'portability' => DB_PORTABILITY_ALL,
* );
*
* $db =& DB::connect($dsn, $options);
* if (PEAR::isError($db)) {
* die($db->getMessage());
* }
* </code>
*
* @param mixed $dsn the string "data source name" or array in the
* format returned by DB::parseDSN()
* @param array $options an associative array of option names and values
*
* @return object a new DB object. A DB_Error object on failure.
*
* @uses DB_dbase::connect(), DB_fbsql::connect(), DB_ibase::connect(),
* DB_ifx::connect(), DB_msql::connect(), DB_mssql::connect(),
* DB_mysql::connect(), DB_mysqli::connect(), DB_oci8::connect(),
* DB_odbc::connect(), DB_pgsql::connect(), DB_sqlite::connect(),
* DB_sybase::connect()
*
* @uses DB::parseDSN(), DB_common::setOption(), PEAR::isError()
*/
function &connect($dsn, $options = array())
{
$dsninfo = DB::parseDSN($dsn);
$type = $dsninfo['phptype'];

if (!is_array($options)) {
/*
* For backwards compatibility. $options used to be boolean,
* indicating whether the connection should be persistent.
*/
$options = array('persistent' => $options);
}

if (isset($options['debug']) && $options['debug'] >= 2) {
// expose php errors with sufficient debug level
include_once "DB/${type}.php";
} else {
@include_once "DB/${type}.php";
}

$classname = "DB_${type}";
if (!class_exists($classname)) {
$tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
"Unable to include the DB/{$type}.php"
. " file for '$dsn'",
'DB_Error', true);
return $tmp;
}

@$obj = new $classname;

foreach ($options as $option => $value) {
$test = $obj->setOption($option, $value);
if (DB::isError($test)) {
return $test;
}
}

$err = $obj->connect($dsninfo, $obj->getOption('persistent'));
if (DB::isError($err)) {
if (is_array($dsn)) {
$err->addUserInfo(DB::getDSNString($dsn, true));
} else {
$err->addUserInfo($dsn);
}
return $err;
}

return $obj;
}

// }}}
// {{{ apiVersion()

/**
* Return the DB API version
*
* @return string the DB API version number
*/
function apiVersion()
{
return '1.7.14RC1';
}

// }}}
// {{{ isError()

/**
* Determines if a variable is a DB_Error object
*
* @param mixed $value the variable to check
*
* @return bool whether $value is DB_Error object
*/
function isError($value)
{
return is_a($value, 'DB_Error');
}

// }}}
// {{{ isConnection()

/**
* Determines if a value is a DB_<driver> object
*
* @param mixed $value the value to test
*
* @return bool whether $value is a DB_<driver> object
*/
function isConnection($value)
{
return (is_object($value) &&
is_subclass_of($value, 'db_common') &&
method_exists($value, 'simpleQuery'));
}

// }}}
// {{{ isManip()

/**
* Tell whether a query is a data manipulation or data definition query
*
* Examples of data manipulation queries are INSERT, UPDATE and DELETE.
* Examples of data definition queries are CREATE, DROP, ALTER, GRANT,
* REVOKE.
*
* @param string $query the query
*
* @return boolean whether $query is a data manipulation query
*/
function isManip($query)
{
$manips = 'INSERT|UPDATE|DELETE|REPLACE|'
. 'CREATE|DROP|'
. 'LOAD DATA|SELECT .* INTO .* FROM|COPY|'
. 'ALTER|GRANT|REVOKE|'
. 'LOCK|UNLOCK';
if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $query)) {
return true;
}
return false;
}

// }}}
// {{{ errorMessage()

/**
* Return a textual error message for a DB error code
*
* @param integer $value the DB error code
*
* @return string the error message or false if the error code was
* not recognized
*/
function errorMessage($value)
{
static $errorMessages;
if (!isset($errorMessages)) {
$errorMessages = array(
DB_ERROR => 'unknown error',
DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions',
DB_ERROR_ALREADY_EXISTS => 'already exists',
DB_ERROR_CANNOT_CREATE => 'can not create',
DB_ERROR_CANNOT_DROP => 'can not drop',
DB_ERROR_CONNECT_FAILED => 'connect failed',
DB_ERROR_CONSTRAINT => 'constraint violation',
DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
DB_ERROR_DIVZERO => 'division by zero',
DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
DB_ERROR_INVALID => 'invalid',
DB_ERROR_INVALID_DATE => 'invalid date or time',
DB_ERROR_INVALID_DSN => 'invalid DSN',
DB_ERROR_INVALID_NUMBER => 'invalid number',
DB_ERROR_MISMATCH => 'mismatch',
DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied',
DB_ERROR_NODBSELECTED => 'no database selected',
DB_ERROR_NOSUCHDB => 'no such database',
DB_ERROR_NOSUCHFIELD => 'no such field',
DB_ERROR_NOSUCHTABLE => 'no such table',
DB_ERROR_NOT_CAPABLE => 'DB backend not capable',
DB_ERROR_NOT_FOUND => 'not found',
DB_ERROR_NOT_LOCKED => 'not locked',
DB_ERROR_SYNTAX => 'syntax error',
DB_ERROR_UNSUPPORTED => 'not supported',
DB_ERROR_TRUNCATED => 'truncated',
DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
DB_OK => 'no error',
);
}

if (DB::isError($value)) {
$value = $value->getCode();
}

return isset($errorMessages[$value]) ? $errorMessages[$value]
: $errorMessages[DB_ERROR];
}

// }}}
// {{{ parseDSN()

/**
* Parse a data source name
*
* Additional keys can be added by appending a URI query string to the
* end of the DSN.
*
* The format of the supplied DSN is in its fullest form:
* <code>
* phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
* </code>
*
* Most variations are allowed:
* <code>
* phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
* phptype://username:password@hostspec/database_name
* phptype://username:password@hostspec
* phptype://username@hostspec
* phptype://hostspec/database
* phptype://hostspec
* phptype(dbsyntax)
* phptype
* </code>
*
* @param string $dsn Data Source Name to be parsed
*
* @return array an associative array with the following keys:
* + phptype: Database backend used in PHP (mysql, odbc etc.)
* + dbsyntax: Database used with regards to SQL syntax etc.
* + protocol: Communication protocol to use (tcp, unix etc.)
* + hostspec: Host specification (hostname[:port])
* + database: Database to use on the DBMS server
* + username: User name for login
* + password: Password for login
*/
function parseDSN($dsn)
{
$parsed = array(
'phptype' => false,
'dbsyntax' => false,
'username' => false,
'password' => false,
'protocol' => false,
'hostspec' => false,
'port' => false,
'socket' => false,
'database' => false,
);

if (is_array($dsn)) {
$dsn = array_merge($parsed, $dsn);
if (!$dsn['dbsyntax']) {
$dsn['dbsyntax'] = $dsn['phptype'];
}
return $dsn;
}

// Find phptype and dbsyntax
if (($pos = strpos($dsn, '://')) !== false) {
$str = substr($dsn, 0, $pos);
$dsn = substr($dsn, $pos + 3);
} else {
$str = $dsn;
$dsn = null;
}

// Get phptype and dbsyntax
// $str => phptype(dbsyntax)
if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
$parsed['phptype'] = $arr[1];
$parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
} else {
$parsed['phptype'] = $str;
$parsed['dbsyntax'] = $str;
}

if (!count($dsn)) {
return $parsed;
}

// Get (if found): username and password
// $dsn => username:password@protocol+hostspec/database
if (($at = strrpos($dsn,'@')) !== false) {
$str = substr($dsn, 0, $at);
$dsn = substr($dsn, $at + 1);
if (($pos = strpos($str, ':')) !== false) {
$parsed['username'] = rawurldecode(substr($str, 0, $pos));
$parsed['password'] = rawurldecode(substr($str, $pos + 1));
} else {
$parsed['username'] = rawurldecode($str);
}
}

// Find protocol and hostspec

if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
// $dsn => proto(proto_opts)/database
$proto = $match[1];
$proto_opts = $match[2] ? $match[2] : false;
$dsn = $match[3];

} else {
// $dsn => protocol+hostspec/database (old format)
if (strpos($dsn, '+') !== false) {
list($proto, $dsn) = explode('+', $dsn, 2);
}
if (strpos($dsn, '/') !== false) {
list($proto_opts, $dsn) = explode('/', $dsn, 2);
} else {
$proto_opts = $dsn;
$dsn = null;
}
}

// process the different protocol options
$parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
$proto_opts = rawurldecode($proto_opts);
if (strpos($proto_opts, ':') !== false) {
list($proto_opts, $parsed['port']) = explode(':', $proto_opts);
}
if ($parsed['protocol'] == 'tcp') {
$parsed['hostspec'] = $proto_opts;
} elseif ($parsed['protocol'] == 'unix') {
$parsed['socket'] = $proto_opts;
}

// Get dabase if any
// $dsn => database
if ($dsn) {
if (($pos = strpos($dsn, '?')) === false) {
// /database
$parsed['database'] = rawurldecode($dsn);
} else {
// /database?param1=value1&param2=value2
$parsed['database'] = rawurldecode(substr($dsn, 0, $pos));
$dsn = substr($dsn, $pos + 1);
if (strpos($dsn, '&') !== false) {
$opts = explode('&', $dsn);
} else { // database?param1=value1
$opts = array($dsn);
}
foreach ($opts as $opt) {
list($key, $value) = explode('=', $opt);
if (!isset($parsed[$key])) {
// don't allow params overwrite
$parsed[$key] = rawurldecode($value);
}
}
}
}

return $parsed;
}

// }}}
// {{{ getDSNString()

/**
* Returns the given DSN in a string format suitable for output.
*
* @param array|string the DSN to parse and format
* @param boolean true to hide the password, false to include it
* @return string
*/
function getDSNString($dsn, $hidePassword) {
/* Calling parseDSN will ensure that we have all the array elements
* defined, and means that we deal with strings and array in the same
* manner. */
$dsnArray = DB::parseDSN($dsn);

if ($hidePassword) {
$dsnArray['password'] = 'PASSWORD';
}

/* Protocol is special-cased, as using the default "tcp" along with an
* Oracle TNS connection string fails. */
if (is_string($dsn) && strpos($dsn, 'tcp') === false && $dsnArray['protocol'] == 'tcp') {
$dsnArray['protocol'] = false;
}

// Now we just have to construct the actual string. This is ugly.
$dsnString = $dsnArray['phptype'];
if ($dsnArray['dbsyntax']) {
$dsnString .= '('.$dsnArray['dbsyntax'].')';
}
$dsnString .= '://'
.$dsnArray['username']
.':'
.$dsnArray['password']
.'@'
.$dsnArray['protocol'];
if ($dsnArray['socket']) {
$dsnString .= '('.$dsnArray['socket'].')';
}
if ($dsnArray['protocol'] && $dsnArray['hostspec']) {
$dsnString .= '+';
}
$dsnString .= $dsnArray['hostspec'];
if ($dsnArray['port']) {
$dsnString .= ':'.$dsnArray['port'];
}
$dsnString .= '/'.$dsnArray['database'];

/* Option handling. Unfortunately, parseDSN simply places options into
* the top-level array, so we'll first get rid of the fields defined by
* DB and see what's left. */
unset($dsnArray['phptype'],
$dsnArray['dbsyntax'],
$dsnArray['username'],
$dsnArray['password'],
$dsnArray['protocol'],
$dsnArray['socket'],
$dsnArray['hostspec'],
$dsnArray['port'],
$dsnArray['database']
);
if (count($dsnArray) > 0) {
$dsnString .= '?';
$i = 0;
foreach ($dsnArray as $key => $value) {
if (++$i > 1) {
$dsnString .= '&';
}
$dsnString .= $key.'='.$value;
}
}

return $dsnString;
}

// }}}
}

// }}}
// {{{ class DB_Error

/**
* DB_Error implements a class for reporting portable database error
* messages
*
* @category Database
* @package DB
* @author Stig Bakken <ssb@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.14RC1
* @link http://pear.php.net/package/DB
*/
class DB_Error extends PEAR_Error
{
// {{{ constructor

/**
* DB_Error constructor
*
* @param mixed $code DB error code, or string with error message
* @param int $mode what "error mode" to operate in
* @param int $level what error level to use for $mode &
* PEAR_ERROR_TRIGGER
* @param mixed $debuginfo additional debug info, such as the last query
*
* @see PEAR_Error
*/
function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE, $debuginfo = null)
{
if (is_int($code)) {
$this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code,
$mode, $level, $debuginfo);
} else {
$this->PEAR_Error("DB Error: $code", DB_ERROR,
$mode, $level, $debuginfo);
}
}

// }}}
}

// }}}
// {{{ class DB_result

/**
* This class implements a wrapper for a DB result set
*
* A new instance of this class will be returned by the DB implementation
* after processing a query that returns data.
*
* @category Database
* @package DB
* @author Stig Bakken <ssb@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.14RC1
* @link http://pear.php.net/package/DB
*/
class DB_result
{
// {{{ properties

/**
* Should results be freed automatically when there are no more rows?
* @var boolean
* @see DB_common::$options
*/
var $autofree;

/**
* A reference to the DB_<driver> object
* @var object
*/
var $dbh;

/**
* The current default fetch mode
* @var integer
* @see DB_common::$fetchmode
*/
var $fetchmode;

/**
* The name of the class into which results should be fetched when
* DB_FETCHMODE_OBJECT is in effect
*
* @var string
* @see DB_common::$fetchmode_object_class
*/
var $fetchmode_object_class;

/**
* The number of rows to fetch from a limit query
* @var integer
*/
var $limit_count = null;

/**
* The row to start fetching from in limit queries
* @var integer
*/
var $limit_from = null;

/**
* The execute parameters that created this result
* @var array
* @since Property available since Release 1.7.0
*/
var $parameters;

/**
* The query string that created this result
*
* Copied here incase it changes in $dbh, which is referenced
*
* @var string
* @since Property available since Release 1.7.0
*/
var $query;

/**
* The query result resource id created by PHP
* @var resource
*/
var $result;

/**
* The present row being dealt with
* @var integer
*/
var $row_counter = null;

/**
* The prepared statement resource id created by PHP in $dbh
*
* This resource is only available when the result set was created using
* a driver's native execute() method, not PEAR DB's emulated one.
*
* Copied here incase it changes in $dbh, which is referenced
*
* {@internal Mainly here because the InterBase/Firebird API is only
* able to retrieve data from result sets if the statemnt handle is
* still in scope.}}
*
* @var resource
* @since Property available since Release 1.7.0
*/
var $statement;


// }}}
// {{{ constructor

/**
* This constructor sets the object's properties
*
* @param object &$dbh the DB object reference
* @param resource $result the result resource id
* @param array $options an associative array with result options
*
* @return void
*/
function DB_result(&$dbh, $result, $options = array())
{
$this->autofree = $dbh->options['autofree'];
$this->dbh = &$dbh;
$this->fetchmode = $dbh->fetchmode;
$this->fetchmode_object_class = $dbh->fetchmode_object_class;
$this->parameters = $dbh->last_parameters;
$this->query = $dbh->last_query;
$this->result = $result;
$this->statement = empty($dbh->last_stmt) ? null : $dbh->last_stmt;
foreach ($options as $key => $value) {
$this->setOption($key, $value);
}
}

/**
* Set options for the DB_result object
*
* @param string $key the option to set
* @param mixed $value the value to set the option to
*
* @return void
*/
function setOption($key, $value = null)
{
switch ($key) {
case 'limit_from':
$this->limit_from = $value;
break;
case 'limit_count':
$this->limit_count = $value;
}
}

// }}}
// {{{ fetchRow()

/**
* Fetch a row of data and return it by reference into an array
*
* The type of array returned can be controlled either by setting this
* method's <var>$fetchmode</var> parameter or by changing the default
* fetch mode setFetchMode() before calling this method.
*
* There are two options for standardizing the information returned
* from databases, ensuring their values are consistent when changing
* DBMS's. These portability options can be turned on when creating a
* new DB object or by using setOption().
*
* + <var>DB_PORTABILITY_LOWERCASE</var>
* convert names of fields to lower case
*
* + <var>DB_PORTABILITY_RTRIM</var>
* right trim the data
*
* @param int $fetchmode the constant indicating how to format the data
* @param int $rownum the row number to fetch (index starts at 0)
*
* @return mixed an array or object containing the row's data,
* NULL when the end of the result set is reached
* or a DB_Error object on failure.
*
* @see DB_common::setOption(), DB_common::setFetchMode()
*/
function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
{
if ($fetchmode === DB_FETCHMODE_DEFAULT) {
$fetchmode = $this->fetchmode;
}
if ($fetchmode === DB_FETCHMODE_OBJECT) {
$fetchmode = DB_FETCHMODE_ASSOC;
$object_class = $this->fetchmode_object_class;
}
if (is_null($rownum) && $this->limit_from !== null) {
if ($this->row_counter === null) {
$this->row_counter = $this->limit_from;
// Skip rows
if ($this->dbh->features['limit'] === false) {
$i = 0;
while ($i++ < $this->limit_from) {
$this->dbh->fetchInto($this->result, $arr, $fetchmode);
}
}
}
if ($this->row_counter >= ($this->limit_from + $this->limit_count))
{
if ($this->autofree) {
$this->free();
}
$tmp = null;
return $tmp;
}
if ($this->dbh->features['limit'] === 'emulate') {
$rownum = $this->row_counter;
}
$this->row_counter++;
}
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
if ($res === DB_OK) {
if (isset($object_class)) {
// The default mode is specified in the
// DB_common::fetchmode_object_class property
if ($object_class == 'stdClass') {
$arr = (object) $arr;
} else {
$arr = new $object_class($arr);
}
}
return $arr;
}
if ($res == null && $this->autofree) {
$this->free();
}
return $res;
}

// }}}
// {{{ fetchInto()

/**
* Fetch a row of data into an array which is passed by reference
*
* The type of array returned can be controlled either by setting this
* method's <var>$fetchmode</var> parameter or by changing the default
* fetch mode setFetchMode() before calling this method.
*
* There are two options for standardizing the information returned
* from databases, ensuring their values are consistent when changing
* DBMS's. These portability options can be turned on when creating a
* new DB object or by using setOption().
*
* + <var>DB_PORTABILITY_LOWERCASE</var>
* convert names of fields to lower case
*
* + <var>DB_PORTABILITY_RTRIM</var>
* right trim the data
*
* @param array &$arr the variable where the data should be placed
* @param int $fetchmode the constant indicating how to format the data
* @param int $rownum the row number to fetch (index starts at 0)
*
* @return mixed DB_OK if a row is processed, NULL when the end of the
* result set is reached or a DB_Error object on failure
*
* @see DB_common::setOption(), DB_common::setFetchMode()
*/
function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
{
if ($fetchmode === DB_FETCHMODE_DEFAULT) {
$fetchmode = $this->fetchmode;
}
if ($fetchmode === DB_FETCHMODE_OBJECT) {
$fetchmode = DB_FETCHMODE_ASSOC;
$object_class = $this->fetchmode_object_class;
}
if (is_null($rownum) && $this->limit_from !== null) {
if ($this->row_counter === null) {
$this->row_counter = $this->limit_from;
// Skip rows
if ($this->dbh->features['limit'] === false) {
$i = 0;
while ($i++ < $this->limit_from) {
$this->dbh->fetchInto($this->result, $arr, $fetchmode);
}
}
}
if ($this->row_counter >= (
$this->limit_from + $this->limit_count))
{
if ($this->autofree) {
$this->free();
}
return null;
}
if ($this->dbh->features['limit'] === 'emulate') {
$rownum = $this->row_counter;
}

$this->row_counter++;
}
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
if ($res === DB_OK) {
if (isset($object_class)) {
// default mode specified in the
// DB_common::fetchmode_object_class property
if ($object_class == 'stdClass') {
$arr = (object) $arr;
} else {
$arr = new $object_class($arr);
}
}
return DB_OK;
}
if ($res == null && $this->autofree) {
$this->free();
}
return $res;
}

// }}}
// {{{ numCols()

/**
* Get the the number of columns in a result set
*
* @return int the number of columns. A DB_Error object on failure.
*/
function numCols()
{
return $this->dbh->numCols($this->result);
}

// }}}
// {{{ numRows()

/**
* Get the number of rows in a result set
*
* @return int the number of rows. A DB_Error object on failure.
*/
function numRows()
{
if ($this->dbh->features['numrows'] === 'emulate'
&& $this->dbh->options['portability'] & DB_PORTABILITY_NUMROWS)
{
if ($this->dbh->features['prepare']) {
$res = $this->dbh->query($this->query, $this->parameters);
} else {
$res = $this->dbh->query($this->query);
}
if (DB::isError($res)) {
return $res;
}
$i = 0;
while ($res->fetchInto($tmp, DB_FETCHMODE_ORDERED)) {
$i++;
}
$count = $i;
} else {
$count = $this->dbh->numRows($this->result);
}

/* fbsql is checked for here because limit queries are implemented
* using a TOP() function, which results in fbsql_num_rows still
* returning the total number of rows that would have been returned,
* rather than the real number. As a result, we'll just do the limit
* calculations for fbsql in the same way as a database with emulated
* limits. Unfortunately, we can't just do this in DB_fbsql::numRows()
* because that only gets the result resource, rather than the full
* DB_Result object. */
if (($this->dbh->features['limit'] === 'emulate'
&& $this->limit_from !== null)
|| $this->dbh->phptype == 'fbsql') {
$limit_count = is_null($this->limit_count) ? $count : $this->limit_count;
if ($count < $this->limit_from) {
$count = 0;
} elseif ($count < ($this->limit_from + $limit_count)) {
$count -= $this->limit_from;
} else {
$count = $limit_count;
}
}

return $count;
}

// }}}
// {{{ nextResult()

/**
* Get the next result if a batch of queries was executed
*
* @return bool true if a new result is available or false if not
*/
function nextResult()
{
return $this->dbh->nextResult($this->result);
}

// }}}
// {{{ free()

/**
* Frees the resources allocated for this result set
*
* @return bool true on success. A DB_Error object on failure.
*/
function free()
{
$err = $this->dbh->freeResult($this->result);
if (DB::isError($err)) {
return $err;
}
$this->result = false;
$this->statement = false;
return true;
}

// }}}
// {{{ tableInfo()

/**
* @see DB_common::tableInfo()
* @deprecated Method deprecated some time before Release 1.2
*/
function tableInfo($mode = null)
{
if (is_string($mode)) {
return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA);
}
return $this->dbh->tableInfo($this, $mode);
}

// }}}
// {{{ getQuery()

/**
* Determine the query string that created this result
*
* @return string the query string
*
* @since Method available since Release 1.7.0
*/
function getQuery()
{
return $this->query;
}

// }}}
// {{{ getRowCounter()

/**
* Tells which row number is currently being processed
*
* @return integer the current row being looked at. Starts at 1.
*/
function getRowCounter()
{
return $this->row_counter;
}

// }}}
}

// }}}
// {{{ class DB_row

/**
* PEAR DB Row Object
*
* The object contains a row of data from a result set. Each column's data
* is placed in a property named for the column.
*
* @category Database
* @package DB
* @author Stig Bakken <ssb@php.net>
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.14RC1
* @link http://pear.php.net/package/DB
* @see DB_common::setFetchMode()
*/
class DB_row
{
// {{{ constructor

/**
* The constructor places a row's data into properties of this object
*
* @param array the array containing the row's data
*
* @return void
*/
function DB_row(&$arr)
{
foreach ($arr as $key => $value) {
$this->$key = &$arr[$key];
}
}

// }}}
}

// }}}

/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/

?>
[/codebox]


PEAR.php

[codebox]<?php
/**
* PEAR, the PHP Extension and Application Repository
*
* PEAR class and PEAR_Error class
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category pear
* @package PEAR
* @author Sterling Hughes <sterling@php.net>
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V.Cox <cox@idecnet.com>
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2008 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: PEAR.php,v 1.104 2008/01/03 20:26:34 cellog Exp $
* @link http://pear.php.net/package/PEAR
* @since File available since Release 0.1
*/

/**#@+
* ERROR constants
*/
define('PEAR_ERROR_RETURN', 1);
define('PEAR_ERROR_PRINT', 2);
define('PEAR_ERROR_TRIGGER', 4);
define('PEAR_ERROR_DIE', 8);
define('PEAR_ERROR_CALLBACK', 16);
/**
* WARNING: obsolete
* @deprecated
*/
define('PEAR_ERROR_EXCEPTION', 32);
/**#@-*/
define('PEAR_ZE2', (function_exists('version_compare') &&
version_compare(zend_version(), "2-dev", "ge")));

if (substr(PHP_OS, 0, 3) == 'WIN') {
define('OS_WINDOWS', true);
define('OS_UNIX', false);
define('PEAR_OS', 'Windows');
} else {
define('OS_WINDOWS', false);
define('OS_UNIX', true);
define('PEAR_OS', 'Unix'); // blatant assumption
}

// instant backwards compatibility
if (!defined('PATH_SEPARATOR')) {
if (OS_WINDOWS) {
define('PATH_SEPARATOR', ';');
} else {
define('PATH_SEPARATOR', ':');
}
}

$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
$GLOBALS['_PEAR_destructor_object_list'] = array();
$GLOBALS['_PEAR_shutdown_funcs'] = array();
$GLOBALS['_PEAR_error_handler_stack'] = array();

@ini_set('track_errors', true);

/**
* Base class for other PEAR classes. Provides rudimentary
* emulation of destructors.
*
* If you want a destructor in your class, inherit PEAR and make a
* destructor method called _yourclassname (same name as the
* constructor, but with a "_" prefix). Also, in your constructor you
* have to call the PEAR constructor: $this->PEAR();.
* The destructor method will be called without parameters. Note that
* at in some SAPI implementations (such as Apache), any output during
* the request shutdown (in which destructors are called) seems to be
* discarded. If you need to get any debug information from your
* destructor, use error_log(), syslog() or something similar.
*
* IMPORTANT! To use the emulated destructors you need to create the
* objects by reference: $obj =& new PEAR_child;
*
* @category pear
* @package PEAR
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V. Cox <cox@idecnet.com>
* @author Greg Beaver <cellog@php.net>
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: 1.7.2
* @link http://pear.php.net/package/PEAR
* @see PEAR_Error
* @since Class available since PHP 4.0.2
* @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
*/
class PEAR
{
// {{{ properties

/**
* Whether to enable internal debug messages.
*
* @var bool
* @access private
*/
var $_debug = false;

/**
* Default error mode for this object.
*
* @var int
* @access private
*/
var $_default_error_mode = null;

/**
* Default error options used for this object when error mode
* is PEAR_ERROR_TRIGGER.
*
* @var int
* @access private
*/
var $_default_error_options = null;

/**
* Default error handler (callback) for this object, if error mode is
* PEAR_ERROR_CALLBACK.
*
* @var string
* @access private
*/
var $_default_error_handler = '';

/**
* Which class to use for error objects.
*
* @var string
* @access private
*/
var $_error_class = 'PEAR_Error';

/**
* An array of expected errors.
*
* @var array
* @access private
*/
var $_expected_errors = array();

// }}}

// {{{ constructor

/**
* Constructor. Registers this object in
* $_PEAR_destructor_object_list for destructor emulation if a
* destructor object exists.
*
* @param string $error_class (optional) which class to use for
* error objects, defaults to PEAR_Error.
* @access public
* @return void
*/
function PEAR($error_class = null)
{
$classname = strtolower(get_class($this));
if ($this->_debug) {
print "PEAR constructor called, class=$classname\n";
}
if ($error_class !== null) {
$this->_error_class = $error_class;
}
while ($classname && strcasecmp($classname, "pear")) {
$destructor = "_$classname";
if (method_exists($this, $destructor)) {
global $_PEAR_destructor_object_list;
$_PEAR_destructor_object_list[] = &$this;
if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
register_shutdown_function("_PEAR_call_destructors");
$GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
}
break;
} else {
$classname = get_parent_class($classname);
}
}
}

// }}}
// {{{ destructor

/**
* Destructor (the emulated type of...). Does nothing right now,
* but is included for forward compatibility, so subclass
* destructors should always call it.
*
* See the note in the class desciption about output from
* destructors.
*
* @access public
* @return void
*/
function _PEAR() {
if ($this->_debug) {
printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
}
}

// }}}
// {{{ getStaticProperty()

/**
* If you have a class that's mostly/entirely static, and you need static
* properties, you can use this method to simulate them. Eg. in your method(s)
* do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
* You MUST use a reference, or they will not persist!
*
* @access public
* @param string $class The calling classname, to prevent clashes
* @param string $var The variable to retrieve.
* @return mixed A reference to the variable. If not set it will be
* auto initialised to NULL.
*/
function &getStaticProperty($class, $var)
{
static $properties;
if (!isset($properties[$class])) {
$properties[$class] = array();
}
if (!array_key_exists($var, $properties[$class])) {
$properties[$class][$var] = null;
}
return $properties[$class][$var];
}

// }}}
// {{{ registerShutdownFunc()

/**
* Use this function to register a shutdown method for static
* classes.
*
* @access public
* @param mixed $func The function name (or array of class/method) to call
* @param mixed $args The arguments to pass to the function
* @return void
*/
function registerShutdownFunc($func, $args = array())
{
// if we are called statically, there is a potential
// that no shutdown func is registered. Bug #6445
if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
register_shutdown_function("_PEAR_call_destructors");
$GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
}
$GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
}

// }}}
// {{{ isError()

/**
* Tell whether a value is a PEAR error.
*
* @param mixed $data the value to test
* @param int $code if $data is an error object, return true
* only if $code is a string and
* $obj->getMessage() == $code or
* $code is an integer and $obj->getCode() == $code
* @access public
* @return bool true if parameter is an error
*/
function isError($data, $code = null)
{
if (is_a($data, 'PEAR_Error')) {
if (is_null($code)) {
return true;
} elseif (is_string($code)) {
return $data->getMessage() == $code;
} else {
return $data->getCode() == $code;
}
}
return false;
}

// }}}
// {{{ setErrorHandling()

/**
* Sets how errors generated by this object should be handled.
* Can be invoked both in objects and statically. If called
* statically, setErrorHandling sets the default behaviour for all
* PEAR objects. If called in an object, setErrorHandling sets
* the default behaviour for that object.
*
* @param int $mode
* One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
*
* @param mixed $options
* When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
* of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
*
* When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
* to be the callback function or method. A callback
* function is a string with the name of the function, a
* callback method is an array of two elements: the element
* at index 0 is the object, and the element at index 1 is
* the name of the method to call in the object.
*
* When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
* a printf format string used when printing the error
* message.
*
* @access public
* @return void
* @see PEAR_ERROR_RETURN
* @see PEAR_ERROR_PRINT
* @see PEAR_ERROR_TRIGGER
* @see PEAR_ERROR_DIE
* @see PEAR_ERROR_CALLBACK
* @see PEAR_ERROR_EXCEPTION
*
* @since PHP 4.0.5
*/

function setErrorHandling($mode = null, $options = null)
{
if (isset($this) && is_a($this, 'PEAR')) {
$setmode = &$this->_default_error_mode;
$setoptions = &$this->_default_error_options;
} else {
$setmode = &$GLOBALS['_PEAR_default_error_mode'];
$setoptions = &$GLOBALS['_PEAR_default_error_options'];
}

switch ($mode) {
case PEAR_ERROR_EXCEPTION:
case PEAR_ERROR_RETURN:
case PEAR_ERROR_PRINT:
case PEAR_ERROR_TRIGGER:
case PEAR_ERROR_DIE:
case null:
$setmode = $mode;
$setoptions = $options;
break;

case PEAR_ERROR_CALLBACK:
$setmode = $mode;
// class/object method callback
if (is_callable($options)) {
$setoptions = $options;
} else {
trigger_error("invalid error callback", E_USER_WARNING);
}
break;

default:
trigger_error("invalid error mode", E_USER_WARNING);
break;
}
}

// }}}
// {{{ expectError()

/**
* This method is used to tell which errors you expect to get.
* Expected errors are always returned with error mode
* PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
* and this method pushes a new element onto it. The list of
* expected errors are in effect until they are popped off the
* stack with the popExpect() method.
*
* Note that this method can not be called statically
*
* @param mixed $code a single error code or an array of error codes to expect
*
* @return int the new depth of the "expected errors" stack
* @access public
*/
function expectError($code = '*')
{
if (is_array($code)) {
array_push($this->_expected_errors, $code);
} else {
array_push($this->_expected_errors, array($code));
}
return sizeof($this->_expected_errors);
}

// }}}
// {{{ popExpect()

/**
* This method pops one element off the expected error codes
* stack.
*
* @return array the list of error codes that were popped
*/
function popExpect()
{
return array_pop($this->_expected_errors);
}

// }}}
// {{{ _checkDelExpect()

/**
* This method checks unsets an error code if available
*
* @param mixed error code
* @return bool true if the error code was unset, false otherwise
* @access private
* @since PHP 4.3.0
*/
function _checkDelExpect($error_code)
{
$deleted = false;

foreach ($this->_expected_errors AS $key => $error_array) {
if (in_array($error_code, $error_array)) {
unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
$deleted = true;
}

// clean up empty arrays
if (0 == count($this->_expected_errors[$key])) {
unset($this->_expected_errors[$key]);
}
}
return $deleted;
}

// }}}
// {{{ delExpect()

/**
* This method deletes all occurences of the specified element from
* the expected error codes stack.
*
* @param mixed $error_code error code that should be deleted
* @return mixed list of error codes that were deleted or error
* @access public
* @since PHP 4.3.0
*/
function delExpect($error_code)
{
$deleted = false;

if ((is_array($error_code) && (0 != count($error_code)))) {
// $error_code is a non-empty array here;
// we walk through it trying to unset all
// values
foreach($error_code as $key => $error) {
if ($this->_checkDelExpect($error)) {
$deleted = true;
} else {
$deleted = false;
}
}
return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
} elseif (!empty($error_code)) {
// $error_code comes alone, trying to unset it
if ($this->_checkDelExpect($error_code)) {
return true;
} else {
return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
}
} else {
// $error_code is empty
return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
}
}

// }}}
// {{{ raiseError()

/**
* This method is a wrapper that returns an instance of the
* configured error class with this object's default error
* handling applied. If the $mode and $options parameters are not
* specified, the object's defaults are used.
*
* @param mixed $message a text error message or a PEAR error object
*
* @param int $code a numeric error code (it is up to your class
* to define these if you want to use codes)
*
* @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
* PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
*
* @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
* specifies the PHP-internal error level (one of
* E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
* If $mode is PEAR_ERROR_CALLBACK, this
* parameter specifies the callback function or
* method. In other error modes this parameter
* is ignored.
*
* @param string $userinfo If you need to pass along for example debug
* information, this parameter is meant for that.
*
* @param string $error_class The returned error object will be
* instantiated from this class, if specified.
*
* @param bool $skipmsg If true, raiseError will only pass error codes,
* the error message parameter will be dropped.
*
* @access public
* @return object a PEAR error object
* @see PEAR::setErrorHandling
* @since PHP 4.0.5
*/
function &raiseError($message = null,
$code = null,
$mode = null,
$options = null,
$userinfo = null,
$error_class = null,
$skipmsg = false)
{
// The error is yet a PEAR error object
if (is_object($message)) {
$code = $message->getCode();
$userinfo = $message->getUserInfo();
$error_class = $message->getType();
$message->error_message_prefix = '';
$message = $message->getMessage();
}

if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
if ($exp[0] == "*" ||
(is_int(reset($exp)) && in_array($code, $exp)) ||
(is_string(reset($exp)) && in_array($message, $exp))) {
$mode = PEAR_ERROR_RETURN;
}
}
// No mode given, try global ones
if ($mode === null) {
// Class error handler

if (isset($this) && isset($this->_default_error_mode)) {
$mode = $this->_default_error_mode;
$options = $this->_default_error_options;
// Global error handler
} elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
$mode = $GLOBALS['_PEAR_default_error_mode'];
$options = $GLOBALS['_PEAR_default_error_options'];
}
}

if ($error_class !== null) {
$ec = $error_class;
} elseif (isset($this) && isset($this->_error_class)) {
$ec = $this->_error_class;
} else {
$ec = 'PEAR_Error';
}
if (intval(PHP_VERSION) < 5) {
// little non-eval hack to fix bug #12147
include 'PEAR/FixPHP5PEARWarnings.php';
return $a;
}
if ($skipmsg) {
$a = new $ec($code, $mode, $options, $userinfo);
} else {
$a = new $ec($message, $code, $mode, $options, $userinfo);
}
return $a;
}

// }}}
// {{{ throwError()

/**
* Simpler form of raiseError with fewer options. In most cases
* message, code and userinfo are enough.
*
* @param string $message
*
*/
function &throwError($message = null,
$code = null,
$userinfo = null)
{
if (isset($this) && is_a($this, 'PEAR')) {
$a = &$this->raiseError($message, $code, null, null, $userinfo);
return $a;
} else {
$a = &PEAR::raiseError($message, $code, null, null, $userinfo);
return $a;
}
}

// }}}
function staticPushErrorHandling($mode, $options = null)
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
$def_options = &$GLOBALS['_PEAR_default_error_options'];
$stack[] = array($def_mode, $def_options);
switch ($mode) {
case PEAR_ERROR_EXCEPTION:
case PEAR_ERROR_RETURN:
case PEAR_ERROR_PRINT:
case PEAR_ERROR_TRIGGER:
case PEAR_ERROR_DIE:
case null:
$def_mode = $mode;
$def_options = $options;
break;

case PEAR_ERROR_CALLBACK:
$def_mode = $mode;
// class/object method callback
if (is_callable($options)) {
$def_options = $options;
} else {
trigger_error("invalid error callback", E_USER_WARNING);
}
break;

default:
trigger_error("invalid error mode", E_USER_WARNING);
break;
}
$stack[] = array($mode, $options);
return true;
}

function staticPopErrorHandling()
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
$setmode = &$GLOBALS['_PEAR_default_error_mode'];
$setoptions = &$GLOBALS['_PEAR_default_error_options'];
array_pop($stack);
list($mode, $options) = $stack[sizeof($stack) - 1];
array_pop($stack);
switch ($mode) {
case PEAR_ERROR_EXCEPTION:
case PEAR_ERROR_RETURN:
case PEAR_ERROR_PRINT:
case PEAR_ERROR_TRIGGER:
case PEAR_ERROR_DIE:
case null:
$setmode = $mode;
$setoptions = $options;
break;

case PEAR_ERROR_CALLBACK:
$setmode = $mode;
// class/object method callback
if (is_callable($options)) {
$setoptions = $options;
} else {
trigger_error("invalid error callback", E_USER_WARNING);
}
break;

default:
trigger_error("invalid error mode", E_USER_WARNING);
break;
}
return true;
}

// {{{ pushErrorHandling()

/**
* Push a new error handler on top of the error handler options stack. With this
* you can easily override the actual error handler for some code and restore
* it later with popErrorHandling.
*
* @param mixed $mode (same as setErrorHandling)
* @param mixed $options (same as setErrorHandling)
*
* @return bool Always true
*
* @see PEAR::setErrorHandling
*/
function pushErrorHandling($mode, $options = null)
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
if (isset($this) && is_a($this, 'PEAR')) {
$def_mode = &$this->_default_error_mode;
$def_options = &$this->_default_error_options;
} else {
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
$def_options = &$GLOBALS['_PEAR_default_error_options'];
}
$stack[] = array($def_mode, $def_options);

if (isset($this) && is_a($this, 'PEAR')) {
$this->setErrorHandling($mode, $options);
} else {
PEAR::setErrorHandling($mode, $options);
}
$stack[] = array($mode, $options);
return true;
}

// }}}
// {{{ popErrorHandling()

/**
* Pop the last error handler used
*
* @return bool Always true
*
* @see PEAR::pushErrorHandling
*/
function popErrorHandling()
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
array_pop($stack);
list($mode, $options) = $stack[sizeof($stack) - 1];
array_pop($stack);
if (isset($this) && is_a($this, 'PEAR')) {
$this->setErrorHandling($mode, $options);
} else {
PEAR::setErrorHandling($mode, $options);
}
return true;
}

// }}}
// {{{ loadExtension()

/**
* OS independant PHP extension load. Remember to take care
* on the correct extension name for case sensitive OSes.
*
* @param string $ext The extension name
* @return bool Success or not on the dl() call
*/
function loadExtension($ext)
{
if (!extension_loaded($ext)) {
// if either returns true dl() will produce a FATAL error, stop that
if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
return false;
}
if (OS_WINDOWS) {
$suffix = '.dll';
} elseif (PHP_OS == 'HP-UX') {
$suffix = '.sl';
} elseif (PHP_OS == 'AIX') {
$suffix = '.a';
} elseif (PHP_OS == 'OSX') {
$suffix = '.bundle';
} else {
$suffix = '.so';
}
return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
}
return true;
}

// }}}
}

// {{{ _PEAR_call_destructors()

function _PEAR_call_destructors()
{
global $_PEAR_destructor_object_list;
if (is_array($_PEAR_destructor_object_list) &&
sizeof($_PEAR_destructor_object_list))
{
reset($_PEAR_destructor_object_list);
if (PEAR::getStaticProperty('PEAR', 'destructlifo')) {
$_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
}
while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
$classname = get_class($objref);
while ($classname) {
$destructor = "_$classname";
if (method_exists($objref, $destructor)) {
$objref->$destructor();
break;
} else {
$classname = get_parent_class($classname);
}
}
}
// Empty the object list to ensure that destructors are
// not called more than once.
$_PEAR_destructor_object_list = array();
}

// Now call the shutdown functions
if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
call_user_func_array($value[0], $value[1]);
}
}
}

// }}}
/**
* Standard PEAR error class for PHP 4
*
* This class is supserseded by {@link PEAR_Exception} in PHP 5
*
* @category pear
* @package PEAR
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V. Cox <cox@idecnet.com>
* @author Gregory Beaver <cellog@php.net>
* @copyright 1997-2006 The PHP Group
* @license