aboutsummaryrefslogtreecommitdiff
path: root/lib/phpFlickr
diff options
context:
space:
mode:
Diffstat (limited to 'lib/phpFlickr')
-rw-r--r--lib/phpFlickr/PEAR/DB.php1388
-rw-r--r--lib/phpFlickr/PEAR/DB/common.php2157
-rw-r--r--lib/phpFlickr/PEAR/DB/mysql.php1034
-rw-r--r--lib/phpFlickr/PEAR/DB/pgsql.php1097
-rw-r--r--lib/phpFlickr/PEAR/DB/storage.php504
-rw-r--r--lib/phpFlickr/PEAR/HTTP/Request.php1484
-rw-r--r--lib/phpFlickr/PEAR/HTTP/Request/Listener.php106
-rw-r--r--lib/phpFlickr/PEAR/Net/Socket.php528
-rw-r--r--lib/phpFlickr/PEAR/Net/URL.php410
-rw-r--r--lib/phpFlickr/PEAR/PEAR.php1108
-rw-r--r--lib/phpFlickr/README.txt215
-rw-r--r--lib/phpFlickr/auth.php37
-rw-r--r--lib/phpFlickr/example.php30
-rw-r--r--lib/phpFlickr/getToken.php19
-rw-r--r--lib/phpFlickr/phpFlickr.php1453
15 files changed, 11570 insertions, 0 deletions
diff --git a/lib/phpFlickr/PEAR/DB.php b/lib/phpFlickr/PEAR/DB.php
new file mode 100644
index 000000000..a3eede070
--- /dev/null
+++ b/lib/phpFlickr/PEAR/DB.php
@@ -0,0 +1,1388 @@
+<?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-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: DB.php 32 2005-08-01 06:21:02Z dancoulter $
+ * @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-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @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)) {
+ $err->addUserInfo($dsn);
+ return $err;
+ }
+
+ return $obj;
+ }
+
+ // }}}
+ // {{{ apiVersion()
+
+ /**
+ * Return the DB API version
+ *
+ * @return string the DB API version number
+ */
+ function apiVersion()
+ {
+ return '@package_version@';
+ }
+
+ // }}}
+ // {{{ 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|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 ($parsed['protocol'] == 'tcp') {
+ if (strpos($proto_opts, ':') !== false) {
+ list($parsed['hostspec'],
+ $parsed['port']) = explode(':', $proto_opts);
+ } else {
+ $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;
+ }
+
+ // }}}
+}
+
+// }}}
+// {{{ 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-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @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-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @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 ($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 ($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++;
+ }
+ return $i;
+ } else {
+ return $this->dbh->numRows($this->result);
+ }
+ }
+
+ // }}}
+ // {{{ 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-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @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:
+ */
+
+?>
diff --git a/lib/phpFlickr/PEAR/DB/common.php b/lib/phpFlickr/PEAR/DB/common.php
new file mode 100644
index 000000000..04e71ff52
--- /dev/null
+++ b/lib/phpFlickr/PEAR/DB/common.php
@@ -0,0 +1,2157 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Contains the DB_common base 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 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-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: common.php 32 2005-08-01 06:21:02Z dancoulter $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the PEAR class so it can be extended from
+ */
+require_once 'PEAR.php';
+
+/**
+ * DB_common is the base class from which each database driver class extends
+ *
+ * All common methods are declared here. If a given DBMS driver contains
+ * a particular method, that method will overload the one here.
+ *
+ * @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-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_common extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * The current default fetch mode
+ * @var integer
+ */
+ var $fetchmode = DB_FETCHMODE_ORDERED;
+
+ /**
+ * The name of the class into which results should be fetched when
+ * DB_FETCHMODE_OBJECT is in effect
+ *
+ * @var string
+ */
+ var $fetchmode_object_class = 'stdClass';
+
+ /**
+ * Was a connection present when the object was serialized()?
+ * @var bool
+ * @see DB_common::__sleep(), DB_common::__wake()
+ */
+ var $was_connected = null;
+
+ /**
+ * The most recently executed query
+ * @var string
+ */
+ var $last_query = '';
+
+ /**
+ * Run-time configuration options
+ *
+ * The 'optimize' option has been deprecated. Use the 'portability'
+ * option instead.
+ *
+ * @var array
+ * @see DB_common::setOption()
+ */
+ var $options = array(
+ 'result_buffering' => 500,
+ 'persistent' => false,
+ 'ssl' => false,
+ 'debug' => 0,
+ 'seqname_format' => '%s_seq',
+ 'autofree' => false,
+ 'portability' => DB_PORTABILITY_NONE,
+ 'optimize' => 'performance', // Deprecated. Use 'portability'.
+ );
+
+ /**
+ * The parameters from the most recently executed query
+ * @var array
+ * @since Property available since Release 1.7.0
+ */
+ var $last_parameters = array();
+
+ /**
+ * The elements from each prepared statement
+ * @var array
+ */
+ var $prepare_tokens = array();
+
+ /**
+ * The data types of the various elements in each prepared statement
+ * @var array
+ */
+ var $prepare_types = array();
+
+ /**
+ * The prepared queries
+ * @var array
+ */
+ var $prepared_queries = array();
+
+
+ // }}}
+ // {{{ DB_common
+
+ /**
+ * This constructor calls <kbd>$this->PEAR('DB_Error')</kbd>
+ *
+ * @return void
+ */
+ function DB_common()
+ {
+ $this->PEAR('DB_Error');
+ }
+
+ // }}}
+ // {{{ __sleep()
+
+ /**
+ * Automatically indicates which properties should be saved
+ * when PHP's serialize() function is called
+ *
+ * @return array the array of properties names that should be saved
+ */
+ function __sleep()
+ {
+ if ($this->connection) {
+ // Don't disconnect(), people use serialize() for many reasons
+ $this->was_connected = true;
+ } else {
+ $this->was_connected = false;
+ }
+ if (isset($this->autocommit)) {
+ return array('autocommit',
+ 'dbsyntax',
+ 'dsn',
+ 'features',
+ 'fetchmode',
+ 'fetchmode_object_class',
+ 'options',
+ 'was_connected',
+ );
+ } else {
+ return array('dbsyntax',
+ 'dsn',
+ 'features',
+ 'fetchmode',
+ 'fetchmode_object_class',
+ 'options',
+ 'was_connected',
+ );
+ }
+ }
+
+ // }}}
+ // {{{ __wakeup()
+
+ /**
+ * Automatically reconnects to the database when PHP's unserialize()
+ * function is called
+ *
+ * The reconnection attempt is only performed if the object was connected
+ * at the time PHP's serialize() function was run.
+ *
+ * @return void
+ */
+ function __wakeup()
+ {
+ if ($this->was_connected) {
+ $this->connect($this->dsn, $this->options);
+ }
+ }
+
+ // }}}
+ // {{{ __toString()
+
+ /**
+ * Automatic string conversion for PHP 5
+ *
+ * @return string a string describing the current PEAR DB object
+ *
+ * @since Method available since Release 1.7.0
+ */
+ function __toString()
+ {
+ $info = strtolower(get_class($this));
+ $info .= ': (phptype=' . $this->phptype .
+ ', dbsyntax=' . $this->dbsyntax .
+ ')';
+ if ($this->connection) {
+ $info .= ' [connected]';
+ }
+ return $info;
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * DEPRECATED: String conversion method
+ *
+ * @return string a string describing the current PEAR DB object
+ *
+ * @deprecated Method deprecated in Release 1.7.0
+ */
+ function toString()
+ {
+ return $this->__toString();
+ }
+
+ // }}}
+ // {{{ quoteString()
+
+ /**
+ * DEPRECATED: Quotes a string so it can be safely used within string
+ * delimiters in a query
+ *
+ * @param string $string the string to be quoted
+ *
+ * @return string the quoted string
+ *
+ * @see DB_common::quoteSmart(), DB_common::escapeSimple()
+ * @deprecated Method deprecated some time before Release 1.2
+ */
+ function quoteString($string)
+ {
+ $string = $this->quote($string);
+ if ($string{0} == "'") {
+ return substr($string, 1, -1);
+ }
+ return $string;
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * DEPRECATED: Quotes a string so it can be safely used in a query
+ *
+ * @param string $string the string to quote
+ *
+ * @return string the quoted string or the string <samp>NULL</samp>
+ * if the value submitted is <kbd>null</kbd>.
+ *
+ * @see DB_common::quoteSmart(), DB_common::escapeSimple()
+ * @deprecated Deprecated in release 1.6.0
+ */
+ function quote($string = null)
+ {
+ return ($string === null) ? 'NULL'
+ : "'" . str_replace("'", "''", $string) . "'";
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * Delimiting style depends on which database driver is being used.
+ *
+ * NOTE: just because you CAN use delimited identifiers doesn't mean
+ * you SHOULD use them. In general, they end up causing way more
+ * problems than they solve.
+ *
+ * Portability is broken by using the following characters inside
+ * delimited identifiers:
+ * + backtick (<kbd>`</kbd>) -- due to MySQL
+ * + double quote (<kbd>"</kbd>) -- due to Oracle
+ * + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
+ *
+ * Delimited identifiers are known to generally work correctly under
+ * the following drivers:
+ * + mssql
+ * + mysql
+ * + mysqli
+ * + oci8
+ * + odbc(access)
+ * + odbc(db2)
+ * + pgsql
+ * + sqlite
+ * + sybase (must execute <kbd>set quoted_identifier on</kbd> sometime
+ * prior to use)
+ *
+ * InterBase doesn't seem to be able to use delimited identifiers
+ * via PHP 4. They work fine under PHP 5.
+ *
+ * @param string $str the identifier name to be quoted
+ *
+ * @return string the quoted identifier
+ *
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '"' . str_replace('"', '""', $str) . '"';
+ }
+
+ // }}}
+ // {{{ quoteSmart()
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * The output depends on the PHP data type of input and the database
+ * type being used.
+ *
+ * @param mixed $in the data to be formatted
+ *
+ * @return mixed the formatted data. The format depends on the input's
+ * PHP type:
+ * <ul>
+ * <li>
+ * <kbd>input</kbd> -> <samp>returns</samp>
+ * </li>
+ * <li>
+ * <kbd>null</kbd> -> the string <samp>NULL</samp>
+ * </li>
+ * <li>
+ * <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number
+ * </li>
+ * <li>
+ * <kbd>bool</kbd> -> output depends on the driver in use
+ * Most drivers return integers: <samp>1</samp> if
+ * <kbd>true</kbd> or <samp>0</samp> if
+ * <kbd>false</kbd>.
+ * Some return strings: <samp>TRUE</samp> if
+ * <kbd>true</kbd> or <samp>FALSE</samp> if
+ * <kbd>false</kbd>.
+ * Finally one returns strings: <samp>T</samp> if
+ * <kbd>true</kbd> or <samp>F</samp> if
+ * <kbd>false</kbd>. Here is a list of each DBMS,
+ * the values returned and the suggested column type:
+ * <ul>
+ * <li>
+ * <kbd>dbase</kbd> -> <samp>T/F</samp>
+ * (<kbd>Logical</kbd>)
+ * </li>
+ * <li>
+ * <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp>
+ * (<kbd>BOOLEAN</kbd>)
+ * </li>
+ * <li>
+ * <kbd>ibase</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>ifx</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>msql</kbd> -> <samp>1/0</samp>
+ * (<kbd>INTEGER</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mssql</kbd> -> <samp>1/0</samp>
+ * (<kbd>BIT</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mysql</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>mysqli</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>oci8</kbd> -> <samp>1/0</samp>
+ * (<kbd>NUMBER(1)</kbd>)
+ * </li>
+ * <li>
+ * <kbd>odbc</kbd> -> <samp>1/0</samp>
+ * (<kbd>SMALLINT</kbd>) [1]
+ * </li>
+ * <li>
+ * <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp>
+ * (<kbd>BOOLEAN</kbd>)
+ * </li>
+ * <li>
+ * <kbd>sqlite</kbd> -> <samp>1/0</samp>
+ * (<kbd>INTEGER</kbd>)
+ * </li>
+ * <li>
+ * <kbd>sybase</kbd> -> <samp>1/0</samp>
+ * (<kbd>TINYINT(1)</kbd>)
+ * </li>
+ * </ul>
+ * [1] Accommodate the lowest common denominator because not all
+ * versions of have <kbd>BOOLEAN</kbd>.
+ * </li>
+ * <li>
+ * other (including strings and numeric strings) ->
+ * the data with single quotes escaped by preceeding
+ * single quotes, backslashes are escaped by preceeding
+ * backslashes, then the whole string is encapsulated
+ * between single quotes
+ * </li>
+ * </ul>
+ *
+ * @see DB_common::escapeSimple()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteSmart($in)
+ {
+ if (is_int($in) || is_double($in)) {
+ return $in;
+ } elseif (is_bool($in)) {
+ return $in ? 1 : 0;
+ } elseif (is_null($in)) {
+ return 'NULL';
+ } else {
+ return "'" . $this->escapeSimple($in) . "'";
+ }
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * In SQLite, this makes things safe for inserts/updates, but may
+ * cause problems when performing text comparisons against columns
+ * containing binary data. See the
+ * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ return str_replace("'", "''", $str);
+ }
+
+ // }}}
+ // {{{ provides()
+
+ /**
+ * Tells whether the present driver supports a given feature
+ *
+ * @param string $feature the feature you're curious about
+ *
+ * @return bool whether this driver supports $feature
+ */
+ function provides($feature)
+ {
+ return $this->features[$feature];
+ }
+
+ // }}}
+ // {{{ setFetchMode()
+
+ /**
+ * Sets the fetch mode that should be used by default for query results
+ *
+ * @param integer $fetchmode DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC
+ * or DB_FETCHMODE_OBJECT
+ * @param string $object_class the class name of the object to be returned
+ * by the fetch methods when the
+ * DB_FETCHMODE_OBJECT mode is selected.
+ * If no class is specified by default a cast
+ * to object from the assoc array row will be
+ * done. There is also the posibility to use
+ * and extend the 'DB_row' class.
+ *
+ * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT
+ */
+ function setFetchMode($fetchmode, $object_class = 'stdClass')
+ {
+ switch ($fetchmode) {
+ case DB_FETCHMODE_OBJECT:
+ $this->fetchmode_object_class = $object_class;
+ case DB_FETCHMODE_ORDERED:
+ case DB_FETCHMODE_ASSOC:
+ $this->fetchmode = $fetchmode;
+ break;
+ default:
+ return $this->raiseError('invalid fetchmode mode');
+ }
+ }
+
+ // }}}
+ // {{{ setOption()
+
+ /**
+ * Sets run-time configuration options for PEAR DB
+ *
+ * Options, their data types, default values and description:
+ * <ul>
+ * <li>
+ * <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />should results be freed automatically when there are no
+ * more rows?
+ * </li><li>
+ * <var>result_buffering</var> <kbd>integer</kbd> = <samp>500</samp>
+ * <br />how many rows of the result set should be buffered?
+ * <br />In mysql: mysql_unbuffered_query() is used instead of
+ * mysql_query() if this value is 0. (Release 1.7.0)
+ * <br />In oci8: this value is passed to ocisetprefetch().
+ * (Release 1.7.0)
+ * </li><li>
+ * <var>debug</var> <kbd>integer</kbd> = <samp>0</samp>
+ * <br />debug level
+ * </li><li>
+ * <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />should the connection be persistent?
+ * </li><li>
+ * <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp>
+ * <br />portability mode constant (see below)
+ * </li><li>
+ * <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp>
+ * <br />the sprintf() format string used on sequence names. This
+ * format is applied to sequence names passed to
+ * createSequence(), nextID() and dropSequence().
+ * </li><li>
+ * <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp>
+ * <br />use ssl to connect?
+ * </li>
+ * </ul>
+ *
+ * -----------------------------------------
+ *
+ * PORTABILITY MODES
+ *
+ * These modes are bitwised, so they can be combined using <kbd>|</kbd>
+ * and removed using <kbd>^</kbd>. See the examples section below on how
+ * to do this.
+ *
+ * <samp>DB_PORTABILITY_NONE</samp>
+ * turn off all portability features
+ *
+ * This mode gets automatically turned on if the deprecated
+ * <var>optimize</var> option gets set to <samp>performance</samp>.
+ *
+ *
+ * <samp>DB_PORTABILITY_LOWERCASE</samp>
+ * convert names of tables and fields to lower case when using
+ * <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd>
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + oci8
+ *
+ *
+ * <samp>DB_PORTABILITY_RTRIM</samp>
+ * right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd>
+ *
+ *
+ * <samp>DB_PORTABILITY_DELETE_COUNT</samp>
+ * force reporting the number of rows deleted
+ *
+ * Some DBMS's don't count the number of rows deleted when performing
+ * simple <kbd>DELETE FROM tablename</kbd> queries. This portability
+ * mode tricks such DBMS's into telling the count by adding
+ * <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries.
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + fbsql
+ * + mysql
+ * + mysqli
+ * + sqlite
+ *
+ *
+ * <samp>DB_PORTABILITY_NUMROWS</samp>
+ * enable hack that makes <kbd>numRows()</kbd> work in Oracle
+ *
+ * This mode gets automatically turned on in the following databases
+ * if the deprecated option <var>optimize</var> gets set to
+ * <samp>portability</samp>:
+ * + oci8
+ *
+ *
+ * <samp>DB_PORTABILITY_ERRORS</samp>
+ * 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.
+ * DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD
+ *
+ * <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp>
+ * convert null values to empty strings in data output by get*() and
+ * fetch*(). Needed because Oracle considers empty strings to be null,
+ * while most other DBMS's know the difference between empty and null.
+ *
+ *
+ * <samp>DB_PORTABILITY_ALL</samp>
+ * turn on all portability features
+ *
+ * -----------------------------------------
+ *
+ * Example 1. Simple setOption() example
+ * <code>
+ * $db->setOption('autofree', true);
+ * </code>
+ *
+ * Example 2. Portability for lowercasing and trimming
+ * <code>
+ * $db->setOption('portability',
+ * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
+ * </code>
+ *
+ * Example 3. All portability options except trimming
+ * <code>
+ * $db->setOption('portability',
+ * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
+ * </code>
+ *
+ * @param string $option option name
+ * @param mixed $value value for the option
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::$options
+ */
+ function setOption($option, $value)
+ {
+ if (isset($this->options[$option])) {
+ $this->options[$option] = $value;
+
+ /*
+ * Backwards compatibility check for the deprecated 'optimize'
+ * option. Done here in case settings change after connecting.
+ */
+ if ($option == 'optimize') {
+ if ($value == 'portability') {
+ switch ($this->phptype) {
+ case 'oci8':
+ $this->options['portability'] =
+ DB_PORTABILITY_LOWERCASE |
+ DB_PORTABILITY_NUMROWS;
+ break;
+ case 'fbsql':
+ case 'mysql':
+ case 'mysqli':
+ case 'sqlite':
+ $this->options['portability'] =
+ DB_PORTABILITY_DELETE_COUNT;
+ break;
+ }
+ } else {
+ $this->options['portability'] = DB_PORTABILITY_NONE;
+ }
+ }
+
+ return DB_OK;
+ }
+ return $this->raiseError("unknown option $option");
+ }
+
+ // }}}
+ // {{{ getOption()
+
+ /**
+ * Returns the value of an option
+ *
+ * @param string $option the option name you're curious about
+ *
+ * @return mixed the option's value
+ */
+ function getOption($option)
+ {
+ if (isset($this->options[$option])) {
+ return $this->options[$option];
+ }
+ return $this->raiseError("unknown option $option");
+ }
+
+ // }}}
+ // {{{ prepare()
+
+ /**
+ * Prepares a query for multiple execution with execute()
+ *
+ * Creates a query that can be run multiple times. Each time it is run,
+ * the placeholders, if any, will be replaced by the contents of
+ * execute()'s $data argument.
+ *
+ * Three types of placeholders can be used:
+ * + <kbd>?</kbd> scalar value (i.e. strings, integers). The system
+ * will automatically quote and escape the data.
+ * + <kbd>!</kbd> value is inserted 'as is'
+ * + <kbd>&</kbd> requires a file name. The file's contents get
+ * inserted into the query (i.e. saving binary
+ * data in a db)
+ *
+ * Example 1.
+ * <code>
+ * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
+ * $data = array(
+ * "John's text",
+ * "'it''s good'",
+ * 'filename.txt'
+ * );
+ * $res = $db->execute($sth, $data);
+ * </code>
+ *
+ * Use backslashes to escape placeholder characters if you don't want
+ * them to be interpreted as placeholders:
+ * <pre>
+ * "UPDATE foo SET col=? WHERE col='over \& under'"
+ * </pre>
+ *
+ * With some database backends, this is emulated.
+ *
+ * {@internal ibase and oci8 have their own prepare() methods.}}
+ *
+ * @param string $query the query to be prepared
+ *
+ * @return mixed DB statement resource on success. A DB_Error object
+ * on failure.
+ *
+ * @see DB_common::execute()
+ */
+ function prepare($query)
+ {
+ $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
+ PREG_SPLIT_DELIM_CAPTURE);
+ $token = 0;
+ $types = array();
+ $newtokens = array();
+
+ foreach ($tokens as $val) {
+ switch ($val) {
+ case '?':
+ $types[$token++] = DB_PARAM_SCALAR;
+ break;
+ case '&':
+ $types[$token++] = DB_PARAM_OPAQUE;
+ break;
+ case '!':
+ $types[$token++] = DB_PARAM_MISC;
+ break;
+ default:
+ $newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val);
+ }
+ }
+
+ $this->prepare_tokens[] = &$newtokens;
+ end($this->prepare_tokens);
+
+ $k = key($this->prepare_tokens);
+ $this->prepare_types[$k] = $types;
+ $this->prepared_queries[$k] = implode(' ', $newtokens);
+
+ return $k;
+ }
+
+ // }}}
+ // {{{ autoPrepare()
+
+ /**
+ * Automaticaly generates an insert or update query and pass it to prepare()
+ *
+ * @param string $table the table name
+ * @param array $table_fields the array of field names
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return resource the query handle
+ *
+ * @uses DB_common::prepare(), DB_common::buildManipSQL()
+ */
+ function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT,
+ $where = false)
+ {
+ $query = $this->buildManipSQL($table, $table_fields, $mode, $where);
+ if (DB::isError($query)) {
+ return $query;
+ }
+ return $this->prepare($query);
+ }
+
+ // }}}
+ // {{{ autoExecute()
+
+ /**
+ * Automaticaly generates an insert or update query and call prepare()
+ * and execute() with it
+ *
+ * @param string $table the table name
+ * @param array $fields_values the associative array where $key is a
+ * field name and $value its value
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * @uses DB_common::autoPrepare(), DB_common::execute()
+ */
+ function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT,
+ $where = false)
+ {
+ $sth = $this->autoPrepare($table, array_keys($fields_values), $mode,
+ $where);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $ret =& $this->execute($sth, array_values($fields_values));
+ $this->freePrepared($sth);
+ return $ret;
+
+ }
+
+ // }}}
+ // {{{ buildManipSQL()
+
+ /**
+ * Produces an SQL query string for autoPrepare()
+ *
+ * Example:
+ * <pre>
+ * buildManipSQL('table_sql', array('field1', 'field2', 'field3'),
+ * DB_AUTOQUERY_INSERT);
+ * </pre>
+ *
+ * That returns
+ * <samp>
+ * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
+ * </samp>
+ *
+ * NOTES:
+ * - This belongs more to a SQL Builder class, but this is a simple
+ * facility.
+ * - Be carefull! If you don't give a $where param with an UPDATE
+ * query, all the records of the table will be updated!
+ *
+ * @param string $table the table name
+ * @param array $table_fields the array of field names
+ * @param int $mode a type of query to make:
+ * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
+ * @param string $where for update queries: the WHERE clause to
+ * append to the SQL statement. Don't
+ * include the "WHERE" keyword.
+ *
+ * @return string the sql query for autoPrepare()
+ */
+ function buildManipSQL($table, $table_fields, $mode, $where = false)
+ {
+ if (count($table_fields) == 0) {
+ return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+ $first = true;
+ switch ($mode) {
+ case DB_AUTOQUERY_INSERT:
+ $values = '';
+ $names = '';
+ foreach ($table_fields as $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $names .= ',';
+ $values .= ',';
+ }
+ $names .= $value;
+ $values .= '?';
+ }
+ return "INSERT INTO $table ($names) VALUES ($values)";
+ case DB_AUTOQUERY_UPDATE:
+ $set = '';
+ foreach ($table_fields as $value) {
+ if ($first) {
+ $first = false;
+ } else {
+ $set .= ',';
+ }
+ $set .= "$value = ?";
+ }
+ $sql = "UPDATE $table SET $set";
+ if ($where) {
+ $sql .= " WHERE $where";
+ }
+ return $sql;
+ default:
+ return $this->raiseError(DB_ERROR_SYNTAX);
+ }
+ }
+
+ // }}}
+ // {{{ execute()
+
+ /**
+ * Executes a DB statement prepared with prepare()
+ *
+ * Example 1.
+ * <code>
+ * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
+ * $data = array(
+ * "John's text",
+ * "'it''s good'",
+ * 'filename.txt'
+ * );
+ * $res =& $db->execute($sth, $data);
+ * </code>
+ *
+ * @param resource $stmt a DB statement resource returned from prepare()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * {@internal ibase and oci8 have their own execute() methods.}}
+ *
+ * @see DB_common::prepare()
+ */
+ function &execute($stmt, $data = array())
+ {
+ $realquery = $this->executeEmulateQuery($stmt, $data);
+ if (DB::isError($realquery)) {
+ return $realquery;
+ }
+ $result = $this->simpleQuery($realquery);
+
+ if ($result === DB_OK || DB::isError($result)) {
+ return $result;
+ } else {
+ $tmp =& new DB_result($this, $result);
+ return $tmp;
+ }
+ }
+
+ // }}}
+ // {{{ executeEmulateQuery()
+
+ /**
+ * Emulates executing prepared statements if the DBMS not support them
+ *
+ * @param resource $stmt a DB statement resource returned from execute()
+ * @param mixed $data array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a string containing the real query run when emulating
+ * prepare/execute. A DB_Error object on failure.
+ *
+ * @access protected
+ * @see DB_common::execute()
+ */
+ function executeEmulateQuery($stmt, $data = array())
+ {
+ $stmt = (int)$stmt;
+ $data = (array)$data;
+ $this->last_parameters = $data;
+
+ if (count($this->prepare_types[$stmt]) != count($data)) {
+ $this->last_query = $this->prepared_queries[$stmt];
+ return $this->raiseError(DB_ERROR_MISMATCH);
+ }
+
+ $realquery = $this->prepare_tokens[$stmt][0];
+
+ $i = 0;
+ foreach ($data as $value) {
+ if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {
+ $realquery .= $this->quoteSmart($value);
+ } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {
+ $fp = @fopen($value, 'rb');
+ if (!$fp) {
+ return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
+ }
+ $realquery .= $this->quoteSmart(fread($fp, filesize($value)));
+ fclose($fp);
+ } else {
+ $realquery .= $value;
+ }
+
+ $realquery .= $this->prepare_tokens[$stmt][++$i];
+ }
+
+ return $realquery;
+ }
+
+ // }}}
+ // {{{ executeMultiple()
+
+ /**
+ * Performs several execute() calls on the same statement handle
+ *
+ * $data must be an array indexed numerically
+ * from 0, one execute call is done for every "row" in the array.
+ *
+ * If an error occurs during execute(), executeMultiple() does not
+ * execute the unfinished rows, but rather returns that error.
+ *
+ * @param resource $stmt query handle from prepare()
+ * @param array $data numeric array containing the
+ * data to insert into the query
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::prepare(), DB_common::execute()
+ */
+ function executeMultiple($stmt, $data)
+ {
+ foreach ($data as $value) {
+ $res =& $this->execute($stmt, $value);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freePrepared()
+
+ /**
+ * Frees the internal resources associated with a prepared query
+ *
+ * @param resource $stmt the prepared statement's PHP resource
+ * @param bool $free_resource should the PHP resource be freed too?
+ * Use false if you need to get data
+ * from the result set later.
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_common::prepare()
+ */
+ function freePrepared($stmt, $free_resource = true)
+ {
+ $stmt = (int)$stmt;
+ if (isset($this->prepare_tokens[$stmt])) {
+ unset($this->prepare_tokens[$stmt]);
+ unset($this->prepare_types[$stmt]);
+ unset($this->prepared_queries[$stmt]);
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * It is defined here to ensure all drivers have this method available.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(),
+ * DB_sqlite::modifyQuery()
+ */
+ function modifyQuery($query)
+ {
+ return $query;
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * It is defined here to assure that all implementations
+ * have this method defined.
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ return $query;
+ }
+
+ // }}}
+ // {{{ query()
+
+ /**
+ * Sends a query to the database server
+ *
+ * The query string can be either a normal statement to be sent directly
+ * to the server OR if <var>$params</var> are passed the query can have
+ * placeholders and it will be passed through prepare() and execute().
+ *
+ * @param string $query the SQL query or the statement to prepare
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ *
+ * @see DB_result, DB_common::prepare(), DB_common::execute()
+ */
+ function &query($query, $params = array())
+ {
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $ret =& $this->execute($sth, $params);
+ $this->freePrepared($sth, false);
+ return $ret;
+ } else {
+ $this->last_parameters = array();
+ $result = $this->simpleQuery($query);
+ if ($result === DB_OK || DB::isError($result)) {
+ return $result;
+ } else {
+ $tmp =& new DB_result($this, $result);
+ return $tmp;
+ }
+ }
+ }
+
+ // }}}
+ // {{{ limitQuery()
+
+ /**
+ * Generates and executes a LIMIT query
+ *
+ * @param string $query the query
+ * @param intr $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed a new DB_result object for successful SELECT queries
+ * or DB_OK for successul data manipulation queries.
+ * A DB_Error object on failure.
+ */
+ function &limitQuery($query, $from, $count, $params = array())
+ {
+ $query = $this->modifyLimitQuery($query, $from, $count, $params);
+ if (DB::isError($query)){
+ return $query;
+ }
+ $result =& $this->query($query, $params);
+ if (is_a($result, 'DB_result')) {
+ $result->setOption('limit_from', $from);
+ $result->setOption('limit_count', $count);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ getOne()
+
+ /**
+ * Fetches the first column of the first row from a query result
+ *
+ * Takes care of doing the query and freeing the results when finished.
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return mixed the returned value of the query.
+ * A DB_Error object on failure.
+ */
+ function &getOne($query, $params = array())
+ {
+ $params = (array)$params;
+ // modifyLimitQuery() would be nice here, but it causes BC issues
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
+ $res->free();
+
+ if ($err !== DB_OK) {
+ return $err;
+ }
+
+ return $row[0];
+ }
+
+ // }}}
+ // {{{ getRow()
+
+ /**
+ * Fetches the first row of data returned from a query result
+ *
+ * Takes care of doing the query and freeing the results when finished.
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use
+ *
+ * @return array the first row of results as an array.
+ * A DB_Error object on failure.
+ */
+ function &getRow($query, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT)
+ {
+ // compat check, the params and fetchmode parameters used to
+ // have the opposite order
+ if (!is_array($params)) {
+ if (is_array($fetchmode)) {
+ if ($params === null) {
+ $tmp = DB_FETCHMODE_DEFAULT;
+ } else {
+ $tmp = $params;
+ }
+ $params = $fetchmode;
+ $fetchmode = $tmp;
+ } elseif ($params !== null) {
+ $fetchmode = $params;
+ $params = array();
+ }
+ }
+ // modifyLimitQuery() would be nice here, but it causes BC issues
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $err = $res->fetchInto($row, $fetchmode);
+
+ $res->free();
+
+ if ($err !== DB_OK) {
+ return $err;
+ }
+
+ return $row;
+ }
+
+ // }}}
+ // {{{ getCol()
+
+ /**
+ * Fetches a single column from a query result and returns it as an
+ * indexed array
+ *
+ * @param string $query the SQL query
+ * @param mixed $col which column to return (integer [column number,
+ * starting at 0] or string [column name])
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return array the results as an array. A DB_Error object on failure.
+ *
+ * @see DB_common::query()
+ */
+ function &getCol($query, $col = 0, $params = array())
+ {
+ $params = (array)$params;
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+
+ $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
+
+ if (!is_array($row = $res->fetchRow($fetchmode))) {
+ $ret = array();
+ } else {
+ if (!array_key_exists($col, $row)) {
+ $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD);
+ } else {
+ $ret = array($row[$col]);
+ while (is_array($row = $res->fetchRow($fetchmode))) {
+ $ret[] = $row[$col];
+ }
+ }
+ }
+
+ $res->free();
+
+ if (DB::isError($row)) {
+ $ret = $row;
+ }
+
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getAssoc()
+
+ /**
+ * Fetches an entire query result and returns it as an
+ * associative array using the first column as the key
+ *
+ * If the result set contains more than two columns, the value
+ * will be an array of the values from column 2-n. If the result
+ * set contains only two columns, the returned value will be a
+ * scalar with the value of the second column (unless forced to an
+ * array with the $force_array parameter). A DB error code is
+ * returned on errors. If the result set contains fewer than two
+ * columns, a DB_ERROR_TRUNCATED error is returned.
+ *
+ * For example, if the table "mytable" contains:
+ *
+ * <pre>
+ * ID TEXT DATE
+ * --------------------------------
+ * 1 'one' 944679408
+ * 2 'two' 944679408
+ * 3 'three' 944679408
+ * </pre>
+ *
+ * Then the call getAssoc('SELECT id,text FROM mytable') returns:
+ * <pre>
+ * array(
+ * '1' => 'one',
+ * '2' => 'two',
+ * '3' => 'three',
+ * )
+ * </pre>
+ *
+ * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
+ * <pre>
+ * array(
+ * '1' => array('one', '944679408'),
+ * '2' => array('two', '944679408'),
+ * '3' => array('three', '944679408')
+ * )
+ * </pre>
+ *
+ * If the more than one row occurs with the same value in the
+ * first column, the last row overwrites all previous ones by
+ * default. Use the $group parameter if you don't want to
+ * overwrite like this. Example:
+ *
+ * <pre>
+ * getAssoc('SELECT category,id,name FROM mytable', false, null,
+ * DB_FETCHMODE_ASSOC, true) returns:
+ *
+ * array(
+ * '1' => array(array('id' => '4', 'name' => 'number four'),
+ * array('id' => '6', 'name' => 'number six')
+ * ),
+ * '9' => array(array('id' => '4', 'name' => 'number four'),
+ * array('id' => '6', 'name' => 'number six')
+ * )
+ * )
+ * </pre>
+ *
+ * Keep in mind that database functions in PHP usually return string
+ * values for results regardless of the database's internal type.
+ *
+ * @param string $query the SQL query
+ * @param bool $force_array used only when the query returns
+ * exactly two columns. If true, the values
+ * of the returned array will be one-element
+ * arrays instead of scalars.
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of
+ * items passed must match quantity of
+ * placeholders in query: meaning 1
+ * placeholder for non-array parameters or
+ * 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use
+ * @param bool $group if true, the values of the returned array
+ * is wrapped in another array. If the same
+ * key value (in the first column) repeats
+ * itself, the values will be appended to
+ * this array instead of overwriting the
+ * existing values.
+ *
+ * @return array the associative array containing the query results.
+ * A DB_Error object on failure.
+ */
+ function &getAssoc($query, $force_array = false, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT, $group = false)
+ {
+ $params = (array)$params;
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if (DB::isError($res)) {
+ return $res;
+ }
+ if ($fetchmode == DB_FETCHMODE_DEFAULT) {
+ $fetchmode = $this->fetchmode;
+ }
+ $cols = $res->numCols();
+
+ if ($cols < 2) {
+ $tmp =& $this->raiseError(DB_ERROR_TRUNCATED);
+ return $tmp;
+ }
+
+ $results = array();
+
+ if ($cols > 2 || $force_array) {
+ // return array values
+ // XXX this part can be optimized
+ if ($fetchmode == DB_FETCHMODE_ASSOC) {
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
+ reset($row);
+ $key = current($row);
+ unset($row[key($row)]);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ } elseif ($fetchmode == DB_FETCHMODE_OBJECT) {
+ while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
+ $arr = get_object_vars($row);
+ $key = current($arr);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ } else {
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
+ // we shift away the first element to get
+ // indices running from 0 again
+ $key = array_shift($row);
+ if ($group) {
+ $results[$key][] = $row;
+ } else {
+ $results[$key] = $row;
+ }
+ }
+ }
+ if (DB::isError($row)) {
+ $results = $row;
+ }
+ } else {
+ // return scalar values
+ while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
+ if ($group) {
+ $results[$row[0]][] = $row[1];
+ } else {
+ $results[$row[0]] = $row[1];
+ }
+ }
+ if (DB::isError($row)) {
+ $results = $row;
+ }
+ }
+
+ $res->free();
+
+ return $results;
+ }
+
+ // }}}
+ // {{{ getAll()
+
+ /**
+ * Fetches all of the rows from a query result
+ *
+ * @param string $query the SQL query
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of
+ * items passed must match quantity of
+ * placeholders in query: meaning 1
+ * placeholder for non-array parameters or
+ * 1 placeholder per array element.
+ * @param int $fetchmode the fetch mode to use:
+ * + DB_FETCHMODE_ORDERED
+ * + DB_FETCHMODE_ASSOC
+ * + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED
+ * + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED
+ *
+ * @return array the nested array. A DB_Error object on failure.
+ */
+ function &getAll($query, $params = array(),
+ $fetchmode = DB_FETCHMODE_DEFAULT)
+ {
+ // compat check, the params and fetchmode parameters used to
+ // have the opposite order
+ if (!is_array($params)) {
+ if (is_array($fetchmode)) {
+ if ($params === null) {
+ $tmp = DB_FETCHMODE_DEFAULT;
+ } else {
+ $tmp = $params;
+ }
+ $params = $fetchmode;
+ $fetchmode = $tmp;
+ } elseif ($params !== null) {
+ $fetchmode = $params;
+ $params = array();
+ }
+ }
+
+ if (sizeof($params) > 0) {
+ $sth = $this->prepare($query);
+
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+
+ $res =& $this->execute($sth, $params);
+ $this->freePrepared($sth);
+ } else {
+ $res =& $this->query($query);
+ }
+
+ if ($res === DB_OK || DB::isError($res)) {
+ return $res;
+ }
+
+ $results = array();
+ while (DB_OK === $res->fetchInto($row, $fetchmode)) {
+ if ($fetchmode & DB_FETCHMODE_FLIPPED) {
+ foreach ($row as $key => $val) {
+ $results[$key][] = $val;
+ }
+ } else {
+ $results[] = $row;
+ }
+ }
+
+ $res->free();
+
+ if (DB::isError($row)) {
+ $tmp =& $this->raiseError($row);
+ return $tmp;
+ }
+ return $results;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Determines the number of rows in a query result
+ *
+ * @param resource $result the query result idenifier produced by PHP
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function numRows($result)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ getSequenceName()
+
+ /**
+ * Generates the name used inside the database for a sequence
+ *
+ * The createSequence() docblock contains notes about storing sequence
+ * names.
+ *
+ * @param string $sqn the sequence's public name
+ *
+ * @return string the sequence's name in the backend
+ *
+ * @access protected
+ * @see DB_common::createSequence(), DB_common::dropSequence(),
+ * DB_common::nextID(), DB_common::setOption()
+ */
+ function getSequenceName($sqn)
+ {
+ return sprintf($this->getOption('seqname_format'),
+ preg_replace('/[^a-z0-9_.]/i', '_', $sqn));
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::dropSequence(),
+ * DB_common::getSequenceName()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * The name of a given sequence is determined by passing the string
+ * provided in the <var>$seq_name</var> argument through PHP's sprintf()
+ * function using the value from the <var>seqname_format</var> option as
+ * the sprintf()'s format argument.
+ *
+ * <var>seqname_format</var> is set via setOption().
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_common::nextID()
+ */
+ function createSequence($seq_name)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_common::nextID()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ raiseError()
+
+ /**
+ * Communicates an error and invoke error callbacks, etc
+ *
+ * Basically a wrapper for PEAR::raiseError without the message string.
+ *
+ * @param mixed integer error code, or a PEAR error object (all
+ * other parameters are ignored if this parameter is
+ * an object
+ * @param int error mode, see PEAR_Error docs
+ * @param mixed if error mode is PEAR_ERROR_TRIGGER, this is the
+ * error level (E_USER_NOTICE etc). If error mode is
+ * PEAR_ERROR_CALLBACK, this is the callback function,
+ * either as a function name, or as an array of an
+ * object and method name. For other error modes this
+ * parameter is ignored.
+ * @param string extra debug information. Defaults to the last
+ * query and native error code.
+ * @param mixed native error code, integer or string depending the
+ * backend
+ *
+ * @return object the PEAR_Error object
+ *
+ * @see PEAR_Error
+ */
+ function &raiseError($code = DB_ERROR, $mode = null, $options = null,
+ $userinfo = null, $nativecode = null)
+ {
+ // The error is yet a DB error object
+ if (is_object($code)) {
+ // because we the static PEAR::raiseError, our global
+ // handler should be used if it is set
+ if ($mode === null && !empty($this->_default_error_mode)) {
+ $mode = $this->_default_error_mode;
+ $options = $this->_default_error_options;
+ }
+ $tmp = PEAR::raiseError($code, null, $mode, $options,
+ null, null, true);
+ return $tmp;
+ }
+
+ if ($userinfo === null) {
+ $userinfo = $this->last_query;
+ }
+
+ if ($nativecode) {
+ $userinfo .= ' [nativecode=' . trim($nativecode) . ']';
+ } else {
+ $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']';
+ }
+
+ $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,
+ 'DB_Error', true);
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return mixed the DBMS' error code. A DB_Error object on failure.
+ */
+ function errorNative()
+ {
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Maps native error codes to DB's portable ones
+ *
+ * Uses the <var>$errorcode_map</var> property defined in each driver.
+ *
+ * @param string|int $nativecode the error code returned by the DBMS
+ *
+ * @return int the portable DB error code. Return DB_ERROR if the
+ * current driver doesn't have a mapping for the
+ * $nativecode submitted.
+ */
+ function errorCode($nativecode)
+ {
+ if (isset($this->errorcode_map[$nativecode])) {
+ return $this->errorcode_map[$nativecode];
+ }
+ // Fall back to DB_ERROR if there was no mapping.
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ errorMessage()
+
+ /**
+ * Maps a DB error code to a textual message
+ *
+ * @param integer $dbcode the DB error code
+ *
+ * @return string the error message corresponding to the error code
+ * submitted. FALSE if the error code is unknown.
+ *
+ * @see DB::errorMessage()
+ */
+ function errorMessage($dbcode)
+ {
+ return DB::errorMessage($this->errorcode_map[$dbcode]);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * The format of the resulting array depends on which <var>$mode</var>
+ * you select. The sample output below is based on this query:
+ * <pre>
+ * SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
+ * FROM tblFoo
+ * JOIN tblBar ON tblFoo.fldId = tblBar.fldId
+ * </pre>
+ *
+ * <ul>
+ * <li>
+ *
+ * <kbd>null</kbd> (default)
+ * <pre>
+ * [0] => Array (
+ * [table] => tblFoo
+ * [name] => fldId
+ * [type] => int
+ * [len] => 11
+ * [flags] => primary_key not_null
+ * )
+ * [1] => Array (
+ * [table] => tblFoo
+ * [name] => fldPhone
+ * [type] => string
+ * [len] => 20
+ * [flags] =>
+ * )
+ * [2] => Array (
+ * [table] => tblBar
+ * [name] => fldId
+ * [type] => int
+ * [len] => 11
+ * [flags] => primary_key not_null
+ * )
+ * </pre>
+ *
+ * </li><li>
+ *
+ * <kbd>DB_TABLEINFO_ORDER</kbd>
+ *
+ * <p>In addition to the information found in the default output,
+ * a notation of the number of columns is provided by the
+ * <samp>num_fields</samp> element while the <samp>order</samp>
+ * element provides an array with the column names as the keys and
+ * their location index number (corresponding to the keys in the
+ * the default output) as the values.</p>
+ *
+ * <p>If a result set has identical field names, the last one is
+ * used.</p>
+ *
+ * <pre>
+ * [num_fields] => 3
+ * [order] => Array (
+ * [fldId] => 2
+ * [fldTrans] => 1
+ * )
+ * </pre>
+ *
+ * </li><li>
+ *
+ * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
+ *
+ * <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
+ * dimensions to the array in which the table names are keys and
+ * the field names are sub-keys. This is helpful for queries that
+ * join tables which have identical field names.</p>
+ *
+ * <pre>
+ * [num_fields] => 3
+ * [ordertable] => Array (
+ * [tblFoo] => Array (
+ * [fldId] => 0
+ * [fldPhone] => 1
+ * )
+ * [tblBar] => Array (
+ * [fldId] => 2
+ * )
+ * )
+ * </pre>
+ *
+ * </li>
+ * </ul>
+ *
+ * The <samp>flags</samp> element contains a space separated list
+ * of extra information about the field. This data is inconsistent
+ * between DBMS's due to the way each DBMS works.
+ * + <samp>primary_key</samp>
+ * + <samp>unique_key</samp>
+ * + <samp>multiple_key</samp>
+ * + <samp>not_null</samp>
+ *
+ * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
+ * elements if <var>$result</var> is a table name. The following DBMS's
+ * provide full information from queries:
+ * + fbsql
+ * + mysql
+ *
+ * If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
+ * turned on, the names of tables and fields will be lowercased.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode either unused or one of the tableInfo modes:
+ * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
+ * <kbd>DB_TABLEINFO_ORDER</kbd> or
+ * <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
+ * These are bitwise, so the first two can be
+ * combined using <kbd>|</kbd>.
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::setOption()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ /*
+ * If the DB_<driver> class has a tableInfo() method, that one
+ * overrides this one. But, if the driver doesn't have one,
+ * this method runs and tells users about that fact.
+ */
+ return $this->raiseError(DB_ERROR_NOT_CAPABLE);
+ }
+
+ // }}}
+ // {{{ getTables()
+
+ /**
+ * Lists the tables in the current database
+ *
+ * @return array the list of tables. A DB_Error object on failure.
+ *
+ * @deprecated Method deprecated some time before Release 1.2
+ */
+ function getTables()
+ {
+ return $this->getListOf('tables');
+ }
+
+ // }}}
+ // {{{ getListOf()
+
+ /**
+ * Lists internal database information
+ *
+ * @param string $type type of information being sought.
+ * Common items being sought are:
+ * tables, databases, users, views, functions
+ * Each DBMS's has its own capabilities.
+ *
+ * @return array an array listing the items sought.
+ * A DB DB_Error object on failure.
+ */
+ function getListOf($type)
+ {
+ $sql = $this->getSpecialQuery($type);
+ if ($sql === null) {
+ $this->last_query = '';
+ return $this->raiseError(DB_ERROR_UNSUPPORTED);
+ } elseif (is_int($sql) || DB::isError($sql)) {
+ // Previous error
+ return $this->raiseError($sql);
+ } elseif (is_array($sql)) {
+ // Already the result
+ return $sql;
+ }
+ // Launch this query
+ return $this->getCol($sql);
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ return $this->raiseError(DB_ERROR_UNSUPPORTED);
+ }
+
+ // }}}
+ // {{{ _rtrimArrayValues()
+
+ /**
+ * Right-trims all strings in an array
+ *
+ * @param array $array the array to be trimmed (passed by reference)
+ *
+ * @return void
+ *
+ * @access protected
+ */
+ function _rtrimArrayValues(&$array)
+ {
+ foreach ($array as $key => $value) {
+ if (is_string($value)) {
+ $array[$key] = rtrim($value);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _convertNullArrayValuesToEmpty()
+
+ /**
+ * Converts all null values in an array to empty strings
+ *
+ * @param array $array the array to be de-nullified (passed by reference)
+ *
+ * @return void
+ *
+ * @access protected
+ */
+ function _convertNullArrayValuesToEmpty(&$array)
+ {
+ foreach ($array as $key => $value) {
+ if (is_null($value)) {
+ $array[$key] = '';
+ }
+ }
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/lib/phpFlickr/PEAR/DB/mysql.php b/lib/phpFlickr/PEAR/DB/mysql.php
new file mode 100644
index 000000000..3ae0adf86
--- /dev/null
+++ b/lib/phpFlickr/PEAR/DB/mysql.php
@@ -0,0 +1,1034 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's mysql extension
+ * for interacting with MySQL databases
+ *
+ * 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 Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: mysql.php 32 2005-08-01 06:21:02Z dancoulter $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's mysql extension
+ * for interacting with MySQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_mysql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'mysql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'mysql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => '4.2.0',
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => false,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ 1004 => DB_ERROR_CANNOT_CREATE,
+ 1005 => DB_ERROR_CANNOT_CREATE,
+ 1006 => DB_ERROR_CANNOT_CREATE,
+ 1007 => DB_ERROR_ALREADY_EXISTS,
+ 1008 => DB_ERROR_CANNOT_DROP,
+ 1022 => DB_ERROR_ALREADY_EXISTS,
+ 1044 => DB_ERROR_ACCESS_VIOLATION,
+ 1046 => DB_ERROR_NODBSELECTED,
+ 1048 => DB_ERROR_CONSTRAINT,
+ 1049 => DB_ERROR_NOSUCHDB,
+ 1050 => DB_ERROR_ALREADY_EXISTS,
+ 1051 => DB_ERROR_NOSUCHTABLE,
+ 1054 => DB_ERROR_NOSUCHFIELD,
+ 1061 => DB_ERROR_ALREADY_EXISTS,
+ 1062 => DB_ERROR_ALREADY_EXISTS,
+ 1064 => DB_ERROR_SYNTAX,
+ 1091 => DB_ERROR_NOT_FOUND,
+ 1100 => DB_ERROR_NOT_LOCKED,
+ 1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
+ 1142 => DB_ERROR_ACCESS_VIOLATION,
+ 1146 => DB_ERROR_NOSUCHTABLE,
+ 1216 => DB_ERROR_CONSTRAINT,
+ 1217 => DB_ERROR_CONSTRAINT,
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The database specified in the DSN
+ *
+ * It's a fix to allow calls to different databases in the same script.
+ *
+ * @var string
+ * @access private
+ */
+ var $_db = '';
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_mysql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's mysql driver supports the following extra DSN options:
+ * + new_link If set to true, causes subsequent calls to connect()
+ * to return a new connection link instead of the
+ * existing one. WARNING: this is not portable to
+ * other DBMS's. Available since PEAR DB 1.7.0.
+ * + client_flags Any combination of MYSQL_CLIENT_* constants.
+ * Only used if PHP is at version 4.3.0 or greater.
+ * Available since PEAR DB 1.7.0.
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('mysql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $params = array();
+ if ($dsn['protocol'] && $dsn['protocol'] == 'unix') {
+ $params[0] = ':' . $dsn['socket'];
+ } else {
+ $params[0] = $dsn['hostspec'] ? $dsn['hostspec']
+ : 'localhost';
+ if ($dsn['port']) {
+ $params[0] .= ':' . $dsn['port'];
+ }
+ }
+ $params[] = $dsn['username'] ? $dsn['username'] : null;
+ $params[] = $dsn['password'] ? $dsn['password'] : null;
+
+ if (!$persistent) {
+ if (isset($dsn['new_link'])
+ && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+ {
+ $params[] = true;
+ } else {
+ $params[] = false;
+ }
+ }
+ if (version_compare(phpversion(), '4.3.0', '>=')) {
+ $params[] = isset($dsn['client_flags'])
+ ? $dsn['client_flags'] : null;
+ }
+
+ $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ if (($err = @mysql_error()) != '') {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $err);
+ } else {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ }
+
+ if ($dsn['database']) {
+ if (!@mysql_select_db($dsn['database'], $this->connection)) {
+ return $this->mysqlRaiseError();
+ }
+ $this->_db = $dsn['database'];
+ }
+
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @mysql_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * Generally uses mysql_query(). If you want to use
+ * mysql_unbuffered_query() set the "result_buffering" option to 0 using
+ * setOptions(). This option was added in Release 1.7.0.
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
+ $result = @mysql_query('BEGIN', $this->connection);
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ if (!$this->options['result_buffering']) {
+ $result = @mysql_unbuffered_query($query, $this->connection);
+ } else {
+ $result = @mysql_query($query, $this->connection);
+ }
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ if (is_resource($result)) {
+ return $result;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal mysql result pointer to the next available result
+ *
+ * This method has not been implemented yet.
+ *
+ * @param a valid sql result resource
+ *
+ * @return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ if ($rownum !== null) {
+ if (!@mysql_data_seek($result, $rownum)) {
+ return null;
+ }
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @mysql_fetch_array($result, MYSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @mysql_fetch_row($result);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ /*
+ * Even though this DBMS already trims output, we do this because
+ * a field might have intentional whitespace at the end that
+ * gets removed by DB_PORTABILITY_RTRIM under another driver.
+ */
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ return @mysql_free_result($result);
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @mysql_num_fields($result);
+ if (!$cols) {
+ return $this->mysqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @mysql_num_rows($result);
+ if ($rows === null) {
+ return $this->mysqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysql_query('COMMIT', $this->connection);
+ $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ if ($this->_db) {
+ if (!@mysql_select_db($this->_db, $this->connection)) {
+ return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
+ }
+ }
+ $result = @mysql_query('ROLLBACK', $this->connection);
+ $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->mysqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ if (DB::isManip($this->last_query)) {
+ return @mysql_affected_rows($this->connection);
+ } else {
+ return 0;
+ }
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_mysql::createSequence(), DB_mysql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ do {
+ $repeat = 0;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->query("UPDATE ${seqname} ".
+ 'SET id=LAST_INSERT_ID(id+1)');
+ $this->popErrorHandling();
+ if ($result === DB_OK) {
+ // COMMON CASE
+ $id = @mysql_insert_id($this->connection);
+ if ($id != 0) {
+ return $id;
+ }
+ // EMPTY SEQ TABLE
+ // Sequence table must be empty for some reason, so fill
+ // it and return 1 and obtain a user-level lock
+ $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ if ($result == 0) {
+ // Failed to get the lock
+ return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ // add the default value
+ $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+
+ // Release the lock
+ $result = $this->getOne('SELECT RELEASE_LOCK('
+ . "'${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ // We know what the result will be, so no need to try again
+ return 1;
+
+ } elseif ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE)
+ {
+ // ONDEMAND TABLE CREATION
+ $result = $this->createSequence($seq_name);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ } else {
+ $repeat = 1;
+ }
+
+ } elseif (DB::isError($result) &&
+ $result->getCode() == DB_ERROR_ALREADY_EXISTS)
+ {
+ // BACKWARDS COMPAT
+ // see _BCsequence() comment
+ $result = $this->_BCsequence($seqname);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $repeat = 1;
+ }
+ } while ($repeat);
+
+ return $this->raiseError($result);
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_mysql::nextID(), DB_mysql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $res = $this->query('CREATE TABLE ' . $seqname
+ . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
+ . ' PRIMARY KEY(id))');
+ if (DB::isError($res)) {
+ return $res;
+ }
+ // insert yields value 1, nextId call will generate ID 2
+ $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
+ if (DB::isError($res)) {
+ return $res;
+ }
+ // so reset to zero
+ return $this->query("UPDATE ${seqname} SET id = 0");
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_mysql::nextID(), DB_mysql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ _BCsequence()
+
+ /**
+ * Backwards compatibility with old sequence emulation implementation
+ * (clean up the dupes)
+ *
+ * @param string $seqname the sequence name to clean up
+ *
+ * @return bool true on success. A DB_Error object on failure.
+ *
+ * @access private
+ */
+ function _BCsequence($seqname)
+ {
+ // Obtain a user-level lock... this will release any previous
+ // application locks, but unlike LOCK TABLES, it does not abort
+ // the current transaction and is much less frequently used.
+ $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ if ($result == 0) {
+ // Failed to get the lock, can't do the conversion, bail
+ // with a DB_ERROR_NOT_LOCKED error
+ return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
+ }
+
+ $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
+ if (DB::isError($highest_id)) {
+ return $highest_id;
+ }
+ // This should kill all rows except the highest
+ // We should probably do something if $highest_id isn't
+ // numeric, but I'm at a loss as how to handle that...
+ $result = $this->query('DELETE FROM ' . $seqname
+ . " WHERE id <> $highest_id");
+ if (DB::isError($result)) {
+ return $result;
+ }
+
+ // If another thread has been waiting for this lock,
+ // it will go thru the above procedure, but will have no
+ // real effect
+ $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
+ if (DB::isError($result)) {
+ return $result;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ quoteIdentifier()
+
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * MySQL can't handle the backtick character (<kbd>`</kbd>) in
+ * table or column names.
+ *
+ * @param string $str identifier name to be quoted
+ *
+ * @return string quoted identifier string
+ *
+ * @see DB_common::quoteIdentifier()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteIdentifier($str)
+ {
+ return '`' . $str . '`';
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * @deprecated Deprecated in release 1.6.0
+ */
+ function quote($str)
+ {
+ return $this->quoteSmart($str);
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ if (function_exists('mysql_real_escape_string')) {
+ return @mysql_real_escape_string($str, $this->connection);
+ } else {
+ return @mysql_escape_string($str);
+ }
+ }
+
+ // }}}
+ // {{{ modifyQuery()
+
+ /**
+ * Changes a query string for various DBMS specific reasons
+ *
+ * This little hack lets you know how many rows were deleted
+ * when running a "DELETE FROM table" query. Only implemented
+ * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
+ *
+ * @param string $query the query string to modify
+ *
+ * @return string the modified query string
+ *
+ * @access protected
+ * @see DB_common::setOption()
+ */
+ function modifyQuery($query)
+ {
+ if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
+ // "DELETE FROM table" gives 0 affected rows in MySQL.
+ // This little hack lets you know how many rows were deleted.
+ if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
+ $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
+ 'DELETE FROM \1 WHERE 1=1', $query);
+ }
+ }
+ return $query;
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ if (DB::isManip($query)) {
+ return $query . " LIMIT $count";
+ } else {
+ return $query . " LIMIT $from, $count";
+ }
+ }
+
+ // }}}
+ // {{{ mysqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_mysql::errorNative(), DB_common::errorCode()
+ */
+ function mysqlRaiseError($errno = null)
+ {
+ if ($errno === null) {
+ if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
+ $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
+ $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
+ } else {
+ // Doing this in case mode changes during runtime.
+ $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
+ $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
+ $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
+ }
+ $errno = $this->errorCode(mysql_errno($this->connection));
+ }
+ return $this->raiseError($errno, null, null, null,
+ @mysql_errno($this->connection) . ' ** ' .
+ @mysql_error($this->connection));
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error code produced by the last query
+ *
+ * @return int the DBMS' error code
+ */
+ function errorNative()
+ {
+ return @mysql_errno($this->connection);
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @mysql_list_fields($this->dsn['database'],
+ $result, $this->connection);
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @mysql_num_fields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $case_func(@mysql_field_table($id, $i)),
+ 'name' => $case_func(@mysql_field_name($id, $i)),
+ 'type' => @mysql_field_type($id, $i),
+ 'len' => @mysql_field_len($id, $i),
+ 'flags' => @mysql_field_flags($id, $i),
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @mysql_free_result($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SHOW TABLES';
+ case 'users':
+ return 'SELECT DISTINCT User FROM mysql.user';
+ case 'databases':
+ return 'SHOW DATABASES';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/lib/phpFlickr/PEAR/DB/pgsql.php b/lib/phpFlickr/PEAR/DB/pgsql.php
new file mode 100644
index 000000000..1e58f48d6
--- /dev/null
+++ b/lib/phpFlickr/PEAR/DB/pgsql.php
@@ -0,0 +1,1097 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * The PEAR DB driver for PHP's pgsql extension
+ * for interacting with PostgreSQL databases
+ *
+ * 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 Rui Hirokawa <hirokawa@php.net>
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: pgsql.php 32 2005-08-01 06:21:02Z dancoulter $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB_common class so it can be extended from
+ */
+require_once 'DB/common.php';
+
+/**
+ * The methods PEAR DB uses to interact with PHP's pgsql extension
+ * for interacting with PostgreSQL databases
+ *
+ * These methods overload the ones declared in DB_common.
+ *
+ * @category Database
+ * @package DB
+ * @author Rui Hirokawa <hirokawa@php.net>
+ * @author Stig Bakken <ssb@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_pgsql extends DB_common
+{
+ // {{{ properties
+
+ /**
+ * The DB driver type (mysql, oci8, odbc, etc.)
+ * @var string
+ */
+ var $phptype = 'pgsql';
+
+ /**
+ * The database syntax variant to be used (db2, access, etc.), if any
+ * @var string
+ */
+ var $dbsyntax = 'pgsql';
+
+ /**
+ * The capabilities of this DB implementation
+ *
+ * The 'new_link' element contains the PHP version that first provided
+ * new_link support for this DBMS. Contains false if it's unsupported.
+ *
+ * Meaning of the 'limit' element:
+ * + 'emulate' = emulate with fetch row by number
+ * + 'alter' = alter the query
+ * + false = skip rows
+ *
+ * @var array
+ */
+ var $features = array(
+ 'limit' => 'alter',
+ 'new_link' => '4.3.0',
+ 'numrows' => true,
+ 'pconnect' => true,
+ 'prepare' => false,
+ 'ssl' => true,
+ 'transactions' => true,
+ );
+
+ /**
+ * A mapping of native error codes to DB error codes
+ * @var array
+ */
+ var $errorcode_map = array(
+ );
+
+ /**
+ * The raw database connection created by PHP
+ * @var resource
+ */
+ var $connection;
+
+ /**
+ * The DSN information for connecting to a database
+ * @var array
+ */
+ var $dsn = array();
+
+
+ /**
+ * Should data manipulation queries be committed automatically?
+ * @var bool
+ * @access private
+ */
+ var $autocommit = true;
+
+ /**
+ * The quantity of transactions begun
+ *
+ * {@internal While this is private, it can't actually be designated
+ * private in PHP 5 because it is directly accessed in the test suite.}}
+ *
+ * @var integer
+ * @access private
+ */
+ var $transaction_opcount = 0;
+
+ /**
+ * The number of rows affected by a data manipulation query
+ * @var integer
+ */
+ var $affected = 0;
+
+ /**
+ * The current row being looked at in fetchInto()
+ * @var array
+ * @access private
+ */
+ var $row = array();
+
+ /**
+ * The number of rows in a given result set
+ * @var array
+ * @access private
+ */
+ var $_num_rows = array();
+
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * This constructor calls <kbd>$this->DB_common()</kbd>
+ *
+ * @return void
+ */
+ function DB_pgsql()
+ {
+ $this->DB_common();
+ }
+
+ // }}}
+ // {{{ connect()
+
+ /**
+ * Connect to the database server, log in and open the database
+ *
+ * Don't call this method directly. Use DB::connect() instead.
+ *
+ * PEAR DB's pgsql driver supports the following extra DSN options:
+ * + connect_timeout How many seconds to wait for a connection to
+ * be established. Available since PEAR DB 1.7.0.
+ * + new_link If set to true, causes subsequent calls to
+ * connect() to return a new connection link
+ * instead of the existing one. WARNING: this is
+ * not portable to other DBMS's. Available only
+ * if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0.
+ * + options Command line options to be sent to the server.
+ * Available since PEAR DB 1.6.4.
+ * + service Specifies a service name in pg_service.conf that
+ * holds additional connection parameters.
+ * Available since PEAR DB 1.7.0.
+ * + sslmode How should SSL be used when connecting? Values:
+ * disable, allow, prefer or require.
+ * Available since PEAR DB 1.7.0.
+ * + tty This was used to specify where to send server
+ * debug output. Available since PEAR DB 1.6.4.
+ *
+ * Example of connecting to a new link via a socket:
+ * <code>
+ * require_once 'DB.php';
+ *
+ * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true';
+ * $options = array(
+ * 'portability' => DB_PORTABILITY_ALL,
+ * );
+ *
+ * $db =& DB::connect($dsn, $options);
+ * if (PEAR::isError($db)) {
+ * die($db->getMessage());
+ * }
+ * </code>
+ *
+ * @param array $dsn the data source name
+ * @param bool $persistent should the connection be persistent?
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT
+ */
+ function connect($dsn, $persistent = false)
+ {
+ if (!PEAR::loadExtension('pgsql')) {
+ return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
+ }
+
+ $this->dsn = $dsn;
+ if ($dsn['dbsyntax']) {
+ $this->dbsyntax = $dsn['dbsyntax'];
+ }
+
+ $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp';
+
+ $params = array('');
+ if ($protocol == 'tcp') {
+ if ($dsn['hostspec']) {
+ $params[0] .= 'host=' . $dsn['hostspec'];
+ }
+ if ($dsn['port']) {
+ $params[0] .= ' port=' . $dsn['port'];
+ }
+ } elseif ($protocol == 'unix') {
+ // Allow for pg socket in non-standard locations.
+ if ($dsn['socket']) {
+ $params[0] .= 'host=' . $dsn['socket'];
+ }
+ if ($dsn['port']) {
+ $params[0] .= ' port=' . $dsn['port'];
+ }
+ }
+ if ($dsn['database']) {
+ $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\'';
+ }
+ if ($dsn['username']) {
+ $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\'';
+ }
+ if ($dsn['password']) {
+ $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\'';
+ }
+ if (!empty($dsn['options'])) {
+ $params[0] .= ' options=' . $dsn['options'];
+ }
+ if (!empty($dsn['tty'])) {
+ $params[0] .= ' tty=' . $dsn['tty'];
+ }
+ if (!empty($dsn['connect_timeout'])) {
+ $params[0] .= ' connect_timeout=' . $dsn['connect_timeout'];
+ }
+ if (!empty($dsn['sslmode'])) {
+ $params[0] .= ' sslmode=' . $dsn['sslmode'];
+ }
+ if (!empty($dsn['service'])) {
+ $params[0] .= ' service=' . $dsn['service'];
+ }
+
+ if (isset($dsn['new_link'])
+ && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
+ {
+ if (version_compare(phpversion(), '4.3.0', '>=')) {
+ $params[] = PGSQL_CONNECT_FORCE_NEW;
+ }
+ }
+
+ $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
+
+ $ini = ini_get('track_errors');
+ $php_errormsg = '';
+ if ($ini) {
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ } else {
+ ini_set('track_errors', 1);
+ $this->connection = @call_user_func_array($connect_function,
+ $params);
+ ini_set('track_errors', $ini);
+ }
+
+ if (!$this->connection) {
+ return $this->raiseError(DB_ERROR_CONNECT_FAILED,
+ null, null, null,
+ $php_errormsg);
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ disconnect()
+
+ /**
+ * Disconnects from the database server
+ *
+ * @return bool TRUE on success, FALSE on failure
+ */
+ function disconnect()
+ {
+ $ret = @pg_close($this->connection);
+ $this->connection = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ simpleQuery()
+
+ /**
+ * Sends a query to the database server
+ *
+ * @param string the SQL query string
+ *
+ * @return mixed + a PHP result resrouce for successful SELECT queries
+ * + the DB_OK constant for other successful queries
+ * + a DB_Error object on failure
+ */
+ function simpleQuery($query)
+ {
+ $ismanip = DB::isManip($query);
+ $this->last_query = $query;
+ $query = $this->modifyQuery($query);
+ if (!$this->autocommit && $ismanip) {
+ if ($this->transaction_opcount == 0) {
+ $result = @pg_exec($this->connection, 'begin;');
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ $this->transaction_opcount++;
+ }
+ $result = @pg_exec($this->connection, $query);
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ // Determine which queries that should return data, and which
+ // should return an error code only.
+ if ($ismanip) {
+ $this->affected = @pg_affected_rows($result);
+ return DB_OK;
+ } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|SHOW)\s/si', $query)) {
+ /* PostgreSQL commands:
+ ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
+ CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
+ GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
+ REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
+ UNLISTEN, UPDATE, VACUUM
+ */
+ $this->row[(int)$result] = 0; // reset the row counter.
+ $numrows = $this->numRows($result);
+ if (is_object($numrows)) {
+ return $numrows;
+ }
+ $this->_num_rows[(int)$result] = $numrows;
+ $this->affected = 0;
+ return $result;
+ } else {
+ $this->affected = 0;
+ return DB_OK;
+ }
+ }
+
+ // }}}
+ // {{{ nextResult()
+
+ /**
+ * Move the internal pgsql result pointer to the next available result
+ *
+ * @param a valid fbsql result resource
+ *
+ * @access public
+ *
+ * @return true if a result is available otherwise return false
+ */
+ function nextResult($result)
+ {
+ return false;
+ }
+
+ // }}}
+ // {{{ fetchInto()
+
+ /**
+ * Places a row from the result set into the given array
+ *
+ * Formating of the array and the data therein are configurable.
+ * See DB_result::fetchInto() for more information.
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::fetchInto() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result the query result resource
+ * @param array $arr the referenced array to put the data in
+ * @param int $fetchmode how the resulting array should be indexed
+ * @param int $rownum the row number to fetch (0 = first row)
+ *
+ * @return mixed DB_OK on success, NULL when the end of a result set is
+ * reached or on failure
+ *
+ * @see DB_result::fetchInto()
+ */
+ function fetchInto($result, &$arr, $fetchmode, $rownum = null)
+ {
+ $result_int = (int)$result;
+ $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int];
+ if ($rownum >= $this->_num_rows[$result_int]) {
+ return null;
+ }
+ if ($fetchmode & DB_FETCHMODE_ASSOC) {
+ $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
+ $arr = array_change_key_case($arr, CASE_LOWER);
+ }
+ } else {
+ $arr = @pg_fetch_row($result, $rownum);
+ }
+ if (!$arr) {
+ return null;
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
+ $this->_rtrimArrayValues($arr);
+ }
+ if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
+ $this->_convertNullArrayValuesToEmpty($arr);
+ }
+ $this->row[$result_int] = ++$rownum;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ freeResult()
+
+ /**
+ * Deletes the result set and frees the memory occupied by the result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::free() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return bool TRUE on success, FALSE if $result is invalid
+ *
+ * @see DB_result::free()
+ */
+ function freeResult($result)
+ {
+ if (is_resource($result)) {
+ unset($this->row[(int)$result]);
+ unset($this->_num_rows[(int)$result]);
+ $this->affected = 0;
+ return @pg_freeresult($result);
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ quote()
+
+ /**
+ * @deprecated Deprecated in release 1.6.0
+ * @internal
+ */
+ function quote($str)
+ {
+ return $this->quoteSmart($str);
+ }
+
+ // }}}
+ // {{{ quoteSmart()
+
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * @param mixed $in the data to be formatted
+ *
+ * @return mixed the formatted data. The format depends on the input's
+ * PHP type:
+ * + null = the string <samp>NULL</samp>
+ * + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp>
+ * + integer or double = the unquoted number
+ * + other (including strings and numeric strings) =
+ * the data escaped according to MySQL's settings
+ * then encapsulated between single quotes
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function quoteSmart($in)
+ {
+ if (is_int($in) || is_double($in)) {
+ return $in;
+ } elseif (is_bool($in)) {
+ return $in ? 'TRUE' : 'FALSE';
+ } elseif (is_null($in)) {
+ return 'NULL';
+ } else {
+ return "'" . $this->escapeSimple($in) . "'";
+ }
+ }
+
+ // }}}
+ // {{{ escapeSimple()
+
+ /**
+ * Escapes a string according to the current DBMS's standards
+ *
+ * {@internal PostgreSQL treats a backslash as an escape character,
+ * so they are escaped as well.
+ *
+ * Not using pg_escape_string() yet because it requires PostgreSQL
+ * to be at version 7.2 or greater.}}
+ *
+ * @param string $str the string to be escaped
+ *
+ * @return string the escaped string
+ *
+ * @see DB_common::quoteSmart()
+ * @since Method available since Release 1.6.0
+ */
+ function escapeSimple($str)
+ {
+ return str_replace("'", "''", str_replace('\\', '\\\\', $str));
+ }
+
+ // }}}
+ // {{{ numCols()
+
+ /**
+ * Gets the number of columns in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numCols() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of columns. A DB_Error object on failure.
+ *
+ * @see DB_result::numCols()
+ */
+ function numCols($result)
+ {
+ $cols = @pg_numfields($result);
+ if (!$cols) {
+ return $this->pgsqlRaiseError();
+ }
+ return $cols;
+ }
+
+ // }}}
+ // {{{ numRows()
+
+ /**
+ * Gets the number of rows in a result set
+ *
+ * This method is not meant to be called directly. Use
+ * DB_result::numRows() instead. It can't be declared "protected"
+ * because DB_result is a separate object.
+ *
+ * @param resource $result PHP's query result resource
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ *
+ * @see DB_result::numRows()
+ */
+ function numRows($result)
+ {
+ $rows = @pg_numrows($result);
+ if ($rows === null) {
+ return $this->pgsqlRaiseError();
+ }
+ return $rows;
+ }
+
+ // }}}
+ // {{{ autoCommit()
+
+ /**
+ * Enables or disables automatic commits
+ *
+ * @param bool $onoff true turns it on, false turns it off
+ *
+ * @return int DB_OK on success. A DB_Error object if the driver
+ * doesn't support auto-committing transactions.
+ */
+ function autoCommit($onoff = false)
+ {
+ // XXX if $this->transaction_opcount > 0, we should probably
+ // issue a warning here.
+ $this->autocommit = $onoff ? true : false;
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ commit()
+
+ /**
+ * Commits the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function commit()
+ {
+ if ($this->transaction_opcount > 0) {
+ // (disabled) hack to shut up error messages from libpq.a
+ //@fclose(@fopen("php://stderr", "w"));
+ $result = @pg_exec($this->connection, 'end;');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ rollback()
+
+ /**
+ * Reverts the current transaction
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ */
+ function rollback()
+ {
+ if ($this->transaction_opcount > 0) {
+ $result = @pg_exec($this->connection, 'abort;');
+ $this->transaction_opcount = 0;
+ if (!$result) {
+ return $this->pgsqlRaiseError();
+ }
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ affectedRows()
+
+ /**
+ * Determines the number of rows affected by a data maniuplation query
+ *
+ * 0 is returned for queries that don't manipulate data.
+ *
+ * @return int the number of rows. A DB_Error object on failure.
+ */
+ function affectedRows()
+ {
+ return $this->affected;
+ }
+
+ // }}}
+ // {{{ nextId()
+
+ /**
+ * Returns the next free id in a sequence
+ *
+ * @param string $seq_name name of the sequence
+ * @param boolean $ondemand when true, the seqence is automatically
+ * created if it does not exist
+ *
+ * @return int the next id number in the sequence.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::nextID(), DB_common::getSequenceName(),
+ * DB_pgsql::createSequence(), DB_pgsql::dropSequence()
+ */
+ function nextId($seq_name, $ondemand = true)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $repeat = false;
+ do {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result =& $this->query("SELECT NEXTVAL('${seqname}')");
+ $this->popErrorHandling();
+ if ($ondemand && DB::isError($result) &&
+ $result->getCode() == DB_ERROR_NOSUCHTABLE) {
+ $repeat = true;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $result = $this->createSequence($seq_name);
+ $this->popErrorHandling();
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ } else {
+ $repeat = false;
+ }
+ } while ($repeat);
+ if (DB::isError($result)) {
+ return $this->raiseError($result);
+ }
+ $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
+ $result->free();
+ return $arr[0];
+ }
+
+ // }}}
+ // {{{ createSequence()
+
+ /**
+ * Creates a new sequence
+ *
+ * @param string $seq_name name of the new sequence
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::createSequence(), DB_common::getSequenceName(),
+ * DB_pgsql::nextID(), DB_pgsql::dropSequence()
+ */
+ function createSequence($seq_name)
+ {
+ $seqname = $this->getSequenceName($seq_name);
+ $result = $this->query("CREATE SEQUENCE ${seqname}");
+ return $result;
+ }
+
+ // }}}
+ // {{{ dropSequence()
+
+ /**
+ * Deletes a sequence
+ *
+ * @param string $seq_name name of the sequence to be deleted
+ *
+ * @return int DB_OK on success. A DB_Error object on failure.
+ *
+ * @see DB_common::dropSequence(), DB_common::getSequenceName(),
+ * DB_pgsql::nextID(), DB_pgsql::createSequence()
+ */
+ function dropSequence($seq_name)
+ {
+ return $this->query('DROP SEQUENCE '
+ . $this->getSequenceName($seq_name));
+ }
+
+ // }}}
+ // {{{ modifyLimitQuery()
+
+ /**
+ * Adds LIMIT clauses to a query string according to current DBMS standards
+ *
+ * @param string $query the query to modify
+ * @param int $from the row to start to fetching (0 = the first row)
+ * @param int $count the numbers of rows to fetch
+ * @param mixed $params array, string or numeric data to be used in
+ * execution of the statement. Quantity of items
+ * passed must match quantity of placeholders in
+ * query: meaning 1 placeholder for non-array
+ * parameters or 1 placeholder per array element.
+ *
+ * @return string the query string with LIMIT clauses added
+ *
+ * @access protected
+ */
+ function modifyLimitQuery($query, $from, $count, $params = array())
+ {
+ return "$query LIMIT $count OFFSET $from";
+ }
+
+ // }}}
+ // {{{ pgsqlRaiseError()
+
+ /**
+ * Produces a DB_Error object regarding the current problem
+ *
+ * @param int $errno if the error is being manually raised pass a
+ * DB_ERROR* constant here. If this isn't passed
+ * the error information gathered from the DBMS.
+ *
+ * @return object the DB_Error object
+ *
+ * @see DB_common::raiseError(),
+ * DB_pgsql::errorNative(), DB_pgsql::errorCode()
+ */
+ function pgsqlRaiseError($errno = null)
+ {
+ $native = $this->errorNative();
+ if ($errno === null) {
+ $errno = $this->errorCode($native);
+ }
+ return $this->raiseError($errno, null, null, null, $native);
+ }
+
+ // }}}
+ // {{{ errorNative()
+
+ /**
+ * Gets the DBMS' native error message produced by the last query
+ *
+ * {@internal Error messages are used instead of error codes
+ * in order to support older versions of PostgreSQL.}}
+ *
+ * @return string the DBMS' error message
+ */
+ function errorNative()
+ {
+ return @pg_errormessage($this->connection);
+ }
+
+ // }}}
+ // {{{ errorCode()
+
+ /**
+ * Determines PEAR::DB error code from the database's text error message.
+ *
+ * @param string $errormsg error message returned from the database
+ * @return integer an error number from a DB error constant
+ */
+ function errorCode($errormsg)
+ {
+ static $error_regexps;
+ if (!isset($error_regexps)) {
+ $error_regexps = array(
+ '/(relation|sequence|table).*does not exist|class .* not found/i'
+ => DB_ERROR_NOSUCHTABLE,
+ '/index .* does not exist/'
+ => DB_ERROR_NOT_FOUND,
+ '/column .* does not exist/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/relation .* already exists/i'
+ => DB_ERROR_ALREADY_EXISTS,
+ '/(divide|division) by zero$/i'
+ => DB_ERROR_DIVZERO,
+ '/pg_atoi: error in .*: can\'t parse /i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/invalid input syntax for( type)? (integer|numeric)/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/value .* is out of range for type \w*int/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/integer out of range/i'
+ => DB_ERROR_INVALID_NUMBER,
+ '/value too long for type character/i'
+ => DB_ERROR_INVALID,
+ '/attribute .* not found|relation .* does not have attribute/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/column .* specified in USING clause does not exist in (left|right) table/i'
+ => DB_ERROR_NOSUCHFIELD,
+ '/parser: parse error at or near/i'
+ => DB_ERROR_SYNTAX,
+ '/syntax error at/'
+ => DB_ERROR_SYNTAX,
+ '/column reference .* is ambiguous/i'
+ => DB_ERROR_SYNTAX,
+ '/permission denied/'
+ => DB_ERROR_ACCESS_VIOLATION,
+ '/violates not-null constraint/'
+ => DB_ERROR_CONSTRAINT_NOT_NULL,
+ '/violates [\w ]+ constraint/'
+ => DB_ERROR_CONSTRAINT,
+ '/referential integrity violation/'
+ => DB_ERROR_CONSTRAINT,
+ '/more expressions than target columns/i'
+ => DB_ERROR_VALUE_COUNT_ON_ROW,
+ );
+ }
+ foreach ($error_regexps as $regexp => $code) {
+ if (preg_match($regexp, $errormsg)) {
+ return $code;
+ }
+ }
+ // Fall back to DB_ERROR if there was no mapping.
+ return DB_ERROR;
+ }
+
+ // }}}
+ // {{{ tableInfo()
+
+ /**
+ * Returns information about a table or a result set
+ *
+ * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+ * is a table name.
+ *
+ * @param object|string $result DB_result object from a query or a
+ * string containing the name of a table.
+ * While this also accepts a query result
+ * resource identifier, this behavior is
+ * deprecated.
+ * @param int $mode a valid tableInfo mode
+ *
+ * @return array an associative array with the information requested.
+ * A DB_Error object on failure.
+ *
+ * @see DB_common::tableInfo()
+ */
+ function tableInfo($result, $mode = null)
+ {
+ if (is_string($result)) {
+ /*
+ * Probably received a table name.
+ * Create a result resource identifier.
+ */
+ $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
+ $got_string = true;
+ } elseif (isset($result->result)) {
+ /*
+ * Probably received a result object.
+ * Extract the result resource identifier.
+ */
+ $id = $result->result;
+ $got_string = false;
+ } else {
+ /*
+ * Probably received a result resource identifier.
+ * Copy it.
+ * Deprecated. Here for compatibility only.
+ */
+ $id = $result;
+ $got_string = false;
+ }
+
+ if (!is_resource($id)) {
+ return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
+ }
+
+ if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
+ $case_func = 'strtolower';
+ } else {
+ $case_func = 'strval';
+ }
+
+ $count = @pg_numfields($id);
+ $res = array();
+
+ if ($mode) {
+ $res['num_fields'] = $count;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $res[$i] = array(
+ 'table' => $got_string ? $case_func($result) : '',
+ 'name' => $case_func(@pg_fieldname($id, $i)),
+ 'type' => @pg_fieldtype($id, $i),
+ 'len' => @pg_fieldsize($id, $i),
+ 'flags' => $got_string
+ ? $this->_pgFieldFlags($id, $i, $result)
+ : '',
+ );
+ if ($mode & DB_TABLEINFO_ORDER) {
+ $res['order'][$res[$i]['name']] = $i;
+ }
+ if ($mode & DB_TABLEINFO_ORDERTABLE) {
+ $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+ }
+ }
+
+ // free the result only if we were called on a table
+ if ($got_string) {
+ @pg_freeresult($id);
+ }
+ return $res;
+ }
+
+ // }}}
+ // {{{ _pgFieldFlags()
+
+ /**
+ * Get a column's flags
+ *
+ * Supports "not_null", "default_value", "primary_key", "unique_key"
+ * and "multiple_key". The default value is passed through
+ * rawurlencode() in case there are spaces in it.
+ *
+ * @param int $resource the PostgreSQL result identifier
+ * @param int $num_field the field number
+ *
+ * @return string the flags
+ *
+ * @access private
+ */
+ function _pgFieldFlags($resource, $num_field, $table_name)
+ {
+ $field_name = @pg_fieldname($resource, $num_field);
+
+ $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
+ FROM pg_attribute f, pg_class tab, pg_type typ
+ WHERE tab.relname = typ.typname
+ AND typ.typrelid = f.attrelid
+ AND f.attname = '$field_name'
+ AND tab.relname = '$table_name'");
+ if (@pg_numrows($result) > 0) {
+ $row = @pg_fetch_row($result, 0);
+ $flags = ($row[0] == 't') ? 'not_null ' : '';
+
+ if ($row[1] == 't') {
+ $result = @pg_exec($this->connection, "SELECT a.adsrc
+ FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a
+ WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
+ AND f.attrelid = a.adrelid AND f.attname = '$field_name'
+ AND tab.relname = '$table_name' AND f.attnum = a.adnum");
+ $row = @pg_fetch_row($result, 0);
+ $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
+ $flags .= 'default_' . rawurlencode($num) . ' ';
+ }
+ } else {
+ $flags = '';
+ }
+ $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
+ FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i
+ WHERE tab.relname = typ.typname
+ AND typ.typrelid = f.attrelid
+ AND f.attrelid = i.indrelid
+ AND f.attname = '$field_name'
+ AND tab.relname = '$table_name'");
+ $count = @pg_numrows($result);
+
+ for ($i = 0; $i < $count ; $i++) {
+ $row = @pg_fetch_row($result, $i);
+ $keys = explode(' ', $row[2]);
+
+ if (in_array($num_field + 1, $keys)) {
+ $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
+ $flags .= ($row[1] == 't') ? 'primary_key ' : '';
+ if (count($keys) > 1)
+ $flags .= 'multiple_key ';
+ }
+ }
+
+ return trim($flags);
+ }
+
+ // }}}
+ // {{{ getSpecialQuery()
+
+ /**
+ * Obtains the query string needed for listing a given type of objects
+ *
+ * @param string $type the kind of objects you want to retrieve
+ *
+ * @return string the SQL query string or null if the driver doesn't
+ * support the object type requested
+ *
+ * @access protected
+ * @see DB_common::getListOf()
+ */
+ function getSpecialQuery($type)
+ {
+ switch ($type) {
+ case 'tables':
+ return 'SELECT c.relname AS "Name"'
+ . ' FROM pg_class c, pg_user u'
+ . ' WHERE c.relowner = u.usesysid'
+ . " AND c.relkind = 'r'"
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_views'
+ . ' WHERE viewname = c.relname)'
+ . " AND c.relname !~ '^(pg_|sql_)'"
+ . ' UNION'
+ . ' SELECT c.relname AS "Name"'
+ . ' FROM pg_class c'
+ . " WHERE c.relkind = 'r'"
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_views'
+ . ' WHERE viewname = c.relname)'
+ . ' AND NOT EXISTS'
+ . ' (SELECT 1 FROM pg_user'
+ . ' WHERE usesysid = c.relowner)'
+ . " AND c.relname !~ '^pg_'";
+ case 'schema.tables':
+ return "SELECT schemaname || '.' || tablename"
+ . ' AS "Name"'
+ . ' FROM pg_catalog.pg_tables'
+ . ' WHERE schemaname NOT IN'
+ . " ('pg_catalog', 'information_schema', 'pg_toast')";
+ case 'views':
+ // Table cols: viewname | viewowner | definition
+ return 'SELECT viewname from pg_views WHERE schemaname'
+ . " NOT IN ('information_schema', 'pg_catalog')";
+ case 'users':
+ // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil
+ return 'SELECT usename FROM pg_user';
+ case 'databases':
+ return 'SELECT datname FROM pg_database';
+ case 'functions':
+ case 'procedures':
+ return 'SELECT proname FROM pg_proc WHERE proowner <> 1';
+ default:
+ return null;
+ }
+ }
+
+ // }}}
+
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/lib/phpFlickr/PEAR/DB/storage.php b/lib/phpFlickr/PEAR/DB/storage.php
new file mode 100644
index 000000000..f597b36d7
--- /dev/null
+++ b/lib/phpFlickr/PEAR/DB/storage.php
@@ -0,0 +1,504 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Provides an object interface to a table row
+ *
+ * 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 <stig@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: storage.php 32 2005-08-01 06:21:02Z dancoulter $
+ * @link http://pear.php.net/package/DB
+ */
+
+/**
+ * Obtain the DB class so it can be extended from
+ */
+require_once 'DB.php';
+
+/**
+ * Provides an object interface to a table row
+ *
+ * It lets you add, delete and change rows using objects rather than SQL
+ * statements.
+ *
+ * @category Database
+ * @package DB
+ * @author Stig Bakken <stig@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/DB
+ */
+class DB_storage extends PEAR
+{
+ // {{{ properties
+
+ /** the name of the table (or view, if the backend database supports
+ updates in views) we hold data from */
+ var $_table = null;
+
+ /** which column(s) in the table contains primary keys, can be a
+ string for single-column primary keys, or an array of strings
+ for multiple-column primary keys */
+ var $_keycolumn = null;
+
+ /** DB connection handle used for all transactions */
+ var $_dbh = null;
+
+ /** an assoc with the names of database fields stored as properties
+ in this object */
+ var $_properties = array();
+
+ /** an assoc with the names of the properties in this object that
+ have been changed since they were fetched from the database */
+ var $_changes = array();
+
+ /** flag that decides if data in this object can be changed.
+ objects that don't have their table's key column in their
+ property lists will be flagged as read-only. */
+ var $_readonly = false;
+
+ /** function or method that implements a validator for fields that
+ are set, this validator function returns true if the field is
+ valid, false if not */
+ var $_validator = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Constructor
+ *
+ * @param $table string the name of the database table
+ *
+ * @param $keycolumn mixed string with name of key column, or array of
+ * strings if the table has a primary key of more than one column
+ *
+ * @param $dbh object database connection object
+ *
+ * @param $validator mixed function or method used to validate
+ * each new value, called with three parameters: the name of the
+ * field/column that is changing, a reference to the new value and
+ * a reference to this object
+ *
+ */
+ function DB_storage($table, $keycolumn, &$dbh, $validator = null)
+ {
+ $this->PEAR('DB_Error');
+ $this->_table = $table;
+ $this->_keycolumn = $keycolumn;
+ $this->_dbh = $dbh;
+ $this->_readonly = false;
+ $this->_validator = $validator;
+ }
+
+ // }}}
+ // {{{ _makeWhere()
+
+ /**
+ * Utility method to build a "WHERE" clause to locate ourselves in
+ * the table.
+ *
+ * XXX future improvement: use rowids?
+ *
+ * @access private
+ */
+ function _makeWhere($keyval = null)
+ {
+ if (is_array($this->_keycolumn)) {
+ if ($keyval === null) {
+ for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
+ $keyval[] = $this->{$this->_keycolumn[$i]};
+ }
+ }
+ $whereclause = '';
+ for ($i = 0; $i < sizeof($this->_keycolumn); $i++) {
+ if ($i > 0) {
+ $whereclause .= ' AND ';
+ }
+ $whereclause .= $this->_keycolumn[$i];
+ if (is_null($keyval[$i])) {
+ // there's not much point in having a NULL key,
+ // but we support it anyway
+ $whereclause .= ' IS NULL';
+ } else {
+ $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]);
+ }
+ }
+ } else {
+ if ($keyval === null) {
+ $keyval = @$this->{$this->_keycolumn};
+ }
+ $whereclause = $this->_keycolumn;
+ if (is_null($keyval)) {
+ // there's not much point in having a NULL key,
+ // but we support it anyway
+ $whereclause .= ' IS NULL';
+ } else {
+ $whereclause .= ' = ' . $this->_dbh->quote($keyval);
+ }
+ }
+ return $whereclause;
+ }
+
+ // }}}
+ // {{{ setup()
+
+ /**
+ * Method used to initialize a DB_storage object from the
+ * configured table.
+ *
+ * @param $keyval mixed the key[s] of the row to fetch (string or array)
+ *
+ * @return int DB_OK on success, a DB error if not
+ */
+ function setup($keyval)
+ {
+ $whereclause = $this->_makeWhere($keyval);
+ $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause;
+ $sth = $this->_dbh->query($query);
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ $row = $sth->fetchRow(DB_FETCHMODE_ASSOC);
+ if (DB::isError($row)) {
+ return $row;
+ }
+ if (!$row) {
+ return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null,
+ $query, null, true);
+ }
+ foreach ($row as $key => $value) {
+ $this->_properties[$key] = true;
+ $this->$key = $value;
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ insert()
+
+ /**
+ * Create a new (empty) row in the configured table for this
+ * object.
+ */
+ function insert($newpk)
+ {
+ if (is_array($this->_keycolumn)) {
+ $primarykey = $this->_keycolumn;
+ } else {
+ $primarykey = array($this->_keycolumn);
+ }
+ settype($newpk, "array");
+ for ($i = 0; $i < sizeof($primarykey); $i++) {
+ $pkvals[] = $this->_dbh->quote($newpk[$i]);
+ }
+
+ $sth = $this->_dbh->query("INSERT INTO $this->_table (" .
+ implode(",", $primarykey) . ") VALUES(" .
+ implode(",", $pkvals) . ")");
+ if (DB::isError($sth)) {
+ return $sth;
+ }
+ if (sizeof($newpk) == 1) {
+ $newpk = $newpk[0];
+ }
+ $this->setup($newpk);
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * Output a simple description of this DB_storage object.
+ * @return string object description
+ */
+ function toString()
+ {
+ $info = strtolower(get_class($this));
+ $info .= " (table=";
+ $info .= $this->_table;
+ $info .= ", keycolumn=";
+ if (is_array($this->_keycolumn)) {
+ $info .= "(" . implode(",", $this->_keycolumn) . ")";
+ } else {
+ $info .= $this->_keycolumn;
+ }
+ $info .= ", dbh=";
+ if (is_object($this->_dbh)) {
+ $info .= $this->_dbh->toString();
+ } else {
+ $info .= "null";
+ }
+ $info .= ")";
+ if (sizeof($this->_properties)) {
+ $info .= " [loaded, key=";
+ $keyname = $this->_keycolumn;
+ if (is_array($keyname)) {
+ $info .= "(";
+ for ($i = 0; $i < sizeof($keyname); $i++) {
+ if ($i > 0) {
+ $info .= ",";
+ }
+ $info .= $this->$keyname[$i];
+ }
+ $info .= ")";
+ } else {
+ $info .= $this->$keyname;
+ }
+ $info .= "]";
+ }
+ if (sizeof($this->_changes)) {
+ $info .= " [modified]";
+ }
+ return $info;
+ }
+
+ // }}}
+ // {{{ dump()
+
+ /**
+ * Dump the contents of this object to "standard output".
+ */
+ function dump()
+ {
+ foreach ($this->_properties as $prop => $foo) {
+ print "$prop = ";
+ print htmlentities($this->$prop);
+ print "<br />\n";
+ }
+ }
+
+ // }}}
+ // {{{ &create()
+
+ /**
+ * Static method used to create new DB storage objects.
+ * @param $data assoc. array where the keys are the names
+ * of properties/columns
+ * @return object a new instance of DB_storage or a subclass of it
+ */
+ function &create($table, &$data)
+ {
+ $classname = strtolower(get_class($this));
+ $obj =& new $classname($table);
+ foreach ($data as $name => $value) {
+ $obj->_properties[$name] = true;
+ $obj->$name = &$value;
+ }
+ return $obj;
+ }
+
+ // }}}
+ // {{{ loadFromQuery()
+
+ /**
+ * Loads data into this object from the given query. If this
+ * object already contains table data, changes will be saved and
+ * the object re-initialized first.
+ *
+ * @param $query SQL query
+ *
+ * @param $params parameter list in case you want to use
+ * prepare/execute mode
+ *
+ * @return int DB_OK on success, DB_WARNING_READ_ONLY if the
+ * returned object is read-only (because the object's specified
+ * key column was not found among the columns returned by $query),
+ * or another DB error code in case of errors.
+ */
+// XXX commented out for now
+/*
+ function loadFromQuery($query, $params = null)
+ {
+ if (sizeof($this->_properties)) {
+ if (sizeof($this->_changes)) {
+ $this->store();
+ $this->_changes = array();
+ }
+ $this->_properties = array();
+ }
+ $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params);
+ if (DB::isError($rowdata)) {
+ return $rowdata;
+ }
+ reset($rowdata);
+ $found_keycolumn = false;
+ while (list($key, $value) = each($rowdata)) {
+ if ($key == $this->_keycolumn) {
+ $found_keycolumn = true;
+ }
+ $this->_properties[$key] = true;
+ $this->$key = &$value;
+ unset($value); // have to unset, or all properties will
+ // refer to the same value
+ }
+ if (!$found_keycolumn) {
+ $this->_readonly = true;
+ return DB_WARNING_READ_ONLY;
+ }
+ return DB_OK;
+ }
+ */
+
+ // }}}
+ // {{{ set()
+
+ /**
+ * Modify an attriute value.
+ */
+ function set($property, $newvalue)
+ {
+ // only change if $property is known and object is not
+ // read-only
+ if ($this->_readonly) {
+ return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
+ null, null, null, true);
+ }
+ if (@isset($this->_properties[$property])) {
+ if (empty($this->_validator)) {
+ $valid = true;
+ } else {
+ $valid = @call_user_func($this->_validator,
+ $this->_table,
+ $property,
+ $newvalue,
+ $this->$property,
+ $this);
+ }
+ if ($valid) {
+ $this->$property = $newvalue;
+ if (empty($this->_changes[$property])) {
+ $this->_changes[$property] = 0;
+ } else {
+ $this->_changes[$property]++;
+ }
+ } else {
+ return $this->raiseError(null, DB_ERROR_INVALID, null,
+ null, "invalid field: $property",
+ null, true);
+ }
+ return true;
+ }
+ return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null,
+ null, "unknown field: $property",
+ null, true);
+ }
+
+ // }}}
+ // {{{ &get()
+
+ /**
+ * Fetch an attribute value.
+ *
+ * @param string attribute name
+ *
+ * @return attribute contents, or null if the attribute name is
+ * unknown
+ */
+ function &get($property)
+ {
+ // only return if $property is known
+ if (isset($this->_properties[$property])) {
+ return $this->$property;
+ }
+ $tmp = null;
+ return $tmp;
+ }
+
+ // }}}
+ // {{{ _DB_storage()
+
+ /**
+ * Destructor, calls DB_storage::store() if there are changes
+ * that are to be kept.
+ */
+ function _DB_storage()
+ {
+ if (sizeof($this->_changes)) {
+ $this->store();
+ }
+ $this->_properties = array();
+ $this->_changes = array();
+ $this->_table = null;
+ }
+
+ // }}}
+ // {{{ store()
+
+ /**
+ * Stores changes to this object in the database.
+ *
+ * @return DB_OK or a DB error
+ */
+ function store()
+ {
+ foreach ($this->_changes as $name => $foo) {
+ $params[] = &$this->$name;
+ $vars[] = $name . ' = ?';
+ }
+ if ($vars) {
+ $query = 'UPDATE ' . $this->_table . ' SET ' .
+ implode(', ', $vars) . ' WHERE ' .
+ $this->_makeWhere();
+ $stmt = $this->_dbh->prepare($query);
+ $res = $this->_dbh->execute($stmt, $params);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ $this->_changes = array();
+ }
+ return DB_OK;
+ }
+
+ // }}}
+ // {{{ remove()
+
+ /**
+ * Remove the row represented by this object from the database.
+ *
+ * @return mixed DB_OK or a DB error
+ */
+ function remove()
+ {
+ if ($this->_readonly) {
+ return $this->raiseError(null, DB_WARNING_READ_ONLY, null,
+ null, null, null, true);
+ }
+ $query = 'DELETE FROM ' . $this->_table .' WHERE '.
+ $this->_makeWhere();
+ $res = $this->_dbh->query($query);
+ if (DB::isError($res)) {
+ return $res;
+ }
+ foreach ($this->_properties as $prop => $foo) {
+ unset($this->$prop);
+ }
+ $this->_properties = array();
+ $this->_changes = array();
+ return DB_OK;
+ }
+
+ // }}}
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
+?>
diff --git a/lib/phpFlickr/PEAR/HTTP/Request.php b/lib/phpFlickr/PEAR/HTTP/Request.php
new file mode 100644
index 000000000..55e227b3a
--- /dev/null
+++ b/lib/phpFlickr/PEAR/HTTP/Request.php
@@ -0,0 +1,1484 @@
+<?php
+/**
+ * Class for performing HTTP requests
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2002-2007, Richard Heyes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Richard Heyes <richard@phpguru.org>
+ * @author Alexey Borzov <avb@php.net>
+ * @copyright 2002-2007 Richard Heyes
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Request.php 127 2008-01-17 20:21:37Z dcoulter $
+ * @link http://pear.php.net/package/HTTP_Request/
+ */
+
+/**
+ * PEAR and PEAR_Error classes (for error handling)
+ */
+require_once 'PEAR.php';
+/**
+ * Socket class
+ */
+require_once 'Net/Socket.php';
+/**
+ * URL handling class
+ */
+require_once 'Net/URL.php';
+
+/**#@+
+ * Constants for HTTP request methods
+ */
+define('HTTP_REQUEST_METHOD_GET', 'GET', true);
+define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true);
+define('HTTP_REQUEST_METHOD_POST', 'POST', true);
+define('HTTP_REQUEST_METHOD_PUT', 'PUT', true);
+define('HTTP_REQUEST_METHOD_DELETE', 'DELETE', true);
+define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
+define('HTTP_REQUEST_METHOD_TRACE', 'TRACE', true);
+/**#@-*/
+
+/**#@+
+ * Constants for HTTP request error codes
+ */
+define('HTTP_REQUEST_ERROR_FILE', 1);
+define('HTTP_REQUEST_ERROR_URL', 2);
+define('HTTP_REQUEST_ERROR_PROXY', 4);
+define('HTTP_REQUEST_ERROR_REDIRECTS', 8);
+define('HTTP_REQUEST_ERROR_RESPONSE', 16);
+define('HTTP_REQUEST_ERROR_GZIP_METHOD', 32);
+define('HTTP_REQUEST_ERROR_GZIP_READ', 64);
+define('HTTP_REQUEST_ERROR_GZIP_DATA', 128);
+define('HTTP_REQUEST_ERROR_GZIP_CRC', 256);
+/**#@-*/
+
+/**#@+
+ * Constants for HTTP protocol versions
+ */
+define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
+define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
+/**#@-*/
+
+if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
+ /**
+ * Whether string functions are overloaded by their mbstring equivalents
+ */
+ define('HTTP_REQUEST_MBSTRING', true);
+} else {
+ /**
+ * @ignore
+ */
+ define('HTTP_REQUEST_MBSTRING', false);
+}
+
+/**
+ * Class for performing HTTP requests
+ *
+ * Simple example (fetches yahoo.com and displays it):
+ * <code>
+ * $a = &new HTTP_Request('http://www.yahoo.com/');
+ * $a->sendRequest();
+ * echo $a->getResponseBody();
+ * </code>
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Richard Heyes <richard@phpguru.org>
+ * @author Alexey Borzov <avb@php.net>
+ * @version Release: 1.4.2
+ */
+class HTTP_Request
+{
+ /**#@+
+ * @access private
+ */
+ /**
+ * Instance of Net_URL
+ * @var Net_URL
+ */
+ var $_url;
+
+ /**
+ * Type of request
+ * @var string
+ */
+ var $_method;
+
+ /**
+ * HTTP Version
+ * @var string
+ */
+ var $_http;
+
+ /**
+ * Request headers
+ * @var array
+ */
+ var $_requestHeaders;
+
+ /**
+ * Basic Auth Username
+ * @var string
+ */
+ var $_user;
+
+ /**
+ * Basic Auth Password
+ * @var string
+ */
+ var $_pass;
+
+ /**
+ * Socket object
+ * @var Net_Socket
+ */
+ var $_sock;
+
+ /**
+ * Proxy server
+ * @var string
+ */
+ var $_proxy_host;
+
+ /**
+ * Proxy port
+ * @var integer
+ */
+ var $_proxy_port;
+
+ /**
+ * Proxy username
+ * @var string
+ */
+ var $_proxy_user;
+
+ /**
+ * Proxy password
+ * @var string
+ */
+ var $_proxy_pass;
+
+ /**
+ * Post data
+ * @var array
+ */
+ var $_postData;
+
+ /**
+ * Request body
+ * @var string
+ */
+ var $_body;
+
+ /**
+ * A list of methods that MUST NOT have a request body, per RFC 2616
+ * @var array
+ */
+ var $_bodyDisallowed = array('TRACE');
+
+ /**
+ * Files to post
+ * @var array
+ */
+ var $_postFiles = array();
+
+ /**
+ * Connection timeout.
+ * @var float
+ */
+ var $_timeout;
+
+ /**
+ * HTTP_Response object
+ * @var HTTP_Response
+ */
+ var $_response;
+
+ /**
+ * Whether to allow redirects
+ * @var boolean
+ */
+ var $_allowRedirects;
+
+ /**
+ * Maximum redirects allowed
+ * @var integer
+ */
+ var $_maxRedirects;
+
+ /**
+ * Current number of redirects
+ * @var integer
+ */
+ var $_redirects;
+
+ /**
+ * Whether to append brackets [] to array variables
+ * @var bool
+ */
+ var $_useBrackets = true;
+
+ /**
+ * Attached listeners
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Whether to save response body in response object property
+ * @var bool
+ */
+ var $_saveBody = true;
+
+ /**
+ * Timeout for reading from socket (array(seconds, microseconds))
+ * @var array
+ */
+ var $_readTimeout = null;
+
+ /**
+ * Options to pass to Net_Socket::connect. See stream_context_create
+ * @var array
+ */
+ var $_socketOptions = null;
+ /**#@-*/
+
+ /**
+ * Constructor
+ *
+ * Sets up the object
+ * @param string The url to fetch/access
+ * @param array Associative array of parameters which can have the following keys:
+ * <ul>
+ * <li>method - Method to use, GET, POST etc (string)</li>
+ * <li>http - HTTP Version to use, 1.0 or 1.1 (string)</li>
+ * <li>user - Basic Auth username (string)</li>
+ * <li>pass - Basic Auth password (string)</li>
+ * <li>proxy_host - Proxy server host (string)</li>
+ * <li>proxy_port - Proxy server port (integer)</li>
+ * <li>proxy_user - Proxy auth username (string)</li>
+ * <li>proxy_pass - Proxy auth password (string)</li>
+ * <li>timeout - Connection timeout in seconds (float)</li>
+ * <li>allowRedirects - Whether to follow redirects or not (bool)</li>
+ * <li>maxRedirects - Max number of redirects to follow (integer)</li>
+ * <li>useBrackets - Whether to append [] to array variable names (bool)</li>
+ * <li>saveBody - Whether to save response body in response object property (bool)</li>
+ * <li>readTimeout - Timeout for reading / writing data over the socket (array (seconds, microseconds))</li>
+ * <li>socketOptions - Options to pass to Net_Socket object (array)</li>
+ * </ul>
+ * @access public
+ */
+ function HTTP_Request($url = '', $params = array())
+ {
+ $this->_method = HTTP_REQUEST_METHOD_GET;
+ $this->_http = HTTP_REQUEST_HTTP_VER_1_1;
+ $this->_requestHeaders = array();
+ $this->_postData = array();
+ $this->_body = null;
+
+ $this->_user = null;
+ $this->_pass = null;
+
+ $this->_proxy_host = null;
+ $this->_proxy_port = null;
+ $this->_proxy_user = null;
+ $this->_proxy_pass = null;
+
+ $this->_allowRedirects = false;
+ $this->_maxRedirects = 3;
+ $this->_redirects = 0;
+
+ $this->_timeout = null;
+ $this->_response = null;
+
+ foreach ($params as $key => $value) {
+ $this->{'_' . $key} = $value;
+ }
+
+ if (!empty($url)) {
+ $this->setURL($url);
+ }
+
+ // Default useragent
+ $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
+
+ // We don't do keep-alives by default
+ $this->addHeader('Connection', 'close');
+
+ // Basic authentication
+ if (!empty($this->_user)) {
+ $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass));
+ }
+
+ // Proxy authentication (see bug #5913)
+ if (!empty($this->_proxy_user)) {
+ $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($this->_proxy_user . ':' . $this->_proxy_pass));
+ }
+
+ // Use gzip encoding if possible
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib')) {
+ $this->addHeader('Accept-Encoding', 'gzip');
+ }
+ }
+
+ /**
+ * Generates a Host header for HTTP/1.1 requests
+ *
+ * @access private
+ * @return string
+ */
+ function _generateHostHeader()
+ {
+ if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } else {
+ $host = $this->_url->host;
+ }
+
+ return $host;
+ }
+
+ /**
+ * Resets the object to its initial state (DEPRECATED).
+ * Takes the same parameters as the constructor.
+ *
+ * @param string $url The url to be requested
+ * @param array $params Associative array of parameters
+ * (see constructor for details)
+ * @access public
+ * @deprecated deprecated since 1.2, call the constructor if this is necessary
+ */
+ function reset($url, $params = array())
+ {
+ $this->HTTP_Request($url, $params);
+ }
+
+ /**
+ * Sets the URL to be requested
+ *
+ * @param string The url to be requested
+ * @access public
+ */
+ function setURL($url)
+ {
+ $this->_url = &new Net_URL($url, $this->_useBrackets);
+
+ if (!empty($this->_url->user) || !empty($this->_url->pass)) {
+ $this->setBasicAuth($this->_url->user, $this->_url->pass);
+ }
+
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
+ $this->addHeader('Host', $this->_generateHostHeader());
+ }
+
+ // set '/' instead of empty path rather than check later (see bug #8662)
+ if (empty($this->_url->path)) {
+ $this->_url->path = '/';
+ }
+ }
+
+ /**
+ * Returns the current request URL
+ *
+ * @return string Current request URL
+ * @access public
+ */
+ function getUrl()
+ {
+ return empty($this->_url)? '': $this->_url->getUrl();
+ }
+
+ /**
+ * Sets a proxy to be used
+ *
+ * @param string Proxy host
+ * @param int Proxy port
+ * @param string Proxy username
+ * @param string Proxy password
+ * @access public
+ */
+ function setProxy($host, $port = 8080, $user = null, $pass = null)
+ {
+ $this->_proxy_host = $host;
+ $this->_proxy_port = $port;
+ $this->_proxy_user = $user;
+ $this->_proxy_pass = $pass;
+
+ if (!empty($user)) {
+ $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+ }
+ }
+
+ /**
+ * Sets basic authentication parameters
+ *
+ * @param string Username
+ * @param string Password
+ */
+ function setBasicAuth($user, $pass)
+ {
+ $this->_user = $user;
+ $this->_pass = $pass;
+
+ $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+ }
+
+ /**
+ * Sets the method to be used, GET, POST etc.
+ *
+ * @param string Method to use. Use the defined constants for this
+ * @access public
+ */
+ function setMethod($method)
+ {
+ $this->_method = $method;
+ }
+
+ /**
+ * Sets the HTTP version to use, 1.0 or 1.1
+ *
+ * @param string Version to use. Use the defined constants for this
+ * @access public
+ */
+ function setHttpVer($http)
+ {
+ $this->_http = $http;
+ }
+
+ /**
+ * Adds a request header
+ *
+ * @param string Header name
+ * @param string Header value
+ * @access public
+ */
+ function addHeader($name, $value)
+ {
+ $this->_requestHeaders[strtolower($name)] = $value;
+ }
+
+ /**
+ * Removes a request header
+ *
+ * @param string Header name to remove
+ * @access public
+ */
+ function removeHeader($name)
+ {
+ if (isset($this->_requestHeaders[strtolower($name)])) {
+ unset($this->_requestHeaders[strtolower($name)]);
+ }
+ }
+
+ /**
+ * Adds a querystring parameter
+ *
+ * @param string Querystring parameter name
+ * @param string Querystring parameter value
+ * @param bool Whether the value is already urlencoded or not, default = not
+ * @access public
+ */
+ function addQueryString($name, $value, $preencoded = false)
+ {
+ $this->_url->addQueryString($name, $value, $preencoded);
+ }
+
+ /**
+ * Sets the querystring to literally what you supply
+ *
+ * @param string The querystring data. Should be of the format foo=bar&x=y etc
+ * @param bool Whether data is already urlencoded or not, default = already encoded
+ * @access public
+ */
+ function addRawQueryString($querystring, $preencoded = true)
+ {
+ $this->_url->addRawQueryString($querystring, $preencoded);
+ }
+
+ /**
+ * Adds postdata items
+ *
+ * @param string Post data name
+ * @param string Post data value
+ * @param bool Whether data is already urlencoded or not, default = not
+ * @access public
+ */
+ function addPostData($name, $value, $preencoded = false)
+ {
+ if ($preencoded) {
+ $this->_postData[$name] = $value;
+ } else {
+ $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
+ }
+ }
+
+ /**
+ * Recursively applies the callback function to the value
+ *
+ * @param mixed Callback function
+ * @param mixed Value to process
+ * @access private
+ * @return mixed Processed value
+ */
+ function _arrayMapRecursive($callback, $value)
+ {
+ if (!is_array($value)) {
+ return call_user_func($callback, $value);
+ } else {
+ $map = array();
+ foreach ($value as $k => $v) {
+ $map[$k] = $this->_arrayMapRecursive($callback, $v);
+ }
+ return $map;
+ }
+ }
+
+ /**
+ * Adds a file to upload
+ *
+ * This also changes content-type to 'multipart/form-data' for proper upload
+ *
+ * @access public
+ * @param string name of file-upload field
+ * @param mixed file name(s)
+ * @param mixed content-type(s) of file(s) being uploaded
+ * @return bool true on success
+ * @throws PEAR_Error
+ */
+ function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
+ {
+ if (!is_array($fileName) && !is_readable($fileName)) {
+ return PEAR::raiseError("File '{$fileName}' is not readable", HTTP_REQUEST_ERROR_FILE);
+ } elseif (is_array($fileName)) {
+ foreach ($fileName as $name) {
+ if (!is_readable($name)) {
+ return PEAR::raiseError("File '{$name}' is not readable", HTTP_REQUEST_ERROR_FILE);
+ }
+ }
+ }
+ $this->addHeader('Content-Type', 'multipart/form-data');
+ $this->_postFiles[$inputName] = array(
+ 'name' => $fileName,
+ 'type' => $contentType
+ );
+ return true;
+ }
+
+ /**
+ * Adds raw postdata (DEPRECATED)
+ *
+ * @param string The data
+ * @param bool Whether data is preencoded or not, default = already encoded
+ * @access public
+ * @deprecated deprecated since 1.3.0, method setBody() should be used instead
+ */
+ function addRawPostData($postdata, $preencoded = true)
+ {
+ $this->_body = $preencoded ? $postdata : urlencode($postdata);
+ }
+
+ /**
+ * Sets the request body (for POST, PUT and similar requests)
+ *
+ * @param string Request body
+ * @access public
+ */
+ function setBody($body)
+ {
+ $this->_body = $body;
+ }
+
+ /**
+ * Clears any postdata that has been added (DEPRECATED).
+ *
+ * Useful for multiple request scenarios.
+ *
+ * @access public
+ * @deprecated deprecated since 1.2
+ */
+ function clearPostData()
+ {
+ $this->_postData = null;
+ }
+
+ /**
+ * Appends a cookie to "Cookie:" header
+ *
+ * @param string $name cookie name
+ * @param string $value cookie value
+ * @access public
+ */
+ function addCookie($name, $value)
+ {
+ $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : '';
+ $this->addHeader('Cookie', $cookies . $name . '=' . $value);
+ }
+
+ /**
+ * Clears any cookies that have been added (DEPRECATED).
+ *
+ * Useful for multiple request scenarios
+ *
+ * @access public
+ * @deprecated deprecated since 1.2
+ */
+ function clearCookies()
+ {
+ $this->removeHeader('Cookie');
+ }
+
+ /**
+ * Sends the request
+ *
+ * @access public
+ * @param bool Whether to store response body in Response object property,
+ * set this to false if downloading a LARGE file and using a Listener
+ * @return mixed PEAR error on error, true otherwise
+ */
+ function sendRequest($saveBody = true)
+ {
+ if (!is_a($this->_url, 'Net_URL')) {
+ return PEAR::raiseError('No URL given', HTTP_REQUEST_ERROR_URL);
+ }
+
+ $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
+ $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
+
+ // 4.3.0 supports SSL connections using OpenSSL. The function test determines
+ // we running on at least 4.3.0
+ if (strcasecmp($this->_url->protocol, 'https') == 0 AND function_exists('file_get_contents') AND extension_loaded('openssl')) {
+ if (isset($this->_proxy_host)) {
+ return PEAR::raiseError('HTTPS proxies are not supported', HTTP_REQUEST_ERROR_PROXY);
+ }
+ $host = 'ssl://' . $host;
+ }
+
+ // magic quotes may fuck up file uploads and chunked response processing
+ $magicQuotes = ini_get('magic_quotes_runtime');
+ ini_set('magic_quotes_runtime', false);
+
+ // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
+ // connection token to a proxy server...
+ if (isset($this->_proxy_host) && !empty($this->_requestHeaders['connection']) &&
+ 'Keep-Alive' == $this->_requestHeaders['connection'])
+ {
+ $this->removeHeader('connection');
+ }
+
+ $keepAlive = (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && empty($this->_requestHeaders['connection'])) ||
+ (!empty($this->_requestHeaders['connection']) && 'Keep-Alive' == $this->_requestHeaders['connection']);
+ $sockets = &PEAR::getStaticProperty('HTTP_Request', 'sockets');
+ $sockKey = $host . ':' . $port;
+ unset($this->_sock);
+
+ // There is a connected socket in the "static" property?
+ if ($keepAlive && !empty($sockets[$sockKey]) &&
+ !empty($sockets[$sockKey]->fp))
+ {
+ $this->_sock =& $sockets[$sockKey];
+ $err = null;
+ } else {
+ $this->_notify('connect');
+ $this->_sock =& new Net_Socket();
+ $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions);
+ }
+ PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest());
+
+ if (!PEAR::isError($err)) {
+ if (!empty($this->_readTimeout)) {
+ $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
+ }
+
+ $this->_notify('sentRequest');
+
+ // Read the response
+ $this->_response = &new HTTP_Response($this->_sock, $this->_listeners);
+ $err = $this->_response->process(
+ $this->_saveBody && $saveBody,
+ HTTP_REQUEST_METHOD_HEAD != $this->_method
+ );
+
+ if ($keepAlive) {
+ $keepAlive = (isset($this->_response->_headers['content-length'])
+ || (isset($this->_response->_headers['transfer-encoding'])
+ && strtolower($this->_response->_headers['transfer-encoding']) == 'chunked'));
+ if ($keepAlive) {
+ if (isset($this->_response->_headers['connection'])) {
+ $keepAlive = strtolower($this->_response->_headers['connection']) == 'keep-alive';
+ } else {
+ $keepAlive = 'HTTP/'.HTTP_REQUEST_HTTP_VER_1_1 == $this->_response->_protocol;
+ }
+ }
+ }
+ }
+
+ ini_set('magic_quotes_runtime', $magicQuotes);
+
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+ if (!$keepAlive) {
+ $this->disconnect();
+ // Store the connected socket in "static" property
+ } elseif (empty($sockets[$sockKey]) || empty($sockets[$sockKey]->fp)) {
+ $sockets[$sockKey] =& $this->_sock;
+ }
+
+ // Check for redirection
+ if ( $this->_allowRedirects
+ AND $this->_redirects <= $this->_maxRedirects
+ AND $this->getResponseCode() > 300
+ AND $this->getResponseCode() < 399
+ AND !empty($this->_response->_headers['location'])) {
+
+
+ $redirect = $this->_response->_headers['location'];
+
+ // Absolute URL
+ if (preg_match('/^https?:\/\//i', $redirect)) {
+ $this->_url = &new Net_URL($redirect);
+ $this->addHeader('Host', $this->_generateHostHeader());
+ // Absolute path
+ } elseif ($redirect{0} == '/') {
+ $this->_url->path = $redirect;
+
+ // Relative path
+ } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
+ if (substr($this->_url->path, -1) == '/') {
+ $redirect = $this->_url->path . $redirect;
+ } else {
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
+ }
+ $redirect = Net_URL::resolvePath($redirect);
+ $this->_url->path = $redirect;
+
+ // Filename, no path
+ } else {
+ if (substr($this->_url->path, -1) == '/') {
+ $redirect = $this->_url->path . $redirect;
+ } else {
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
+ }
+ $this->_url->path = $redirect;
+ }
+
+ $this->_redirects++;
+ return $this->sendRequest($saveBody);
+
+ // Too many redirects
+ } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
+ return PEAR::raiseError('Too many redirects', HTTP_REQUEST_ERROR_REDIRECTS);
+ }
+
+ return true;
+ }
+
+ /**
+ * Disconnect the socket, if connected. Only useful if using Keep-Alive.
+ *
+ * @access public
+ */
+ function disconnect()
+ {
+ if (!empty($this->_sock) && !empty($this->_sock->fp)) {
+ $this->_notify('disconnect');
+ $this->_sock->disconnect();
+ }
+ }
+
+ /**
+ * Returns the response code
+ *
+ * @access public
+ * @return mixed Response code, false if not set
+ */
+ function getResponseCode()
+ {
+ return isset($this->_response->_code) ? $this->_response->_code : false;
+ }
+
+ /**
+ * Returns either the named header or all if no name given
+ *
+ * @access public
+ * @param string The header name to return, do not set to get all headers
+ * @return mixed either the value of $headername (false if header is not present)
+ * or an array of all headers
+ */
+ function getResponseHeader($headername = null)
+ {
+ if (!isset($headername)) {
+ return isset($this->_response->_headers)? $this->_response->_headers: array();
+ } else {
+ $headername = strtolower($headername);
+ return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
+ }
+ }
+
+ /**
+ * Returns the body of the response
+ *
+ * @access public
+ * @return mixed response body, false if not set
+ */
+ function getResponseBody()
+ {
+ return isset($this->_response->_body) ? $this->_response->_body : false;
+ }
+
+ /**
+ * Returns cookies set in response
+ *
+ * @access public
+ * @return mixed array of response cookies, false if none are present
+ */
+ function getResponseCookies()
+ {
+ return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
+ }
+
+ /**
+ * Builds the request string
+ *
+ * @access private
+ * @return string The request string
+ */
+ function _buildRequest()
+ {
+ $separator = ini_get('arg_separator.output');
+ ini_set('arg_separator.output', '&');
+ $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
+ ini_set('arg_separator.output', $separator);
+
+ $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
+ $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
+ $path = $this->_url->path . $querystring;
+ $url = $host . $port . $path;
+
+ if (!strlen($url)) {
+ $url = '/';
+ }
+
+ $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
+
+ if (in_array($this->_method, $this->_bodyDisallowed) ||
+ (0 == strlen($this->_body) && (HTTP_REQUEST_METHOD_POST != $this->_method ||
+ (empty($this->_postData) && empty($this->_postFiles)))))
+ {
+ $this->removeHeader('Content-Type');
+ } else {
+ if (empty($this->_requestHeaders['content-type'])) {
+ // Add default content-type
+ $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
+ } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) {
+ $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
+ $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
+ }
+ }
+
+ // Request Headers
+ if (!empty($this->_requestHeaders)) {
+ foreach ($this->_requestHeaders as $name => $value) {
+ $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
+ $request .= $canonicalName . ': ' . $value . "\r\n";
+ }
+ }
+
+ // No post data or wrong method, so simply add a final CRLF
+ if (in_array($this->_method, $this->_bodyDisallowed) ||
+ (HTTP_REQUEST_METHOD_POST != $this->_method && 0 == strlen($this->_body))) {
+
+ $request .= "\r\n";
+
+ // Post data if it's an array
+ } elseif (HTTP_REQUEST_METHOD_POST == $this->_method &&
+ (!empty($this->_postData) || !empty($this->_postFiles))) {
+
+ // "normal" POST request
+ if (!isset($boundary)) {
+ $postdata = implode('&', array_map(
+ create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
+ $this->_flattenArray('', $this->_postData)
+ ));
+
+ // multipart request, probably with file uploads
+ } else {
+ $postdata = '';
+ if (!empty($this->_postData)) {
+ $flatData = $this->_flattenArray('', $this->_postData);
+ foreach ($flatData as $item) {
+ $postdata .= '--' . $boundary . "\r\n";
+ $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
+ $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
+ }
+ }
+ foreach ($this->_postFiles as $name => $value) {
+ if (is_array($value['name'])) {
+ $varname = $name . ($this->_useBrackets? '[]': '');
+ } else {
+ $varname = $name;
+ $value['name'] = array($value['name']);
+ }
+ foreach ($value['name'] as $key => $filename) {
+ $fp = fopen($filename, 'r');
+ $data = fread($fp, filesize($filename));
+ fclose($fp);
+ $basename = basename($filename);
+ $type = is_array($value['type'])? @$value['type'][$key]: $value['type'];
+
+ $postdata .= '--' . $boundary . "\r\n";
+ $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
+ $postdata .= "\r\nContent-Type: " . $type;
+ $postdata .= "\r\n\r\n" . $data . "\r\n";
+ }
+ }
+ $postdata .= '--' . $boundary . "--\r\n";
+ }
+ $request .= 'Content-Length: ' .
+ (HTTP_REQUEST_MBSTRING? mb_strlen($postdata, 'iso-8859-1'): strlen($postdata)) .
+ "\r\n\r\n";
+ $request .= $postdata;
+
+ // Explicitly set request body
+ } elseif (0 < strlen($this->_body)) {
+
+ $request .= 'Content-Length: ' .
+ (HTTP_REQUEST_MBSTRING? mb_strlen($this->_body, 'iso-8859-1'): strlen($this->_body)) .
+ "\r\n\r\n";
+ $request .= $this->_body;
+
+ // Terminate headers with CRLF on POST request with no body, too
+ } else {
+
+ $request .= "\r\n";
+ }
+
+ return $request;
+ }
+
+ /**
+ * Helper function to change the (probably multidimensional) associative array
+ * into the simple one.
+ *
+ * @param string name for item
+ * @param mixed item's values
+ * @return array array with the following items: array('item name', 'item value');
+ * @access private
+ */
+ function _flattenArray($name, $values)
+ {
+ if (!is_array($values)) {
+ return array(array($name, $values));
+ } else {
+ $ret = array();
+ foreach ($values as $k => $v) {
+ if (empty($name)) {
+ $newName = $k;
+ } elseif ($this->_useBrackets) {
+ $newName = $name . '[' . $k . ']';
+ } else {
+ $newName = $name;
+ }
+ $ret = array_merge($ret, $this->_flattenArray($newName, $v));
+ }
+ return $ret;
+ }
+ }
+
+
+ /**
+ * Adds a Listener to the list of listeners that are notified of
+ * the object's events
+ *
+ * Events sent by HTTP_Request object
+ * - 'connect': on connection to server
+ * - 'sentRequest': after the request was sent
+ * - 'disconnect': on disconnection from server
+ *
+ * Events sent by HTTP_Response object
+ * - 'gotHeaders': after receiving response headers (headers are passed in $data)
+ * - 'tick': on receiving a part of response body (the part is passed in $data)
+ * - 'gzTick': on receiving a gzip-encoded part of response body (ditto)
+ * - 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
+ *
+ * @param HTTP_Request_Listener listener to attach
+ * @return boolean whether the listener was successfully attached
+ * @access public
+ */
+ function attach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener')) {
+ return false;
+ }
+ $this->_listeners[$listener->getId()] =& $listener;
+ return true;
+ }
+
+
+ /**
+ * Removes a Listener from the list of listeners
+ *
+ * @param HTTP_Request_Listener listener to detach
+ * @return boolean whether the listener was successfully detached
+ * @access public
+ */
+ function detach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener') ||
+ !isset($this->_listeners[$listener->getId()])) {
+ return false;
+ }
+ unset($this->_listeners[$listener->getId()]);
+ return true;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ * @see HTTP_Request::attach()
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+}
+
+
+/**
+ * Response class to complement the Request class
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Richard Heyes <richard@phpguru.org>
+ * @author Alexey Borzov <avb@php.net>
+ * @version Release: 1.4.2
+ */
+class HTTP_Response
+{
+ /**
+ * Socket object
+ * @var Net_Socket
+ */
+ var $_sock;
+
+ /**
+ * Protocol
+ * @var string
+ */
+ var $_protocol;
+
+ /**
+ * Return code
+ * @var string
+ */
+ var $_code;
+
+ /**
+ * Response headers
+ * @var array
+ */
+ var $_headers;
+
+ /**
+ * Cookies set in response
+ * @var array
+ */
+ var $_cookies;
+
+ /**
+ * Response body
+ * @var string
+ */
+ var $_body = '';
+
+ /**
+ * Used by _readChunked(): remaining length of the current chunk
+ * @var string
+ */
+ var $_chunkLength = 0;
+
+ /**
+ * Attached listeners
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Bytes left to read from message-body
+ * @var null|int
+ */
+ var $_toRead;
+
+ /**
+ * Constructor
+ *
+ * @param Net_Socket socket to read the response from
+ * @param array listeners attached to request
+ */
+ function HTTP_Response(&$sock, &$listeners)
+ {
+ $this->_sock =& $sock;
+ $this->_listeners =& $listeners;
+ }
+
+
+ /**
+ * Processes a HTTP response
+ *
+ * This extracts response code, headers, cookies and decodes body if it
+ * was encoded in some way
+ *
+ * @access public
+ * @param bool Whether to store response body in object property, set
+ * this to false if downloading a LARGE file and using a Listener.
+ * This is assumed to be true if body is gzip-encoded.
+ * @param bool Whether the response can actually have a message-body.
+ * Will be set to false for HEAD requests.
+ * @throws PEAR_Error
+ * @return mixed true on success, PEAR_Error in case of malformed response
+ */
+ function process($saveBody = true, $canHaveBody = true)
+ {
+ do {
+ $line = $this->_sock->readLine();
+ if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) {
+ return PEAR::raiseError('Malformed response', HTTP_REQUEST_ERROR_RESPONSE);
+ } else {
+ $this->_protocol = 'HTTP/' . $http_version;
+ $this->_code = intval($returncode);
+ }
+ while ('' !== ($header = $this->_sock->readLine())) {
+ $this->_processHeader($header);
+ }
+ } while (100 == $this->_code);
+
+ $this->_notify('gotHeaders', $this->_headers);
+
+ // RFC 2616, section 4.4:
+ // 1. Any response message which "MUST NOT" include a message-body ...
+ // is always terminated by the first empty line after the header fields
+ // 3. ... If a message is received with both a
+ // Transfer-Encoding header field and a Content-Length header field,
+ // the latter MUST be ignored.
+ $canHaveBody = $canHaveBody && $this->_code >= 200 &&
+ $this->_code != 204 && $this->_code != 304;
+
+ // If response body is present, read it and decode
+ $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
+ $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
+ $hasBody = false;
+ if ($canHaveBody && ($chunked || !isset($this->_headers['content-length']) ||
+ 0 != $this->_headers['content-length']))
+ {
+ if ($chunked || !isset($this->_headers['content-length'])) {
+ $this->_toRead = null;
+ } else {
+ $this->_toRead = $this->_headers['content-length'];
+ }
+ while (!$this->_sock->eof() && (is_null($this->_toRead) || 0 < $this->_toRead)) {
+ if ($chunked) {
+ $data = $this->_readChunked();
+ } elseif (is_null($this->_toRead)) {
+ $data = $this->_sock->read(4096);
+ } else {
+ $data = $this->_sock->read(min(4096, $this->_toRead));
+ $this->_toRead -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
+ }
+ if ('' == $data) {
+ break;
+ } else {
+ $hasBody = true;
+ if ($saveBody || $gzipped) {
+ $this->_body .= $data;
+ }
+ $this->_notify($gzipped? 'gzTick': 'tick', $data);
+ }
+ }
+ }
+
+ if ($hasBody) {
+ // Uncompress the body if needed
+ if ($gzipped) {
+ $body = $this->_decodeGzip($this->_body);
+ if (PEAR::isError($body)) {
+ return $body;
+ }
+ $this->_body = $body;
+ $this->_notify('gotBody', $this->_body);
+ } else {
+ $this->_notify('gotBody');
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Processes the response header
+ *
+ * @access private
+ * @param string HTTP header
+ */
+ function _processHeader($header)
+ {
+ if (false === strpos($header, ':')) {
+ return;
+ }
+ list($headername, $headervalue) = explode(':', $header, 2);
+ $headername = strtolower($headername);
+ $headervalue = ltrim($headervalue);
+
+ if ('set-cookie' != $headername) {
+ if (isset($this->_headers[$headername])) {
+ $this->_headers[$headername] .= ',' . $headervalue;
+ } else {
+ $this->_headers[$headername] = $headervalue;
+ }
+ } else {
+ $this->_parseCookie($headervalue);
+ }
+ }
+
+
+ /**
+ * Parse a Set-Cookie header to fill $_cookies array
+ *
+ * @access private
+ * @param string value of Set-Cookie header
+ */
+ function _parseCookie($headervalue)
+ {
+ $cookie = array(
+ 'expires' => null,
+ 'domain' => null,
+ 'path' => null,
+ 'secure' => false
+ );
+
+ // Only a name=value pair
+ if (!strpos($headervalue, ';')) {
+ $pos = strpos($headervalue, '=');
+ $cookie['name'] = trim(substr($headervalue, 0, $pos));
+ $cookie['value'] = trim(substr($headervalue, $pos + 1));
+
+ // Some optional parameters are supplied
+ } else {
+ $elements = explode(';', $headervalue);
+ $pos = strpos($elements[0], '=');
+ $cookie['name'] = trim(substr($elements[0], 0, $pos));
+ $cookie['value'] = trim(substr($elements[0], $pos + 1));
+
+ for ($i = 1; $i < count($elements); $i++) {
+ if (false === strpos($elements[$i], '=')) {
+ $elName = trim($elements[$i]);
+ $elValue = null;
+ } else {
+ list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
+ }
+ $elName = strtolower($elName);
+ if ('secure' == $elName) {
+ $cookie['secure'] = true;
+ } elseif ('expires' == $elName) {
+ $cookie['expires'] = str_replace('"', '', $elValue);
+ } elseif ('path' == $elName || 'domain' == $elName) {
+ $cookie[$elName] = urldecode($elValue);
+ } else {
+ $cookie[$elName] = $elValue;
+ }
+ }
+ }
+ $this->_cookies[] = $cookie;
+ }
+
+
+ /**
+ * Read a part of response body encoded with chunked Transfer-Encoding
+ *
+ * @access private
+ * @return string
+ */
+ function _readChunked()
+ {
+ // at start of the next chunk?
+ if (0 == $this->_chunkLength) {
+ $line = $this->_sock->readLine();
+ if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
+ $this->_chunkLength = hexdec($matches[1]);
+ // Chunk with zero length indicates the end
+ if (0 == $this->_chunkLength) {
+ $this->_sock->readLine(); // make this an eof()
+ return '';
+ }
+ } else {
+ return '';
+ }
+ }
+ $data = $this->_sock->read($this->_chunkLength);
+ $this->_chunkLength -= HTTP_REQUEST_MBSTRING? mb_strlen($data, 'iso-8859-1'): strlen($data);
+ if (0 == $this->_chunkLength) {
+ $this->_sock->readLine(); // Trailing CRLF
+ }
+ return $data;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ * @see HTTP_Request::_notify()
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+
+
+ /**
+ * Decodes the message-body encoded by gzip
+ *
+ * The real decoding work is done by gzinflate() built-in function, this
+ * method only parses the header and checks data for compliance with
+ * RFC 1952
+ *
+ * @access private
+ * @param string gzip-encoded data
+ * @return string decoded data
+ */
+ function _decodeGzip($data)
+ {
+ if (HTTP_REQUEST_MBSTRING) {
+ $oldEncoding = mb_internal_encoding();
+ mb_internal_encoding('iso-8859-1');
+ }
+ $length = strlen($data);
+ // If it doesn't look like gzip-encoded data, don't bother
+ if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
+ return $data;
+ }
+ $method = ord(substr($data, 2, 1));
+ if (8 != $method) {
+ return PEAR::raiseError('_decodeGzip(): unknown compression method', HTTP_REQUEST_ERROR_GZIP_METHOD);
+ }
+ $flags = ord(substr($data, 3, 1));
+ if ($flags & 224) {
+ return PEAR::raiseError('_decodeGzip(): reserved bits are set', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+
+ // header is 10 bytes minimum. may be longer, though.
+ $headerLength = 10;
+ // extra fields, need to skip 'em
+ if ($flags & 4) {
+ if ($length - $headerLength - 2 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $extraLength = unpack('v', substr($data, 10, 2));
+ if ($length - $headerLength - 2 - $extraLength[1] < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $headerLength += $extraLength[1] + 2;
+ }
+ // file name, need to skip that
+ if ($flags & 8) {
+ if ($length - $headerLength - 1 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $filenameLength = strpos(substr($data, $headerLength), chr(0));
+ if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $headerLength += $filenameLength + 1;
+ }
+ // comment, need to skip that also
+ if ($flags & 16) {
+ if ($length - $headerLength - 1 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $commentLength = strpos(substr($data, $headerLength), chr(0));
+ if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $headerLength += $commentLength + 1;
+ }
+ // have a CRC for header. let's check
+ if ($flags & 1) {
+ if ($length - $headerLength - 2 < 8) {
+ return PEAR::raiseError('_decodeGzip(): data too short', HTTP_REQUEST_ERROR_GZIP_DATA);
+ }
+ $crcReal = 0xffff & crc32(substr($data, 0, $headerLength));
+ $crcStored = unpack('v', substr($data, $headerLength, 2));
+ if ($crcReal != $crcStored[1]) {
+ return PEAR::raiseError('_decodeGzip(): header CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
+ }
+ $headerLength += 2;
+ }
+ // unpacked data CRC and size at the end of encoded data
+ $tmp = unpack('V2', substr($data, -8));
+ $dataCrc = $tmp[1];
+ $dataSize = $tmp[2];
+
+ // finally, call the gzinflate() function
+ $unpacked = @gzinflate(substr($data, $headerLength, -8), $dataSize);
+ if (false === $unpacked) {
+ return PEAR::raiseError('_decodeGzip(): gzinflate() call failed', HTTP_REQUEST_ERROR_GZIP_READ);
+ } elseif ($dataSize != strlen($unpacked)) {
+ return PEAR::raiseError('_decodeGzip(): data size check failed', HTTP_REQUEST_ERROR_GZIP_READ);
+ } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
+ return PEAR::raiseError('_decodeGzip(): data CRC check failed', HTTP_REQUEST_ERROR_GZIP_CRC);
+ }
+ if (HTTP_REQUEST_MBSTRING) {
+ mb_internal_encoding($oldEncoding);
+ }
+ return $unpacked;
+ }
+} // End class HTTP_Response
+?>
diff --git a/lib/phpFlickr/PEAR/HTTP/Request/Listener.php b/lib/phpFlickr/PEAR/HTTP/Request/Listener.php
new file mode 100644
index 000000000..b00514206
--- /dev/null
+++ b/lib/phpFlickr/PEAR/HTTP/Request/Listener.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * Listener for HTTP_Request and HTTP_Response objects
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2002-2007, Richard Heyes
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * o Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * o Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * o The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Alexey Borzov <avb@php.net>
+ * @copyright 2002-2007 Richard Heyes
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version CVS: $Id: Listener.php 127 2008-01-17 20:21:37Z dcoulter $
+ * @link http://pear.php.net/package/HTTP_Request/
+ */
+
+/**
+ * Listener for HTTP_Request and HTTP_Response objects
+ *
+ * This class implements the Observer part of a Subject-Observer
+ * design pattern.
+ *
+ * @category HTTP
+ * @package HTTP_Request
+ * @author Alexey Borzov <avb@php.net>
+ * @version Release: 1.4.2
+ */
+class HTTP_Request_Listener
+{
+ /**
+ * A listener's identifier
+ * @var string
+ */
+ var $_id;
+
+ /**
+ * Constructor, sets the object's identifier
+ *
+ * @access public
+ */
+ function HTTP_Request_Listener()
+ {
+ $this->_id = md5(uniqid('http_request_', 1));
+ }
+
+
+ /**
+ * Returns the listener's identifier
+ *
+ * @access public
+ * @return string
+ */
+ function getId()
+ {
+ return $this->_id;
+ }
+
+
+ /**
+ * This method is called when Listener is notified of an event
+ *
+ * @access public
+ * @param object an object the listener is attached to
+ * @param string Event name
+ * @param mixed Additional data
+ * @abstract
+ */
+ function update(&$subject, $event, $data = null)
+ {
+ echo "Notified of event: '$event'\n";
+ if (null !== $data) {
+ echo "Additional data: ";
+ var_dump($data);
+ }
+ }
+}
+?>
diff --git a/lib/phpFlickr/PEAR/Net/Socket.php b/lib/phpFlickr/PEAR/Net/Socket.php
new file mode 100644
index 000000000..96f2c4bf1
--- /dev/null
+++ b/lib/phpFlickr/PEAR/Net/Socket.php
@@ -0,0 +1,528 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Chuck Hagenbuch <chuck@horde.org> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Socket.php 32 2005-08-01 06:21:02Z dancoulter $
+
+require_once 'PEAR.php';
+
+define('NET_SOCKET_READ', 1);
+define('NET_SOCKET_WRITE', 2);
+define('NET_SOCKET_ERROR', 3);
+
+/**
+ * Generalized Socket class.
+ *
+ * @version 1.1
+ * @author Stig Bakken <ssb@php.net>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+class Net_Socket extends PEAR {
+
+ /**
+ * Socket file pointer.
+ * @var resource $fp
+ */
+ var $fp = null;
+
+ /**
+ * Whether the socket is blocking. Defaults to true.
+ * @var boolean $blocking
+ */
+ var $blocking = true;
+
+ /**
+ * Whether the socket is persistent. Defaults to false.
+ * @var boolean $persistent
+ */
+ var $persistent = false;
+
+ /**
+ * The IP address to connect to.
+ * @var string $addr
+ */
+ var $addr = '';
+
+ /**
+ * The port number to connect to.
+ * @var integer $port
+ */
+ var $port = 0;
+
+ /**
+ * Number of seconds to wait on socket connections before assuming
+ * there's no more data. Defaults to no timeout.
+ * @var integer $timeout
+ */
+ var $timeout = false;
+
+ /**
+ * Number of bytes to read at a time in readLine() and
+ * readAll(). Defaults to 2048.
+ * @var integer $lineLength
+ */
+ var $lineLength = 2048;
+
+ /**
+ * Connect to the specified port. If called when the socket is
+ * already connected, it disconnects and connects again.
+ *
+ * @param string $addr IP address or host name.
+ * @param integer $port TCP port number.
+ * @param boolean $persistent (optional) Whether the connection is
+ * persistent (kept open between requests
+ * by the web server).
+ * @param integer $timeout (optional) How long to wait for data.
+ * @param array $options See options for stream_context_create.
+ *
+ * @access public
+ *
+ * @return boolean | PEAR_Error True on success or a PEAR_Error on failure.
+ */
+ function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)
+ {
+ if (is_resource($this->fp)) {
+ @fclose($this->fp);
+ $this->fp = null;
+ }
+
+ if (!$addr) {
+ return $this->raiseError('$addr cannot be empty');
+ } elseif (strspn($addr, '.0123456789') == strlen($addr) ||
+ strstr($addr, '/') !== false) {
+ $this->addr = $addr;
+ } else {
+ $this->addr = @gethostbyname($addr);
+ }
+
+ $this->port = $port % 65536;
+
+ if ($persistent !== null) {
+ $this->persistent = $persistent;
+ }
+
+ if ($timeout !== null) {
+ $this->timeout = $timeout;
+ }
+
+ $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
+ $errno = 0;
+ $errstr = '';
+ if ($options && function_exists('stream_context_create')) {
+ if ($this->timeout) {
+ $timeout = $this->timeout;
+ } else {
+ $timeout = 0;
+ }
+ $context = stream_context_create($options);
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
+ } else {
+ if ($this->timeout) {
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
+ } else {
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
+ }
+ }
+
+ if (!$fp) {
+ return $this->raiseError($errstr, $errno);
+ }
+
+ $this->fp = $fp;
+
+ return $this->setBlocking($this->blocking);
+ }
+
+ /**
+ * Disconnects from the peer, closes the socket.
+ *
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function disconnect()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ @fclose($this->fp);
+ $this->fp = null;
+ return true;
+ }
+
+ /**
+ * Find out if the socket is in blocking mode.
+ *
+ * @access public
+ * @return boolean The current blocking mode.
+ */
+ function isBlocking()
+ {
+ return $this->blocking;
+ }
+
+ /**
+ * Sets whether the socket connection should be blocking or
+ * not. A read call to a non-blocking socket will return immediately
+ * if there is no data available, whereas it will block until there
+ * is data for blocking sockets.
+ *
+ * @param boolean $mode True for blocking sockets, false for nonblocking.
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function setBlocking($mode)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $this->blocking = $mode;
+ socket_set_blocking($this->fp, $this->blocking);
+ return true;
+ }
+
+ /**
+ * Sets the timeout value on socket descriptor,
+ * expressed in the sum of seconds and microseconds
+ *
+ * @param integer $seconds Seconds.
+ * @param integer $microseconds Microseconds.
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function setTimeout($seconds, $microseconds)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return socket_set_timeout($this->fp, $seconds, $microseconds);
+ }
+
+ /**
+ * Returns information about an existing socket resource.
+ * Currently returns four entries in the result array:
+ *
+ * <p>
+ * timed_out (bool) - The socket timed out waiting for data<br>
+ * blocked (bool) - The socket was blocked<br>
+ * eof (bool) - Indicates EOF event<br>
+ * unread_bytes (int) - Number of bytes left in the socket buffer<br>
+ * </p>
+ *
+ * @access public
+ * @return mixed Array containing information about existing socket resource or an error object otherwise
+ */
+ function getStatus()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return socket_get_status($this->fp);
+ }
+
+ /**
+ * Get a specified line of data
+ *
+ * @access public
+ * @return $size bytes of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function gets($size)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return @fgets($this->fp, $size);
+ }
+
+ /**
+ * Read a specified amount of data. This is guaranteed to return,
+ * and has the added benefit of getting everything in one fread()
+ * chunk; if you know the size of the data you're getting
+ * beforehand, this is definitely the way to go.
+ *
+ * @param integer $size The number of bytes to read from the socket.
+ * @access public
+ * @return $size bytes of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function read($size)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return @fread($this->fp, $size);
+ }
+
+ /**
+ * Write a specified amount of data.
+ *
+ * @param string $data Data to write.
+ * @param integer $blocksize Amount of data to write at once.
+ * NULL means all at once.
+ *
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function write($data, $blocksize = null)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ if (is_null($blocksize) && !OS_WINDOWS) {
+ return fwrite($this->fp, $data);
+ } else {
+ if (is_null($blocksize)) {
+ $blocksize = 1024;
+ }
+
+ $pos = 0;
+ $size = strlen($data);
+ while ($pos < $size) {
+ $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
+ if ($written === false) {
+ return false;
+ }
+ $pos += $written;
+ }
+
+ return $pos;
+ }
+ }
+
+ /**
+ * Write a line of data to the socket, followed by a trailing "\r\n".
+ *
+ * @access public
+ * @return mixed fputs result, or an error
+ */
+ function writeLine($data)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return fwrite($this->fp, $data . "\r\n");
+ }
+
+ /**
+ * Tests for end-of-file on a socket descriptor.
+ *
+ * @access public
+ * @return bool
+ */
+ function eof()
+ {
+ return (is_resource($this->fp) && feof($this->fp));
+ }
+
+ /**
+ * Reads a byte of data
+ *
+ * @access public
+ * @return 1 byte of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readByte()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return ord(@fread($this->fp, 1));
+ }
+
+ /**
+ * Reads a word of data
+ *
+ * @access public
+ * @return 1 word of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readWord()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $buf = @fread($this->fp, 2);
+ return (ord($buf[0]) + (ord($buf[1]) << 8));
+ }
+
+ /**
+ * Reads an int of data
+ *
+ * @access public
+ * @return integer 1 int of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readInt()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $buf = @fread($this->fp, 4);
+ return (ord($buf[0]) + (ord($buf[1]) << 8) +
+ (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
+ }
+
+ /**
+ * Reads a zero-terminated string of data
+ *
+ * @access public
+ * @return string, or a PEAR_Error if
+ * not connected.
+ */
+ function readString()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $string = '';
+ while (($char = @fread($this->fp, 1)) != "\x00") {
+ $string .= $char;
+ }
+ return $string;
+ }
+
+ /**
+ * Reads an IP Address and returns it in a dot formated string
+ *
+ * @access public
+ * @return Dot formated string, or a PEAR_Error if
+ * not connected.
+ */
+ function readIPAddress()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $buf = @fread($this->fp, 4);
+ return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),
+ ord($buf[2]), ord($buf[3]));
+ }
+
+ /**
+ * Read until either the end of the socket or a newline, whichever
+ * comes first. Strips the trailing newline from the returned data.
+ *
+ * @access public
+ * @return All available data up to a newline, without that
+ * newline, or until the end of the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readLine()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $line = '';
+ $timeout = time() + $this->timeout;
+ while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
+ $line .= @fgets($this->fp, $this->lineLength);
+ if (substr($line, -1) == "\n") {
+ return rtrim($line, "\r\n");
+ }
+ }
+ return $line;
+ }
+
+ /**
+ * Read until the socket closes, or until there is no more data in
+ * the inner PHP buffer. If the inner buffer is empty, in blocking
+ * mode we wait for at least 1 byte of data. Therefore, in
+ * blocking mode, if there is no data at all to be read, this
+ * function will never exit (unless the socket is closed on the
+ * remote end).
+ *
+ * @access public
+ *
+ * @return string All data until the socket closes, or a PEAR_Error if
+ * not connected.
+ */
+ function readAll()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $data = '';
+ while (!feof($this->fp)) {
+ $data .= @fread($this->fp, $this->lineLength);
+ }
+ return $data;
+ }
+
+ /**
+ * Runs the equivalent of the select() system call on the socket
+ * with a timeout specified by tv_sec and tv_usec.
+ *
+ * @param integer $state Which of read/write/error to check for.
+ * @param integer $tv_sec Number of seconds for timeout.
+ * @param integer $tv_usec Number of microseconds for timeout.
+ *
+ * @access public
+ * @return False if select fails, integer describing which of read/write/error
+ * are ready, or PEAR_Error if not connected.
+ */
+ function select($state, $tv_sec, $tv_usec = 0)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $read = null;
+ $write = null;
+ $except = null;
+ if ($state & NET_SOCKET_READ) {
+ $read[] = $this->fp;
+ }
+ if ($state & NET_SOCKET_WRITE) {
+ $write[] = $this->fp;
+ }
+ if ($state & NET_SOCKET_ERROR) {
+ $except[] = $this->fp;
+ }
+ if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {
+ return false;
+ }
+
+ $result = 0;
+ if (count($read)) {
+ $result |= NET_SOCKET_READ;
+ }
+ if (count($write)) {
+ $result |= NET_SOCKET_WRITE;
+ }
+ if (count($except)) {
+ $result |= NET_SOCKET_ERROR;
+ }
+ return $result;
+ }
+
+}
diff --git a/lib/phpFlickr/PEAR/Net/URL.php b/lib/phpFlickr/PEAR/Net/URL.php
new file mode 100644
index 000000000..38e26fd15
--- /dev/null
+++ b/lib/phpFlickr/PEAR/Net/URL.php
@@ -0,0 +1,410 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2004, Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard at php net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: URL.php 32 2005-08-01 06:21:02Z dancoulter $
+//
+// Net_URL Class
+
+class Net_URL
+{
+ /**
+ * Full url
+ * @var string
+ */
+ var $url;
+
+ /**
+ * Protocol
+ * @var string
+ */
+ var $protocol;
+
+ /**
+ * Username
+ * @var string
+ */
+ var $username;
+
+ /**
+ * Password
+ * @var string
+ */
+ var $password;
+
+ /**
+ * Host
+ * @var string
+ */
+ var $host;
+
+ /**
+ * Port
+ * @var integer
+ */
+ var $port;
+
+ /**
+ * Path
+ * @var string
+ */
+ var $path;
+
+ /**
+ * Query string
+ * @var array
+ */
+ var $querystring;
+
+ /**
+ * Anchor
+ * @var string
+ */
+ var $anchor;
+
+ /**
+ * Whether to use []
+ * @var bool
+ */
+ var $useBrackets;
+
+ /**
+ * PHP4 Constructor
+ *
+ * @see __construct()
+ */
+ function Net_URL($url = null, $useBrackets = true)
+ {
+ $this->__construct($url, $useBrackets);
+ }
+
+ /**
+ * PHP5 Constructor
+ *
+ * Parses the given url and stores the various parts
+ * Defaults are used in certain cases
+ *
+ * @param string $url Optional URL
+ * @param bool $useBrackets Whether to use square brackets when
+ * multiple querystrings with the same name
+ * exist
+ */
+ function __construct($url = null, $useBrackets = true)
+ {
+ $HTTP_SERVER_VARS = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
+
+ $this->useBrackets = $useBrackets;
+ $this->url = $url;
+ $this->user = '';
+ $this->pass = '';
+ $this->host = '';
+ $this->port = 80;
+ $this->path = '';
+ $this->querystring = array();
+ $this->anchor = '';
+
+ // Only use defaults if not an absolute URL given
+ if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) {
+
+ $this->protocol = (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http');
+
+ /**
+ * Figure out host/port
+ */
+ if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) {
+ $host = $matches[1];
+ if (!empty($matches[3])) {
+ $port = $matches[3];
+ } else {
+ $port = $this->getStandardPort($this->protocol);
+ }
+ }
+
+ $this->user = '';
+ $this->pass = '';
+ $this->host = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
+ $this->port = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
+ $this->path = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
+ $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
+ $this->anchor = '';
+ }
+
+ // Parse the url and store the various parts
+ if (!empty($url)) {
+ $urlinfo = parse_url($url);
+
+ // Default querystring
+ $this->querystring = array();
+
+ foreach ($urlinfo as $key => $value) {
+ switch ($key) {
+ case 'scheme':
+ $this->protocol = $value;
+ $this->port = $this->getStandardPort($value);
+ break;
+
+ case 'user':
+ case 'pass':
+ case 'host':
+ case 'port':
+ $this->$key = $value;
+ break;
+
+ case 'path':
+ if ($value{0} == '/') {
+ $this->path = $value;
+ } else {
+ $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
+ $this->path = sprintf('%s/%s', $path, $value);
+ }
+ break;
+
+ case 'query':
+ $this->querystring = $this->_parseRawQueryString($value);
+ break;
+
+ case 'fragment':
+ $this->anchor = $value;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns full url
+ *
+ * @return string Full url
+ * @access public
+ */
+ function getURL()
+ {
+ $querystring = $this->getQueryString();
+
+ $this->url = $this->protocol . '://'
+ . $this->user . (!empty($this->pass) ? ':' : '')
+ . $this->pass . (!empty($this->user) ? '@' : '')
+ . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
+ . $this->path
+ . (!empty($querystring) ? '?' . $querystring : '')
+ . (!empty($this->anchor) ? '#' . $this->anchor : '');
+
+ return $this->url;
+ }
+
+ /**
+ * Adds a querystring item
+ *
+ * @param string $name Name of item
+ * @param string $value Value of item
+ * @param bool $preencoded Whether value is urlencoded or not, default = not
+ * @access public
+ */
+ function addQueryString($name, $value, $preencoded = false)
+ {
+ if ($preencoded) {
+ $this->querystring[$name] = $value;
+ } else {
+ $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
+ }
+ }
+
+ /**
+ * Removes a querystring item
+ *
+ * @param string $name Name of item
+ * @access public
+ */
+ function removeQueryString($name)
+ {
+ if (isset($this->querystring[$name])) {
+ unset($this->querystring[$name]);
+ }
+ }
+
+ /**
+ * Sets the querystring to literally what you supply
+ *
+ * @param string $querystring The querystring data. Should be of the format foo=bar&x=y etc
+ * @access public
+ */
+ function addRawQueryString($querystring)
+ {
+ $this->querystring = $this->_parseRawQueryString($querystring);
+ }
+
+ /**
+ * Returns flat querystring
+ *
+ * @return string Querystring
+ * @access public
+ */
+ function getQueryString()
+ {
+ if (!empty($this->querystring)) {
+ foreach ($this->querystring as $name => $value) {
+ if (is_array($value)) {
+ foreach ($value as $k => $v) {
+ $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
+ }
+ } elseif (!is_null($value)) {
+ $querystring[] = $name . '=' . $value;
+ } else {
+ $querystring[] = $name;
+ }
+ }
+ $querystring = implode(ini_get('arg_separator.output'), $querystring);
+ } else {
+ $querystring = '';
+ }
+
+ return $querystring;
+ }
+
+ /**
+ * Parses raw querystring and returns an array of it
+ *
+ * @param string $querystring The querystring to parse
+ * @return array An array of the querystring data
+ * @access private
+ */
+ function _parseRawQuerystring($querystring)
+ {
+ $parts = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
+ $return = array();
+
+ foreach ($parts as $part) {
+ if (strpos($part, '=') !== false) {
+ $value = substr($part, strpos($part, '=') + 1);
+ $key = substr($part, 0, strpos($part, '='));
+ } else {
+ $value = null;
+ $key = $part;
+ }
+ if (substr($key, -2) == '[]') {
+ $key = substr($key, 0, -2);
+ if (@!is_array($return[$key])) {
+ $return[$key] = array();
+ $return[$key][] = $value;
+ } else {
+ $return[$key][] = $value;
+ }
+ } elseif (!$this->useBrackets AND !empty($return[$key])) {
+ $return[$key] = (array)$return[$key];
+ $return[$key][] = $value;
+ } else {
+ $return[$key] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Resolves //, ../ and ./ from a path and returns
+ * the result. Eg:
+ *
+ * /foo/bar/../boo.php => /foo/boo.php
+ * /foo/bar/../../boo.php => /boo.php
+ * /foo/bar/.././/boo.php => /foo/boo.php
+ *
+ * This method can also be called statically.
+ *
+ * @param string $url URL path to resolve
+ * @return string The result
+ */
+ function resolvePath($path)
+ {
+ $path = explode('/', str_replace('//', '/', $path));
+
+ for ($i=0; $i<count($path); $i++) {
+ if ($path[$i] == '.') {
+ unset($path[$i]);
+ $path = array_values($path);
+ $i--;
+
+ } elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {
+ unset($path[$i]);
+ unset($path[$i-1]);
+ $path = array_values($path);
+ $i -= 2;
+
+ } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
+ unset($path[$i]);
+ $path = array_values($path);
+ $i--;
+
+ } else {
+ continue;
+ }
+ }
+
+ return implode('/', $path);
+ }
+
+ /**
+ * Returns the standard port number for a protocol
+ *
+ * @param string $scheme The protocol to lookup
+ * @return integer Port number or NULL if no scheme matches
+ *
+ * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
+ */
+ function getStandardPort($scheme)
+ {
+ switch (strtolower($scheme)) {
+ case 'http': return 80;
+ case 'https': return 443;
+ case 'ftp': return 21;
+ case 'imap': return 143;
+ case 'imaps': return 993;
+ case 'pop3': return 110;
+ case 'pop3s': return 995;
+ default: return null;
+ }
+ }
+
+ /**
+ * Forces the URL to a particular protocol
+ *
+ * @param string $protocol Protocol to force the URL to
+ * @param integer $port Optional port (standard port is used by default)
+ */
+ function setProtocol($protocol, $port = null)
+ {
+ $this->protocol = $protocol;
+ $this->port = is_null($port) ? $this->getStandardPort() : $port;
+ }
+
+}
+?>
diff --git a/lib/phpFlickr/PEAR/PEAR.php b/lib/phpFlickr/PEAR/PEAR.php
new file mode 100644
index 000000000..e442d8c7a
--- /dev/null
+++ b/lib/phpFlickr/PEAR/PEAR.php
@@ -0,0 +1,1108 @@
+<?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-2006 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: PEAR.php 127 2008-01-17 20:21:37Z dcoulter $
+ * @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.6.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 ($skipmsg) {
+ $a = &new $ec($code, $mode, $options, $userinfo);
+ return $a;
+ } 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 http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: 1.6.2
+ * @link http://pear.php.net/manual/en/core.pear.pear-error.php
+ * @see PEAR::raiseError(), PEAR::throwError()
+ * @since Class available since PHP 4.0.2
+ */
+class PEAR_Error
+{
+ // {{{ properties
+
+ var $error_message_prefix = '';
+ var $mode = PEAR_ERROR_RETURN;
+ var $level = E_USER_NOTICE;
+ var $code = -1;
+ var $message = '';
+ var $userinfo = '';
+ var $backtrace = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Error constructor
+ *
+ * @param string $message message
+ *
+ * @param int $code (optional) error code
+ *
+ * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
+ * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
+ *
+ * @param mixed $options (optional) error level, _OR_ in the case of
+ * PEAR_ERROR_CALLBACK, the callback function or object/method
+ * tuple.
+ *
+ * @param string $userinfo (optional) additional user/debug info
+ *
+ * @access public
+ *
+ */
+ function PEAR_Error($message = 'unknown error', $code = null,
+ $mode = null, $options = null, $userinfo = null)
+ {
+ if ($mode === null) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ $this->message = $message;
+ $this->code = $code;
+ $this->mode = $mode;
+ $this->userinfo = $userinfo;
+ if (!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
+ $this->backtrace = debug_backtrace();
+ if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
+ unset($this->backtrace[0]['object']);
+ }
+ }
+ if ($mode & PEAR_ERROR_CALLBACK) {
+ $this->level = E_USER_NOTICE;
+ $this->callback = $options;
+ } else {
+ if ($options === null) {
+ $options = E_USER_NOTICE;
+ }
+ $this->level = $options;
+ $this->callback = null;
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ } else {
+ $format = $options;
+ }
+ printf($format, $this->getMessage());
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ trigger_error($this->getMessage(), $this->level);
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $msg = $this->getMessage();
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ if (substr($msg, -1) != "\n") {
+ $msg .= "\n";
+ }
+ } else {
+ $format = $options;
+ }
+ die(sprintf($format, $msg));
+ }
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_callable($this->callback)) {
+ call_user_func($this->callback, $this);
+ }
+ }
+ if ($this->mode & PEAR_ERROR_EXCEPTION) {
+ trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
+ eval('$e = new Exception($this->message, $this->code);throw($e);');
+ }
+ }
+
+ // }}}
+ // {{{ getMode()
+
+ /**
+ * Get the error mode from an error object.
+ *
+ * @return int error mode
+ * @access public
+ */
+ function getMode() {
+ return $this->mode;
+ }
+
+ // }}}
+ // {{{ getCallback()
+
+ /**
+ * Get the callback function/method from an error object.
+ *
+ * @return mixed callback function or object/method array
+ * @access public
+ */
+ function getCallback() {
+ return $this->callback;
+ }
+
+ // }}}
+ // {{{ getMessage()
+
+
+ /**
+ * Get the error message from an error object.
+ *
+ * @return string full error message
+ * @access public
+ */
+ function getMessage()
+ {
+ return ($this->error_message_prefix . $this->message);
+ }
+
+
+ // }}}
+ // {{{ getCode()
+
+ /**
+ * Get error code from an error object
+ *
+ * @return int error code
+ * @access public
+ */
+ function getCode()
+ {
+ return $this->code;
+ }
+
+ // }}}
+ // {{{ getType()
+
+ /**
+ * Get the name of this error/exception.
+ *
+ * @return string error/exception name (type)
+ * @access public
+ */
+ function getType()
+ {
+ return get_class($this);
+ }
+
+ // }}}
+ // {{{ getUserInfo()
+
+ /**
+ * Get additional user-supplied information.
+ *
+ * @return string user-supplied information
+ * @access public
+ */
+ function getUserInfo()
+ {
+ return $this->userinfo;
+ }
+
+ // }}}
+ // {{{ getDebugInfo()
+
+ /**
+ * Get additional debug information supplied by the application.
+ *
+ * @return string debug information
+ * @access public
+ */
+ function getDebugInfo()
+ {
+ return $this->getUserInfo();
+ }
+
+ // }}}
+ // {{{ getBacktrace()
+
+ /**
+ * Get the call backtrace from where the error was generated.
+ * Supported with PHP 4.3.0 or newer.
+ *
+ * @param int $frame (optional) what frame to fetch
+ * @return array Backtrace, or NULL if not available.
+ * @access public
+ */
+ function getBacktrace($frame = null)
+ {
+ if (defined('PEAR_IGNORE_BACKTRACE')) {
+ return null;
+ }
+ if ($frame === null) {
+ return $this->backtrace;
+ }
+ return $this->backtrace[$frame];
+ }
+
+ // }}}
+ // {{{ addUserInfo()
+
+ function addUserInfo($info)
+ {
+ if (empty($this->userinfo)) {
+ $this->userinfo = $info;
+ } else {
+ $this->userinfo .= " ** $info";
+ }
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * Make a string representation of this object.
+ *
+ * @return string a string with an object summary
+ * @access public
+ */
+ function toString() {
+ $modes = array();
+ $levels = array(E_USER_NOTICE => 'notice',
+ E_USER_WARNING => 'warning',
+ E_USER_ERROR => 'error');
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_array($this->callback)) {
+ $callback = (is_object($this->callback[0]) ?
+ strtolower(get_class($this->callback[0])) :
+ $this->callback[0]) . '::' .
+ $this->callback[1];
+ } else {
+ $callback = $this->callback;
+ }
+ return sprintf('[%s: message="%s" code=%d mode=callback '.
+ 'callback=%s prefix="%s" info="%s"]',
+ strtolower(get_class($this)), $this->message, $this->code,
+ $callback, $this->error_message_prefix,
+ $this->userinfo);
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ $modes[] = 'print';
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ $modes[] = 'trigger';
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $modes[] = 'die';
+ }
+ if ($this->mode & PEAR_ERROR_RETURN) {
+ $modes[] = 'return';
+ }
+ return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
+ 'prefix="%s" info="%s"]',
+ strtolower(get_class($this)), $this->message, $this->code,
+ implode("|", $modes), $levels[$this->level],
+ $this->error_message_prefix,
+ $this->userinfo);
+ }
+
+ // }}}
+}
+
+/*
+ * Local Variables:
+ * mode: php
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+?>
diff --git a/lib/phpFlickr/README.txt b/lib/phpFlickr/README.txt
new file mode 100644
index 000000000..df0a56d98
--- /dev/null
+++ b/lib/phpFlickr/README.txt
@@ -0,0 +1,215 @@
+phpFlickr Class 2.3.1
+Written by Dan Coulter (dancoulter@users.sourceforge.net)
+Project Homepage: http://www.phpflickr.com/
+Google Code Project Page: http://code.google.com/p/phpflickr/
+Released under GNU Lesser General Public License (http://www.gnu.org/copyleft/lgpl.html)
+For more information about the class and upcoming tools and applications using it,
+visit http://www.phpflickr.com/ or http://code.google.com/p/phpflickr/
+
+If you are interested in hiring me for a project (involving phpFlickr
+or not), feel free to email me.
+
+Installation instructions:
+1. Copy the files from the installation package into a folder on your
+ server. They need to be readible by your web server. You can put
+ them into an include folder defined in your php.ini file, if you
+ like, though it's not required.
+
+2. All you have to do now is include the file in your PHP scripts and
+ create an instance. For example:
+ $f = new phpFlickr();
+
+ The constructor has three arguments:
+ A. $api_key - This is the API key given to you by flickr.com. This
+ argument is required and you can get an API Key at:
+ http://www.flickr.com/services/api/key.gne
+
+ B. $secret - The "secret" is optional because is not required to
+ make unauthenticated calls, but is absolutely required for the
+ new authentication API (see Authentication section below). You
+ will get one assigned alongside your api key.
+
+ C. $die_on_error - This takes a boolean value and determines
+ whether the class will die (aka cease operation) if the API
+ returns an error statement. It defaults to false. Every method
+ will return false if the API returns an error. You can access
+ error messages using the getErrorCode() and getErrorMsg()
+ methods.
+
+3. All of the API methods have been implemented in my class. You can
+ see a full list and documentation here:
+ http://www.flickr.com/services/api/
+ To call a method, remove the "flickr." part of the name and replace
+ any periods with underscores. For example, instead of
+ flickr.photos.search, you would call $f->photos_search() or instead
+ of flickr.photos.licenses.getInfo, you would call
+ $f->photos_licenses_getInfo() (yes, it is case sensitive).
+
+ All functions have their arguments implemented in the list order on
+ their documentation page (a link to which is included with each
+ method in the phpFlickr clasS). The only exceptions to this are
+ photos_search(), photos_getWithoutGeodata() and
+ photos_getWithoutGeodata() which have so many optional arguments
+ that it's easier for everyone if you just have to pass an
+ associative array of arguments. See the comment in the
+ photos_search() definition in phpFlickr.php for more information.
+
+
+
+Authentication:
+ As of this release of the phpFlickr class there is only one authentication method
+ available to the API. This method is somewhat complex, but is far more secure and
+ allows your users to feel a little safer authenticating to your application. You'll
+ no longer have to ask for their username and password.
+
+ Authentication API - http://www.flickr.com/services/api/auth.spec.html
+
+ I know how complicated this API looks at first glance, so I've tried to
+ make this as transparent to the coding process. I'll go through the steps
+ you'll need to use this. Both the auth.php and getToken.php file will
+ need your API Key and Secret entered before you can use them.
+
+ To have end users authenticate their accounts:
+ First, setup a callback script. I've included a callback script that
+ is pretty flexible. You'll find it in the package entitled "auth.php".
+ You'll need to go to flickr and point your api key to this file as the
+ callback script. Once you've done this, on any page that you want to
+ require the end user end user to authenticate their flickr account to
+ your app, just call the phpFlickr::auth() function with whatever
+ permission you need to use.
+ For example:
+ $f->auth("write");
+ The three permissions are "read", "write" and "delete". The function
+ defaults to "read", if you leave it blank.
+
+ Calling this function will send the user's browser to Flickr's page to
+ authenticate to your app. Once they have logged in, it will bounce
+ them back to your callback script which will redirect back to the
+ original page that you called the auth() function from after setting
+ a session variable to save their authentication token. If that session
+ variable exists, calling the auth() function will return the permissions
+ that the user granted your app on the Flickr page instead of redirecting
+ to an external page.
+
+ To authenticate the app to your account to show your private pictures (for example)
+ This method will allow you to have the app authenticate to one specific
+ account, no matter who views your website. This is useful to display
+ private photos or photosets (among other things).
+
+ *Note*: The method below is a little hard to understand, so I've setup a tool
+ to help you through this: http://www.phpflickr.com/tools/auth/.
+
+ First, you'll have to setup a callback script with Flickr. Once you've
+ done that, edit line 12 of the included getToken.php file to reflect
+ which permissions you'll need for the app. Then browse to the page.
+ Once you've authorized the app with Flickr, it'll send you back to that
+ page which will give you a token which will look something like this:
+ 1234-567890abcdef1234
+ Go to the file where you are creating an instance of phpFlickr (I suggest
+ an include file) and after you've created it set the token to use:
+ $f->setToken("<token string>");
+ This token never expires, so you don't have to worry about having to
+ login periodically.
+
+
+Using Caching:
+ Caching can be very important to a project. Just a few calls to the Flickr API
+ can take long enough to bore your average web user (depending on the calls you
+ are making). I've built in caching that will access either a database or files
+ in your filesystem. To enable caching, use the phpFlickr::enableCache() function.
+ This function requires at least two arguments. The first will be the type of
+ cache you're using (either "db" or "fs")
+
+ 1. If you're using database caching, you'll need to supply a PEAR::DB connection
+ string. For example:
+ $flickr->enableCache("db", "mysql://user:password@server/database");
+ The third (optional) argument is expiration of the cache in seconds (defaults
+ to 600). The fourth (optional) argument is the table where you want to store
+ the cache. This defaults to flickr_cache and will attempt to create the table
+ if it does not already exist.
+
+ 2. If you're using filesystem caching, you'll need to supply a folder where the
+ web server has write access. For example:
+ $flickr->enableCache("fs", "/var/www/phpFlickrCache");
+ The third (optional) argument is, the same as in the Database caching, an
+ expiration in seconds for the cache.
+ Note: filesystem caching will probably be slower than database caching. I
+ haven't done any tests of this, but if you get large amounts of calls, the
+ process of cleaning out old calls may get hard on your server.
+
+ You may not want to allow the world to view the files that are created during
+ caching. If you want to hide this information, either make sure that your
+ permissions are set correctly, or disable the webserver from displaying
+ *.cache files. In Apache, you can specify this in the configuration files
+ or in a .htaccess file with the following directives:
+
+ <FilesMatch "\.cache$">
+ Deny from all
+ </FilesMatch>
+
+ Alternatively, you can specify a directory that is outside of the web server's
+ document root.
+
+Uploading
+ Uploading is pretty simple. Aside from being authenticated (see Authentication
+ section) the very minimum that you'll have to pass is a path to an image file on
+ your php server. You can do either synchronous or asynchronous uploading as follows:
+ synchronous: sync_upload("photo.jpg");
+ asynchronous: async_upload("photo.jpg");
+
+ The basic difference is that synchronous uploading waits around until Flickr
+ processes the photo and returns a PhotoID. Asynchronous just uploads the
+ picture and gets a "ticketid" that you can use to check on the status of your
+ upload. Asynchronous is much faster, though the photoid won't be instantly
+ available for you. You can read more about asynchronous uploading here:
+ http://www.flickr.com/services/api/upload.async.html
+
+ Both of the functions take the same arguments which are:
+ Photo: The path of the file to upload.
+ Title: The title of the photo.
+ Description: A description of the photo. May contain some limited HTML.
+ Tags: A space-separated list of tags to apply to the photo.
+ is_public: Set to 0 for no, 1 for yes.
+ is_friend: Set to 0 for no, 1 for yes.
+ is_family: Set to 0 for no, 1 for yes.
+
+Replacing Photos
+ Flickr has released API support for uploading a replacement photo. To use this
+ new method, just use the "replace" function in phpFlickr. You'll be required
+ to pass the file name and Flickr's photo ID. You need to authenticate your script
+ with "write" permissions before you can replace a photo. The arguments are:
+ Photo: The path of the file to upload.
+ Photo ID: The numeric Flickr ID of the photo you want to replace.
+ Async (optional): Set to 0 for a synchronous call, 1 for asynchronous.
+ If you use the asynchronous call, it will return a ticketid instead
+ of photoid.
+
+Other Notes:
+ 1. Many of the methods have optional arguments. For these, I have implemented
+ them in the same order that the Flickr API documentation lists them. PHP
+ allows for optional arguments in function calls, but if you want to use the
+ third optional argument, you have to fill in the others to the left first.
+ You can use the "NULL" value (without quotes) in the place of an actual
+ argument. For example:
+ $f->groups_pools_getPhotos($group_id, NULL, NULL, 10);
+ This will get the first ten photos from a specific group's pool. If you look
+ at the documentation, you will see that there is another argument, "page". I've
+ left it off because it appears after "per_page".
+ 2. Some people will need to ues phpFlickr from behind a proxy server. I've
+ implemented a method that will allow you to use an HTTP proxy for all of your
+ traffic. Let's say that you have a proxy server on your local server running
+ at port 8181. This is the code you would use:
+ $f = new phpFlickr("[api key]");
+ $f->setProxy("localhost", "8181");
+ After that, all of your calls will be automatically made through your proxy server.
+
+
+That's it! Enjoy the class. Check out the project page (listed above) for updates
+and news. I plan to implement file uploads and functions to aggregate data from
+several different methods for easier use in a web application. Thanks for your
+interest in this project!
+
+ Please email me if you have any questions or problems. You'll find my email
+ at the top of this file.
+
+
diff --git a/lib/phpFlickr/auth.php b/lib/phpFlickr/auth.php
new file mode 100644
index 000000000..d272d49bf
--- /dev/null
+++ b/lib/phpFlickr/auth.php
@@ -0,0 +1,37 @@
+<?php
+ /* Last updated with phpFlickr 1.3.1
+ *
+ * Edit these variables to reflect the values you need. $default_redirect
+ * and $permissions are only important if you are linking here instead of
+ * using phpFlickr::auth() from another page or if you set the remember_uri
+ * argument to false.
+ */
+ $api_key = "26b2abba37182aca62fe0eb2c7782050";
+ $api_secret = "475e45cc580334da";
+ $default_redirect = "/";
+ $permissions = "read";
+ $path_to_phpFlickr_class = "./";
+
+ ob_start();
+ require_once($path_to_phpFlickr_class . "phpFlickr.php");
+ unset($_SESSION['phpFlickr_auth_token']);
+
+ if (!empty($_GET['extra'])) {
+ $redirect = $_GET['extra'];
+ }
+
+ $f = new phpFlickr($api_key, $api_secret);
+
+ if (empty($_GET['frob'])) {
+ $f->auth($permissions, false);
+ } else {
+ $f->auth_getToken($_GET['frob']);
+ }
+
+ if (empty($redirect)) {
+ header("Location: " . $default_redirect);
+ } else {
+ header("Location: " . $redirect);
+ }
+
+?> \ No newline at end of file
diff --git a/lib/phpFlickr/example.php b/lib/phpFlickr/example.php
new file mode 100644
index 000000000..8b409b425
--- /dev/null
+++ b/lib/phpFlickr/example.php
@@ -0,0 +1,30 @@
+<?php
+/* Last updated with phpFlickr 1.3.2
+ *
+ * This example file shows you how to call the 100 most recent public
+ * photos. It parses through them and prints out a link to each of them
+ * along with the owner's name.
+ *
+ * Most of the processing time in this file comes from the 100 calls to
+ * flickr.people.getInfo. Enabling caching will help a whole lot with
+ * this as there are many people who post multiple photos at once.
+ *
+ * Obviously, you'll want to replace the "<api key>" with one provided
+ * by Flickr: http://www.flickr.com/services/api/key.gne
+ */
+
+require_once("phpFlickr.php");
+$f = new phpFlickr("<api key>");
+
+$recent = $f->photos_getRecent();
+
+foreach ($recent['photo'] as $photo) {
+ $owner = $f->people_getInfo($photo['owner']);
+ echo "<a href='http://www.flickr.com/photos/" . $photo['owner'] . "/" . $photo['id'] . "/'>";
+ echo $photo['title'];
+ echo "</a> Owner: ";
+ echo "<a href='http://www.flickr.com/people/" . $photo['owner'] . "/'>";
+ echo $owner['username'];
+ echo "</a><br>";
+}
+?>
diff --git a/lib/phpFlickr/getToken.php b/lib/phpFlickr/getToken.php
new file mode 100644
index 000000000..55180ea29
--- /dev/null
+++ b/lib/phpFlickr/getToken.php
@@ -0,0 +1,19 @@
+<?php
+ /* Last updated with phpFlickr 1.4
+ *
+ * If you need your app to always login with the same user (to see your private
+ * photos or photosets, for example), you can use this file to login and get a
+ * token assigned so that you can hard code the token to be used. To use this
+ * use the phpFlickr::setToken() function whenever you create an instance of
+ * the class.
+ */
+
+ require_once("phpFlickr.php");
+ $f = new phpFlickr("26b2abba37182aca62fe0eb2c7782050", "475e45cc580334da");
+
+ //change this to the permissions you will need
+ $f->auth("read");
+
+ echo "Copy this token into your code: " . $_SESSION['phpFlickr_auth_token'];
+
+?> \ No newline at end of file
diff --git a/lib/phpFlickr/phpFlickr.php b/lib/phpFlickr/phpFlickr.php
new file mode 100644
index 000000000..8ef65c581
--- /dev/null
+++ b/lib/phpFlickr/phpFlickr.php
@@ -0,0 +1,1453 @@
+<?php
+/* phpFlickr Class 2.3.1
+ * Written by Dan Coulter (dan@dancoulter.com)
+ * Project Home Page: http://phpflickr.com/
+ * Released under GNU Lesser General Public License (http://www.gnu.org/copyleft/lgpl.html)
+ * For more information about the class and upcoming tools and toys using it,
+ * visit http://www.phpflickr.com/
+ *
+ * For installation instructions, open the README.txt file packaged with this
+ * class. If you don't have a copy, you can see it at:
+ * http://www.phpflickr.com/README.txt
+ *
+ * Please submit all problems or questions to the Help Forum on my Google Code project page:
+ * http://code.google.com/p/phpflickr/issues/list
+ *
+ */
+if (session_id() == "") {
+ @session_start();
+}
+
+// Decides which include path delimiter to use. Windows should be using a semi-colon
+// and everything else should be using a colon. If this isn't working on your system,
+// comment out this if statement and manually set the correct value into $path_delimiter.
+if (strpos(__FILE__, ':') !== false) {
+ $path_delimiter = ';';
+} else {
+ $path_delimiter = ':';
+}
+
+// This will add the packaged PEAR files into the include path for PHP, allowing you
+// to use them transparently. This will prefer officially installed PEAR files if you
+// have them. If you want to prefer the packaged files (there shouldn't be any reason
+// to), swap the two elements around the $path_delimiter variable. If you don't have
+// the PEAR packages installed, you can leave this like it is and move on.
+
+ini_set('include_path', ini_get('include_path') . $path_delimiter . dirname(__FILE__) . '/PEAR');
+
+// If you have problems including the default PEAR install (like if your open_basedir
+// setting doesn't allow you to include files outside of your web root), comment out
+// the line above and uncomment the next line:
+
+// ini_set('include_path', dirname(__FILE__) . '/PEAR' . $path_delimiter . ini_get('include_path'));
+
+class phpFlickr {
+ var $api_key;
+ var $secret;
+ var $REST = 'http://api.flickr.com/services/rest/';
+ var $Upload = 'http://api.flickr.com/services/upload/';
+ var $Replace = 'http://api.flickr.com/services/replace/';
+ var $req;
+ var $response;
+ var $parsed_response;
+ var $cache = false;
+ var $cache_db = null;
+ var $cache_table = null;
+ var $cache_dir = null;
+ var $cache_expire = null;
+ var $die_on_error;
+ var $error_code;
+ Var $error_msg;
+ var $token;
+ var $php_version;
+
+ /*
+ * When your database cache table hits this many rows, a cleanup
+ * will occur to get rid of all of the old rows and cleanup the
+ * garbage in the table. For most personal apps, 1000 rows should
+ * be more than enough. If your site gets hit by a lot of traffic
+ * or you have a lot of disk space to spare, bump this number up.
+ * You should try to set it high enough that the cleanup only
+ * happens every once in a while, so this will depend on the growth
+ * of your table.
+ */
+ var $max_cache_rows = 1000;
+
+ function phpFlickr ($api_key, $secret = NULL, $die_on_error = false) {
+ //The API Key must be set before any calls can be made. You can
+ //get your own at http://www.flickr.com/services/api/misc.api_keys.html
+ $this->api_key = $api_key;
+ $this->secret = $secret;
+ $this->die_on_error = $die_on_error;
+ $this->service = "flickr";
+
+ //Find the PHP version and store it for future reference
+ $this->php_version = explode("-", phpversion());
+ $this->php_version = explode(".", $this->php_version[0]);
+
+ //All calls to the API are done via the POST method using the PEAR::HTTP_Request package.
+ require_once 'HTTP/Request.php';
+ $this->req =& new HTTP_Request();
+ $this->req->setMethod(HTTP_REQUEST_METHOD_POST);
+ }
+
+ function enableCache ($type, $connection, $cache_expire = 600, $table = 'flickr_cache') {
+ // Turns on caching. $type must be either "db" (for database caching) or "fs" (for filesystem).
+ // When using db, $connection must be a PEAR::DB connection string. Example:
+ // "mysql://user:password@server/database"
+ // If the $table, doesn't exist, it will attempt to create it.
+ // When using file system, caching, the $connection is the folder that the web server has write
+ // access to. Use absolute paths for best results. Relative paths may have unexpected behavior
+ // when you include this. They'll usually work, you'll just want to test them.
+ if ($type == 'db') {
+ require_once 'DB.php';
+ $db =& DB::connect($connection);
+ if (PEAR::isError($db)) {
+ die($db->getMessage());
+ }
+
+ /*
+ * If high performance is crucial, you can easily comment
+ * out this query once you've created your database table.
+ */
+
+ $db->query("
+ CREATE TABLE IF NOT EXISTS `$table` (
+ `request` CHAR( 35 ) NOT NULL ,
+ `response` MEDIUMTEXT NOT NULL ,
+ `expiration` DATETIME NOT NULL ,
+ INDEX ( `request` )
+ ) TYPE = MYISAM");
+
+ if ($db->getOne("SELECT COUNT(*) FROM $table") > $this->max_cache_rows) {
+ $db->query("DELETE FROM $table WHERE expiration < DATE_SUB(NOW(), INTERVAL $cache_expire second)");
+ $db->query('OPTIMIZE TABLE ' . $this->cache_table);
+ }
+
+ $this->cache = 'db';
+ $this->cache_db = $db;
+ $this->cache_table = $table;
+ } elseif ($type == 'fs') {
+ $this->cache = 'fs';
+ $connection = realpath($connection);
+ $this->cache_dir = $connection;
+ if ($dir = opendir($this->cache_dir)) {
+ while ($file = readdir($dir)) {
+ if (substr($file, -6) == '.cache' && ((filemtime($this->cache_dir . '/' . $file) + $cache_expire) < time()) ) {
+ unlink($this->cache_dir . '/' . $file);
+ }
+ }
+ }
+ }
+ $this->cache_expire = $cache_expire;
+ }
+
+ function getCached ($request) {
+ //Checks the database or filesystem for a cached result to the request.
+ //If there is no cache result, it returns a value of false. If it finds one,
+ //it returns the unparsed XML.
+ $reqhash = md5(serialize($request));
+ if ($this->cache == 'db') {
+ $result = $this->cache_db->getOne("SELECT response FROM " . $this->cache_table . " WHERE request = ? AND DATE_SUB(NOW(), INTERVAL " . (int) $this->cache_expire . " SECOND) < expiration", $reqhash);
+ if (!empty($result)) {
+ return $result;
+ }
+ } elseif ($this->cache == 'fs') {
+ $file = $this->cache_dir . '/' . $reqhash . '.cache';
+ if (file_exists($file)) {
+ if ($this->php_version[0] > 4 || ($this->php_version[0] == 4 && $this->php_version[1] >= 3)) {
+ return file_get_contents($file);
+ } else {
+ return implode('', file($file));
+ }
+ }
+ }
+ return false;
+ }
+
+ function cache ($request, $response) {
+ //Caches the unparsed XML of a request.
+ $reqhash = md5(serialize($request));
+ if ($this->cache == 'db') {
+ //$this->cache_db->query("DELETE FROM $this->cache_table WHERE request = '$reqhash'");
+ if ($this->cache_db->getOne("SELECT COUNT(*) FROM {$this->cache_table} WHERE request = '$reqhash'")) {
+ $sql = "UPDATE " . $this->cache_table . " SET response = ?, expiration = ? WHERE request = ?";
+ $this->cache_db->query($sql, array($response, strftime("%Y-%m-%d %H:%M:%S"), $reqhash));
+ } else {
+ $sql = "INSERT INTO " . $this->cache_table . " (request, response, expiration) VALUES ('$reqhash', '" . str_replace("'", "''", $response) . "', '" . strftime("%Y-%m-%d %H:%M:%S") . "')";
+ $this->cache_db->query($sql);
+ }
+ } elseif ($this->cache == "fs") {
+ $file = $this->cache_dir . "/" . $reqhash . ".cache";
+ $fstream = fopen($file, "w");
+ $result = fwrite($fstream,$response);
+ fclose($fstream);
+ return $result;
+ }
+ return false;
+ }
+
+ function request ($command, $args = array(), $nocache = false) {
+ //Sends a request to Flickr's REST endpoint via POST.
+ $this->req->setURL($this->REST);
+ $this->req->clearPostData();
+ if (substr($command,0,7) != "flickr.") {
+ $command = "flickr." . $command;
+ }
+
+ //Process arguments, including method and login data.
+ $args = array_merge(array("method" => $command, "format" => "php_serial", "api_key" => $this->api_key), $args);
+ if (!empty($this->token)) {
+ $args = array_merge($args, array("auth_token" => $this->token));
+ } elseif (!empty($_SESSION['phpFlickr_auth_token'])) {
+ $args = array_merge($args, array("auth_token" => $_SESSION['phpFlickr_auth_token']));
+ }
+ ksort($args);
+ $auth_sig = "";
+ if (!($this->response = $this->getCached($args)) || $nocache) {
+ foreach ($args as $key => $data) {
+ $auth_sig .= $key . $data;
+ $this->req->addPostData($key, $data);
+ }
+ if (!empty($this->secret)) {
+ $api_sig = md5($this->secret . $auth_sig);
+ $this->req->addPostData("api_sig", $api_sig);
+ }
+
+ $this->req->addHeader("Connection", "Keep-Alive");
+
+ //Send Requests
+ if ($this->req->sendRequest()) {
+ $this->response = $this->req->getResponseBody();
+ $this->cache($args, $this->response);
+ } else {
+ die("There has been a problem sending your command to the server.");
+ }
+ }
+ /*
+ * Uncomment this line (and comment out the next one) if you're doing large queries
+ * and you're concerned about time. This will, however, change the structure of
+ * the result, so be sure that you look at the results.
+ */
+ //$this->parsed_response = unserialize($this->response);
+ $this->parsed_response = $this->clean_text_nodes(unserialize($this->response));
+ if ($this->parsed_response['stat'] == 'fail') {
+ if ($this->die_on_error) die("The Flickr API returned the following error: #{$this->parsed_response['code']} - {$this->parsed_response['message']}");
+ else {
+ $this->error_code = $this->parsed_response['code'];
+ $this->error_msg = $this->parsed_response['message'];
+ $this->parsed_response = false;
+ }
+ } else {
+ $this->error_code = false;
+ $this->error_msg = false;
+ }
+ return $this->response;
+ }
+
+ function clean_text_nodes ($arr) {
+ if (!is_array($arr)) {
+ return $arr;
+ } elseif (count($arr) == 0) {
+ return $arr;
+ } elseif (count($arr) == 1 && array_key_exists('_content', $arr)) {
+ return $arr['_content'];
+ } else {
+ foreach ($arr as $key => $element) {
+ $arr[$key] = $this->clean_text_nodes($element);
+ }
+ return($arr);
+ }
+ }
+
+ function setToken ($token) {
+ // Sets an authentication token to use instead of the session variable
+ $this->token = $token;
+ }
+
+ function setProxy ($server, $port) {
+ // Sets the proxy for all phpFlickr calls.
+ $this->req->setProxy($server, $port);
+ }
+
+ function getErrorCode () {
+ // Returns the error code of the last call. If the last call did not
+ // return an error. This will return a false boolean.
+ return $this->error_code;
+ }
+
+ function getErrorMsg () {
+ // Returns the error message of the last call. If the last call did not
+ // return an error. This will return a false boolean.
+ return $this->error_msg;
+ }
+
+ /* These functions are front ends for the flickr calls */
+
+ function buildPhotoURL ($photo, $size = "Medium") {
+ //receives an array (can use the individual photo data returned
+ //from an API call) and returns a URL (doesn't mean that the
+ //file size exists)
+ $sizes = array(
+ "square" => "_s",
+ "thumbnail" => "_t",
+ "small" => "_m",
+ "medium" => "",
+ "large" => "_b",
+ "original" => "_o"
+ );
+
+ $size = strtolower($size);
+ if (!array_key_exists($size, $sizes)) {
+ $size = "medium";
+ }
+
+ if ($size == "original") {
+ $url = "http://farm" . $photo['farm'] . ".static.flickr.com/" . $photo['server'] . "/" . $photo['id'] . "_" . $photo['originalsecret'] . "_o" . "." . $photo['originalformat'];
+ } else {
+ $url = "http://farm" . $photo['farm'] . ".static.flickr.com/" . $photo['server'] . "/" . $photo['id'] . "_" . $photo['secret'] . $sizes[$size] . ".jpg";
+ }
+ return $url;
+ }
+
+ function getFriendlyGeodata ($lat, $lon) {
+ /* I've added this method to get the friendly geodata (i.e. 'in New York, NY') that the
+ * website provides, but isn't available in the API. I'm providing this service as long
+ * as it doesn't flood my server with requests and crash it all the time.
+ */
+ return unserialize(file_get_contents('http://phpflickr.com/geodata/?format=php&lat=' . $lat . '&lon=' . $lon));
+ }
+
+ function sync_upload ($photo, $title = null, $description = null, $tags = null, $is_public = null, $is_friend = null, $is_family = null) {
+ $upload_req =& new HTTP_Request();
+ $upload_req->setMethod(HTTP_REQUEST_METHOD_POST);
+
+
+ $upload_req->setURL($this->Upload);
+ $upload_req->clearPostData();
+
+ //Process arguments, including method and login data.
+ $args = array("api_key" => $this->api_key, "title" => $title, "description" => $description, "tags" => $tags, "is_public" => $is_public, "is_friend" => $is_friend, "is_family" => $is_family);
+ if (!empty($this->email)) {
+ $args = array_merge($args, array("email" => $this->email));
+ }
+ if (!empty($this->password)) {
+ $args = array_merge($args, array("password" => $this->password));
+ }
+ if (!empty($this->token)) {
+ $args = array_merge($args, array("auth_token" => $this->token));
+ } elseif (!empty($_SESSION['phpFlickr_auth_token'])) {
+ $args = array_merge($args, array("auth_token" => $_SESSION['phpFlickr_auth_token']));
+ }
+
+ ksort($args);
+ $auth_sig = "";
+ foreach ($args as $key => $data) {
+ if ($data !== null) {
+ $auth_sig .= $key . $data;
+ $upload_req->addPostData($key, $data);
+ }
+ }
+ if (!empty($this->secret)) {
+ $api_sig = md5($this->secret . $auth_sig);
+ $upload_req->addPostData("api_sig", $api_sig);
+ }
+
+ $photo = realpath($photo);
+
+ $result = $upload_req->addFile("photo", $photo);
+
+ if (PEAR::isError($result)) {
+ die($result->getMessage());
+ }
+
+ //Send Requests
+ if ($upload_req->sendRequest()) {
+ $this->response = $upload_req->getResponseBody();
+ } else {
+ die("There has been a problem sending your command to the server.");
+ }
+
+ $rsp = explode("\n", $this->response);
+ foreach ($rsp as $line) {
+ if (ereg('<err code="([0-9]+)" msg="(.*)"', $line, $match)) {
+ if ($this->die_on_error)
+ die("The Flickr API returned the following error: #{$match[1]} - {$match[2]}");
+ else {
+ $this->error_code = $match[1];
+ $this->error_msg = $match[2];
+ $this->parsed_response = false;
+ return false;
+ }
+ } elseif (ereg("<photoid>(.*)</photoid>", $line, $match)) {
+ $this->error_code = false;
+ $this->error_msg = false;
+ return $match[1];
+ }
+ }
+ }
+
+ function async_upload ($photo, $title = null, $description = null, $tags = null, $is_public = null, $is_friend = null, $is_family = null) {
+ $upload_req =& new HTTP_Request();
+ $upload_req->setMethod(HTTP_REQUEST_METHOD_POST);
+
+ $upload_req->setURL($this->Upload);
+ $upload_req->clearPostData();
+
+ //Process arguments, including method and login data.
+ $args = array("async" => 1, "api_key" => $this->api_key, "title" => $title, "description" => $description, "tags" => $tags, "is_public" => $is_public, "is_friend" => $is_friend, "is_family" => $is_family);
+ if (!empty($this->email)) {
+ $args = array_merge($args, array("email" => $this->email));
+ }
+ if (!empty($this->password)) {
+ $args = array_merge($args, array("password" => $this->password));
+ }
+ if (!empty($this->token)) {
+ $args = array_merge($args, array("auth_token" => $this->token));
+ } elseif (!empty($_SESSION['phpFlickr_auth_token'])) {
+ $args = array_merge($args, array("auth_token" => $_SESSION['phpFlickr_auth_token']));
+ }
+
+ ksort($args);
+ $auth_sig = "";
+ foreach ($args as $key => $data) {
+ if ($data !== null) {
+ $auth_sig .= $key . $data;
+ $upload_req->addPostData($key, $data);
+ }
+ }
+ if (!empty($this->secret)) {
+ $api_sig = md5($this->secret . $auth_sig);
+ $upload_req->addPostData("api_sig", $api_sig);
+ }
+
+ $photo = realpath($photo);
+
+ $result = $upload_req->addFile("photo", $photo);
+
+ if (PEAR::isError($result)) {
+ die($result->getMessage());
+ }
+
+ //Send Requests
+ if ($upload_req->sendRequest()) {
+ $this->response = $upload_req->getResponseBody();
+ } else {
+ die("There has been a problem sending your command to the server.");
+ }
+
+ $rsp = explode("\n", $this->response);
+ foreach ($rsp as $line) {
+ if (ereg('<err code="([0-9]+)" msg="(.*)"', $line, $match)) {
+ if ($this->die_on_error)
+ die("The Flickr API returned the following error: #{$match[1]} - {$match[2]}");
+ else {
+ $this->error_code = $match[1];
+ $this->error_msg = $match[2];
+ $this->parsed_response = false;
+ return false;
+ }
+ } elseif (ereg("<ticketid>(.*)</", $line, $match)) {
+ $this->error_code = false;
+ $this->error_msg = false;
+ return $match[1];
+ }
+ }
+ }
+
+ // Interface for new replace API method.
+ function replace ($photo, $photo_id, $async = null) {
+ $upload_req =& new HTTP_Request();
+ $upload_req->setMethod(HTTP_REQUEST_METHOD_POST);
+
+ $upload_req->setURL($this->Replace);
+ $upload_req->clearPostData();
+
+ //Process arguments, including method and login data.
+ $args = array("api_key" => $this->api_key, "photo_id" => $photo_id, "async" => $async);
+ if (!empty($this->email)) {
+ $args = array_merge($args, array("email" => $this->email));
+ }
+ if (!empty($this->password)) {
+ $args = array_merge($args, array("password" => $this->password));
+ }
+ if (!empty($this->token)) {
+ $args = array_merge($args, array("auth_token" => $this->token));
+ } elseif (!empty($_SESSION['phpFlickr_auth_token'])) {
+ $args = array_merge($args, array("auth_token" => $_SESSION['phpFlickr_auth_token']));
+ }
+
+ ksort($args);
+ $auth_sig = "";
+ foreach ($args as $key => $data) {
+ if ($data !== null) {
+ $auth_sig .= $key . $data;
+ $upload_req->addPostData($key, $data);
+ }
+ }
+ if (!empty($this->secret)) {
+ $api_sig = md5($this->secret . $auth_sig);
+ $upload_req->addPostData("api_sig", $api_sig);
+ }
+
+ $photo = realpath($photo);
+
+ $result = $upload_req->addFile("photo", $photo);
+
+ if (PEAR::isError($result)) {
+ die($result->getMessage());
+ }
+
+ //Send Requests
+ if ($upload_req->sendRequest()) {
+ $this->response = $upload_req->getResponseBody();
+ } else {
+ die("There has been a problem sending your command to the server.");
+ }
+ if ($async == 1)
+ $find = 'ticketid';
+ else
+ $find = 'photoid';
+
+ $rsp = explode("\n", $this->response);
+ foreach ($rsp as $line) {
+ if (ereg('<err code="([0-9]+)" msg="(.*)"', $line, $match)) {
+ if ($this->die_on_error)
+ die("The Flickr API returned the following error: #{$match[1]} - {$match[2]}");
+ else {
+ $this->error_code = $match[1];
+ $this->error_msg = $match[2];
+ $this->parsed_response = false;
+ return false;
+ }
+ } elseif (ereg("<" . $find . ">(.*)</", $line, $match)) {
+ $this->error_code = false;
+ $this->error_msg = false;
+ return $match[1];
+ }
+ }
+ }
+
+ function auth ($perms = "read", $remember_uri = true) {
+ // Redirects to Flickr's authentication piece if there is no valid token.
+ // If remember_uri is set to false, the callback script (included) will
+ // redirect to its default page.
+
+ if (empty($_SESSION['phpFlickr_auth_token']) && empty($this->token)) {
+ if ($remember_uri) {
+ $redirect = $_SERVER['REQUEST_URI'];
+ }
+ $api_sig = md5($this->secret . "api_key" . $this->api_key . "extra" . $redirect . "perms" . $perms);
+ if ($this->service == "23") {
+ header("Location: http://www.23hq.com/services/auth/?api_key=" . $this->api_key . "&extra=" . $redirect . "&perms=" . $perms . "&api_sig=". $api_sig);
+ } else {
+ header("Location: http://www.flickr.com/services/auth/?api_key=" . $this->api_key . "&extra=" . $redirect . "&perms=" . $perms . "&api_sig=". $api_sig);
+ }
+ exit;
+ } else {
+ $tmp = $this->die_on_error;
+ $this->die_on_error = false;
+ $rsp = $this->auth_checkToken();
+ if ($this->error_code !== false) {
+ unset($_SESSION['phpFlickr_auth_token']);
+ $this->auth($perms, $remember_uri);
+ }
+ $this->die_on_error = $tmp;
+ return $rsp['perms'];
+ }
+ }
+
+ /*******************************
+
+ To use the phpFlickr::call method, pass a string containing the API method you want
+ to use and an associative array of arguments. For example:
+ $result = $f->call("flickr.photos.comments.getList", array("photo_id"=>'34952612'));
+ This method will allow you to make calls to arbitrary methods that haven't been
+ implemented in phpFlickr yet.
+
+ *******************************/
+
+ function call ($method, $arguments) {
+ foreach ( $arguments as $key => $value ) {
+ if ( is_null($value) ) unset($arguments[$key]);
+ }
+ $this->request($method, $arguments);
+ return $this->parsed_response ? $this->parsed_response : false;
+ }
+
+ /*
+ These functions are the direct implementations of flickr calls.
+ For method documentation, including arguments, visit the address
+ included in a comment in the function.
+ */
+
+ /* Activity methods */
+ function activity_userComments ($per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.activity.userComments.html */
+ $this->request('flickr.activity.userComments', array("per_page" => $per_page, "page" => $page));
+ return $this->parsed_response ? $this->parsed_response['items']['item'] : false;
+ }
+
+ function activity_userPhotos ($timeframe = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.activity.userPhotos.html */
+ $this->request('flickr.activity.userPhotos', array("timeframe" => $timeframe, "per_page" => $per_page, "page" => $page));
+ return $this->parsed_response ? $this->parsed_response['items']['item'] : false;
+ }
+
+ /* Authentication methods */
+ function auth_checkToken () {
+ /* http://www.flickr.com/services/api/flickr.auth.checkToken.html */
+ $this->request('flickr.auth.checkToken');
+ return $this->parsed_response ? $this->parsed_response['auth'] : false;
+ }
+
+ function auth_getFrob () {
+ /* http://www.flickr.com/services/api/flickr.auth.getFrob.html */
+ $this->request('flickr.auth.getFrob');
+ return $this->parsed_response ? $this->parsed_response['frob'] : false;
+ }
+
+ function auth_getFullToken ($mini_token) {
+ /* http://www.flickr.com/services/api/flickr.auth.getFullToken.html */
+ $this->request('flickr.auth.getFullToken', array('mini_token'=>$mini_token));
+ return $this->parsed_response ? $this->parsed_response['auth'] : false;
+ }
+
+ function auth_getToken ($frob) {
+ /* http://www.flickr.com/services/api/flickr.auth.getToken.html */
+ $this->request('flickr.auth.getToken', array('frob'=>$frob));
+ session_register('phpFlickr_auth_token');
+ $_SESSION['phpFlickr_auth_token'] = $this->parsed_response['auth']['token'];
+ return $this->parsed_response ? $this->parsed_response['auth'] : false;
+ }
+
+ /* Blogs methods */
+ function blogs_getList ($service = NULL) {
+ /* http://www.flickr.com/services/api/flickr.blogs.getList.html */
+ $rsp = $this->call('flickr.blogs.getList', array('service' => $service));
+ return $rsp['blogs']['blog'];
+ }
+
+ function blogs_getServices () {
+ /* http://www.flickr.com/services/api/flickr.blogs.getServices.html */
+ return $this->call('flickr.blogs.getServices', array());
+ }
+
+ function blogs_postPhoto ($blog_id = NULL, $photo_id, $title, $description, $blog_password = NULL, $service = NULL) {
+ /* http://www.flickr.com/services/api/flickr.blogs.postPhoto.html */
+ return $this->call('flickr.blogs.postPhoto', array('blog_id' => $blog_id, 'photo_id' => $photo_id, 'title' => $title, 'description' => $description, 'blog_password' => $blog_password, 'service' => $service));
+ }
+
+ /* Collections Methods */
+ function collections_getInfo ($collection_id) {
+ /* http://www.flickr.com/services/api/flickr.collections.getInfo.html */
+ return $this->call('flickr.collections.getInfo', array('collection_id' => $collection_id));
+ }
+
+ function collections_getTree ($collection_id = NULL, $user_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.collections.getTree.html */
+ return $this->call('flickr.collections.getTree', array('collection_id' => $collection_id, 'user_id' => $user_id));
+ }
+
+ /* Commons Methods */
+ function commons_getInstitutions () {
+ /* http://www.flickr.com/services/api/flickr.commons.getInstitutions.html */
+ return $this->call('flickr.commons.getInstitutions', array());
+ }
+
+ /* Contacts Methods */
+ function contacts_getList ($filter = NULL, $page = NULL, $per_page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.contacts.getList.html */
+ $this->request('flickr.contacts.getList', array('filter'=>$filter, 'page'=>$page, 'per_page'=>$per_page));
+ return $this->parsed_response ? $this->parsed_response['contacts'] : false;
+ }
+
+ function contacts_getPublicList ($user_id, $page = NULL, $per_page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.contacts.getPublicList.html */
+ $this->request('flickr.contacts.getPublicList', array('user_id'=>$user_id, 'page'=>$page, 'per_page'=>$per_page));
+ return $this->parsed_response ? $this->parsed_response['contacts'] : false;
+ }
+
+ function contacts_getListRecentlyUploaded ($date_lastupload = NULL, $filter = NULL) {
+ /* http://www.flickr.com/services/api/flickr.contacts.getListRecentlyUploaded.html */
+ return $this->call('flickr.contacts.getListRecentlyUploaded', array('date_lastupload' => $date_lastupload, 'filter' => $filter));
+ }
+
+ /* Favorites Methods */
+ function favorites_add ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.favorites.add.html */
+ $this->request('flickr.favorites.add', array('photo_id'=>$photo_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function favorites_getList ($user_id = NULL, $min_fave_date = NULL, $max_fave_date = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.favorites.getList.html */
+ return $this->call('flickr.favorites.getList', array('user_id' => $user_id, 'min_fave_date' => $min_fave_date, 'max_fave_date' => $max_fave_date, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function favorites_getPublicList ($user_id, $min_fave_date = NULL, $max_fave_date = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.favorites.getPublicList.html */
+ return $this->call('flickr.favorites.getPublicList', array('user_id' => $user_id, 'min_fave_date' => $min_fave_date, 'max_fave_date' => $max_fave_date, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function favorites_remove ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.favorites.remove.html */
+ $this->request("flickr.favorites.remove", array("photo_id"=>$photo_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ /* Groups Methods */
+ function groups_browse ($cat_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.groups.browse.html */
+ $this->request("flickr.groups.browse", array("cat_id"=>$cat_id));
+ return $this->parsed_response ? $this->parsed_response['category'] : false;
+ }
+
+ function groups_getInfo ($group_id, $lang = NULL) {
+ /* http://www.flickr.com/services/api/flickr.groups.getInfo.html */
+ return $this->call('flickr.groups.getInfo', array('group_id' => $group_id, 'lang' => $lang));
+ }
+
+ function groups_search ($text, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.groups.search.html */
+ $this->request("flickr.groups.search", array("text"=>$text,"per_page"=>$per_page,"page"=>$page));
+ return $this->parsed_response ? $this->parsed_response['groups'] : false;
+ }
+
+ /* Groups Members Methods */
+ function groups_members_getList ($group_id, $membertypes = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.groups.members.getList.html */
+ return $this->call('flickr.groups.members.getList', array('group_id' => $group_id, 'membertypes' => $membertypes, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ /* Groups Pools Methods */
+ function groups_pools_add ($photo_id, $group_id) {
+ /* http://www.flickr.com/services/api/flickr.groups.pools.add.html */
+ $this->request("flickr.groups.pools.add", array("photo_id"=>$photo_id, "group_id"=>$group_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function groups_pools_getContext ($photo_id, $group_id) {
+ /* http://www.flickr.com/services/api/flickr.groups.pools.getContext.html */
+ $this->request("flickr.groups.pools.getContext", array("photo_id"=>$photo_id, "group_id"=>$group_id));
+ return $this->parsed_response ? $this->parsed_response : false;
+ }
+
+ function groups_pools_getGroups ($page = NULL, $per_page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.groups.pools.getGroups.html */
+ $this->request("flickr.groups.pools.getGroups", array('page'=>$page, 'per_page'=>$per_page));
+ return $this->parsed_response ? $this->parsed_response['groups'] : false;
+ }
+
+ function groups_pools_getPhotos ($group_id, $tags = NULL, $user_id = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.groups.pools.getPhotos.html */
+ if (is_array($extras)) {
+ $extras = implode(",", $extras);
+ }
+ $this->request("flickr.groups.pools.getPhotos", array("group_id"=>$group_id, "tags"=>$tags, "user_id"=>$user_id, "extras"=>$extras, "per_page"=>$per_page, "page"=>$page));
+ return $this->parsed_response ? $this->parsed_response['photos'] : false;
+ }
+
+ function groups_pools_remove ($photo_id, $group_id) {
+ /* http://www.flickr.com/services/api/flickr.groups.pools.remove.html */
+ $this->request("flickr.groups.pools.remove", array("photo_id"=>$photo_id, "group_id"=>$group_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ /* Interestingness methods */
+ function interestingness_getList ($date = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.interestingness.getList.html */
+ if (is_array($extras)) {
+ $extras = implode(",", $extras);
+ }
+
+ $this->request("flickr.interestingness.getList", array("date"=>$date, "extras"=>$extras, "per_page"=>$per_page, "page"=>$page));
+ return $this->parsed_response ? $this->parsed_response['photos'] : false;
+ }
+
+ /* Machine Tag methods */
+ function machinetags_getNamespaces ($predicate = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.machinetags.getNamespaces.html */
+ return $this->call('flickr.machinetags.getNamespaces', array('predicate' => $predicate, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function machinetags_getPairs ($namespace = NULL, $predicate = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.machinetags.getPairs.html */
+ return $this->call('flickr.machinetags.getPairs', array('namespace' => $namespace, 'predicate' => $predicate, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function machinetags_getPredicates ($namespace = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.machinetags.getPredicates.html */
+ return $this->call('flickr.machinetags.getPredicates', array('namespace' => $namespace, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function machinetags_getRecentValues ($namespace = NULL, $predicate = NULL, $added_since = NULL) {
+ /* http://www.flickr.com/services/api/flickr.machinetags.getRecentValues.html */
+ return $this->call('flickr.machinetags.getRecentValues', array('namespace' => $namespace, 'predicate' => $predicate, 'added_since' => $added_since));
+ }
+
+ function machinetags_getValues ($namespace, $predicate, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.machinetags.getValues.html */
+ return $this->call('flickr.machinetags.getValues', array('namespace' => $namespace, 'predicate' => $predicate, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ /* Panda methods */
+ function panda_getList () {
+ /* http://www.flickr.com/services/api/flickr.panda.getList.html */
+ return $this->call('flickr.panda.getList', array());
+ }
+
+ function panda_getPhotos ($panda_name, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.panda.getPhotos.html */
+ return $this->call('flickr.panda.getPhotos', array('panda_name' => $panda_name, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ /* People methods */
+ function people_findByEmail ($find_email) {
+ /* http://www.flickr.com/services/api/flickr.people.findByEmail.html */
+ $this->request("flickr.people.findByEmail", array("find_email"=>$find_email));
+ return $this->parsed_response ? $this->parsed_response['user'] : false;
+ }
+
+ function people_findByUsername ($username) {
+ /* http://www.flickr.com/services/api/flickr.people.findByUsername.html */
+ $this->request("flickr.people.findByUsername", array("username"=>$username));
+ return $this->parsed_response ? $this->parsed_response['user'] : false;
+ }
+
+ function people_getInfo ($user_id) {
+ /* http://www.flickr.com/services/api/flickr.people.getInfo.html */
+ $this->request("flickr.people.getInfo", array("user_id"=>$user_id));
+ return $this->parsed_response ? $this->parsed_response['person'] : false;
+ }
+
+ function people_getPublicGroups ($user_id) {
+ /* http://www.flickr.com/services/api/flickr.people.getPublicGroups.html */
+ $this->request("flickr.people.getPublicGroups", array("user_id"=>$user_id));
+ return $this->parsed_response ? $this->parsed_response['groups']['group'] : false;
+ }
+
+ function people_getPublicPhotos ($user_id, $safe_search = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.people.getPublicPhotos.html */
+ return $this->call('flickr.people.getPublicPhotos', array('user_id' => $user_id, 'safe_search' => $safe_search, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function people_getUploadStatus () {
+ /* http://www.flickr.com/services/api/flickr.people.getUploadStatus.html */
+ /* Requires Authentication */
+ $this->request("flickr.people.getUploadStatus");
+ return $this->parsed_response ? $this->parsed_response['user'] : false;
+ }
+
+
+ /* Photos Methods */
+ function photos_addTags ($photo_id, $tags) {
+ /* http://www.flickr.com/services/api/flickr.photos.addTags.html */
+ $this->request("flickr.photos.addTags", array("photo_id"=>$photo_id, "tags"=>$tags), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_delete ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.delete.html */
+ $this->request("flickr.photos.delete", array("photo_id"=>$photo_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_getAllContexts ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.getAllContexts.html */
+ $this->request("flickr.photos.getAllContexts", array("photo_id"=>$photo_id));
+ return $this->parsed_response ? $this->parsed_response : false;
+ }
+
+ function photos_getContactsPhotos ($count = NULL, $just_friends = NULL, $single_photo = NULL, $include_self = NULL, $extras = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getContactsPhotos.html */
+ $this->request("flickr.photos.getContactsPhotos", array("count"=>$count, "just_friends"=>$just_friends, "single_photo"=>$single_photo, "include_self"=>$include_self, "extras"=>$extras));
+ return $this->parsed_response ? $this->parsed_response['photos']['photo'] : false;
+ }
+
+ function photos_getContactsPublicPhotos ($user_id, $count = NULL, $just_friends = NULL, $single_photo = NULL, $include_self = NULL, $extras = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getContactsPublicPhotos.html */
+ $this->request("flickr.photos.getContactsPublicPhotos", array("user_id"=>$user_id, "count"=>$count, "just_friends"=>$just_friends, "single_photo"=>$single_photo, "include_self"=>$include_self, "extras"=>$extras));
+ return $this->parsed_response ? $this->parsed_response['photos']['photo'] : false;
+ }
+
+ function photos_getContext ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.getContext.html */
+ $this->request("flickr.photos.getContext", array("photo_id"=>$photo_id));
+ return $this->parsed_response ? $this->parsed_response : false;
+ }
+
+ function photos_getCounts ($dates = NULL, $taken_dates = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getCounts.html */
+ $this->request("flickr.photos.getCounts", array("dates"=>$dates, "taken_dates"=>$taken_dates));
+ return $this->parsed_response ? $this->parsed_response['photocounts']['photocount'] : false;
+ }
+
+ function photos_getExif ($photo_id, $secret = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getExif.html */
+ $this->request("flickr.photos.getExif", array("photo_id"=>$photo_id, "secret"=>$secret));
+ return $this->parsed_response ? $this->parsed_response['photo'] : false;
+ }
+
+ function photos_getFavorites ($photo_id, $page = NULL, $per_page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getFavorites.html */
+ $this->request("flickr.photos.getFavorites", array("photo_id"=>$photo_id, "page"=>$page, "per_page"=>$per_page));
+ return $this->parsed_response ? $this->parsed_response['photo'] : false;
+ }
+
+ function photos_getInfo ($photo_id, $secret = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getInfo.html */
+ $this->request("flickr.photos.getInfo", array("photo_id"=>$photo_id, "secret"=>$secret));
+ return $this->parsed_response ? $this->parsed_response['photo'] : false;
+ }
+
+ function photos_getNotInSet ($min_upload_date = NULL, $max_upload_date = NULL, $min_taken_date = NULL, $max_taken_date = NULL, $privacy_filter = NULL, $media = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getNotInSet.html */
+ return $this->call('flickr.photos.getNotInSet', array('min_upload_date' => $min_upload_date, 'max_upload_date' => $max_upload_date, 'min_taken_date' => $min_taken_date, 'max_taken_date' => $max_taken_date, 'privacy_filter' => $privacy_filter, 'media' => $media, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function photos_getPerms ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.getPerms.html */
+ $this->request("flickr.photos.getPerms", array("photo_id"=>$photo_id));
+ return $this->parsed_response ? $this->parsed_response['perms'] : false;
+ }
+
+ function photos_getRecent ($extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getRecent.html */
+
+ if (is_array($extras)) {
+ $extras = implode(",", $extras);
+ }
+ $this->request("flickr.photos.getRecent", array("extras"=>$extras, "per_page"=>$per_page, "page"=>$page));
+ return $this->parsed_response ? $this->parsed_response['photos'] : false;
+ }
+
+ function photos_getSizes ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.getSizes.html */
+ $this->request("flickr.photos.getSizes", array("photo_id"=>$photo_id));
+ return $this->parsed_response ? $this->parsed_response['sizes']['size'] : false;
+ }
+
+ function photos_getUntagged ($min_upload_date = NULL, $max_upload_date = NULL, $min_taken_date = NULL, $max_taken_date = NULL, $privacy_filter = NULL, $media = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.getUntagged.html */
+ return $this->call('flickr.photos.getUntagged', array('min_upload_date' => $min_upload_date, 'max_upload_date' => $max_upload_date, 'min_taken_date' => $min_taken_date, 'max_taken_date' => $max_taken_date, 'privacy_filter' => $privacy_filter, 'media' => $media, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function photos_getWithGeoData ($args = array()) {
+ /* See the documentation included with the photos_search() function.
+ * I'm using the same style of arguments for this function. The only
+ * difference here is that this doesn't require any arguments. The
+ * flickr.photos.search method requires at least one search parameter.
+ */
+ /* http://www.flickr.com/services/api/flickr.photos.getWithGeoData.html */
+ $this->request("flickr.photos.getWithGeoData", $args);
+ return $this->parsed_response ? $this->parsed_response['photos'] : false;
+ }
+
+ function photos_getWithoutGeoData ($args = array()) {
+ /* See the documentation included with the photos_search() function.
+ * I'm using the same style of arguments for this function. The only
+ * difference here is that this doesn't require any arguments. The
+ * flickr.photos.search method requires at least one search parameter.
+ */
+ /* http://www.flickr.com/services/api/flickr.photos.getWithoutGeoData.html */
+ $this->request("flickr.photos.getWithoutGeoData", $args);
+ return $this->parsed_response ? $this->parsed_response['photos'] : false;
+ }
+
+ function photos_recentlyUpdated ($min_date, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.recentlyUpdated.html */
+ return $this->call('flickr.photos.recentlyUpdated', array('min_date' => $min_date, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function photos_removeTag ($tag_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.removeTag.html */
+ $this->request("flickr.photos.removeTag", array("tag_id"=>$tag_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_search ($args = array()) {
+ /* This function strays from the method of arguments that I've
+ * used in the other functions for the fact that there are just
+ * so many arguments to this API method. What you'll need to do
+ * is pass an associative array to the function containing the
+ * arguments you want to pass to the API. For example:
+ * $photos = $f->photos_search(array("tags"=>"brown,cow", "tag_mode"=>"any"));
+ * This will return photos tagged with either "brown" or "cow"
+ * or both. See the API documentation (link below) for a full
+ * list of arguments.
+ */
+
+ /* http://www.flickr.com/services/api/flickr.photos.search.html */
+ $this->request("flickr.photos.search", $args);
+ return $this->parsed_response ? $this->parsed_response['photos'] : false;
+ }
+
+ function photos_setContentType ($photo_id, $content_type) {
+ /* http://www.flickr.com/services/api/flickr.photos.setContentType.html */
+ return $this->call('flickr.photos.setContentType', array('photo_id' => $photo_id, 'content_type' => $content_type));
+ }
+
+ function photos_setDates ($photo_id, $date_posted = NULL, $date_taken = NULL, $date_taken_granularity = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.setDates.html */
+ $this->request("flickr.photos.setDates", array("photo_id"=>$photo_id, "date_posted"=>$date_posted, "date_taken"=>$date_taken, "date_taken_granularity"=>$date_taken_granularity), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_setMeta ($photo_id, $title, $description) {
+ /* http://www.flickr.com/services/api/flickr.photos.setMeta.html */
+ $this->request("flickr.photos.setMeta", array("photo_id"=>$photo_id, "title"=>$title, "description"=>$description), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_setPerms ($photo_id, $is_public, $is_friend, $is_family, $perm_comment, $perm_addmeta) {
+ /* http://www.flickr.com/services/api/flickr.photos.setPerms.html */
+ $this->request("flickr.photos.setPerms", array("photo_id"=>$photo_id, "is_public"=>$is_public, "is_friend"=>$is_friend, "is_family"=>$is_family, "perm_comment"=>$perm_comment, "perm_addmeta"=>$perm_addmeta), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_setSafetyLevel ($photo_id, $safety_level = NULL, $hidden = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.setSafetyLevel.html */
+ return $this->call('flickr.photos.setSafetyLevel', array('photo_id' => $photo_id, 'safety_level' => $safety_level, 'hidden' => $hidden));
+ }
+
+ function photos_setTags ($photo_id, $tags) {
+ /* http://www.flickr.com/services/api/flickr.photos.setTags.html */
+ $this->request("flickr.photos.setTags", array("photo_id"=>$photo_id, "tags"=>$tags), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ /* Photos - Comments Methods */
+ function photos_comments_addComment ($photo_id, $comment_text) {
+ /* http://www.flickr.com/services/api/flickr.photos.comments.addComment.html */
+ $this->request("flickr.photos.comments.addComment", array("photo_id" => $photo_id, "comment_text"=>$comment_text), TRUE);
+ return $this->parsed_response ? $this->parsed_response['comment'] : false;
+ }
+
+ function photos_comments_deleteComment ($comment_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.comments.deleteComment.html */
+ $this->request("flickr.photos.comments.deleteComment", array("comment_id" => $comment_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_comments_editComment ($comment_id, $comment_text) {
+ /* http://www.flickr.com/services/api/flickr.photos.comments.editComment.html */
+ $this->request("flickr.photos.comments.editComment", array("comment_id" => $comment_id, "comment_text"=>$comment_text), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_comments_getList ($photo_id, $min_comment_date = NULL, $max_comment_date = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.comments.getList.html */
+ return $this->call('flickr.photos.comments.getList', array('photo_id' => $photo_id, 'min_comment_date' => $min_comment_date, 'max_comment_date' => $max_comment_date));
+ }
+
+ function photos_comments_getRecentForContacts ($date_lastcomment = NULL, $contacts_filter = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.comments.getRecentForContacts.html */
+ return $this->call('flickr.photos.comments.getRecentForContacts', array('date_lastcomment' => $date_lastcomment, 'contacts_filter' => $contacts_filter, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ /* Photos - Geo Methods */
+ function photos_geo_batchCorrectLocation ($lat, $lon, $accuracy, $place_id = NULL, $woe_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.batchCorrectLocation.html */
+ return $this->call('flickr.photos.geo.batchCorrectLocation', array('lat' => $lat, 'lon' => $lon, 'accuracy' => $accuracy, 'place_id' => $place_id, 'woe_id' => $woe_id));
+ }
+
+ function photos_geo_correctLocation ($photo_id, $place_id = NULL, $woe_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.correctLocation.html */
+ return $this->call('flickr.photos.geo.correctLocation', array('photo_id' => $photo_id, 'place_id' => $place_id, 'woe_id' => $woe_id));
+ }
+
+ function photos_geo_getLocation ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.getLocation.html */
+ $this->request("flickr.photos.geo.getLocation", array("photo_id"=>$photo_id));
+ return $this->parsed_response ? $this->parsed_response['photo'] : false;
+ }
+
+ function photos_geo_getPerms ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.getPerms.html */
+ $this->request("flickr.photos.geo.getPerms", array("photo_id"=>$photo_id));
+ return $this->parsed_response ? $this->parsed_response['perms'] : false;
+ }
+
+ function photos_geo_photosForLocation ($lat, $lon, $accuracy = NULL, $extras = NULL, $per_page = NULL, $page = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.photosForLocation.html */
+ return $this->call('flickr.photos.geo.photosForLocation', array('lat' => $lat, 'lon' => $lon, 'accuracy' => $accuracy, 'extras' => $extras, 'per_page' => $per_page, 'page' => $page));
+ }
+
+ function photos_geo_removeLocation ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.removeLocation.html */
+ $this->request("flickr.photos.geo.removeLocation", array("photo_id"=>$photo_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_geo_setContext ($photo_id, $context) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.setContext.html */
+ return $this->call('flickr.photos.geo.setContext', array('photo_id' => $photo_id, 'context' => $context));
+ }
+
+ function photos_geo_setLocation ($photo_id, $lat, $lon, $accuracy = NULL, $context = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.setLocation.html */
+ return $this->call('flickr.photos.geo.setLocation', array('photo_id' => $photo_id, 'lat' => $lat, 'lon' => $lon, 'accuracy' => $accuracy, 'context' => $context));
+ }
+
+ function photos_geo_setPerms ($is_public, $is_contact, $is_friend, $is_family, $photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.geo.setPerms.html */
+ return $this->call('flickr.photos.geo.setPerms', array('is_public' => $is_public, 'is_contact' => $is_contact, 'is_friend' => $is_friend, 'is_family' => $is_family, 'photo_id' => $photo_id));
+ }
+
+ /* Photos - Licenses Methods */
+ function photos_licenses_getInfo () {
+ /* http://www.flickr.com/services/api/flickr.photos.licenses.getInfo.html */
+ $this->request("flickr.photos.licenses.getInfo");
+ return $this->parsed_response ? $this->parsed_response['licenses']['license'] : false;
+ }
+
+ function photos_licenses_setLicense ($photo_id, $license_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.licenses.setLicense.html */
+ /* Requires Authentication */
+ $this->request("flickr.photos.licenses.setLicense", array("photo_id"=>$photo_id, "license_id"=>$license_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ /* Photos - Notes Methods */
+ function photos_notes_add ($photo_id, $note_x, $note_y, $note_w, $note_h, $note_text) {
+ /* http://www.flickr.com/services/api/flickr.photos.notes.add.html */
+ $this->request("flickr.photos.notes.add", array("photo_id" => $photo_id, "note_x" => $note_x, "note_y" => $note_y, "note_w" => $note_w, "note_h" => $note_h, "note_text" => $note_text), TRUE);
+ return $this->parsed_response ? $this->parsed_response['note'] : false;
+ }
+
+ function photos_notes_delete ($note_id) {
+ /* http://www.flickr.com/services/api/flickr.photos.notes.delete.html */
+ $this->request("flickr.photos.notes.delete", array("note_id" => $note_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photos_notes_edit ($note_id, $note_x, $note_y, $note_w, $note_h, $note_text) {
+ /* http://www.flickr.com/services/api/flickr.photos.notes.edit.html */
+ $this->request("flickr.photos.notes.edit", array("note_id" => $note_id, "note_x" => $note_x, "note_y" => $note_y, "note_w" => $note_w, "note_h" => $note_h, "note_text" => $note_text), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ /* Photos - Transform Methods */
+ function photos_transform_rotate ($photo_id, $degrees) {
+ /* http://www.flickr.com/services/api/flickr.photos.transform.rotate.html */
+ $this->request("flickr.photos.transform.rotate", array("photo_id" => $photo_id, "degrees" => $degrees), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ /* Photos - Upload Methods */
+ function photos_upload_checkTickets ($tickets) {
+ /* http://www.flickr.com/services/api/flickr.photos.upload.checkTickets.html */
+ if (is_array($tickets)) {
+ $tickets = implode(",", $tickets);
+ }
+ $this->request("flickr.photos.upload.checkTickets", array("tickets" => $tickets), TRUE);
+ return $this->parsed_response ? $this->parsed_response['uploader']['ticket'] : false;
+ }
+
+ /* Photosets Methods */
+ function photosets_addPhoto ($photoset_id, $photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photosets.addPhoto.html */
+ $this->request("flickr.photosets.addPhoto", array("photoset_id" => $photoset_id, "photo_id" => $photo_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photosets_create ($title, $description, $primary_photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photosets.create.html */
+ $this->request("flickr.photosets.create", array("title" => $title, "primary_photo_id" => $primary_photo_id, "description" => $description), TRUE);
+ return $this->parsed_response ? $this->parsed_response['photoset'] : false;
+ }
+
+ function photosets_delete ($photoset_id) {
+ /* http://www.flickr.com/services/api/flickr.photosets.delete.html */
+ $this->request("flickr.photosets.delete", array("photoset_id" => $photoset_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photosets_editMeta ($photoset_id, $title, $description = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photosets.editMeta.html */
+ $this->request("flickr.photosets.editMeta", array("photoset_id" => $photoset_id, "title" => $title, "description" => $description), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photosets_editPhotos ($photoset_id, $primary_photo_id, $photo_ids) {
+ /* http://www.flickr.com/services/api/flickr.photosets.editPhotos.html */
+ $this->request("flickr.photosets.editPhotos", array("photoset_id" => $photoset_id, "primary_photo_id" => $primary_photo_id, "photo_ids" => $photo_ids), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photosets_getContext ($photo_id, $photoset_id) {
+ /* http://www.flickr.com/services/api/flickr.photosets.getContext.html */
+ $this->request("flickr.photosets.getContext", array("photo_id" => $photo_id, "photoset_id" => $photoset_id));
+ return $this->parsed_response ? $this->parsed_response : false;
+ }
+
+ function photosets_getInfo ($photoset_id) {
+ /* http://www.flickr.com/services/api/flickr.photosets.getInfo.html */
+ $this->request("flickr.photosets.getInfo", array("photoset_id" => $photoset_id));
+ return $this->parsed_response ? $this->parsed_response['photoset'] : false;
+ }
+
+ function photosets_getList ($user_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photosets.getList.html */
+ $this->request("flickr.photosets.getList", array("user_id" => $user_id));
+ return $this->parsed_response ? $this->parsed_response['photosets'] : false;
+ }
+
+ function photosets_getPhotos ($photoset_id, $extras = NULL, $privacy_filter = NULL, $per_page = NULL, $page = NULL, $media = NULL) {
+ /* http://www.flickr.com/services/api/flickr.photosets.getPhotos.html */
+ return $this->call('flickr.photosets.getPhotos', array('photoset_id' => $photoset_id, 'extras' => $extras, 'privacy_filter' => $privacy_filter, 'per_page' => $per_page, 'page' => $page, 'media' => $media));
+ }
+
+ function photosets_orderSets ($photoset_ids) {
+ /* http://www.flickr.com/services/api/flickr.photosets.orderSets.html */
+ if (is_array($photoset_ids)) {
+ $photoset_ids = implode(",", $photoset_ids);
+ }
+ $this->request("flickr.photosets.orderSets", array("photoset_ids" => $photoset_ids), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photosets_removePhoto ($photoset_id, $photo_id) {
+ /* http://www.flickr.com/services/api/flickr.photosets.removePhoto.html */
+ $this->request("flickr.photosets.removePhoto", array("photoset_id" => $photoset_id, "photo_id" => $photo_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ /* Photosets Comments Methods */
+ function photosets_comments_addComment ($photoset_id, $comment_text) {
+ /* http://www.flickr.com/services/api/flickr.photosets.comments.addComment.html */
+ $this->request("flickr.photosets.comments.addComment", array("photoset_id" => $photoset_id, "comment_text"=>$comment_text), TRUE);
+ return $this->parsed_response ? $this->parsed_response['comment'] : false;
+ }
+
+ function photosets_comments_deleteComment ($comment_id) {
+ /* http://www.flickr.com/services/api/flickr.photosets.comments.deleteComment.html */
+ $this->request("flickr.photosets.comments.deleteComment", array("comment_id" => $comment_id), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photosets_comments_editComment ($comment_id, $comment_text) {
+ /* http://www.flickr.com/services/api/flickr.photosets.comments.editComment.html */
+ $this->request("flickr.photosets.comments.editComment", array("comment_id" => $comment_id, "comment_text"=>$comment_text), TRUE);
+ return $this->parsed_response ? true : false;
+ }
+
+ function photosets_comments_getList ($photoset_id) {
+ /* http://www.flickr.com/services/api/flickr.photosets.comments.getList.html */
+ $this->request("flickr.photosets.comments.getList", array("photoset_id"=>$photoset_id));
+ return $this->parsed_response ? $this->parsed_response['comments'] : false;
+ }
+
+ /* Places Methods */
+ function places_find ($query) {
+ /* http://www.flickr.com/services/api/flickr.places.find.html */
+ return $this->call('flickr.places.find', array('query' => $query));
+ }
+
+ function places_findByLatLon ($lat, $lon, $accuracy = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.findByLatLon.html */
+ return $this->call('flickr.places.findByLatLon', array('lat' => $lat, 'lon' => $lon, 'accuracy' => $accuracy));
+ }
+
+ function places_getChildrenWithPhotosPublic ($place_id = NULL, $woe_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.getChildrenWithPhotosPublic.html */
+ return $this->call('flickr.places.getChildrenWithPhotosPublic', array('place_id' => $place_id, 'woe_id' => $woe_id));
+ }
+
+ function places_getInfo ($place_id = NULL, $woe_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.getInfo.html */
+ return $this->call('flickr.places.getInfo', array('place_id' => $place_id, 'woe_id' => $woe_id));
+ }
+
+ function places_getInfoByUrl ($url) {
+ /* http://www.flickr.com/services/api/flickr.places.getInfoByUrl.html */
+ return $this->call('flickr.places.getInfoByUrl', array('url' => $url));
+ }
+
+ function places_getPlaceTypes () {
+ /* http://www.flickr.com/services/api/flickr.places.getPlaceTypes.html */
+ return $this->call('flickr.places.getPlaceTypes', array());
+ }
+
+ function places_getShapeHistory ($place_id = NULL, $woe_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.getShapeHistory.html */
+ return $this->call('flickr.places.getShapeHistory', array('place_id' => $place_id, 'woe_id' => $woe_id));
+ }
+
+ function places_getTopPlacesList ($place_type_id, $date = NULL, $woe_id = NULL, $place_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.getTopPlacesList.html */
+ return $this->call('flickr.places.getTopPlacesList', array('place_type_id' => $place_type_id, 'date' => $date, 'woe_id' => $woe_id, 'place_id' => $place_id));
+ }
+
+ function places_placesForBoundingBox ($bbox, $place_type = NULL, $place_type_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.placesForBoundingBox.html */
+ return $this->call('flickr.places.placesForBoundingBox', array('bbox' => $bbox, 'place_type' => $place_type, 'place_type_id' => $place_type_id));
+ }
+
+ function places_placesForContacts ($place_type = NULL, $place_type_id = NULL, $woe_id = NULL, $place_id = NULL, $threshold = NULL, $contacts = NULL, $min_upload_date = NULL, $max_upload_date = NULL, $min_taken_date = NULL, $max_taken_date = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.placesForContacts.html */
+ return $this->call('flickr.places.placesForContacts', array('place_type' => $place_type, 'place_type_id' => $place_type_id, 'woe_id' => $woe_id, 'place_id' => $place_id, 'threshold' => $threshold, 'contacts' => $contacts, 'min_upload_date' => $min_upload_date, 'max_upload_date' => $max_upload_date, 'min_taken_date' => $min_taken_date, 'max_taken_date' => $max_taken_date));
+ }
+
+ function places_placesForTags ($place_type_id, $woe_id = NULL, $place_id = NULL, $threshold = NULL, $tags = NULL, $tag_mode = NULL, $machine_tags = NULL, $machine_tag_mode = NULL, $min_upload_date = NULL, $max_upload_date = NULL, $min_taken_date = NULL, $max_taken_date = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.placesForTags.html */
+ return $this->call('flickr.places.placesForTags', array('place_type_id' => $place_type_id, 'woe_id' => $woe_id, 'place_id' => $place_id, 'threshold' => $threshold, 'tags' => $tags, 'tag_mode' => $tag_mode, 'machine_tags' => $machine_tags, 'machine_tag_mode' => $machine_tag_mode, 'min_upload_date' => $min_upload_date, 'max_upload_date' => $max_upload_date, 'min_taken_date' => $min_taken_date, 'max_taken_date' => $max_taken_date));
+ }
+
+ function places_placesForUser ($place_type_id = NULL, $place_type = NULL, $woe_id = NULL, $place_id = NULL, $threshold = NULL, $min_upload_date = NULL, $max_upload_date = NULL, $min_taken_date = NULL, $max_taken_date = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.placesForUser.html */
+ return $this->call('flickr.places.placesForUser', array('place_type_id' => $place_type_id, 'place_type' => $place_type, 'woe_id' => $woe_id, 'place_id' => $place_id, 'threshold' => $threshold, 'min_upload_date' => $min_upload_date, 'max_upload_date' => $max_upload_date, 'min_taken_date' => $min_taken_date, 'max_taken_date' => $max_taken_date));
+ }
+
+ function places_resolvePlaceId ($place_id) {
+ /* http://www.flickr.com/services/api/flickr.places.resolvePlaceId.html */
+ $rsp = $this->call('flickr.places.resolvePlaceId', array('place_id' => $place_id));
+ return $rsp ? $rsp['location'] : $rsp;
+ }
+
+ function places_resolvePlaceURL ($url) {
+ /* http://www.flickr.com/services/api/flickr.places.resolvePlaceURL.html */
+ $rsp = $this->call('flickr.places.resolvePlaceURL', array('url' => $url));
+ return $rsp ? $rsp['location'] : $rsp;
+ }
+
+ function places_tagsForPlace ($woe_id = NULL, $place_id = NULL, $min_upload_date = NULL, $max_upload_date = NULL, $min_taken_date = NULL, $max_taken_date = NULL) {
+ /* http://www.flickr.com/services/api/flickr.places.tagsForPlace.html */
+ return $this->call('flickr.places.tagsForPlace', array('woe_id' => $woe_id, 'place_id' => $place_id, 'min_upload_date' => $min_upload_date, 'max_upload_date' => $max_upload_date, 'min_taken_date' => $min_taken_date, 'max_taken_date' => $max_taken_date));
+ }
+
+ /* Prefs Methods */
+ function prefs_getContentType () {
+ /* http://www.flickr.com/services/api/flickr.prefs.getContentType.html */
+ $rsp = $this->call('flickr.prefs.getContentType', array());
+ return $rsp ? $rsp['person'] : $rsp;
+ }
+
+ function prefs_getGeoPerms () {
+ /* http://www.flickr.com/services/api/flickr.prefs.getGeoPerms.html */
+ return $this->call('flickr.prefs.getGeoPerms', array());
+ }
+
+ function prefs_getHidden () {
+ /* http://www.flickr.com/services/api/flickr.prefs.getHidden.html */
+ $rsp = $this->call('flickr.prefs.getHidden', array());
+ return $rsp ? $rsp['person'] : $rsp;
+ }
+
+ function prefs_getPrivacy () {
+ /* http://www.flickr.com/services/api/flickr.prefs.getPrivacy.html */
+ $rsp = $this->call('flickr.prefs.getPrivacy', array());
+ return $rsp ? $rsp['person'] : $rsp;
+ }
+
+ function prefs_getSafetyLevel () {
+ /* http://www.flickr.com/services/api/flickr.prefs.getSafetyLevel.html */
+ $rsp = $this->call('flickr.prefs.getSafetyLevel', array());
+ return $rsp ? $rsp['person'] : $rsp;
+ }
+
+ /* Reflection Methods */
+ function reflection_getMethodInfo ($method_name) {
+ /* http://www.flickr.com/services/api/flickr.reflection.getMethodInfo.html */
+ $this->request("flickr.reflection.getMethodInfo", array("method_name" => $method_name));
+ return $this->parsed_response ? $this->parsed_response : false;
+ }
+
+ function reflection_getMethods () {
+ /* http://www.flickr.com/services/api/flickr.reflection.getMethods.html */
+ $this->request("flickr.reflection.getMethods");
+ return $this->parsed_response ? $this->parsed_response['methods']['method'] : false;
+ }
+
+ /* Tags Methods */
+ function tags_getClusterPhotos ($tag, $cluster_id) {
+ /* http://www.flickr.com/services/api/flickr.tags.getClusterPhotos.html */
+ return $this->call('flickr.tags.getClusterPhotos', array('tag' => $tag, 'cluster_id' => $cluster_id));
+ }
+
+ function tags_getClusters ($tag) {
+ /* http://www.flickr.com/services/api/flickr.tags.getClusters.html */
+ return $this->call('flickr.tags.getClusters', array('tag' => $tag));
+ }
+
+ function tags_getHotList ($period = NULL, $count = NULL) {
+ /* http://www.flickr.com/services/api/flickr.tags.getHotList.html */
+ $this->request("flickr.tags.getHotList", array("period" => $period, "count" => $count));
+ return $this->parsed_response ? $this->parsed_response['hottags'] : false;
+ }
+
+ function tags_getListPhoto ($photo_id) {
+ /* http://www.flickr.com/services/api/flickr.tags.getListPhoto.html */
+ $this->request("flickr.tags.getListPhoto", array("photo_id" => $photo_id));
+ return $this->parsed_response ? $this->parsed_response['photo']['tags']['tag'] : false;
+ }
+
+ function tags_getListUser ($user_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.tags.getListUser.html */
+ $this->request("flickr.tags.getListUser", array("user_id" => $user_id));
+ return $this->parsed_response ? $this->parsed_response['who']['tags']['tag'] : false;
+ }
+
+ function tags_getListUserPopular ($user_id = NULL, $count = NULL) {
+ /* http://www.flickr.com/services/api/flickr.tags.getListUserPopular.html */
+ $this->request("flickr.tags.getListUserPopular", array("user_id" => $user_id, "count" => $count));
+ return $this->parsed_response ? $this->parsed_response['who']['tags']['tag'] : false;
+ }
+
+ function tags_getListUserRaw ($tag = NULL) {
+ /* http://www.flickr.com/services/api/flickr.tags.getListUserRaw.html */
+ return $this->call('flickr.tags.getListUserRaw', array('tag' => $tag));
+ }
+
+ function tags_getRelated ($tag) {
+ /* http://www.flickr.com/services/api/flickr.tags.getRelated.html */
+ $this->request("flickr.tags.getRelated", array("tag" => $tag));
+ return $this->parsed_response ? $this->parsed_response['tags'] : false;
+ }
+
+ function test_echo ($args = array()) {
+ /* http://www.flickr.com/services/api/flickr.test.echo.html */
+ $this->request("flickr.test.echo", $args);
+ return $this->parsed_response ? $this->parsed_response : false;
+ }
+
+ function test_login () {
+ /* http://www.flickr.com/services/api/flickr.test.login.html */
+ $this->request("flickr.test.login");
+ return $this->parsed_response ? $this->parsed_response['user'] : false;
+ }
+
+ function urls_getGroup ($group_id) {
+ /* http://www.flickr.com/services/api/flickr.urls.getGroup.html */
+ $this->request("flickr.urls.getGroup", array("group_id"=>$group_id));
+ return $this->parsed_response ? $this->parsed_response['group']['url'] : false;
+ }
+
+ function urls_getUserPhotos ($user_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.urls.getUserPhotos.html */
+ $this->request("flickr.urls.getUserPhotos", array("user_id"=>$user_id));
+ return $this->parsed_response ? $this->parsed_response['user']['url'] : false;
+ }
+
+ function urls_getUserProfile ($user_id = NULL) {
+ /* http://www.flickr.com/services/api/flickr.urls.getUserProfile.html */
+ $this->request("flickr.urls.getUserProfile", array("user_id"=>$user_id));
+ return $this->parsed_response ? $this->parsed_response['user']['url'] : false;
+ }
+
+ function urls_lookupGroup ($url) {
+ /* http://www.flickr.com/services/api/flickr.urls.lookupGroup.html */
+ $this->request("flickr.urls.lookupGroup", array("url"=>$url));
+ return $this->parsed_response ? $this->parsed_response['group'] : false;
+ }
+
+ function urls_lookupUser ($url) {
+ /* http://www.flickr.com/services/api/flickr.photos.notes.edit.html */
+ $this->request("flickr.urls.lookupUser", array("url"=>$url));
+ return $this->parsed_response ? $this->parsed_response['user'] : false;
+ }
+}
+
+
+?>