/tags/Racine_livraison_narmer/api/pear/DB/NestedSet.php |
---|
New file |
0,0 → 1,2747 |
<?php |
// |
// +----------------------------------------------------------------------+ |
// | PEAR :: DB_NestedSet | |
// +----------------------------------------------------------------------+ |
// | 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 |f |
// | obtain it through the world-wide-web, please send a note to | |
// | license@php.net so we can mail you a copy immediately. | |
// +----------------------------------------------------------------------+ |
// | Authors: Daniel Khan <dk@webcluster.at> | |
// | Jason Rust <jason@rustyparts.com> | |
// +----------------------------------------------------------------------+ |
// $Id: NestedSet.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
// |
// CREDITS: |
// -------- |
// - Thanks to Kristian Koehntopp for publishing an explanation of the Nested Set |
// technique and for the great work he did and does for the php community |
// - Thanks to Daniel T. Gorski for his great tutorial on www.develnet.org |
// |
// - Thanks to my parents for ... just kidding :] |
require_once 'PEAR.php'; |
// {{{ constants |
// Error and message codes |
define('NESE_ERROR_RECURSION', 'E100'); |
define('NESE_ERROR_NODRIVER', 'E200'); |
define('NESE_ERROR_NOHANDLER', 'E300'); |
define('NESE_ERROR_TBLOCKED', 'E010'); |
define('NESE_MESSAGE_UNKNOWN', 'E0'); |
define('NESE_ERROR_NOTSUPPORTED', 'E1'); |
define('NESE_ERROR_PARAM_MISSING','E400'); |
define('NESE_ERROR_NOT_FOUND', 'E500'); |
define('NESE_ERROR_WRONG_MPARAM', 'E2'); |
// for moving a node before another |
define('NESE_MOVE_BEFORE', 'BE'); |
// for moving a node after another |
define('NESE_MOVE_AFTER', 'AF'); |
// for moving a node below another |
define('NESE_MOVE_BELOW', 'SUB'); |
// Sortorders |
define('NESE_SORT_LEVEL', 'SLV'); |
define('NESE_SORT_PREORDER', 'SPO'); |
// }}} |
// {{{ DB_NestedSet:: class |
/** |
* DB_NestedSet is a class for handling nested sets |
* |
* @author Daniel Khan <dk@webcluster.at> |
* @package DB_NestedSet |
* @version $Revision: 1.1 $ |
* @access public |
*/ |
// }}} |
class DB_NestedSet { |
// {{{ properties |
/** |
* @var array The field parameters of the table with the nested set. Format: 'realFieldName' => 'fieldId' |
* @access public |
*/ |
var $params = array( |
'STRID' => 'id', |
'ROOTID'=> 'rootid', |
'l' => 'l', |
'r' => 'r', |
'STREH' => 'norder', |
'LEVEL' => 'level', |
// 'parent'=>'parent', // Optional but very useful |
'STRNA' => 'name' |
); |
// To be used with 2.0 - would be an api break atm |
// var $quotedParams = array('name'); |
/** |
* @var string The table with the actual tree data |
* @access public |
*/ |
var $node_table = 'tb_nodes'; |
/** |
* @var string The table to handle locking |
* @access public |
*/ |
var $lock_table = 'tb_locks'; |
/** |
* @var string The table used for sequences |
* @access public |
*/ |
var $sequence_table; |
/** |
* Secondary order field. Normally this is the order field, but can be changed to |
* something else (i.e. the name field so that the tree can be shown alphabetically) |
* |
* @var string |
* @access public |
*/ |
var $secondarySort; |
/** |
* Used to store the secondary sort method set by the user while doing manipulative queries |
* |
* @var string |
* @access private |
*/ |
var $_userSecondarySort = false; |
/** |
* The default sorting field - will be set to the table column inside the constructor |
* |
* @var string |
* @access private |
*/ |
var $_defaultSecondarySort = 'norder'; |
/** |
* @var int The time to live of the lock |
* @access public |
*/ |
var $lockTTL = 1; |
/** |
* @var bool Enable debugging statements? |
* @access public |
*/ |
var $debug = 0; |
/** |
* @var bool Lock the structure of the table? |
* @access private |
*/ |
var $_structureTableLock = false; |
/** |
* @var bool Don't allow unlocking (used inside of moves) |
* @access private |
*/ |
var $_lockExclusive = false; |
/** |
* @var object cache Optional PEAR::Cache object |
* @access public |
*/ |
var $cache = false; |
/** |
* Specify the sortMode of the query methods |
* NESE_SORT_LEVEL is the 'old' sorting method and sorts a tree by level |
* all nodes of level 1, all nodes of level 2,... |
* NESE_SORT_PREORDER will sort doing a preorder walk. |
* So all children of node x will come right after it |
* Note that moving a node within it's siblings will obviously not change the output |
* in this mode |
* |
* @var constant Order method (NESE_SORT_LEVEL|NESE_SORT_PREORDER) |
* @access private |
*/ |
var $_sortMode = NESE_SORT_LEVEL; |
/** |
* @var array Available sortModes |
* @access private |
*/ |
var $_sortModes = array(NESE_SORT_LEVEL, NESE_SORT_PREORDER); |
/** |
* @var array An array of field ids that must exist in the table |
* @access private |
*/ |
var $_requiredParams = array('id', 'rootid', 'l', 'r', 'norder', 'level'); |
/** |
* @var bool Skip the callback events? |
* @access private |
*/ |
var $_skipCallbacks = false; |
/** |
* @var bool Do we want to use caching |
* @access private |
*/ |
var $_caching = false; |
/** |
* @var array The above parameters flipped for easy access |
* @access private |
*/ |
var $_flparams = array(); |
/** |
* |
* @var bool Temporary switch for cache |
* @access private |
*/ |
var $_restcache = false; |
/** |
* Used to determine the presence of listeners for an event in triggerEvent() |
* |
* If any event listeners are registered for an event, the event name will |
* have a key set in this array, otherwise, it will not be set. |
* @see triggerEvent() |
* @var arrayg |
* @access private |
*/ |
var $_hasListeners = array(); |
/** |
* @var string packagename |
* @access private |
*/ |
var $_packagename = 'DB_NestedSet'; |
/** |
* @var int Majorversion |
* @access private |
*/ |
var $_majorversion = 1; |
/** |
* @var string Minorversion |
* @access private |
*/ |
var $_minorversion = '3'; |
/** |
* @var array Used for mapping a cloned tree to the real tree for move_* operations |
* @access private |
*/ |
var $_relations = array(); |
/** |
* Used for _internal_ tree conversion |
* @var bool Turn off user param verification and id generation |
* @access private |
*/ |
var $_dumbmode = false; |
/** |
* @var array Map of error messages to their descriptions |
*/ |
var $messages = array( |
NESE_ERROR_RECURSION => '%s: This operation would lead to a recursion', |
NESE_ERROR_TBLOCKED => 'The structure Table is locked for another database operation, please retry.', |
NESE_ERROR_NODRIVER => 'The selected database driver %s wasn\'t found', |
NESE_ERROR_NOTSUPPORTED => 'Method not supported yet', |
NESE_ERROR_NOHANDLER => 'Event handler not found', |
NESE_ERROR_PARAM_MISSING=> 'Parameter missing', |
NESE_MESSAGE_UNKNOWN => 'Unknown error or message', |
NESE_ERROR_NOT_FOUND => '%s: Node %s not found', |
NESE_ERROR_WRONG_MPARAM => '%s: %s' |
); |
/** |
* @var array The array of event listeners |
* @access private |
*/ |
var $eventListeners = array(); |
// }}} |
// +---------------------------------------+ |
// | Base methods | |
// +---------------------------------------+ |
// {{{ constructor |
/** |
* Constructor |
* |
* @param array $params Database column fields which should be returned |
* |
* @access private |
* @return void |
*/ |
function DB_NestedSet($params) { |
if ($this->debug) { |
$this->_debugMessage('DB_NestedSet()'); |
} |
if (is_array($params) && count($params) > 0) { |
$this->params = $params; |
} |
$this->_flparams = array_flip($this->params); |
$this->sequence_table = $this->node_table . '_' . $this->_flparams['id']; |
$this->secondarySort = $this->_flparams[$this->_defaultSecondarySort]; |
register_shutdown_function(array(&$this,'_DB_NestedSet')); |
} |
// }}} |
// {{{ destructor |
/** |
* PEAR Destructor |
* Releases all locks |
* Closes open database connections |
* |
* @access private |
* @return void |
*/ |
function _DB_NestedSet() { |
if ($this->debug) { |
$this->_debugMessage('_DB_NestedSet()'); |
} |
$this->_releaseLock(true); |
} |
// }}} |
// {{{ factory |
/** |
* Handles the returning of a concrete instance of DB_NestedSet based on the driver. |
* If the class given by $driver allready exists it will be used. |
* If not the driver will be searched inside the default path ./NestedSet/ |
* |
* @param string $driver The driver, such as DB or MDB |
* @param string $dsn The dsn for connecting to the database |
* @param array $params The field name params for the node table |
* |
* @static |
* @access public |
* @return object The DB_NestedSet object |
*/ |
function & factory($driver, $dsn, $params = array()) { |
$classname = 'DB_NestedSet_' . $driver; |
if (!class_exists($classname)) { |
$driverpath = dirname(__FILE__).'/NestedSet/'.$driver.'.php'; |
if(!file_exists($driverpath) || !$driver) { |
return PEAR::raiseError("factory(): The database driver '$driver' wasn't found", NESE_ERROR_NODRIVER, PEAR_ERROR_TRIGGER, E_USER_ERROR); |
} |
include_once($driverpath); |
} |
return new $classname($dsn, $params); |
} |
// }}} |
// }}} |
// +----------------------------------------------+ |
// | NestedSet manipulation and query methods | |
// |----------------------------------------------+ |
// | Querying the tree | |
// +----------------------------------------------+ |
// {{{ getAllNodes() |
/** |
* Fetch the whole NestedSet |
* |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @access public |
* @return mixed False on error, or an array of nodes |
*/ |
function getAllNodes($keepAsArray = false, $aliasFields = true, $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('getAllNodes()'); |
} |
if($this->_sortMode == NESE_SORT_LEVEL) { |
$sql = sprintf('SELECT %s %s FROM %s %s %s ORDER BY %s.%s, %s.%s ASC', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->_addSQL($addSQL, 'append'), |
$this->node_table, |
$this->_flparams['level'], |
$this->node_table, |
$this->secondarySort); |
} elseif ($this->_sortMode == NESE_SORT_PREORDER) { |
$nodeSet = array(); |
$rootnodes = $this->getRootNodes(true); |
foreach($rootnodes AS $rid=>$rootnode) { |
$nodeSet = $nodeSet+$this->getBranch($rootnode, true); |
} |
return $nodeSet; |
} |
if (!$this->_caching) { |
$nodeSet = $this->_processResultSet($sql, $keepAsArray, $aliasFields); |
} else { |
$nodeSet = $this->cache->call('DB_NestedSet->_processResultSet', $sql, $keepAsArray, $aliasFields); |
} |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeLoad'])) { |
// EVENT (nodeLoad) |
foreach (array_keys($nodeSet) as $key) { |
$this->triggerEvent('nodeLoad', $nodeSet[$key]); |
} |
} |
return $nodeSet; |
} |
// }}} |
// {{{ getRootNodes() |
/** |
* Fetches the first level (the rootnodes) of the NestedSet |
* |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @see _addSQL() |
* @access public |
* @return mixed False on error, or an array of nodes |
*/ |
function getRootNodes($keepAsArray = false, $aliasFields = true, $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('getRootNodes()'); |
} |
$sql = sprintf('SELECT %s %s FROM %s %s WHERE %s.%s=%s.%s %s ORDER BY %s.%s ASC', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->node_table, |
$this->_flparams['id'], |
$this->node_table, |
$this->_flparams['rootid'], |
$this->_addSQL($addSQL, 'append'), |
$this->node_table, |
$this->secondarySort); |
if (!$this->_caching) { |
$nodeSet = $this->_processResultSet($sql, $keepAsArray, $aliasFields); |
} else { |
$nodeSet = $this->cache->call('DB_NestedSet->_processResultSet', $sql, $keepAsArray, $aliasFields); |
} |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeLoad'])) { |
// EVENT (nodeLoad) |
foreach (array_keys($nodeSet) as $key) { |
$this->triggerEvent('nodeLoad', $nodeSet[$key]); |
} |
} |
return $nodeSet; |
} |
// }}} |
// {{{ getBranch() |
/** |
* Fetch the whole branch where a given node id is in |
* |
* @param int $id The node ID |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @see _addSQL() |
* @access public |
* @return mixed False on error, or an array of nodes |
*/ |
function getBranch($id, $keepAsArray = false, $aliasFields = true, $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('getBranch($id)'); |
} |
if (!($thisnode = $this->pickNode($id, true))) { |
$epr = array('getBranch()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_NOTICE, $epr); |
} |
if($this->_sortMode == NESE_SORT_LEVEL) { |
$firstsort = $this->_flparams['level']; |
$sql = sprintf('SELECT %s %s FROM %s %s WHERE %s.%s=%s %s ORDER BY %s.%s, %s.%s ASC', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->node_table, |
$this->_flparams['rootid'], |
$thisnode['rootid'], |
$this->_addSQL($addSQL, 'append'), |
$this->node_table, |
$firstsort, |
$this->node_table, |
$this->secondarySort); |
} elseif($this->_sortMode == NESE_SORT_PREORDER) { |
$firstsort = $this->_flparams['l']; |
$sql = sprintf('SELECT %s %s FROM %s %s WHERE %s.%s=%s %s ORDER BY %s.%s ASC', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->node_table, |
$this->_flparams['rootid'], |
$thisnode['rootid'], |
$this->_addSQL($addSQL, 'append'), |
$this->node_table, |
$firstsort); |
} |
if (!$this->_caching) { |
$nodeSet = $this->_processResultSet($sql, $keepAsArray, $aliasFields); |
} else { |
$nodeSet = $this->cache->call('DB_NestedSet->_processResultSet', $sql, $keepAsArray, $aliasFields); |
} |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeLoad'])) { |
// EVENT (nodeLoad) |
foreach (array_keys($nodeSet) as $key) { |
$this->triggerEvent('nodeLoad', $nodeSet[$key]); |
} |
} |
if($this->_sortMode == NESE_SORT_PREORDER && ($this->params[$this->secondarySort] != $this->_defaultSecondarySort)) { |
uasort($nodeSet, array($this, '_secSort')); |
} |
return $nodeSet; |
} |
// }}} |
// {{{ getParents() |
/** |
* Fetch the parents of a node given by id |
* |
* @param int $id The node ID |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @see _addSQL() |
* @access public |
* @return mixed False on error, or an array of nodes |
*/ |
function getParents($id, $keepAsArray = false, $aliasFields = true, $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('getParents($id)'); |
} |
if (!($child = $this->pickNode($id, true))) { |
$epr = array('getParents()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_NOTICE, $epr); |
} |
$sql = sprintf('SELECT %s %s FROM %s %s |
WHERE %s.%s=%s AND %s.%s<%s AND %s.%s<%s AND %s.%s>%s %s |
ORDER BY %s.%s ASC', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->node_table, |
$this->_flparams['rootid'], |
$child['rootid'], |
$this->node_table, |
$this->_flparams['level'], |
$child['level'], |
$this->node_table, |
$this->_flparams['l'], |
$child['l'], |
$this->node_table, |
$this->_flparams['r'], |
$child['r'], |
$this->_addSQL($addSQL, 'append'), |
$this->node_table, |
$this->_flparams['level']); |
if (!$this->_caching) { |
$nodeSet = $this->_processResultSet($sql, $keepAsArray, $aliasFields); |
} else { |
$nodeSet = $this->cache->call('DB_NestedSet->_processResultSet', $sql, $keepAsArray, $aliasFields); |
} |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeLoad'])) { |
// EVENT (nodeLoad) |
foreach (array_keys($nodeSet) as $key) { |
$this->triggerEvent('nodeLoad', $nodeSet[$key]); |
} |
} |
return $nodeSet; |
} |
// }}} |
// {{{ getParent() |
/** |
* Fetch the immediate parent of a node given by id |
* |
* @param int $id The node ID |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @see _addSQL() |
* @access public |
* @return mixed False on error, or the parent node |
*/ |
function getParent($id, $keepAsArray = false, $aliasFields = true, $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('getParent($id)'); |
} |
if (!($child = $this->pickNode($id, true))) { |
$epr = array('getParent()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_NOTICE, $epr); |
} |
if($child['id'] == $child['rootid']) { |
return false; |
} |
// If parent node is set inside the db simply return it |
if(isset($child['parent']) && !empty($child['parent'])) { |
return $this->pickNode($child['parent'], $keepAsArray, $aliasFields, 'id', $addSQL); |
} |
$addSQL['append'] = sprintf('AND %s.%s = %s', |
$this->node_table, |
$this->_flparams['level'], |
$child['level']-1); |
$nodeSet = $this->getParents($id, $keepAsArray, $aliasFields, $addSQL); |
if(!empty($nodeSet)) { |
$keys = array_keys($nodeSet); |
return $nodeSet[$keys[0]]; |
} else { |
return false; |
} |
} |
// }}} |
// {{{ getSiblings) |
/** |
* Fetch all siblings of the node given by id |
* Important: The node given by ID will also be returned |
* Do a unset($array[$id]) on the result if you don't want that |
* |
* @param int $id The node ID |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @see _addSQL() |
* @access public |
* @return mixed False on error, or the parent node |
*/ |
function getSiblings($id, $keepAsArray = false, $aliasFields = true, $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('getParents($id)'); |
} |
if (!($sibling = $this->pickNode($id, true))) { |
$epr = array('getSibling()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_NOTICE, $epr); |
} |
$parent = $this->getParent($sibling, true); |
return $this->getChildren($parent, $keepAsArray, $aliasFields, $addSQL); |
} |
// }}} |
// {{{ getChildren() |
/** |
* Fetch the children _one level_ after of a node given by id |
* |
* @param int $id The node ID |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param bool $forceNorder (optional) Force the result to be ordered by the norder |
* param (as opposed to the value of secondary sort). Used by the move and |
* add methods. |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @see _addSQL() |
* @access public |
* @return mixed False on error, or an array of nodes |
*/ |
function getChildren($id, $keepAsArray = false, $aliasFields = true, $forceNorder = false, $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('getChildren($id)'); |
} |
if (!($parent = $this->pickNode($id, true))) { |
$epr = array('getChildren()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_NOTICE, $epr); |
} |
if (!$parent || $parent['l'] == ($parent['r'] - 1)) { |
return false; |
} |
$sql = sprintf('SELECT %s %s FROM %s %s |
WHERE %s.%s=%s AND %s.%s=%s+1 AND %s.%s BETWEEN %s AND %s %s |
ORDER BY %s.%s ASC', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->node_table, |
$this->_flparams['rootid'], |
$parent['rootid'], |
$this->node_table, |
$this->_flparams['level'], |
$parent['level'], |
$this->node_table, |
$this->_flparams['l'], |
$parent['l'], |
$parent['r'], |
$this->_addSQL($addSQL, 'append'), |
$this->node_table, |
$this->secondarySort); |
if (!$this->_caching) { |
$nodeSet = $this->_processResultSet($sql, $keepAsArray, $aliasFields); |
} else { |
$nodeSet = $this->cache->call('DB_NestedSet->_processResultSet', $sql, $keepAsArray, $aliasFields); |
} |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeLoad'])) { |
// EVENT (nodeLoad) |
foreach (array_keys($nodeSet) as $key) { |
$this->triggerEvent('nodeLoad', $nodeSet[$key]); |
} |
} |
return $nodeSet; |
} |
// }}} |
// {{{ getSubBranch() |
/** |
* Fetch all the children of a node given by id |
* |
* getChildren only queries the immediate children |
* getSubBranch returns all nodes below the given node |
* |
* @param string $id The node ID |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @see _addSQL() |
* @access public |
* @return mixed False on error, or an array of nodes |
*/ |
function getSubBranch($id, $keepAsArray = false, $aliasFields = true, $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('getSubBranch($id)'); |
} |
if (!($parent = $this->pickNode($id, true))) { |
$epr = array('getSubBranch()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, E_USER_NOTICE, $epr); |
} |
if($this->_sortMode == NESE_SORT_LEVEL) { |
$firstsort = $this->_flparams['level']; |
$sql = sprintf('SELECT %s %s FROM %s %s WHERE %s.%s BETWEEN %s AND %s AND %s.%s=%s AND %s.%s!=%s %s ORDER BY %s.%s, %s.%s ASC', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->node_table, |
$this->_flparams['l'], |
$parent['l'], |
$parent['r'], |
$this->node_table, |
$this->_flparams['rootid'], |
$parent['rootid'], |
$this->node_table, |
$this->_flparams['id'], |
$this->_addSQL($addSQL, 'append'), |
$id, |
$this->node_table, |
$firstsort, |
$this->node_table, |
$this->secondarySort |
); |
} elseif($this->_sortMode == NESE_SORT_PREORDER) { |
$firstsort = $this->_flparams['l']; |
$firstsort = $this->_flparams['level']; |
$sql = sprintf('SELECT %s %s FROM %s %s WHERE %s.%s BETWEEN %s AND %s AND %s.%s=%s AND %s.%s!=%s %s ORDER BY %s.%s ASC', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->node_table, |
$this->_flparams['l'], |
$parent['l'], |
$parent['r'], |
$this->node_table, |
$this->_flparams['rootid'], |
$parent['rootid'], |
$this->node_table, |
$this->_flparams['id'], |
$this->_addSQL($addSQL, 'append'), |
$id, |
$this->node_table, |
$firstsort |
); |
} |
if (!$this->_caching) { |
$nodeSet = $this->_processResultSet($sql, $keepAsArray, $aliasFields); |
} else { |
$nodeSet = $this->cache->call('DB_NestedSet->_processResultSet', $sql, $keepAsArray, $aliasFields); |
} |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeLoad'])) { |
// EVENT (nodeLoad) |
foreach (array_keys($nodeSet) as $key) { |
$this->triggerEvent('nodeLoad', $nodeSet[$key]); |
} |
} |
if($this->params[$this->secondarySort] != $this->_defaultSecondarySort) { |
uasort($nodeSet, array($this, '_secSort')); |
} |
return $nodeSet; |
} |
// }}} |
// {{{ pickNode() |
/** |
* Fetch the data of a node with the given id |
* |
* @param int $id The node id of the node to fetch |
* @param bool $keepAsArray (optional) Keep the result as an array or transform it into |
* a set of DB_NestedSet_Node objects? |
* @param bool $aliasFields (optional) Should we alias the fields so they are the names |
* of the parameter keys, or leave them as is? |
* @param string $idfield (optional) Which field has to be compared with $id? |
* This is can be used to pick a node by other values (e.g. it's name). |
* @param array $addSQL (optional) Array of additional params to pass to the query. |
* |
* @see _addSQL() |
* @access public |
* @return mixed False on error, or an array of nodes |
*/ |
function pickNode($id, $keepAsArray = false, $aliasFields = true, $idfield = 'id', $addSQL = array()) { |
if ($this->debug) { |
$this->_debugMessage('pickNode($id)'); |
} |
if (is_object($id) && $id->id) { |
return $id; |
} elseif (is_array($id) && isset($id['id'])) { |
return $id; |
} |
if(!$id) { |
return false; |
} |
$sql = sprintf('SELECT %s %s FROM %s %s WHERE %s.%s=%s %s', |
$this->_getSelectFields($aliasFields), |
$this->_addSQL($addSQL, 'cols'), |
$this->node_table, |
$this->_addSQL($addSQL, 'join'), |
$this->node_table, |
$this->_flparams[$idfield], |
$id, |
$this->_addSQL($addSQL, 'append')); |
if (!$this->_caching) { |
$nodeSet = $this->_processResultSet($sql, $keepAsArray, $aliasFields); |
} else { |
$nodeSet = $this->cache->call('DB_NestedSet->_processResultSet', $sql, $keepAsArray, $aliasFields); |
} |
$nsKey = false; |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeLoad'])) { |
// EVENT (nodeLoad) |
foreach (array_keys($nodeSet) as $key) { |
$this->triggerEvent('nodeLoad', $nodeSet[$key]); |
$nsKey = $key; |
} |
} else { |
foreach (array_keys($nodeSet) as $key) { |
$nsKey = $key; |
} |
} |
if (is_array($nodeSet) && $idfield != 'id') { |
$id = $nsKey; |
} |
return isset($nodeSet[$id]) ? $nodeSet[$id] : false; |
} |
// }}} |
// {{{ isParent() |
/** |
* See if a given node is a parent of another given node |
* |
* A node is considered to be a parent if it resides above the child |
* So it doesn't mean that the node has to be an immediate parent. |
* To get this information simply compare the levels of the two nodes |
* after you know that you have a parent relation. |
* |
* @param mixed $parent The parent node as array or object |
* @param mixed $child The child node as array or object |
* |
* @access public |
* @return bool True if it's a parent |
*/ |
function isParent($parent, $child) { |
if ($this->debug) { |
$this->_debugMessage('isParent($parent, $child)'); |
} |
if (!isset($parent)|| !isset($child)) { |
return false; |
} |
if (is_array($parent)) { |
$p_rootid = $parent['rootid']; |
$p_l = $parent['l']; |
$p_r = $parent['r']; |
} elseif (is_object($parent)) { |
$p_rootid = $parent->rootid; |
$p_l = $parent->l; |
$p_r = $parent->r; |
} |
if (is_array($child)) { |
$c_rootid = $child['rootid']; |
$c_l = $child['l']; |
$c_r = $child['r']; |
} elseif (is_object($child)) { |
$c_rootid = $child->rootid; |
$c_l = $child->l; |
$c_r = $child->r; |
} |
if (($p_rootid == $c_rootid) && ($p_l < $c_l && $p_r > $c_r)) { |
return true; |
} |
return false; |
} |
// }}} |
// +----------------------------------------------+ |
// | NestedSet manipulation and query methods | |
// |----------------------------------------------+ |
// | insert / delete / update of nodes | |
// +----------------------------------------------+ |
// | [PUBLIC] | |
// +----------------------------------------------+ |
// {{{ createRootNode() |
/** |
* Creates a new root node |
* Optionally it deletes the whole tree and creates one initial rootnode |
* |
* <pre> |
* +-- root1 [target] |
* | |
* +-- root2 [new] |
* | |
* +-- root3 |
* </pre> |
* |
* @param array $values Hash with param => value pairs of the node (see $this->params) |
* @param integer $id ID of target node (the rootnode after which the node should be inserted) |
* @param bool $first Danger: Deletes and (re)init's the hole tree - sequences are reset |
* |
* @access public |
* @return mixed The node id or false on error |
*/ |
function createRootNode($values, $id = false, $first = false, $_pos = 'AF') { |
if ($this->debug) { |
$this->_debugMessage('createRootNode($values, $id = false, $first = false, $_pos = \'AF\')'); |
} |
$this->_verifyUserValues('createRootNode()', $values); |
if(!$first && (!$id || !$parent = $this->pickNode($id, true))) { |
$epr = array('createRootNode()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR, $epr); |
} elseif($first && $id) { |
// No notice for now. |
// But tehese 2 params don't make sense together |
$epr = array( |
'createRootNode()', |
'[id] AND [first] were passed - that doesn\'t make sense'); |
//$this->_raiseError(NESE_ERROR_WRONG_MPARAM, E_USER_WARNING, $epr); |
} |
// Try to aquire a table lock |
if(PEAR::isError($lock=$this->_setLock())) { |
return $lock; |
} |
$sql = array(); |
$addval = array(); |
$addval[$this->_flparams['level']] = 1; |
// Shall we delete the existing tree (reinit) |
if ($first) { |
$dsql = sprintf('DELETE FROM %s', |
$this->node_table); |
$this->db->query($dsql); |
$this->db->dropSequence($this->sequence_table); |
// New order of the new node will be 1 |
$addval[$this->_flparams['norder']] = 1; |
} else { |
// Let's open a gap for the new node |
if($_pos == NESE_MOVE_AFTER) { |
$addval[$this->_flparams['norder']] = $parent['norder'] + 1; |
$sql[] = sprintf('UPDATE %s SET %s=%s+1 WHERE %s=%s AND %s > %s', |
$this->node_table, |
$this->_flparams['norder'], |
$this->_flparams['norder'], |
$this->_flparams['id'], |
$this->_flparams['rootid'], |
$this->_flparams['norder'], |
$parent['norder']); |
} elseif($_pos == NESE_MOVE_BEFORE) { |
$addval[$this->_flparams['norder']] = $parent['norder']; |
$sql[] = sprintf('UPDATE %s SET %s=%s+1 WHERE %s=%s AND %s >= %s', |
$this->node_table, |
$this->_flparams['norder'], |
$this->_flparams['norder'], |
$this->_flparams['id'], |
$this->_flparams['rootid'], |
$this->_flparams['norder'], |
$parent['norder']); |
} |
} |
if(isset($this->_flparams['parent'])) { |
$addval[$this->_flparams['parent']] = 0; |
} |
// Sequence of node id (equals to root id in this case |
if(!$this->_dumbmode || !$node_id=isset($values[$this->_flparams['id']]) || !isset($values[$this->_flparams['rootid']])) { |
$addval[$this->_flparams['rootid']] = $node_id = $addval[$this->_flparams['id']] = $this->db->nextId($this->sequence_table); |
} else { |
$node_id = $values[$this->_flparams['id']]; |
} |
// Left/Right values for rootnodes |
$addval[$this->_flparams['l']] = 1; |
$addval[$this->_flparams['r']] = 2; |
// Transform the node data hash to a query |
if (!$qr = $this->_values2Query($values, $addval)) { |
$this->_releaseLock(); |
return false; |
} |
// Insert the new node |
$sql[] = sprintf('INSERT INTO %s SET %s', |
$this->node_table, |
$qr); |
for($i=0;$i<count($sql);$i++) { |
$res = $this->db->query($sql[$i]); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
// EVENT (nodeCreate) |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeCreate'])) { |
$this->triggerEvent('nodeCreate', $this->pickNode($node_id)); |
} |
$this->_releaseLock(); |
return $node_id; |
} |
// }}} |
// {{{ createSubNode() |
/** |
* Creates a subnode |
* |
* <pre> |
* +-- root1 |
* | |
* +-\ root2 [target] |
* | | |
* | |-- subnode1 [new] |
* | |
* +-- root3 |
* </pre> |
* |
* @param integer $id Parent node ID |
* @param array $values Hash with param => value pairs of the node (see $this->params) |
* |
* @access public |
* @return mixed The node id or false on error |
*/ |
function createSubNode($id, $values) { |
if ($this->debug) { |
$this->_debugMessage('createSubNode($id, $values)'); |
} |
// invalid parent id, bail out |
if (!($thisnode = $this->pickNode($id, true))) { |
$epr = array('createSubNode()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR, $epr); |
} |
// Try to aquire a table lock |
if(PEAR::isError($lock = $this->_setLock())) { |
return $lock; |
} |
$this->_verifyUserValues('createRootNode()', $values); |
// Get the children of the target node |
$children = $this->getChildren($id, true); |
// We have children here |
if ($thisnode['r']-1 != $thisnode['l']) { |
// Get the last child |
$last = array_pop($children); |
// What we have to do is virtually an insert of a node after the last child |
// So we don't have to proceed creating a subnode |
$newNode = $this->createRightNode($last['id'], $values); |
$this->_releaseLock(); |
return $newNode; |
} |
$sql = array(); |
$sql[] = sprintf(' |
UPDATE %s SET |
%s=IF(%s>=%s, %s+2, %s), |
%s=IF(%s>=%s, %s+2, %s) |
WHERE %s=%s', |
$this->node_table, |
$this->_flparams['l'], |
$this->_flparams['l'], |
$thisnode['r'], |
$this->_flparams['l'], |
$this->_flparams['l'], |
$this->_flparams['r'], |
$this->_flparams['r'], |
$thisnode['r'], |
$this->_flparams['r'], |
$this->_flparams['r'], |
$this->_flparams['rootid'], |
$thisnode['rootid'] |
); |
$addval = array(); |
if(isset($this->_flparams['parent'])) { |
$addval[$this->_flparams['parent']] = $thisnode['id']; |
} |
$addval[$this->_flparams['l']] = $thisnode['r']; |
$addval[$this->_flparams['r']] = $thisnode['r'] + 1; |
$addval[$this->_flparams['rootid']] = $thisnode['rootid']; |
$addval[$this->_flparams['norder']] = 1; |
$addval[$this->_flparams['level']] = $thisnode['level'] + 1; |
if(!$this->_dumbmode || !$node_id=isset($values[$this->_flparams['id']])) { |
$node_id = $addval[$this->_flparams['id']] = $this->db->nextId($this->sequence_table); |
} else { |
$node_id = $values[$this->_flparams['id']]; |
} |
if (!$qr = $this->_values2Query($values, $addval)) { |
$this->_releaseLock(); |
return false; |
} |
$sql[] = sprintf('INSERT INTO %s SET %s', |
$this->node_table, |
$qr); |
for($i=0;$i<count($sql);$i++) { |
$res = $this->db->query($sql[$i]); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
// EVENT (NodeCreate) |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeCreate'])) { |
$thisnode = $this->pickNode($node_id); |
$this->triggerEvent('nodeCreate', $this->pickNode($id)); |
} |
$this->_releaseLock(); |
return $node_id; |
} |
// }}} |
// {{{ createLeftNode() |
/** |
* Creates a node before a given node |
* <pre> |
* +-- root1 |
* | |
* +-\ root2 |
* | | |
* | |-- subnode2 [new] |
* | |-- subnode1 [target] |
* | |-- subnode3 |
* | |
* +-- root3 |
* </pre> |
* |
* @param int $id Target node ID |
* @param array $values Hash with param => value pairs of the node (see $this->params) |
* @param bool $returnID Tell the method to return a node id instead of an object. |
* ATTENTION: That the method defaults to return an object instead of the node id |
* has been overseen and is basically a bug. We have to keep this to maintain BC. |
* You will have to set $returnID to true to make it behave like the other creation methods. |
* This flaw will get fixed with the next major version. |
* |
* @access public |
* @return mixed The node id or false on error |
*/ |
function createLeftNode($id, $values) { |
if ($this->debug) { |
$this->_debugMessage('createLeftNode($target, $values)'); |
} |
$this->_verifyUserValues('createLeftode()', $values); |
// invalid target node, bail out |
if (!($thisnode = $this->pickNode($id, true))) { |
$epr = array('createLeftNode()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR, $epr); |
} |
if(PEAR::isError($lock=$this->_setLock())) { |
return $lock; |
} |
// If the target node is a rootnode we virtually want to create a new root node |
if ($thisnode['rootid'] == $thisnode['id']) { |
return $this->createRootNode($values, $id, false, NESE_MOVE_BEFORE); |
} |
$addval = array(); |
$parent = $this->getParent($id, true); |
if(isset($this->_flparams['parent'])) { |
$addval[$this->_flparams['parent']] = $parent['id']; |
} |
$sql = array(); |
$sql[] = sprintf('UPDATE %s SET %s=%s+1 |
WHERE |
%s=%s AND %s>=%s AND %s=%s AND %s BETWEEN %s AND %s', |
$this->node_table, |
$this->_flparams['norder'], |
$this->_flparams['norder'], |
$this->_flparams['rootid'], |
$thisnode['rootid'], |
$this->_flparams['norder'], |
$thisnode['norder'], |
$this->_flparams['level'], |
$thisnode['level'], |
$this->_flparams['l'], |
$parent['l'], |
$parent['r']); |
// Update all nodes which have dependent left and right values |
$sql[] = sprintf(' |
UPDATE %s SET |
%s=IF(%s>=%s, %s+2, %s), |
%s=IF(%s>=%s, %s+2, %s) |
WHERE %s=%s', |
$this->node_table, |
$this->_flparams['l'], |
$this->_flparams['l'], |
$thisnode['l'], |
$this->_flparams['l'], |
$this->_flparams['l'], |
$this->_flparams['r'], |
$this->_flparams['r'], |
$thisnode['r'], |
$this->_flparams['r'], |
$this->_flparams['r'], |
$this->_flparams['rootid'], |
$thisnode['rootid'] |
); |
$addval[$this->_flparams['norder']] = $thisnode['norder']; |
$addval[$this->_flparams['l']] = $thisnode['l']; |
$addval[$this->_flparams['r']] = $thisnode['l']+1; |
$addval[$this->_flparams['rootid']] = $thisnode['rootid']; |
$addval[$this->_flparams['level']] = $thisnode['level']; |
if(!$this->_dumbmode || !$node_id=isset($values[$this->_flparams['id']])) { |
$node_id = $addval[$this->_flparams['id']] = $this->db->nextId($this->sequence_table); |
} else { |
$node_id = $values[$this->_flparams['id']]; |
} |
if (!$qr = $this->_values2Query($values, $addval)) { |
$this->_releaseLock(); |
return false; |
} |
// Insert the new node |
$sql[] = sprintf('INSERT INTO %s SET %s', |
$this->node_table, |
$qr); |
for($i=0;$i<count($sql);$i++) { |
$res = $this->db->query($sql[$i]); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
// EVENT (NodeCreate) |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeCreate'])) { |
$this->triggerEvent('nodeCreate', $this->pickNode($id)); |
} |
$this->_releaseLock(); |
return $node_id; |
} |
/** |
* Creates a node after a given node |
* <pre> |
* +-- root1 |
* | |
* +-\ root2 |
* | | |
* | |-- subnode1 [target] |
* | |-- subnode2 [new] |
* | |-- subnode3 |
* | |
* +-- root3 |
* </pre> |
* |
* @param int $id Target node ID |
* @param array $values Hash with param => value pairs of the node (see $this->params) |
* @param bool $returnID Tell the method to return a node id instead of an object. |
* ATTENTION: That the method defaults to return an object instead of the node id |
* has been overseen and is basically a bug. We have to keep this to maintain BC. |
* You will have to set $returnID to true to make it behave like the other creation methods. |
* This flaw will get fixed with the next major version. |
* |
* @access public |
* @return mixed The node id or false on error |
*/ |
function createRightNode($id, $values) { |
if ($this->debug) { |
$this->_debugMessage('createRightNode($target, $values)'); |
} |
$this->_verifyUserValues('createRootNode()', $values); |
// invalid target node, bail out |
if (!($thisnode = $this->pickNode($id, true))) { |
$epr = array('createRightNode()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR, $epr); |
} |
if(PEAR::isError($lock=$this->_setLock())) { |
return $lock; |
} |
// If the target node is a rootnode we virtually want to create a new root node |
if ($thisnode['rootid'] == $thisnode['id']) { |
$nid = $this->createRootNode($values, $id); |
$this->_releaseLock(); |
return $nid; |
} |
$addval = array(); |
$parent = $this->getParent($id, true); |
if(isset($this->_flparams['parent'])) { |
$addval[$this->_flparams['parent']] = $parent['id']; |
} |
$sql = array(); |
$sql[] = sprintf('UPDATE %s SET %s=%s+1 |
WHERE |
%s=%s AND %s>%s AND %s=%s AND %s BETWEEN %s AND %s', |
$this->node_table, |
$this->_flparams['norder'], |
$this->_flparams['norder'], |
$this->_flparams['rootid'], |
$thisnode['rootid'], |
$this->_flparams['norder'], |
$thisnode['norder'], |
$this->_flparams['level'], |
$thisnode['level'], |
$this->_flparams['l'], |
$parent['l'], |
$parent['r']); |
// Update all nodes which have dependent left and right values |
$sql[] = sprintf(' |
UPDATE %s SET |
%s=IF(%s>%s, %s+2, %s), |
%s=IF(%s>%s, %s+2, %s) |
WHERE %s=%s', |
$this->node_table, |
$this->_flparams['l'], |
$this->_flparams['l'], |
$thisnode['r'], |
$this->_flparams['l'], |
$this->_flparams['l'], |
$this->_flparams['r'], |
$this->_flparams['r'], |
$thisnode['r'], |
$this->_flparams['r'], |
$this->_flparams['r'], |
$this->_flparams['rootid'], |
$thisnode['rootid'] |
); |
$addval[$this->_flparams['norder']] = $thisnode['norder'] + 1; |
$addval[$this->_flparams['l']] = $thisnode['r'] + 1; |
$addval[$this->_flparams['r']] = $thisnode['r'] + 2; |
$addval[$this->_flparams['rootid']] = $thisnode['rootid']; |
$addval[$this->_flparams['level']] = $thisnode['level']; |
if(!$this->_dumbmode || !isset($values[$this->_flparams['id']])) { |
$node_id = $addval[$this->_flparams['id']] = $this->db->nextId($this->sequence_table); |
} else { |
$node_id = $values[$this->_flparams['id']]; |
} |
if (!$qr = $this->_values2Query($values, $addval)) { |
$this->_releaseLock(); |
return false; |
} |
// Insert the new node |
$sql[] = sprintf('INSERT INTO %s SET %s', $this->node_table, $qr); |
for($i=0;$i<count($sql);$i++) { |
$res = $this->db->query($sql[$i]); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
// EVENT (NodeCreate) |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeCreate'])) { |
$this->triggerEvent('nodeCreate', $this->pickNode($id)); |
} |
$this->_releaseLock(); |
return $node_id; |
} |
// }}} |
// {{{ deleteNode() |
/** |
* Deletes a node |
* |
* @param int $id ID of the node to be deleted |
* |
* @access public |
* @return bool True if the delete succeeds |
*/ |
function deleteNode($id) { |
if ($this->debug) { |
$this->_debugMessage("deleteNode($id)"); |
} |
// invalid target node, bail out |
if (!($thisnode = $this->pickNode($id, true))) { |
$epr = array('deleteNode()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR, $epr); |
} |
if (PEAR::isError($lock = $this->_setLock())) { |
return $lock; |
} |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeDelete'])) { |
// EVENT (NodeDelete) |
$this->triggerEvent('nodeDelete', $this->pickNode($id)); |
} |
$parent = $this->getParent($id, true); |
$len = $thisnode['r'] - $thisnode['l'] + 1; |
$sql = array(); |
// Delete the node |
$sql[] = sprintf('DELETE FROM %s WHERE %s BETWEEN %s AND %s AND %s=%s', |
$this->node_table, |
$this->_flparams['l'], |
$thisnode['l'], |
$thisnode['r'], |
$this->_flparams['rootid'], |
$thisnode['rootid'] |
); |
if ($thisnode['id'] != $thisnode['rootid']) { |
// The node isn't a rootnode so close the gap |
$sql[] = sprintf('UPDATE %s SET |
%s=IF(%s>%s, %s-%s, %s), |
%s=IF(%s>%s, %s-%s, %s) |
WHERE %s=%s AND |
(%s>%s OR %s>%s)', |
$this->node_table, |
$this->_flparams['l'], |
$this->_flparams['l'], |
$thisnode['l'], |
$this->_flparams['l'], |
$len, |
$this->_flparams['l'], |
$this->_flparams['r'], |
$this->_flparams['r'], |
$thisnode['l'], |
$this->_flparams['r'], |
$len, |
$this->_flparams['r'], |
$this->_flparams['rootid'], |
$thisnode['rootid'], |
$this->_flparams['l'], |
$thisnode['l'], |
$this->_flparams['r'], |
$thisnode['r'] |
); |
// Re-order |
$sql[] = sprintf('UPDATE %s SET %s=%s-1 WHERE %s=%s AND %s=%s AND %s>%s AND %s BETWEEN %s AND %s', |
$this->node_table, |
$this->_flparams['norder'], |
$this->_flparams['norder'], |
$this->_flparams['rootid'], |
$thisnode['rootid'], |
$this->_flparams['level'], |
$thisnode['level'], |
$this->_flparams['norder'], |
$thisnode['norder'], |
$this->_flparams['l'], |
$parent['l'], |
$parent['r']); |
} else { |
// A rootnode was deleted and we only have to close the gap inside the order |
$sql[] = sprintf('UPDATE %s SET %s=%s+1 WHERE %s=%s AND %s > %s', |
$this->node_table, |
$this->_flparams['norder'], |
$this->_flparams['norder'], |
$this->_flparams['rootid'], |
$this->_flparams['id'], |
$this->_flparams['norder'], |
$thisnode['norder']); |
} |
for($i=0;$i<count($sql);$i++) { |
$res = $this->db->query($sql[$i]); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
$this->_releaseLock(); |
return true; |
} |
// }}} |
// {{{ updateNode() |
/** |
* Changes the payload of a node |
* |
* @param int $id Node ID |
* @param array $values Hash with param => value pairs of the node (see $this->params) |
* @param bool $_intermal Internal use only. Used to skip value validation. Leave this as it is. |
* |
* @access public |
* @return bool True if the update is successful |
*/ |
function updateNode($id, $values, $_internal=false) { |
if ($this->debug) { |
$this->_debugMessage('updateNode($id, $values)'); |
} |
if (PEAR::isError($lock = $this->_setLock())) { |
return $lock; |
} |
if(!$_internal) { |
$this->_verifyUserValues('createRootNode()', $values); |
} |
$eparams = array('values' => $values); |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeUpdate'])) { |
// EVENT (NodeUpdate) |
$this->triggerEvent('nodeUpdate', $this->pickNode($id), $eparams); |
} |
$addvalues = array(); |
if (!$qr = $this->_values2Query($values, $addvalues)) { |
$this->_releaseLock(); |
return false; |
} |
$sql = sprintf('UPDATE %s SET %s WHERE %s = %s', |
$this->node_table, |
$qr, |
$this->_flparams['id'], |
$id); |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
$this->_releaseLock(); |
return true; |
} |
// }}} |
// +----------------------------------------------+ |
// | Moving and copying | |
// |----------------------------------------------+ |
// | [PUBLIC] | |
// +----------------------------------------------+ |
// {{{ moveTree() |
/** |
* Wrapper for node moving and copying |
* |
* @param int $id Source ID |
* @param int $target Target ID |
* @param constant $pos Position (use one of the NESE_MOVE_* constants) |
* @param bool $copy Shall we create a copy |
* |
* @see _moveInsideLevel |
* @see _moveAcross |
* @see _moveRoot2Root |
* @access public |
* @return int ID of the moved node or false on error |
*/ |
function moveTree($id, $targetid, $pos, $copy = false) { |
if ($this->debug) { |
$this->_debugMessage('moveTree($id, $target, $pos, $copy = false)'); |
} |
if($id == $targetid && !$copy) { |
// TRIGGER BOGUS MESSAGE |
return false; |
} |
// Get information about source and target |
if (!($source = $this->pickNode($id, true))) { |
$epr = array('moveTree()', $id); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR, $epr); |
} |
if (!($target = $this->pickNode($targetid, true))) { |
$epr = array('moveTree()', $targetid); |
return $this->_raiseError(NESE_ERROR_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR, $epr); |
} |
if (PEAR::isError($lock = $this->_setLock(true))) { |
return $lock; |
} |
$this->_relations = array(); |
// This operations don't need callbacks except the copy handler |
// which ignores this setting |
$this->_skipCallbacks = true; |
if(!$copy) { |
// We have a recursion - let's stop |
if (($target['rootid'] == $source['rootid']) && |
(($source['l'] <= $target['l']) && |
($source['r'] >= $target['r']))) { |
$this->_releaseLock(true); |
$epr = array('moveTree()'); |
return $this->_raiseError(NESE_ERROR_RECURSION, PEAR_ERROR_RETURN, E_USER_NOTICE, $epr); |
} |
// Insert/move before or after |
if (($source['rootid'] == $source['id']) && |
($target['rootid'] == $target['id'])) { |
// We have to move a rootnode which is different from moving inside a tree |
$nid = $this->_moveRoot2Root($source, $target, $pos, $copy); |
$this->_releaseLock(true); |
return $nid; |
} |
} elseif(($target['rootid'] == $source['rootid']) && |
(($source['l'] < $target['l']) && |
($source['r'] > $target['r']))) { |
$this->_releaseLock(true); |
$epr = array('moveTree()'); |
return $this->_raiseError(NESE_ERROR_RECURSION, PEAR_ERROR_RETURN, E_USER_NOTICE, $epr); |
} |
// We have to move between different levels and maybe subtrees - let's rock ;) |
$this->_moveAcross($source, $target, $pos); |
$this->_moveCleanup($copy); |
$this->_releaseLock(true); |
} |
// }}} |
// {{{ _moveAcross() |
/** |
* Moves nodes and trees to other subtrees or levels |
* |
* <pre> |
* [+] <--------------------------------+ |
* +-[\] root1 [target] | |
* <-------------------------+ |p |
* +-\ root2 | | |
* | | | | |
* | |-- subnode1 [target] | |B |
* | |-- subnode2 [new] |S |E |
* | |-- subnode3 |U |F |
* | |B |O |
* +-\ root3 | |R |
* |-- subnode 3.1 | |E |
* |-\ subnode 3.2 [source] >--+------+ |
* |-- subnode 3.2.1 |
*</pre> |
* |
* @param object NodeCT $source Source node |
* @param object NodeCT $target Target node |
* @param string $pos Position [SUBnode/BEfore] |
* @param bool $copy Shall we create a copy |
* |
* @access private |
* @see moveTree |
* @see _r_moveAcross |
* @see _moveCleanup |
*/ |
function _moveAcross($source, $target, $pos) { |
if ($this->debug) { |
$this->_debugMessage('_moveAcross($source, $target, $pos, $copy = false)'); |
} |
// Get the current data from a node and exclude the id params which will be changed |
// because of the node move |
$values = array(); |
foreach($this->params as $key => $val) { |
if ($source[$val] && !in_array($val, $this->_requiredParams)) { |
$values[$key] = trim($source[$val]); |
} |
} |
switch($pos) { |
case NESE_MOVE_BEFORE: |
$clone_id = $this->createLeftNode($target['id'], $values); |
break; |
case NESE_MOVE_AFTER: |
$clone_id = $this->createRightNode($target['id'], $values); |
break; |
case NESE_MOVE_BELOW: |
$clone_id = $this->createSubNode($target['id'], $values); |
break; |
} |
$children = $this->getChildren($source['id'], true, true, true); |
if ($children) { |
$pos = NESE_MOVE_BELOW; |
$sclone_id = $clone_id; |
// Recurse through the child nodes |
foreach($children AS $cid => $child) { |
$sclone = $this->pickNode($sclone_id, true); |
$sclone_id = $this->_moveAcross($child, $sclone, $pos); |
$pos = NESE_MOVE_AFTER; |
} |
} |
$this->_relations[$source['id']] = $clone_id; |
return $clone_id; |
} |
// }}} |
// {{{ _moveCleanup() |
/** |
* Deletes the old subtree (node) and writes the node id's into the cloned tree |
* |
* |
* @param array $relations Hash in der Form $h[alteid]=neueid |
* @param array $copy Are we in copy mode? |
* @access private |
*/ |
function _moveCleanup($copy = false) { |
$relations = $this->_relations; |
if ($this->debug) { |
$this->_debugMessage('_moveCleanup($relations, $copy = false)'); |
} |
$deletes = array(); |
$updates = array(); |
$tb = $this->node_table; |
$fid = $this->_flparams['id']; |
$froot = $this->_flparams['rootid']; |
foreach($relations AS $key => $val) { |
$clone = $this->pickNode($val); |
if ($copy) { |
// EVENT (NodeCopy) |
$eparams = array('clone' => $clone); |
if (!$this->_skipCallbacks && isset($this->_hasListeners['nodeCopy'])) { |
$this->triggerEvent('nodeCopy', $this->pickNode($key), $eparams); |
} |
continue; |
} |
// No callbacks here because the node itself doesn't get changed |
// Only it's position |
// If one needs a callback here please let me know |
$deletes[] = $key; |
// It's isn't a rootnode |
if ($clone->id != $clone->rootid) { |
$sql = sprintf('UPDATE %s SET %s=%s WHERE %s = %s', |
$this->node_table, |
$fid, |
$key, |
$fid, |
$val); |
$updates[] = $sql; |
} else { |
$sql = sprintf('UPDATE %s SET %s=%s, %s=%s WHERE %s=%s', |
$tb, |
$fid, |
$key, |
$froot, |
$val, |
$fid, |
$val); |
$updates[] = $sql; |
$orootid = $clone->rootid; |
$sql = sprintf('UPDATE %s SET %s=%s WHERE %s=%s', |
$tb, |
$froot, |
$key, |
$froot, |
$orootid); |
$updates[] = $sql; |
} |
$this->_skipCallbacks = false; |
} |
if(!empty($deletes)) { |
for($i=0;$i<count($deletes);$i++) { |
$this->deleteNode($deletes[$i]); |
} |
} |
if(!empty($updates)) { |
for($i=0;$i<count($updates);$i++) { |
$res = $this->db->query($updates[$i]); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
} |
return true; |
} |
// }}} |
// {{{ _moveRoot2Root() |
/** |
* Moves rootnodes |
* |
* <pre> |
* +-- root1 |
* | |
* +-\ root2 |
* | | |
* | |-- subnode1 [target] |
* | |-- subnode2 [new] |
* | |-- subnode3 |
* | |
* +-\ root3 |
* [|] <-----------------------+ |
* |-- subnode 3.1 [target] | |
* |-\ subnode 3.2 [source] >--+ |
* |-- subnode 3.2.1 |
* </pre> |
* |
* @param object NodeCT $source Source |
* @param object NodeCT $target Target |
* @param string $pos BEfore | AFter |
* @access private |
* @see moveTree |
*/ |
function _moveRoot2Root($source, $target, $pos) { |
if ($this->debug) { |
$this->_debugMessage('_moveRoot2Root($source, $target, $pos, $copy)'); |
} |
if(PEAR::isError($lock=$this->_setLock())) { |
return $lock; |
} |
$tb = $this->node_table; |
$fid = $this->_flparams['id']; |
$froot = $this->_flparams['rootid']; |
$freh = $this->_flparams['norder']; |
$s_order = $source['norder']; |
$t_order = $target['norder']; |
$s_id = $source['id']; |
$t_id = $target['id']; |
if ($s_order < $t_order) { |
if ($pos == NESE_MOVE_BEFORE) { |
$sql = "UPDATE $tb SET $freh=$freh-1 |
WHERE $freh BETWEEN $s_order AND $t_order AND |
$fid!=$t_id AND |
$fid!=$s_id AND |
$froot=$fid"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
$sql = "UPDATE $tb SET $freh=$t_order -1 WHERE $fid=$s_id"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
elseif($pos == NESE_MOVE_AFTER) { |
$sql = "UPDATE $tb SET $freh=$freh-1 |
WHERE $freh BETWEEN $s_order AND $t_order AND |
$fid!=$s_id AND |
$froot=$fid"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
$sql = "UPDATE $tb SET $freh=$t_order WHERE $fid=$s_id"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
} |
if ($s_order > $t_order) { |
if ($pos == NESE_MOVE_BEFORE) { |
$sql = "UPDATE $tb SET $freh=$freh+1 |
WHERE $freh BETWEEN $t_order AND $s_order AND |
$fid != $s_id AND |
$froot=$fid"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
$sql = "UPDATE $tb SET $freh=$t_order WHERE $fid=$s_id"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
elseif ($pos == NESE_MOVE_AFTER) { |
$sql = "UPDATE $tb SET $freh=$freh+1 |
WHERE $freh BETWEEN $t_order AND $s_order AND |
$fid!=$t_id AND |
$fid!=$s_id AND |
$froot=$fid"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
$sql = "UPDATE $tb SET $freh=$t_order+1 WHERE $fid = $s_id"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
} |
$this->_releaseLock(); |
return $source->id; |
} |
// }}} |
// +-----------------------+ |
// | Helper methods | |
// +-----------------------+ |
// }}} |
// {{{ _secSort() |
/** |
* Callback for uasort used to sort siblings |
* |
* @access private |
*/ |
function _secSort($node1, $node2) { |
// Within the same level? |
if($node1['level'] != $node2['level']) { |
return strnatcmp($node1['l'], $node2['l']); |
} |
// Are they siblings? |
$p1 = $this->getParent($node1); |
$p2 = $this->getParent($node2); |
if($p1['id'] != $p2['id']) { |
return strnatcmp($node1['l'], $node2['l']); |
} |
// Same field value? Use the lft value then |
$field = $this->params[$this->secondarySort]; |
if($node1[$field] == $node2[$field]) { |
return strnatcmp($node1['l'], $node2[l]); |
} |
// Compare between siblings with different field value |
return strnatcmp($node1[$field], $node2[$field]); |
} |
// }}} |
// {{{ _addSQL() |
/** |
* Adds a specific type of SQL to a query string |
* |
* @param array $addSQL The array of SQL strings to add. Example value: |
* $addSQL = array( |
* 'cols' => 'tb2.col2, tb2.col3', // Additional tables/columns |
* 'join' => 'LEFT JOIN tb1 USING(STRID)', // Join statement |
* 'append' => 'GROUP by tb1.STRID'); // Group condition |
* @param string $type The type of SQL. Can be 'cols', 'join', or 'append'. |
* |
* @access private |
* @return string The SQL, properly formatted |
*/ |
function _addSQL($addSQL, $type) { |
if (!isset($addSQL[$type])) { |
return ''; |
} |
switch($type) { |
case 'cols': |
return ', ' . $addSQL[$type]; |
default: |
return $addSQL[$type]; |
} |
} |
// }}} |
// {{{ _getSelectFields() |
/** |
* Gets the select fields based on the params |
* |
* @param bool $aliasFields Should we alias the fields so they are the names of the |
* parameter keys, or leave them as is? |
* |
* @access private |
* @return string A string of query fields to select |
*/ |
function _getSelectFields($aliasFields) { |
$queryFields = array(); |
foreach ($this->params as $key => $val) { |
$tmp_field = $this->node_table . '.' . $key; |
if ($aliasFields) { |
$tmp_field .= ' AS ' . $val; |
} |
$queryFields[] = $tmp_field; |
} |
$fields = implode(', ', $queryFields); |
return $fields; |
} |
// }}} |
// {{{ _processResultSet() |
/** |
* Processes a DB result set by checking for a DB error and then transforming the result |
* into a set of DB_NestedSet_Node objects or leaving it as an array. |
* |
* @param string $sql The sql query to be done |
* @param bool $keepAsArray Keep the result as an array or transform it into a set of |
* DB_NestedSet_Node objects? |
* @param bool $fieldsAreAliased Are the fields aliased? |
* |
* @access private |
* @return mixed False on error or the transformed node set. |
*/ |
function _processResultSet($sql, $keepAsArray, $fieldsAreAliased) { |
$result = $this->db->getAll($sql); |
if ($this->_testFatalAbort($result, __FILE__, __LINE__)) { |
return false; |
} |
$nodes = array(); |
$idKey = $fieldsAreAliased ? 'id' : $this->_flparams['id']; |
foreach ($result as $row) { |
$node_id = $row[$idKey]; |
if ($keepAsArray) { |
$nodes[$node_id] = $row; |
} else { |
// Create an instance of the node container |
$nodes[$node_id] =& new DB_NestedSet_Node($row); |
} |
} |
return $nodes; |
} |
// }}} |
// {{{ _testFatalAbort() |
/** |
* Error Handler |
* |
* Tests if a given ressource is a PEAR error object |
* ans raises a fatal error in case of an error object |
* |
* @param object PEAR::Error $errobj The object to test |
* @param string $file The filename wher the error occured |
* @param int $line The line number of the error |
* @return void |
* @access private |
*/ |
function _testFatalAbort($errobj, $file, $line) { |
if (!$this->_isDBError($errobj)) { |
return false; |
} |
if ($this->debug) { |
$this->_debugMessage('_testFatalAbort($errobj, $file, $line)'); |
} |
if ($this->debug) { |
$message = $errobj->getUserInfo(); |
$code = $errobj->getCode(); |
$msg = "$message ($code) in file $file at line $line"; |
} else { |
$msg = $errobj->getMessage(); |
$code = $errobj->getCode(); } |
PEAR::raiseError($msg, $code, PEAR_ERROR_TRIGGER, E_USER_ERROR); |
} |
// {{{ __raiseError() |
/** |
* @access private |
*/ |
function _raiseError($code, $mode, $option, $epr=array()) { |
$message = vsprintf($this->_getMessage($code), $epr); |
return PEAR::raiseError($message, $code, $mode, $option); |
} |
// }}} |
// {{{ addListener() |
/** |
* Add an event listener |
* |
* Adds an event listener and returns an ID for it |
* |
* @param string $event The ivent name |
* @param string $listener The listener object |
* @return string |
* @access public |
*/ |
function addListener($event, &$listener) { |
$listenerID = uniqid('el'); |
$this->eventListeners[$event][$listenerID] =& $listener; |
$this->_hasListeners[$event] = true; |
return $listenerID; |
} |
// }}} |
// {{{ removeListener() |
/** |
* Removes an event listener |
* |
* Removes the event listener with the given ID |
* |
* @param string $event The ivent name |
* @param string $listenerID The listener's ID |
* @return bool |
* @access public |
*/ |
function removeListener($event, $listenerID) { |
unset($this->eventListeners[$event][$listenerID]); |
if (!isset($this->eventListeners[$event]) || |
!is_array($this->eventListeners[$event]) || |
count($this->eventListeners[$event]) == 0) { |
unset($this->_hasListeners[$event]); |
} |
return true; |
} |
// }}} |
// {{{ triggerEvent() |
/** |
* Triggers and event an calls the event listeners |
* |
* @param string $event The Event that occured |
* @param object node $node A Reference to the node object which was subject to changes |
* @param array $eparams A associative array of params which may be needed by the handler |
* @return bool |
* @access public |
*/ |
function triggerEvent($event, &$node, $eparams = false) { |
if ($this->_skipCallbacks || !isset($this->_hasListeners[$event])) { |
return false; |
} |
foreach($this->eventListeners[$event] as $key => $val) { |
if (!method_exists($val, 'callEvent')) { |
return new PEAR_Error($this->_getMessage(NESE_ERROR_NOHANDLER), NESE_ERROR_NOHANDLER); |
} |
$val->callEvent($event, $node, $eparams); |
} |
return true; |
} |
// }}} |
// {{{ apiVersion() |
function apiVersion() { |
return array( |
'package:'=>$this->_packagename, |
'majorversion'=>$this->_majorversion, |
'minorversion'=>$this->_minorversion, |
'version'=>sprintf('%s.%s',$this->_majorversion, $this->_minorversion), |
'revision'=>str_replace('$', '',"$Revision: 1.1 $") |
); |
} |
// }}} |
// {{{ setAttr() |
/** |
* Sets an object attribute |
* |
* @param array $attr An associative array with attributes |
* |
* @return bool |
* @access public |
*/ |
function setAttr($attr) { |
static $hasSetSequence; |
if (!isset($hasSetSequence)) { |
$hasSetSequence = false; |
} |
if (!is_array($attr) || count($attr) == 0) { |
return false; |
} |
foreach ($attr as $key => $val) { |
$this->$key = $val; |
if ($key == 'sequence_table') { |
$hasSetSequence = true; |
} |
// only update sequence to reflect new table if they haven't set it manually |
if (!$hasSetSequence && $key == 'node_table') { |
$this->sequence_table = $this->node_table . '_' . $this->_flparams['id']; |
} |
if($key == 'cache' && is_object($val)) { |
$this->_caching = true; |
$GLOBALS['DB_NestedSet'] = & $this; |
} |
} |
return true; |
} |
// }}} |
// {{{ setsortMode() |
/** |
* This enables you to set specific options for each output method |
* |
* @param constant $sortMode |
* |
* @access public |
* @return Current sortMode |
*/ |
function setsortMode($sortMode=false) { |
if($sortMode && in_array($sortMode, $this->_sortModes)) { |
$this->_sortMode = $sortMode; |
} else { |
return $this->_sortMode; |
} |
return $this->_sortMode; |
} |
// }}} |
// {{{ setDbOption() |
/** |
* Sets a db option. Example, setting the sequence table format |
* |
* @var string $option The option to set |
* @var string $val The value of the option |
* |
* @access public |
* @return void |
*/ |
function setDbOption($option, $val) { |
$this->db->setOption($option, $val); |
} |
// }}} |
// {{{ testLock() |
/** |
* Tests if a database lock is set |
* |
* @access public |
*/ |
function testLock() { |
if ($this->debug) { |
$this->_debugMessage('testLock()'); |
} |
if($lockID = $this->_structureTableLock) { |
return $lockID; |
} |
$this->_lockGC(); |
$sql = sprintf('SELECT lockID FROM %s WHERE lockTable=%s', |
$this->lock_table, |
$this->_quote($this->node_table)) ; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
if ($this->_numRows($res)) { |
return new PEAR_Error($this->_getMessage(NESE_ERROR_TBLOCKED),NESE_ERROR_TBLOCKED); |
} |
return false; |
} |
// }}} |
// {{{ _setLock() |
/** |
* @access private |
*/ |
function _setLock($exclusive=false) { |
$lock = $this->testLock(); |
if(PEAR::isError($lock)) { |
return $lock; |
} |
if ($this->debug) { |
$this->_debugMessage('_setLock()'); |
} |
if($this->_caching) { |
@$this->cache->flush('function_cache'); |
$this->_caching = false; |
$this->_restcache = true; |
} |
if (!$lockID = $this->_structureTableLock) { |
$lockID = $this->_structureTableLock = uniqid('lck-'); |
$sql = sprintf('INSERT INTO %s SET lockID=%s, lockTable=%s, lockStamp=%s', |
$this->lock_table, |
$this->_quote($lockID), |
$this->_quote($this->node_table), |
time()); |
} else { |
$sql = sprintf('UPDATE %s set lockStamp=%s WHERE lockID=%s AND lockTable=%s', |
$this->lock_table, |
time(), |
$this->_quote($lockID), |
$this->_quote($this->node_table)); |
} |
if($exclusive) { |
$this->_lockExclusive = true; |
} |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
return $lockID; |
} |
// }}} |
// {{{ _releaseLock() |
/** |
* @access private |
*/ |
function _releaseLock($exclusive=false) { |
if ($this->debug) { |
$this->_debugMessage('_releaseLock()'); |
} |
if($exclusive) { |
$this->_lockExclusive = false; |
} |
if ((!$lockID = $this->_structureTableLock) || $this->_lockExclusive) { |
return false; |
} |
$tb = $this->lock_table; |
$stb = $this->node_table; |
$sql = "DELETE FROM $tb |
WHERE lockTable=" . $this->_quote($stb) . " AND |
lockID=" . $this->_quote($lockID); |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
$this->_structureTableLock = false; |
if($this->_restcache) { |
$this->_caching = true; |
$this->_restcache = false; |
} |
return true; |
} |
// }}} |
// {{{ _lockGC() |
/** |
* @access private |
*/ |
function _lockGC() { |
if ($this->debug) { |
$this->_debugMessage('_lockGC()'); |
} |
$tb = $this->lock_table; |
$stb = $this->node_table; |
$lockTTL = time() - $this->lockTTL; |
$sql = "DELETE FROM $tb |
WHERE lockTable=" . $this->_quote($stb) . " AND |
lockStamp < $lockTTL"; |
$res = $this->db->query($sql); |
$this->_testFatalAbort($res, __FILE__, __LINE__); |
} |
// }}} |
// {{{ _values2Query() |
/** |
* @access private |
*/ |
function _values2Query($values, $addval = false) { |
if ($this->debug) { |
$this->_debugMessage('_values2Query($values, $addval = false)'); |
} |
if (is_array($addval)) { |
$values = $values + $addval; |
} |
$arq = array(); |
foreach($values AS $key => $val) { |
$k = trim($key); |
$v = trim($val); |
if ($k) { |
// To be used with the next mahor version |
// $iv = in_array($this->params[$k], $this->_quotedParams) ? $this->_quote($v) : $v; |
$iv = $this->_quote($v); |
$arq[] = "$k=$iv"; |
} |
} |
if (!is_array($arq) || count($arq) == 0) { |
return false; |
} |
$query = implode(', ', $arq); |
return $query; |
} |
// }}} |
// {{{ _verifyUserValues() |
/** |
* Clean values from protected or unknown columns |
* |
* @var string $caller The calling method |
* @var string $values The values array |
* |
* @access private |
* @return void |
*/ |
function _verifyUserValues($caller, &$values) { |
if($this->_dumbmode) { |
return true; |
} |
foreach($values AS $field=>$value) { |
if(!isset($this->params[$field])) { |
$epr = array( |
$caller, |
sprintf('Unknown column/param \'%s\'', $field)); |
$this->_raiseError(NESE_ERROR_WRONG_MPARAM, PEAR_ERROR_RETURN, E_USER_NOTICE, $epr); |
unset($values[$field]); |
} else { |
$flip = $this->params[$field]; |
if(in_array($flip, $this->_requiredParams)) { |
$epr = array( |
$caller, |
sprintf('\'%s\' is autogenerated and can\'t be passed - it will be ignored', $field)); |
$this->_raiseError(NESE_ERROR_WRONG_MPARAM, PEAR_ERROR_RETURN, E_USER_NOTICE, $epr); |
unset($values[$field]); |
} |
} |
} |
} |
// }}} |
// {{{ _debugMessage() |
/** |
* @access private |
*/ |
function _debugMessage($msg) { |
if ($this->debug) { |
$time = $this->_getmicrotime(); |
echo "$time::Debug:: $msg<br />\n"; |
} |
} |
// }}} |
// {{{ _getMessage() |
/** |
* @access private |
*/ |
function _getMessage($code) { |
if ($this->debug) { |
$this->_debugMessage('_getMessage($code)'); |
} |
return isset($this->messages[$code]) ? $this->messages[$code] : $this->messages[NESE_MESSAGE_UNKNOWN]; |
} |
// }}} |
// {{{ _getmicrotime() |
/** |
* @access private |
*/ |
function _getmicrotime() { |
list($usec, $sec) = explode(' ', microtime()); |
return ((float)$usec + (float)$sec); |
} |
// }}} |
// {{{ convertTreeModel() |
/** |
* Convert a <1.3 tree into a 1.3 tree format |
* |
* This will convert the tree into a format needed for some new features in |
* 1.3. Your <1.3 tree will still work without converting but some new features |
* like preorder sorting won't work as expected. |
* |
* <pre> |
* Usage: |
* - Create a new node table (tb_nodes2) from the current node table (tb_nodes1) (only copy the structure). |
* - Create a nested set instance of the 'old' set (NeSe1) and one of the new set (NeSe2) |
* - Now you have 2 identical objects where only node_table differs |
* - Call DB_NestedSet::convertTreeModel(&$orig, &$copy); |
* - After that you have a cleaned up copy of tb_nodes1 inside tb_nodes2 |
* </pre> |
* |
* @param object DB_NestedSet $orig Nested set we want to copy |
* @param object DB_NestedSet $copy Object where the new tree is copied to |
* @param integer $_parent ID of the parent node (private) |
* |
* @static |
* @access public |
* @return bool True uns success |
*/ |
function convertTreeModel(&$orig, &$copy, $_parent=false) { |
static $firstSet; |
$isRoot = false; |
if(!$_parent) { |
if(!is_object($orig) || !is_object($copy)) { |
return false; |
} |
if($orig->node_table == $copy->node_table) { |
return false; |
} |
$copy->_dumbmode = true; |
$orig->sortMode = NESE_SORT_LEVEL; |
$copy->sortMode = NESE_SORT_LEVEL; |
$sibl = $orig->getRootNodes(true); |
$isRoot = true; |
} else { |
$sibl = $orig->getChildren($_parent, true); |
} |
if(empty($sibl)) { |
return false; |
} |
foreach($sibl AS $sid=>$sibling) { |
unset($sibling['l']); |
unset($sibling['r']); |
unset($sibling['norder']); |
$values = array(); |
foreach($sibling AS $key=>$val) { |
if(!isset($copy->_flparams[$key])) { |
continue; |
} |
$values[$copy->_flparams[$key]] = $val; |
} |
if(!$firstSet) { |
$psid = $copy->createRootNode($values, false, true); |
$firstSet = true; |
} elseif($isRoot) { |
$psid = $copy->createRightNode($psid, $values); |
} else { |
$copy->createSubNode($_parent, $values); |
} |
DB_NestedSet::convertTreeModel($orig, $copy, $sid); |
} |
return true; |
} |
// }}} |
// {{{ _numRows() |
/** |
* Fetches the number of rows the last query returned |
* @access private |
* @abstract |
*/ |
function _numRows($res) { |
} |
// }}} |
// {{{ _isDBError() |
/** |
* Returns true if a db return value is an error object |
* @access private |
* @abstract |
*/ |
function _isDBError($err) { |
} |
// }}} |
// {{{ quote() |
/** |
* Quotes a string to use it inside queries |
* @access private |
* @abstract |
*/ |
function _quote($str) { |
} |
} |
// {{{ DB_NestedSet_Node:: class |
/** |
* Generic class for node objects |
* |
* @autor Daniel Khan <dk@webcluster.at>; |
* @version $Revision: 1.1 $ |
* @package DB_NestedSet |
* |
* @access private |
*/ |
class DB_NestedSet_Node { |
// {{{ constructor |
/** |
* Constructor |
*/ |
function DB_NestedSet_Node($data) { |
if (!is_array($data) || count($data) == 0) { |
return new PEAR_ERROR($data, NESE_ERROR_PARAM_MISSING); |
} |
$this->setAttr($data); |
return true; |
} |
// }}} |
// {{{ setAttr() |
function setAttr($data) { |
if(!is_array($data) || count($data) == 0) { |
return false; |
} |
foreach ($data as $key => $val) { |
$this->$key = $val; |
} |
} |
// }}} |
} |
// }}} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/Pager.php |
---|
New file |
0,0 → 1,250 |
<?php |
// |
// Pear DB Pager - Retrieve and return information of databases |
// result sets |
// |
// Copyright (C) 2001 Tomas Von Veschler Cox <cox@idecnet.com> |
// |
// This library is free software; you can redistribute it and/or |
// modify it under the terms of the GNU Lesser General Public |
// License as published by the Free Software Foundation; either |
// version 2.1 of the License, or (at your option) any later version. |
// |
// This library is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// Lesser General Public License for more details. |
// |
// You should have received a copy of the GNU Lesser General Public |
// License along with this library; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// |
// $Id: Pager.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
require_once 'PEAR.php'; |
require_once 'DB.php'; |
/** |
* This class handles all the stuff needed for displaying paginated results |
* from a database query of Pear DB, in a very easy way. |
* Documentation and examples of use, can be found in: |
* http://vulcanonet.com/soft/pager/ (could be outdated) |
* |
* IMPORTANT! |
* Since PEAR DB already support native row limit (more fast and avaible in |
* all the drivers), there is no more need to use $pager->build() or |
* the $pager->fetch*() methods. |
* |
* Usage example: |
* |
*< ?php |
* require_once 'DB/Pager.php'; |
* $db = DB::connect('your DSN string'); |
* $from = 0; // The row to start to fetch from (you might want to get this |
* // param from the $_GET array |
* $limit = 10; // The number of results per page |
* $maxpages = 10; // The number of pages for displaying in the pager (optional) |
* $res = $db->limitQuery($sql, $from, $limit); |
* $nrows = 0; // Alternative you could use $res->numRows() |
* while ($row = $res->fetchrow()) { |
* // XXX code for building the page here |
* $nrows++; |
* } |
* $data = DB_Pager::getData($from, $limit, $nrows, $maxpages); |
* // XXX code for building the pager here |
* ? > |
* |
* @version 0.7 |
* @author Tomas V.V.Cox <cox@idecnet.com> |
* @see http://vulcanonet.com/soft/pager/ |
*/ |
class DB_Pager extends PEAR |
{ |
/** |
* Constructor |
* |
* @param object $res A DB_result object from Pear_DB |
* @param int $from The row to start fetching |
* @param int $limit How many results per page |
* @param int $numrows Pager will automatically |
* find this param if is not given. If your Pear_DB backend extension |
* doesn't support numrows(), you can manually calculate it |
* and supply later to the constructor |
* @deprecated |
*/ |
function DB_Pager (&$res, $from, $limit, $numrows = null) |
{ |
$this->res = $res; |
$this->from = $from; |
$this->limit = $limit; |
$this->numrows = $numrows; |
} |
/** |
* Calculates all the data needed by Pager to work |
* |
* @return mixed An assoc array with all the data (see getData) |
* or DB_Error on error |
* @see DB_Pager::getData |
* @deprecated |
*/ |
function build() |
{ |
// if there is no numrows given, calculate it |
if ($this->numrows === null) { |
$this->numrows = $this->res->numrows(); |
if (DB::isError($this->numrows)) { |
return $this->numrows; |
} |
} |
$data = $this->getData($this->from, $this->limit, $this->numrows); |
if (DB::isError($data)) { |
return $data; |
} |
$this->current = $this->from - 1; |
$this->top = $data['to']; |
return $data; |
} |
/** |
* @deprecated |
*/ |
function fetchRow($mode=DB_FETCHMODE_DEFAULT) |
{ |
$this->current++; |
if ($this->current >= $this->top) { |
return null; |
} |
return $this->res->fetchRow($mode, $this->current); |
} |
/** |
* @deprecated |
*/ |
function fetchInto(&$arr, $mode=DB_FETCHMODE_DEFAULT) |
{ |
$this->current++; |
if ($this->current >= $this->top) { |
return null; |
} |
return $this->res->fetchInto($arr, $mode, $this->current); |
} |
/* |
* Gets all the data needed to paginate results |
* This is an associative array with the following |
* values filled in: |
* |
* array( |
* 'current' => X, // current page you are |
* 'numrows' => X, // total number of results |
* 'next' => X, // row number where next page starts |
* 'prev' => X, // row number where prev page starts |
* 'remain' => X, // number of results remaning *in next page* |
* 'numpages'=> X, // total number of pages |
* 'from' => X, // the row to start fetching |
* 'to' => X, // the row to stop fetching |
* 'limit' => X, // how many results per page |
* 'maxpages' => X, // how many pages to show (google style) |
* 'firstpage' => X, // the row number of the first page |
* 'lastpage' => X, // the row number where the last page starts |
* 'pages' => array( // assoc with page "number => start row" |
* 1 => X, |
* 2 => X, |
* 3 => X |
* ) |
* ); |
* @param int $from The row to start fetching |
* @param int $limit How many results per page |
* @param int $numrows Number of results from query |
* |
* @return array associative array with data or DB_error on error |
* |
*/ |
function &getData($from, $limit, $numrows, $maxpages = false) |
{ |
if (empty($numrows) || ($numrows < 0)) { |
return null; |
} |
$from = (empty($from)) ? 0 : $from; |
if ($limit <= 0) { |
return PEAR::raiseError (null, 'wrong "limit" param', null, |
null, null, 'DB_Error', true); |
} |
// Total number of pages |
$pages = ceil($numrows/$limit); |
$data['numpages'] = $pages; |
// first & last page |
$data['firstpage'] = 1; |
$data['lastpage'] = $pages; |
// Build pages array |
$data['pages'] = array(); |
for ($i=1; $i <= $pages; $i++) { |
$offset = $limit * ($i-1); |
$data['pages'][$i] = $offset; |
// $from must point to one page |
if ($from == $offset) { |
// The current page we are |
$data['current'] = $i; |
} |
} |
if (!isset($data['current'])) { |
return PEAR::raiseError (null, 'wrong "from" param', null, |
null, null, 'DB_Error', true); |
} |
// Limit number of pages (goole algoritm) |
if ($maxpages) { |
$radio = floor($maxpages/2); |
$minpage = $data['current'] - $radio; |
if ($minpage < 1) { |
$minpage = 1; |
} |
$maxpage = $data['current'] + $radio - 1; |
if ($maxpage > $data['numpages']) { |
$maxpage = $data['numpages']; |
} |
foreach (range($minpage, $maxpage) as $page) { |
$tmp[$page] = $data['pages'][$page]; |
} |
$data['pages'] = $tmp; |
$data['maxpages'] = $maxpages; |
} else { |
$data['maxpages'] = null; |
} |
// Prev link |
$prev = $from - $limit; |
$data['prev'] = ($prev >= 0) ? $prev : null; |
// Next link |
$next = $from + $limit; |
$data['next'] = ($next < $numrows) ? $next : null; |
// Results remaining in next page & Last row to fetch |
if ($data['current'] == $pages) { |
$data['remain'] = 0; |
$data['to'] = $numrows; |
} else { |
if ($data['current'] == ($pages - 1)) { |
$data['remain'] = $numrows - ($limit*($pages-1)); |
} else { |
$data['remain'] = $limit; |
} |
$data['to'] = $data['current'] * $limit; |
} |
$data['numrows'] = $numrows; |
$data['from'] = $from + 1; |
$data['limit'] = $limit; |
return $data; |
} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/mssql.php |
---|
New file |
0,0 → 1,914 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's mssql extension |
* for interacting with Microsoft SQL Server 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 Sterling Hughes <sterling@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: mssql.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 mssql extension |
* for interacting with Microsoft SQL Server databases |
* |
* These methods overload the ones declared in DB_common. |
* |
* @category Database |
* @package DB |
* @author Sterling Hughes <sterling@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: 1.7.6 |
* @link http://pear.php.net/package/DB |
*/ |
class DB_mssql extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'mssql'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'mssql'; |
/** |
* 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' => 'emulate', |
'new_link' => false, |
'numrows' => true, |
'pconnect' => true, |
'prepare' => false, |
'ssl' => false, |
'transactions' => true, |
); |
/** |
* A mapping of native error codes to DB error codes |
* @var array |
*/ |
// XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX |
var $errorcode_map = array( |
110 => DB_ERROR_VALUE_COUNT_ON_ROW, |
155 => DB_ERROR_NOSUCHFIELD, |
170 => DB_ERROR_SYNTAX, |
207 => DB_ERROR_NOSUCHFIELD, |
208 => DB_ERROR_NOSUCHTABLE, |
245 => DB_ERROR_INVALID_NUMBER, |
515 => DB_ERROR_CONSTRAINT_NOT_NULL, |
547 => DB_ERROR_CONSTRAINT, |
1913 => DB_ERROR_ALREADY_EXISTS, |
2627 => DB_ERROR_CONSTRAINT, |
2714 => DB_ERROR_ALREADY_EXISTS, |
3701 => DB_ERROR_NOSUCHTABLE, |
8134 => DB_ERROR_DIVZERO, |
); |
/** |
* 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 = null; |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_mssql() |
{ |
$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. |
* |
* @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('mssql') && !PEAR::loadExtension('sybase') |
&& !PEAR::loadExtension('sybase_ct')) |
{ |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
$params = array( |
$dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', |
$dsn['username'] ? $dsn['username'] : null, |
$dsn['password'] ? $dsn['password'] : null, |
); |
if ($dsn['port']) { |
$params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':') |
. $dsn['port']; |
} |
$connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect'; |
$this->connection = @call_user_func_array($connect_function, $params); |
if (!$this->connection) { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
null, null, null, |
@mssql_get_last_message()); |
} |
if ($dsn['database']) { |
if (!@mssql_select_db($dsn['database'], $this->connection)) { |
return $this->raiseError(DB_ERROR_NODBSELECTED, |
null, null, null, |
@mssql_get_last_message()); |
} |
$this->_db = $dsn['database']; |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
$ret = @mssql_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; |
if (!@mssql_select_db($this->_db, $this->connection)) { |
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
$query = $this->modifyQuery($query); |
if (!$this->autocommit && $ismanip) { |
if ($this->transaction_opcount == 0) { |
$result = @mssql_query('BEGIN TRAN', $this->connection); |
if (!$result) { |
return $this->mssqlRaiseError(); |
} |
} |
$this->transaction_opcount++; |
} |
$result = @mssql_query($query, $this->connection); |
if (!$result) { |
return $this->mssqlRaiseError(); |
} |
// Determine which queries that should return data, and which |
// should return an error code only. |
return $ismanip ? DB_OK : $result; |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal mssql 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 @mssql_next_result($result); |
} |
// }}} |
// {{{ 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 (!@mssql_data_seek($result, $rownum)) { |
return null; |
} |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
$arr = @mssql_fetch_array($result, MSSQL_ASSOC); |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @mssql_fetch_row($result); |
} |
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); |
} |
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 @mssql_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 = @mssql_num_fields($result); |
if (!$cols) { |
return $this->mssqlRaiseError(); |
} |
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 = @mssql_num_rows($result); |
if ($rows === false) { |
return $this->mssqlRaiseError(); |
} |
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 (!@mssql_select_db($this->_db, $this->connection)) { |
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
$result = @mssql_query('COMMIT TRAN', $this->connection); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->mssqlRaiseError(); |
} |
} |
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 (!@mssql_select_db($this->_db, $this->connection)) { |
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
$result = @mssql_query('ROLLBACK TRAN', $this->connection); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->mssqlRaiseError(); |
} |
} |
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)) { |
$res = @mssql_query('select @@rowcount', $this->connection); |
if (!$res) { |
return $this->mssqlRaiseError(); |
} |
$ar = @mssql_fetch_row($res); |
if (!$ar) { |
$result = 0; |
} else { |
@mssql_free_result($res); |
$result = $ar[0]; |
} |
} else { |
$result = 0; |
} |
return $result; |
} |
// }}} |
// {{{ 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_mssql::createSequence(), DB_mssql::dropSequence() |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$seqname = $this->getSequenceName($seq_name); |
if (!@mssql_select_db($this->_db, $this->connection)) { |
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
$repeat = 0; |
do { |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); |
$this->popErrorHandling(); |
if ($ondemand && DB::isError($result) && |
($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) |
{ |
$repeat = 1; |
$result = $this->createSequence($seq_name); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
} elseif (!DB::isError($result)) { |
$result =& $this->query("SELECT @@IDENTITY FROM $seqname"); |
$repeat = 0; |
} else { |
$repeat = false; |
} |
} while ($repeat); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
$result = $result->fetchRow(DB_FETCHMODE_ORDERED); |
return $result[0]; |
} |
/** |
* 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_mssql::nextID(), DB_mssql::dropSequence() |
*/ |
function createSequence($seq_name) |
{ |
return $this->query('CREATE TABLE ' |
. $this->getSequenceName($seq_name) |
. ' ([id] [int] IDENTITY (1, 1) NOT NULL,' |
. ' [vapor] [int] NULL)'); |
} |
// }}} |
// {{{ 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_mssql::nextID(), DB_mssql::createSequence() |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
} |
// }}} |
// {{{ quoteIdentifier() |
/** |
* Quotes a string so it can be safely used as a table or column name |
* |
* @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_replace(']', ']]', $str) . ']'; |
} |
// }}} |
// {{{ mssqlRaiseError() |
/** |
* 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_mssql::errorNative(), DB_mssql::errorCode() |
*/ |
function mssqlRaiseError($code = null) |
{ |
$message = @mssql_get_last_message(); |
if (!$code) { |
$code = $this->errorNative(); |
} |
return $this->raiseError($this->errorCode($code, $message), |
null, null, null, "$code - $message"); |
} |
// }}} |
// {{{ errorNative() |
/** |
* Gets the DBMS' native error code produced by the last query |
* |
* @return int the DBMS' error code |
*/ |
function errorNative() |
{ |
$res = @mssql_query('select @@ERROR as ErrorCode', $this->connection); |
if (!$res) { |
return DB_ERROR; |
} |
$row = @mssql_fetch_row($res); |
return $row[0]; |
} |
// }}} |
// {{{ errorCode() |
/** |
* Determines PEAR::DB error code from mssql's native codes. |
* |
* If <var>$nativecode</var> isn't known yet, it will be looked up. |
* |
* @param mixed $nativecode mssql error code, if known |
* @return integer an error number from a DB error constant |
* @see errorNative() |
*/ |
function errorCode($nativecode = null, $msg = '') |
{ |
if (!$nativecode) { |
$nativecode = $this->errorNative(); |
} |
if (isset($this->errorcode_map[$nativecode])) { |
if ($nativecode == 3701 |
&& preg_match('/Cannot drop the index/i', $msg)) |
{ |
return DB_ERROR_NOT_FOUND; |
} |
return $this->errorcode_map[$nativecode]; |
} else { |
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. |
*/ |
if (!@mssql_select_db($this->_db, $this->connection)) { |
return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
} |
$id = @mssql_query("SELECT * FROM $result WHERE 1=0", |
$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->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = @mssql_num_fields($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(@mssql_field_name($id, $i)), |
'type' => @mssql_field_type($id, $i), |
'len' => @mssql_field_length($id, $i), |
// We only support flags for table |
'flags' => $got_string |
? $this->_mssql_field_flags($result, |
@mssql_field_name($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) { |
@mssql_free_result($id); |
} |
return $res; |
} |
// }}} |
// {{{ _mssql_field_flags() |
/** |
* Get a column's flags |
* |
* Supports "not_null", "primary_key", |
* "auto_increment" (mssql identity), "timestamp" (mssql timestamp), |
* "unique_key" (mssql unique index, unique check or primary_key) and |
* "multiple_key" (multikey index) |
* |
* mssql timestamp is NOT similar to the mysql timestamp so this is maybe |
* not useful at all - is the behaviour of mysql_field_flags that primary |
* keys are alway unique? is the interpretation of multiple_key correct? |
* |
* @param string $table the table name |
* @param string $column the field name |
* |
* @return string the flags |
* |
* @access private |
* @author Joern Barthel <j_barthel@web.de> |
*/ |
function _mssql_field_flags($table, $column) |
{ |
static $tableName = null; |
static $flags = array(); |
if ($table != $tableName) { |
$flags = array(); |
$tableName = $table; |
// get unique and primary keys |
$res = $this->getAll("EXEC SP_HELPINDEX[$table]", DB_FETCHMODE_ASSOC); |
foreach ($res as $val) { |
$keys = explode(', ', $val['index_keys']); |
if (sizeof($keys) > 1) { |
foreach ($keys as $key) { |
$this->_add_flag($flags[$key], 'multiple_key'); |
} |
} |
if (strpos($val['index_description'], 'primary key')) { |
foreach ($keys as $key) { |
$this->_add_flag($flags[$key], 'primary_key'); |
} |
} elseif (strpos($val['index_description'], 'unique')) { |
foreach ($keys as $key) { |
$this->_add_flag($flags[$key], 'unique_key'); |
} |
} |
} |
// get auto_increment, not_null and timestamp |
$res = $this->getAll("EXEC SP_COLUMNS[$table]", DB_FETCHMODE_ASSOC); |
foreach ($res as $val) { |
$val = array_change_key_case($val, CASE_LOWER); |
if ($val['nullable'] == '0') { |
$this->_add_flag($flags[$val['column_name']], 'not_null'); |
} |
if (strpos($val['type_name'], 'identity')) { |
$this->_add_flag($flags[$val['column_name']], 'auto_increment'); |
} |
if (strpos($val['type_name'], 'timestamp')) { |
$this->_add_flag($flags[$val['column_name']], 'timestamp'); |
} |
} |
} |
if (array_key_exists($column, $flags)) { |
return(implode(' ', $flags[$column])); |
} |
return ''; |
} |
// }}} |
// {{{ _add_flag() |
/** |
* Adds a string to the flags array if the flag is not yet in there |
* - if there is no flag present the array is created |
* |
* @param array &$array the reference to the flag-array |
* @param string $value the flag value |
* |
* @return void |
* |
* @access private |
* @author Joern Barthel <j_barthel@web.de> |
*/ |
function _add_flag(&$array, $value) |
{ |
if (!is_array($array)) { |
$array = array($value); |
} elseif (!in_array($value, $array)) { |
array_push($array, $value); |
} |
} |
// }}} |
// {{{ 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 name FROM sysobjects WHERE type = 'U'" |
. ' ORDER BY name'; |
case 'views': |
return "SELECT name FROM sysobjects WHERE type = 'V'"; |
default: |
return null; |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/DataObject.php |
---|
New file |
0,0 → 1,3827 |
<?php |
/** |
* Object Based Database Query Builder and data store |
* |
* 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_DataObject |
* @author Alan Knowles <alan@akbkhome.com> |
* @copyright 1997-2005 The PHP Group |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: DataObject.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_DataObject |
*/ |
/* =========================================================================== |
* |
* !!!!!!!!!!!!! W A R N I N G !!!!!!!!!!! |
* |
* THIS MAY SEGFAULT PHP IF YOU ARE USING THE ZEND OPTIMIZER (to fix it, |
* just add "define('DB_DATAOBJECT_NO_OVERLOAD',true);" before you include |
* this file. reducing the optimization level may also solve the segfault. |
* =========================================================================== |
*/ |
/** |
* The main "DB_DataObject" class is really a base class for your own tables classes |
* |
* // Set up the class by creating an ini file (refer to the manual for more details |
* [DB_DataObject] |
* database = mysql:/username:password@host/database |
* schema_location = /home/myapplication/database |
* class_location = /home/myapplication/DBTables/ |
* clase_prefix = DBTables_ |
* |
* |
* //Start and initialize...................... - dont forget the & |
* $config = parse_ini_file('example.ini',true); |
* $options = &PEAR::getStaticProperty('DB_DataObject','options'); |
* $options = $config['DB_DataObject']; |
* |
* // example of a class (that does not use the 'auto generated tables data') |
* class mytable extends DB_DataObject { |
* // mandatory - set the table |
* var $_database_dsn = "mysql://username:password@localhost/database"; |
* var $__table = "mytable"; |
* function table() { |
* return array( |
* 'id' => 1, // integer or number |
* 'name' => 2, // string |
* ); |
* } |
* function keys() { |
* return array('id'); |
* } |
* } |
* |
* // use in the application |
* |
* |
* Simple get one row |
* |
* $instance = new mytable; |
* $instance->get("id",12); |
* echo $instance->somedata; |
* |
* |
* Get multiple rows |
* |
* $instance = new mytable; |
* $instance->whereAdd("ID > 12"); |
* $instance->whereAdd("ID < 14"); |
* $instance->find(); |
* while ($instance->fetch()) { |
* echo $instance->somedata; |
* } |
/** |
* Needed classes |
* - we use getStaticProperty from PEAR pretty extensively (cant remove it ATM) |
*/ |
require_once 'PEAR.php'; |
/** |
* We are setting a global fetchmode assoc constant of 2 to be compatible with |
* both DB and MDB2 |
*/ |
define('DB_DATAOBJECT_FETCHMODE_ASSOC',2); |
/** |
* these are constants for the get_table array |
* user to determine what type of escaping is required around the object vars. |
*/ |
define('DB_DATAOBJECT_INT', 1); // does not require '' |
define('DB_DATAOBJECT_STR', 2); // requires '' |
define('DB_DATAOBJECT_DATE', 4); // is date #TODO |
define('DB_DATAOBJECT_TIME', 8); // is time #TODO |
define('DB_DATAOBJECT_BOOL', 16); // is boolean #TODO |
define('DB_DATAOBJECT_TXT', 32); // is long text #TODO |
define('DB_DATAOBJECT_BLOB', 64); // is blob type |
define('DB_DATAOBJECT_NOTNULL', 128); // not null col. |
define('DB_DATAOBJECT_MYSQLTIMESTAMP' , 256); // mysql timestamps (ignored by update/insert) |
/* |
* Define this before you include DataObjects.php to disable overload - if it segfaults due to Zend optimizer.. |
*/ |
//define('DB_DATAOBJECT_NO_OVERLOAD',true) |
/** |
* Theses are the standard error codes, most methods will fail silently - and return false |
* to access the error message either use $table->_lastError |
* or $last_error = PEAR::getStaticProperty('DB_DataObject','lastError'); |
* the code is $last_error->code, and the message is $last_error->message (a standard PEAR error) |
*/ |
define('DB_DATAOBJECT_ERROR_INVALIDARGS', -1); // wrong args to function |
define('DB_DATAOBJECT_ERROR_NODATA', -2); // no data available |
define('DB_DATAOBJECT_ERROR_INVALIDCONFIG', -3); // something wrong with the config |
define('DB_DATAOBJECT_ERROR_NOCLASS', -4); // no class exists |
define('DB_DATAOBJECT_ERROR_INVALID_CALL' ,-7); // overlad getter/setter failure |
/** |
* Used in methods like delete() and count() to specify that the method should |
* build the condition only out of the whereAdd's and not the object parameters. |
*/ |
define('DB_DATAOBJECT_WHEREADD_ONLY', true); |
/** |
* |
* storage for connection and result objects, |
* it is done this way so that print_r()'ing the is smaller, and |
* it reduces the memory size of the object. |
* -- future versions may use $this->_connection = & PEAR object.. |
* although will need speed tests to see how this affects it. |
* - includes sub arrays |
* - connections = md5 sum mapp to pear db object |
* - results = [id] => map to pear db object |
* - resultseq = sequence id for results & results field |
* - resultfields = [id] => list of fields return from query (for use with toArray()) |
* - ini = mapping of database to ini file results |
* - links = mapping of database to links file |
* - lasterror = pear error objects for last error event. |
* - config = aliased view of PEAR::getStaticPropery('DB_DataObject','options') * done for performance. |
* - array of loaded classes by autoload method - to stop it doing file access request over and over again! |
*/ |
$GLOBALS['_DB_DATAOBJECT']['RESULTS'] = array(); |
$GLOBALS['_DB_DATAOBJECT']['RESULTSEQ'] = 1; |
$GLOBALS['_DB_DATAOBJECT']['RESULTFIELDS'] = array(); |
$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'] = array(); |
$GLOBALS['_DB_DATAOBJECT']['INI'] = array(); |
$GLOBALS['_DB_DATAOBJECT']['LINKS'] = array(); |
$GLOBALS['_DB_DATAOBJECT']['SEQUENCE'] = array(); |
$GLOBALS['_DB_DATAOBJECT']['LASTERROR'] = null; |
$GLOBALS['_DB_DATAOBJECT']['CONFIG'] = array(); |
$GLOBALS['_DB_DATAOBJECT']['CACHE'] = array(); |
$GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = false; |
$GLOBALS['_DB_DATAOBJECT']['QUERYENDTIME'] = 0; |
// this will be horrifically slow!!!! |
// NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer (see define before..) |
// these two are BC/FC handlers for call in PHP4/5 |
if ( substr(phpversion(),0,1) == 5) { |
class DB_DataObject_Overload |
{ |
function __call($method,$args) |
{ |
$return = null; |
$this->_call($method,$args,$return); |
return $return; |
} |
function __sleep() |
{ |
return array_keys(get_object_vars($this)) ; |
} |
} |
} else { |
if (version_compare(phpversion(),'4.3.10','eq') && !defined('DB_DATAOBJECT_NO_OVERLOAD')) { |
trigger_error( |
"overload does not work with PHP4.3.10, either upgrade |
(snaps.php.net) or more recent version |
or define DB_DATAOBJECT_NO_OVERLOAD as per the manual. |
",E_USER_ERROR); |
} |
if (!function_exists('clone')) { |
// emulate clone - as per php_compact, slow but really the correct behaviour.. |
eval('function clone($t) { $r = $t; if (method_exists($r,"__clone")) { $r->__clone(); } return $r; }'); |
} |
eval(' |
class DB_DataObject_Overload { |
function __call($method,$args,&$return) { |
return $this->_call($method,$args,$return); |
} |
} |
'); |
} |
/* |
* |
* @package DB_DataObject |
* @author Alan Knowles <alan@akbkhome.com> |
* @since PHP 4.0 |
*/ |
class DB_DataObject extends DB_DataObject_Overload |
{ |
/** |
* The Version - use this to check feature changes |
* |
* @access private |
* @var string |
*/ |
var $_DB_DataObject_version = "1.7.15"; |
/** |
* The Database table (used by table extends) |
* |
* @access private |
* @var string |
*/ |
var $__table = ''; // database table |
/** |
* The Number of rows returned from a query |
* |
* @access public |
* @var int |
*/ |
var $N = 0; // Number of rows returned from a query |
/* ============================================================= */ |
/* Major Public Methods */ |
/* (designed to be optionally then called with parent::method()) */ |
/* ============================================================= */ |
/** |
* Get a result using key, value. |
* |
* for example |
* $object->get("ID",1234); |
* Returns Number of rows located (usually 1) for success, |
* and puts all the table columns into this classes variables |
* |
* see the fetch example on how to extend this. |
* |
* if no value is entered, it is assumed that $key is a value |
* and get will then use the first key in keys() |
* to obtain the key. |
* |
* @param string $k column |
* @param string $v value |
* @access public |
* @return int No. of rows |
*/ |
function get($k = null, $v = null) |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
$keys = array(); |
if ($v === null) { |
$v = $k; |
$keys = $this->keys(); |
if (!$keys) { |
$this->raiseError("No Keys available for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return false; |
} |
$k = $keys[0]; |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("$k $v " .print_r($keys,true), "GET"); |
} |
if ($v === null) { |
$this->raiseError("No Value specified for get", DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
$this->$k = $v; |
return $this->find(1); |
} |
/** |
* An autoloading, caching static get method using key, value (based on get) |
* |
* Usage: |
* $object = DB_DataObject::staticGet("DbTable_mytable",12); |
* or |
* $object = DB_DataObject::staticGet("DbTable_mytable","name","fred"); |
* |
* or write it into your extended class: |
* function &staticGet($k,$v=NULL) { return DB_DataObject::staticGet("This_Class",$k,$v); } |
* |
* @param string $class class name |
* @param string $k column (or value if using keys) |
* @param string $v value (optional) |
* @access public |
* @return object |
*/ |
function &staticGet($class, $k, $v = null) |
{ |
$lclass = strtolower($class); |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
$key = "$k:$v"; |
if ($v === null) { |
$key = $k; |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
DB_DataObject::debug("$class $key","STATIC GET - TRY CACHE"); |
} |
if (!empty($_DB_DATAOBJECT['CACHE'][$lclass][$key])) { |
return $_DB_DATAOBJECT['CACHE'][$lclass][$key]; |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
DB_DataObject::debug("$class $key","STATIC GET - NOT IN CACHE"); |
} |
$obj = DB_DataObject::factory(substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix']))); |
if (PEAR::isError($obj)) { |
DB_DataObject::raiseError("could not autoload $class", DB_DATAOBJECT_ERROR_NOCLASS); |
return false; |
} |
if (!isset($_DB_DATAOBJECT['CACHE'][$lclass])) { |
$_DB_DATAOBJECT['CACHE'][$lclass] = array(); |
} |
if (!$obj->get($k,$v)) { |
DB_DataObject::raiseError("No Data return from get $k $v", DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
$_DB_DATAOBJECT['CACHE'][$lclass][$key] = $obj; |
return $_DB_DATAOBJECT['CACHE'][$lclass][$key]; |
} |
/** |
* find results, either normal or crosstable |
* |
* for example |
* |
* $object = new mytable(); |
* $object->ID = 1; |
* $object->find(); |
* |
* |
* will set $object->N to number of rows, and expects next command to fetch rows |
* will return $object->N |
* |
* @param boolean $n Fetch first result |
* @access public |
* @return mixed (number of rows returned, or true if numRows fetching is not supported) |
*/ |
function find($n = false) |
{ |
global $_DB_DATAOBJECT; |
if (!isset($this->_query)) { |
$this->raiseError( |
"You cannot do two queries on the same object (copy it before finding)", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug($n, "__find",1); |
} |
if (!$this->__table) { |
// xdebug can backtrace this! |
php_error("NO \$__table SPECIFIED in class definition",E_USER_ERROR); |
} |
$this->N = 0; |
$query_before = $this->_query; |
$this->_build_condition($this->table()) ; |
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
/* We are checking for method modifyLimitQuery as it is PEAR DB specific */ |
$sql = 'SELECT ' . |
$this->_query['data_select'] . |
' FROM ' . ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table) . " " . |
$this->_join . |
$this->_query['condition'] . ' '. |
$this->_query['group_by'] . ' '. |
$this->_query['having'] . ' '. |
$this->_query['order_by'] . ' '; |
if ((!isset($_DB_DATAOBJECT['CONFIG']['db_driver'])) || |
($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) { |
/* PEAR DB specific */ |
if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) { |
$sql = $DB->modifyLimitQuery($sql,$this->_query['limit_start'], $this->_query['limit_count']); |
} |
} else { |
/* theoretically MDB! */ |
if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) { |
$DB->setLimit($this->_query['limit_count'],$this->_query['limit_start']); |
} |
} |
$this->_query($sql); |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("CHECK autofetchd $n", "__find", 1); |
} |
// unset the |
if ($n && $this->N > 0 ) { |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("ABOUT TO AUTOFETCH", "__find", 1); |
} |
$this->fetch() ; |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("DONE", "__find", 1); |
} |
$this->_query = $query_before; |
return $this->N; |
} |
/** |
* fetches next row into this objects var's |
* |
* returns 1 on success 0 on failure |
* |
* |
* |
* Example |
* $object = new mytable(); |
* $object->name = "fred"; |
* $object->find(); |
* $store = array(); |
* while ($object->fetch()) { |
* echo $this->ID; |
* $store[] = $object; // builds an array of object lines. |
* } |
* |
* to add features to a fetch |
* function fetch () { |
* $ret = parent::fetch(); |
* $this->date_formated = date('dmY',$this->date); |
* return $ret; |
* } |
* |
* @access public |
* @return boolean on success |
*/ |
function fetch() |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
if (empty($this->N)) { |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("No data returned from FIND (eg. N is 0)","FETCH", 3); |
} |
return false; |
} |
if (empty($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]) || |
!is_object($result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) |
{ |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug('fetched on object after fetch completed (no results found)'); |
} |
return false; |
} |
$array = $result->fetchRow(DB_DATAOBJECT_FETCHMODE_ASSOC); |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug(serialize($array),"FETCH"); |
} |
if ($array === null) { |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$t= explode(' ',microtime()); |
$this->debug("Last Data Fetch'ed after " . |
($t[0]+$t[1]- $_DB_DATAOBJECT['QUERYENDTIME'] ) . |
" seconds", |
"FETCH", 1); |
} |
// reduce the memory usage a bit... (but leave the id in, so count() works ok on it) |
unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]); |
// this is probably end of data!! |
//DB_DataObject::raiseError("fetch: no data returned", DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
if (!isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) { |
// note: we dont declare this to keep the print_r size down. |
$_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]= array_flip(array_keys($array)); |
} |
foreach($array as $k=>$v) { |
$kk = str_replace(".", "_", $k); |
$kk = str_replace(" ", "_", $kk); |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("$kk = ". $array[$k], "fetchrow LINE", 3); |
} |
$this->$kk = $array[$k]; |
} |
// set link flag |
$this->_link_loaded=false; |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("{$this->__table} DONE", "fetchrow",2); |
} |
if (isset($this->_query) && empty($_DB_DATAOBJECT['CONFIG']['keep_query_after_fetch'])) { |
unset($this->_query); |
} |
return true; |
} |
/** |
* Adds a condition to the WHERE statement, defaults to AND |
* |
* $object->whereAdd(); //reset or cleaer ewhwer |
* $object->whereAdd("ID > 20"); |
* $object->whereAdd("age > 20","OR"); |
* |
* @param string $cond condition |
* @param string $logic optional logic "OR" (defaults to "AND") |
* @access public |
* @return string|PEAR::Error - previous condition or Error when invalid args found |
*/ |
function whereAdd($cond = false, $logic = 'AND') |
{ |
if (!isset($this->_query)) { |
return $this->raiseError( |
"You cannot do two queries on the same object (clone it before finding)", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
} |
if ($cond === false) { |
$r = $this->_query['condition']; |
$this->_query['condition'] = ''; |
return $r; |
} |
// check input...= 0 or ' ' == error! |
if (!trim($cond)) { |
return $this->raiseError("WhereAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS); |
} |
$r = $this->_query['condition']; |
if ($this->_query['condition']) { |
$this->_query['condition'] .= " {$logic} {$cond}"; |
return $r; |
} |
$this->_query['condition'] = " WHERE {$cond}"; |
return $r; |
} |
/** |
* Adds a order by condition |
* |
* $object->orderBy(); //clears order by |
* $object->orderBy("ID"); |
* $object->orderBy("ID,age"); |
* |
* @param string $order Order |
* @access public |
* @return none|PEAR::Error - invalid args only |
*/ |
function orderBy($order = false) |
{ |
if (!isset($this->_query)) { |
$this->raiseError( |
"You cannot do two queries on the same object (copy it before finding)", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
if ($order === false) { |
$this->_query['order_by'] = ''; |
return; |
} |
// check input...= 0 or ' ' == error! |
if (!trim($order)) { |
return $this->raiseError("orderBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS); |
} |
if (!$this->_query['order_by']) { |
$this->_query['order_by'] = " ORDER BY {$order} "; |
return; |
} |
$this->_query['order_by'] .= " , {$order}"; |
} |
/** |
* Adds a group by condition |
* |
* $object->groupBy(); //reset the grouping |
* $object->groupBy("ID DESC"); |
* $object->groupBy("ID,age"); |
* |
* @param string $group Grouping |
* @access public |
* @return none|PEAR::Error - invalid args only |
*/ |
function groupBy($group = false) |
{ |
if (!isset($this->_query)) { |
$this->raiseError( |
"You cannot do two queries on the same object (copy it before finding)", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
if ($group === false) { |
$this->_query['group_by'] = ''; |
return; |
} |
// check input...= 0 or ' ' == error! |
if (!trim($group)) { |
return $this->raiseError("groupBy: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS); |
} |
if (!$this->_query['group_by']) { |
$this->_query['group_by'] = " GROUP BY {$group} "; |
return; |
} |
$this->_query['group_by'] .= " , {$group}"; |
} |
/** |
* Adds a having clause |
* |
* $object->having(); //reset the grouping |
* $object->having("sum(value) > 0 "); |
* |
* @param string $having condition |
* @access public |
* @return none|PEAR::Error - invalid args only |
*/ |
function having($having = false) |
{ |
if (!isset($this->_query)) { |
$this->raiseError( |
"You cannot do two queries on the same object (copy it before finding)", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
if ($having === false) { |
$this->_query['having'] = ''; |
return; |
} |
// check input...= 0 or ' ' == error! |
if (!trim($having)) { |
return $this->raiseError("Having: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS); |
} |
if (!$this->_query['having']) { |
$this->_query['having'] = " HAVING {$having} "; |
return; |
} |
$this->_query['having'] .= " AND {$having}"; |
} |
/** |
* Sets the Limit |
* |
* $boject->limit(); // clear limit |
* $object->limit(12); |
* $object->limit(12,10); |
* |
* Note this will emit an error on databases other than mysql/postgress |
* as there is no 'clean way' to implement it. - you should consider refering to |
* your database manual to decide how you want to implement it. |
* |
* @param string $a limit start (or number), or blank to reset |
* @param string $b number |
* @access public |
* @return none|PEAR::Error - invalid args only |
*/ |
function limit($a = null, $b = null) |
{ |
if (!isset($this->_query)) { |
$this->raiseError( |
"You cannot do two queries on the same object (copy it before finding)", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
if ($a === null) { |
$this->_query['limit_start'] = ''; |
$this->_query['limit_count'] = ''; |
return; |
} |
// check input...= 0 or ' ' == error! |
if ((!is_int($a) && ((string)((int)$a) !== (string)$a)) |
|| (($b !== null) && (!is_int($b) && ((string)((int)$b) !== (string)$b)))) { |
return $this->raiseError("limit: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS); |
} |
global $_DB_DATAOBJECT; |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
$this->_query['limit_start'] = ($b == null) ? 0 : (int)$a; |
$this->_query['limit_count'] = ($b == null) ? (int)$a : (int)$b; |
} |
/** |
* Adds a select columns |
* |
* $object->selectAdd(); // resets select to nothing! |
* $object->selectAdd("*"); // default select |
* $object->selectAdd("unixtime(DATE) as udate"); |
* $object->selectAdd("DATE"); |
* |
* to prepend distict: |
* $object->selectAdd('distinct ' . $object->selectAdd()); |
* |
* @param string $k |
* @access public |
* @return mixed null or old string if you reset it. |
*/ |
function selectAdd($k = null) |
{ |
if (!isset($this->_query)) { |
$this->raiseError( |
"You cannot do two queries on the same object (copy it before finding)", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
if ($k === null) { |
$old = $this->_query['data_select']; |
$this->_query['data_select'] = ''; |
return $old; |
} |
// check input...= 0 or ' ' == error! |
if (!trim($k)) { |
return $this->raiseError("selectAdd: No Valid Arguments", DB_DATAOBJECT_ERROR_INVALIDARGS); |
} |
if ($this->_query['data_select']) { |
$this->_query['data_select'] .= ', '; |
} |
$this->_query['data_select'] .= " $k "; |
} |
/** |
* Adds multiple Columns or objects to select with formating. |
* |
* $object->selectAs(null); // adds "table.colnameA as colnameA,table.colnameB as colnameB,......" |
* // note with null it will also clear the '*' default select |
* $object->selectAs(array('a','b'),'%s_x'); // adds "a as a_x, b as b_x" |
* $object->selectAs(array('a','b'),'ddd_%s','ccc'); // adds "ccc.a as ddd_a, ccc.b as ddd_b" |
* $object->selectAdd($object,'prefix_%s'); // calls $object->get_table and adds it all as |
* objectTableName.colnameA as prefix_colnameA |
* |
* @param array|object|null the array or object to take column names from. |
* @param string format in sprintf format (use %s for the colname) |
* @param string table name eg. if you have joinAdd'd or send $from as an array. |
* @access public |
* @return void |
*/ |
function selectAs($from = null,$format = '%s',$tableName=false) |
{ |
global $_DB_DATAOBJECT; |
if (!isset($this->_query)) { |
$this->raiseError( |
"You cannot do two queries on the same object (copy it before finding)", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
if ($from === null) { |
// blank the '*' |
$this->selectAdd(); |
$from = $this; |
} |
$table = $this->__table; |
if (is_object($from)) { |
$table = $from->__table; |
$from = array_keys($from->table()); |
} |
if ($tableName !== false) { |
$table = $tableName; |
} |
$s = '%s'; |
if (!empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers'])) { |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
$s = $DB->quoteIdentifier($s); |
} |
foreach ($from as $k) { |
$this->selectAdd(sprintf("{$s}.{$s} as {$format}",$table,$k,$k)); |
} |
$this->_query['data_select'] .= "\n"; |
} |
/** |
* Insert the current objects variables into the database |
* |
* Returns the ID of the inserted element (if auto increment or sequences are used.) |
* |
* for example |
* |
* Designed to be extended |
* |
* $object = new mytable(); |
* $object->name = "fred"; |
* echo $object->insert(); |
* |
* @access public |
* @return mixed false on failure, int when auto increment or sequence used, otherwise true on success |
*/ |
function insert() |
{ |
global $_DB_DATAOBJECT; |
// we need to write to the connection (For nextid) - so us the real |
// one not, a copyied on (as ret-by-ref fails with overload!) |
if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
$this->_connect(); |
} |
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
$items = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ? |
$_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table(); |
if (!$items) { |
$this->raiseError("insert:No table definition for {$this->__table}", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return false; |
} |
$options = &$_DB_DATAOBJECT['CONFIG']; |
$datasaved = 1; |
$leftq = ''; |
$rightq = ''; |
$seqKeys = isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table]) ? |
$_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] : |
$this->sequenceKey(); |
$key = isset($seqKeys[0]) ? $seqKeys[0] : false; |
$useNative = isset($seqKeys[1]) ? $seqKeys[1] : false; |
$seq = isset($seqKeys[2]) ? $seqKeys[2] : false; |
$dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["phptype"]; |
// nativeSequences or Sequences.. |
// big check for using sequences |
if (($key !== false) && !$useNative) { |
if (!$seq) { |
$this->$key = $DB->nextId($this->__table); |
} else { |
$f = $DB->getOption('seqname_format'); |
$DB->setOption('seqname_format','%s'); |
$this->$key = $DB->nextId($seq); |
$DB->setOption('seqname_format',$f); |
} |
} |
foreach($items as $k => $v) { |
// if we are using autoincrement - skip the column... |
if ($key && ($k == $key) && $useNative) { |
continue; |
} |
if (!isset($this->$k)) { |
continue; |
} |
// dont insert data into mysql timestamps |
// use query() if you really want to do this!!!! |
if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) { |
continue; |
} |
if ($leftq) { |
$leftq .= ', '; |
$rightq .= ', '; |
} |
$leftq .= ($quoteIdentifiers ? ($DB->quoteIdentifier($k) . ' ') : "$k "); |
if (is_a($this->$k,'db_dataobject_cast')) { |
$value = $this->$k->toString($v,$DB); |
if (PEAR::isError($value)) { |
$this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG); |
return false; |
} |
$rightq .= $value; |
continue; |
} |
if ((strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) { |
$rightq .= " NULL "; |
continue; |
} |
// DATE is empty... on a col. that can be null.. |
// note: this may be usefull for time as well.. |
if (!$this->$k && |
(($v & DB_DATAOBJECT_DATE) || ($v & DB_DATAOBJECT_TIME)) && |
!($v & DB_DATAOBJECT_NOTNULL)) { |
$rightq .= " NULL "; |
continue; |
} |
if ($v & DB_DATAOBJECT_STR) { |
$rightq .= $this->_quote((string) ( |
($v & DB_DATAOBJECT_BOOL) ? |
// this is thanks to the braindead idea of postgres to |
// use t/f for boolean. |
(($this->$k == 'f') ? 0 : (int)(bool) $this->$k) : |
$this->$k |
)) . " "; |
continue; |
} |
if (is_numeric($this->$k)) { |
$rightq .=" {$this->$k} "; |
continue; |
} |
// at present we only cast to integers |
// - V2 may store additional data about float/int |
$rightq .= ' ' . intval($this->$k) . ' '; |
} |
// not sure why we let empty insert here.. - I guess to generate a blank row.. |
if ($leftq || $useNative) { |
$table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table); |
$r = $this->_query("INSERT INTO {$table} ($leftq) VALUES ($rightq) "); |
if (PEAR::isError($r)) { |
$this->raiseError($r); |
return false; |
} |
if ($r < 1) { |
return 0; |
} |
// now do we have an integer key! |
if ($key && $useNative) { |
switch ($dbtype) { |
case 'mysql': |
case 'mysqli': |
$method = "{$dbtype}_insert_id"; |
$this->$key = $method( |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection |
); |
break; |
case 'mssql': |
// note this is not really thread safe - you should wrapp it with |
// transactions = eg. |
// $db->query('BEGIN'); |
// $db->insert(); |
// $db->query('COMMIT'); |
$mssql_key = $DB->getOne("SELECT @@IDENTITY"); |
if (PEAR::isError($mssql_key)) { |
$this->raiseError($r); |
return false; |
} |
$this->$key = $mssql_key; |
break; |
case 'pgsql': |
if (!$seq) { |
$seq = $DB->getSequenceName($this->__table ); |
} |
$pgsql_key = $DB->getOne("SELECT last_value FROM ".$seq); |
if (PEAR::isError($pgsql_key)) { |
$this->raiseError($r); |
return false; |
} |
$this->$key = $pgsql_key; |
break; |
case 'ifx': |
$this->$key = array_shift ( |
ifx_fetch_row ( |
ifx_query( |
"select DBINFO('sqlca.sqlerrd1') FROM systables where tabid=1", |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->connection, |
IFX_SCROLL |
), |
"FIRST" |
) |
); |
break; |
} |
} |
if (isset($_DB_DATAOBJECT['CACHE'][strtolower(get_class($this))])) { |
$this->_clear_cache(); |
} |
if ($key) { |
return $this->$key; |
} |
return true; |
} |
$this->raiseError("insert: No Data specifed for query", DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
/** |
* Updates current objects variables into the database |
* uses the keys() to decide how to update |
* Returns the true on success |
* |
* for example |
* |
* $object = DB_DataObject::factory('mytable'); |
* $object->get("ID",234); |
* $object->email="testing@test.com"; |
* if(!$object->update()) |
* echo "UPDATE FAILED"; |
* |
* to only update changed items : |
* $dataobject->get(132); |
* $original = $dataobject; // clone/copy it.. |
* $dataobject->setFrom($_POST); |
* if ($dataobject->validate()) { |
* $dataobject->update($original); |
* } // otherwise an error... |
* |
* performing global updates: |
* $object = DB_DataObject::factory('mytable'); |
* $object->status = "dead"; |
* $object->whereAdd('age > 150'); |
* $object->update(DB_DATAOBJECT_WHEREADD_ONLY); |
* |
* @param object dataobject (optional) | DB_DATAOBJECT_WHEREADD_ONLY - used to only update changed items. |
* @access public |
* @return int rows affected or false on failure |
*/ |
function update($dataObject = false) |
{ |
global $_DB_DATAOBJECT; |
// connect will load the config! |
$this->_connect(); |
$original_query = isset($this->_query) ? $this->_query : null; |
$items = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ? |
$_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table(); |
// only apply update against sequence key if it is set????? |
$seq = $this->sequenceKey(); |
if ($seq[0] !== false) { |
$keys = array($seq[0]); |
if (empty($this->{$keys[0]}) && $dataObject !== true) { |
$this->raiseError("update: trying to perform an update without |
the key set, and argument to update is not |
DB_DATAOBJECT_WHEREADD_ONLY |
", DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
} else { |
$keys = $this->keys(); |
} |
if (!$items) { |
$this->raiseError("update:No table definition for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return false; |
} |
$datasaved = 1; |
$settings = ''; |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
$dbtype = $DB->dsn["phptype"]; |
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); |
foreach($items as $k => $v) { |
if (!isset($this->$k)) { |
continue; |
} |
// ignore stuff thats |
// dont write things that havent changed.. |
if (($dataObject !== false) && isset($dataObject->$k) && ($dataObject->$k == $this->$k)) { |
continue; |
} |
// - dont write keys to left.!!! |
if (in_array($k,$keys)) { |
continue; |
} |
// dont insert data into mysql timestamps |
// use query() if you really want to do this!!!! |
if ($v & DB_DATAOBJECT_MYSQLTIMESTAMP) { |
continue; |
} |
if ($settings) { |
$settings .= ', '; |
} |
$kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k); |
if (is_a($this->$k,'db_dataobject_cast')) { |
$value = $this->$k->toString($v,$DB); |
if (PEAR::isError($value)) { |
$this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG); |
return false; |
} |
$settings .= "$kSql = $value "; |
continue; |
} |
// special values ... at least null is handled... |
if ((strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) { |
$settings .= "$kSql = NULL "; |
continue; |
} |
// DATE is empty... on a col. that can be null.. |
// note: this may be usefull for time as well.. |
if (!$this->$k && |
(($v & DB_DATAOBJECT_DATE) || ($v & DB_DATAOBJECT_TIME)) && |
!($v & DB_DATAOBJECT_NOTNULL)) { |
$settings .= "$kSql = NULL "; |
continue; |
} |
if ($v & DB_DATAOBJECT_STR) { |
$settings .= "$kSql = ". $this->_quote((string) ( |
($v & DB_DATAOBJECT_BOOL) ? |
// this is thanks to the braindead idea of postgres to |
// use t/f for boolean. |
(($this->$k == 'f') ? 0 : (int)(bool) $this->$k) : |
$this->$k |
)) . ' '; |
continue; |
} |
if (is_numeric($this->$k)) { |
$settings .= "$kSql = {$this->$k} "; |
continue; |
} |
// at present we only cast to integers |
// - V2 may store additional data about float/int |
$settings .= "$kSql = " . intval($this->$k) . ' '; |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("got keys as ".serialize($keys),3); |
} |
if ($dataObject !== true) { |
$this->_build_condition($items,$keys); |
} else { |
// prevent wiping out of data! |
if (empty($this->_query['condition'])) { |
$this->raiseError("update: global table update not available |
do \$do->whereAdd('1=1'); if you really want to do that. |
", DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
} |
// echo " $settings, $this->condition "; |
if ($settings && isset($this->_query) && $this->_query['condition']) { |
$table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table); |
$r = $this->_query("UPDATE {$table} SET {$settings} {$this->_query['condition']} "); |
// restore original query conditions. |
$this->_query = $original_query; |
if (PEAR::isError($r)) { |
$this->raiseError($r); |
return false; |
} |
if ($r < 1) { |
return 0; |
} |
$this->_clear_cache(); |
return $r; |
} |
// restore original query conditions. |
$this->_query = $original_query; |
// if you manually specified a dataobject, and there where no changes - then it's ok.. |
if ($dataObject !== false) { |
return true; |
} |
$this->raiseError( |
"update: No Data specifed for query $settings , {$this->_query['condition']}", |
DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
/** |
* Deletes items from table which match current objects variables |
* |
* Returns the true on success |
* |
* for example |
* |
* Designed to be extended |
* |
* $object = new mytable(); |
* $object->ID=123; |
* echo $object->delete(); // builds a conditon |
* |
* $object = new mytable(); |
* $object->whereAdd('age > 12'); |
* $object->limit(1); |
* $object->orderBy('age DESC'); |
* $object->delete(true); // dont use object vars, use the conditions, limit and order. |
* |
* @param bool $useWhere (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then |
* we will build the condition only using the whereAdd's. Default is to |
* build the condition only using the object parameters. |
* |
* @access public |
* @return mixed True on success, false on failure, 0 on no data affected |
*/ |
function delete($useWhere = false) |
{ |
global $_DB_DATAOBJECT; |
// connect will load the config! |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); |
$extra_cond = ' ' . (isset($this->_query['order_by']) ? $this->_query['order_by'] : ''); |
if (!$useWhere) { |
$keys = $this->keys(); |
$this->_query = array(); // as it's probably unset! |
$this->_query['condition'] = ''; // default behaviour not to use where condition |
$this->_build_condition($this->table(),$keys); |
// if primary keys are not set then use data from rest of object. |
if (!$this->_query['condition']) { |
$this->_build_condition($this->table(),array(),$keys); |
} |
$extra_cond = ''; |
} |
// don't delete without a condition |
if (isset($this->_query) && $this->_query['condition']) { |
$table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table); |
$sql = "DELETE FROM {$table} {$this->_query['condition']}{$extra_cond}"; |
// add limit.. |
if (isset($this->_query['limit_start']) && strlen($this->_query['limit_start'] . $this->_query['limit_count'])) { |
if (!isset($_DB_DATAOBJECT['CONFIG']['db_driver']) || |
($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) { |
// pear DB |
$sql = $DB->modifyLimitQuery($sql,$this->_query['limit_start'], $this->_query['limit_count']); |
} else { |
// MDB |
$DB->setLimit( $this->_query['limit_count'],$this->_query['limit_start']); |
} |
} |
$r = $this->_query($sql); |
if (PEAR::isError($r)) { |
$this->raiseError($r); |
return false; |
} |
if ($r < 1) { |
return 0; |
} |
$this->_clear_cache(); |
return $r; |
} else { |
$this->raiseError("delete: No condition specifed for query", DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
} |
/** |
* fetches a specific row into this object variables |
* |
* Not recommended - better to use fetch() |
* |
* Returens true on success |
* |
* @param int $row row |
* @access public |
* @return boolean true on success |
*/ |
function fetchRow($row = null) |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
$this->_loadConfig(); |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3); |
} |
if (!$this->__table) { |
$this->raiseError("fetchrow: No table", DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return false; |
} |
if ($row === null) { |
$this->raiseError("fetchrow: No row specified", DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
if (!$this->N) { |
$this->raiseError("fetchrow: No results avaiable", DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("{$this->__table} $row of {$this->N}", "fetchrow",3); |
} |
$result = &$_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]; |
$array = $result->fetchrow(DB_DATAOBJECT_FETCHMODE_ASSOC,$row); |
if (!is_array($array)) { |
$this->raiseError("fetchrow: No results available", DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
foreach($array as $k => $v) { |
$kk = str_replace(".", "_", $k); |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("$kk = ". $array[$k], "fetchrow LINE", 3); |
} |
$this->$kk = $array[$k]; |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("{$this->__table} DONE", "fetchrow", 3); |
} |
return true; |
} |
/** |
* Find the number of results from a simple query |
* |
* for example |
* |
* $object = new mytable(); |
* $object->name = "fred"; |
* echo $object->count(); |
* echo $object->count(true); // dont use object vars. |
* echo $object->count('distinct mycol'); count distinct mycol. |
* echo $object->count('distinct mycol',true); // dont use object vars. |
* echo $object->count('distinct'); // count distinct id (eg. the primary key) |
* |
* |
* @param bool|string (optional) |
* (true|false => see below not on whereAddonly) |
* (string) |
* "DISTINCT" => does a distinct count on the tables 'key' column |
* otherwise => normally it counts primary keys - you can use |
* this to do things like $do->count('distinct mycol'); |
* |
* @param bool $whereAddOnly (optional) If DB_DATAOBJECT_WHEREADD_ONLY is passed in then |
* we will build the condition only using the whereAdd's. Default is to |
* build the condition using the object parameters as well. |
* |
* @access public |
* @return int |
*/ |
function count($countWhat = false,$whereAddOnly = false) |
{ |
global $_DB_DATAOBJECT; |
if (is_bool($countWhat)) { |
$whereAddOnly = $countWhat; |
} |
$t = clone($this); |
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); |
$items = $t->table(); |
if (!isset($t->_query)) { |
$this->raiseError( |
"You cannot do run count after you have run fetch()", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
if (!$whereAddOnly && $items) { |
$t->_build_condition($items); |
} |
$keys = $this->keys(); |
if (!$keys[0] && !is_string($countWhat)) { |
$this->raiseError( |
"You cannot do run count without keys - use \$do->keys('id');", |
DB_DATAOBJECT_ERROR_INVALIDARGS,PEAR_ERROR_DIE); |
return false; |
} |
$table = ($quoteIdentifiers ? $DB->quoteIdentifier($this->__table) : $this->__table); |
$key_col = ($quoteIdentifiers ? $DB->quoteIdentifier($keys[0]) : $keys[0]); |
$as = ($quoteIdentifiers ? $DB->quoteIdentifier('DATAOBJECT_NUM') : 'DATAOBJECT_NUM'); |
// support distinct on default keys. |
$countWhat = (strtoupper($countWhat) == 'DISTINCT') ? |
"DISTINCT {$table}.{$key_col}" : $countWhat; |
$countWhat = is_string($countWhat) ? $countWhat : "{$table}.{$key_col}"; |
$r = $t->_query( |
"SELECT count({$countWhat}) as $as |
FROM $table {$t->_join} {$t->_query['condition']}"); |
if (PEAR::isError($r)) { |
return false; |
} |
$result = &$_DB_DATAOBJECT['RESULTS'][$t->_DB_resultid]; |
$l = $result->fetchRow(); |
return $l[0]; |
} |
/** |
* sends raw query to database |
* |
* Since _query has to be a private 'non overwriteable method', this is a relay |
* |
* @param string $string SQL Query |
* @access public |
* @return void or DB_Error |
*/ |
function query($string) |
{ |
return $this->_query($string); |
} |
/** |
* an escape wrapper around DB->escapeSimple() |
* can be used when adding manual queries or clauses |
* eg. |
* $object->query("select * from xyz where abc like '". $object->escape($_GET['name']) . "'"); |
* |
* @param string $string value to be escaped |
* @access public |
* @return string |
*/ |
function escape($string) |
{ |
global $_DB_DATAOBJECT; |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
// mdb uses escape... |
$dd = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? 'DB' : $_DB_DATAOBJECT['CONFIG']['db_driver']; |
return ($dd == 'DB') ? $DB->escapeSimple($string) : $DB->escape($string); |
} |
/* ==================================================== */ |
/* Major Private Vars */ |
/* ==================================================== */ |
/** |
* The Database connection dsn (as described in the PEAR DB) |
* only used really if you are writing a very simple application/test.. |
* try not to use this - it is better stored in configuration files.. |
* |
* @access private |
* @var string |
*/ |
var $_database_dsn = ''; |
/** |
* The Database connection id (md5 sum of databasedsn) |
* |
* @access private |
* @var string |
*/ |
var $_database_dsn_md5 = ''; |
/** |
* The Database name |
* created in __connection |
* |
* @access private |
* @var string |
*/ |
var $_database = ''; |
/** |
* The QUERY rules |
* This replaces alot of the private variables |
* used to build a query, it is unset after find() is run. |
* |
* |
* |
* @access private |
* @var array |
*/ |
var $_query = array( |
'condition' => '', // the WHERE condition |
'group_by' => '', // the GROUP BY condition |
'order_by' => '', // the ORDER BY condition |
'having' => '', // the HAVING condition |
'limit_start' => '', // the LIMIT condition |
'limit_count' => '', // the LIMIT condition |
'data_select' => '*', // the columns to be SELECTed |
); |
/** |
* Database result id (references global $_DB_DataObject[results] |
* |
* @access private |
* @var integer |
*/ |
var $_DB_resultid; // database result object |
/* ============================================================== */ |
/* Table definition layer (started of very private but 'came out'*/ |
/* ============================================================== */ |
/** |
* Autoload or manually load the table definitions |
* |
* |
* usage : |
* DB_DataObject::databaseStructure( 'databasename', |
* parse_ini_file('mydb.ini',true), |
* parse_ini_file('mydb.link.ini',true)); |
* |
* obviously you dont have to use ini files.. (just return array similar to ini files..) |
* |
* It should append to the table structure array |
* |
* |
* @param optional string name of database to assign / read |
* @param optional array structure of database, and keys |
* @param optional array table links |
* |
* @access public |
* @return true or PEAR:error on wrong paramenters.. or false if no file exists.. |
* or the array(tablename => array(column_name=>type)) if called with 1 argument.. (databasename) |
*/ |
function databaseStructure() |
{ |
global $_DB_DATAOBJECT; |
// Assignment code |
if ($args = func_get_args()) { |
if (count($args) == 1) { |
// this returns all the tables and their structure.. |
$x = new DB_DataObject; |
$x->_database = $args[0]; |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
$tables = $DB->getListOf('tables'); |
require_once 'DB/DataObject/Generator.php'; |
foreach($tables as $table) { |
$y = new DB_DataObject_Generator; |
$y->fillTableSchema($x->_database,$table); |
} |
return $_DB_DATAOBJECT['INI'][$x->_database]; |
} else { |
$_DB_DATAOBJECT['INI'][$args[0]] = isset($_DB_DATAOBJECT['INI'][$args[0]]) ? |
$_DB_DATAOBJECT['INI'][$args[0]] + $args[1] : $args[1]; |
if (isset($args[1])) { |
$_DB_DATAOBJECT['LINKS'][$args[0]] = isset($_DB_DATAOBJECT['LINKS'][$args[0]]) ? |
$_DB_DATAOBJECT['LINKS'][$args[0]] + $args[2] : $args[2]; |
} |
return true; |
} |
} |
if (!$this->_database) { |
$this->_connect(); |
} |
// loaded already? |
if (!empty($_DB_DATAOBJECT['INI'][$this->_database])) { |
// database loaded - but this is table is not available.. |
if (empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) { |
require_once 'DB/DataObject/Generator.php'; |
$x = new DB_DataObject_Generator; |
$x->fillTableSchema($this->_database,$this->__table); |
} |
return true; |
} |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
// if you supply this with arguments, then it will take those |
// as the database and links array... |
$schemas = isset($_DB_DATAOBJECT['CONFIG']['schema_location']) ? |
array("{$_DB_DATAOBJECT['CONFIG']['schema_location']}/{$this->_database}.ini") : |
array() ; |
if (isset($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"])) { |
$schemas = is_array($_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]) ? |
$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"] : |
explode(PATH_SEPARATOR,$_DB_DATAOBJECT['CONFIG']["ini_{$this->_database}"]); |
} |
foreach ($schemas as $ini) { |
$links = |
isset($_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"]) ? |
$_DB_DATAOBJECT['CONFIG']["links_{$this->_database}"] : |
str_replace('.ini','.links.ini',$ini); |
if (file_exists($ini) && is_file($ini)) { |
$_DB_DATAOBJECT['INI'][$this->_database] = parse_ini_file($ini, true); |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("Loaded ini file: $ini","databaseStructure",1); |
} |
} else { |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("Missing ini file: $ini","databaseStructure",1); |
} |
} |
if (empty($_DB_DATAOBJECT['LINKS'][$this->_database]) && file_exists($links) && is_file($links)) { |
/* not sure why $links = ... here - TODO check if that works */ |
$_DB_DATAOBJECT['LINKS'][$this->_database] = parse_ini_file($links, true); |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("Loaded links.ini file: $links","databaseStructure",1); |
} |
} else { |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("Missing links.ini file: $links","databaseStructure",1); |
} |
} |
} |
// now have we loaded the structure.. - if not try building it.. |
if (empty($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) { |
require_once 'DB/DataObject/Generator.php'; |
$x = new DB_DataObject_Generator; |
$x->fillTableSchema($this->_database,$this->__table); |
} |
return true; |
} |
/** |
* Return or assign the name of the current table |
* |
* |
* @param string optinal table name to set |
* @access public |
* @return string The name of the current table |
*/ |
function tableName() |
{ |
$args = func_get_args(); |
if (count($args)) { |
$this->__table = $args[0]; |
} |
return $this->__table; |
} |
/** |
* Return or assign the name of the current database |
* |
* @param string optional database name to set |
* @access public |
* @return string The name of the current database |
*/ |
function database() |
{ |
$args = func_get_args(); |
if (count($args)) { |
$this->_database = $args[0]; |
} |
return $this->_database; |
} |
/** |
* get/set an associative array of table columns |
* |
* @access public |
* @param array key=>type array |
* @return array (associative) |
*/ |
function table() |
{ |
// for temporary storage of database fields.. |
// note this is not declared as we dont want to bloat the print_r output |
$args = func_get_args(); |
if (count($args)) { |
$this->_database_fields = $args[0]; |
} |
if (isset($this->_database_fields)) { |
return $this->_database_fields; |
} |
global $_DB_DATAOBJECT; |
if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
$this->_connect(); |
} |
if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) { |
return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table]; |
} |
$this->databaseStructure(); |
$ret = array(); |
if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table])) { |
$ret = $_DB_DATAOBJECT['INI'][$this->_database][$this->__table]; |
} |
return $ret; |
} |
/** |
* get/set an array of table primary keys |
* |
* set usage: $do->keys('id','code'); |
* |
* This is defined in the table definition if it gets it wrong, |
* or you do not want to use ini tables, you can override this. |
* @param string optional set the key |
* @param * optional set more keys |
* @access private |
* @return array |
*/ |
function keys() |
{ |
// for temporary storage of database fields.. |
// note this is not declared as we dont want to bloat the print_r output |
$args = func_get_args(); |
if (count($args)) { |
$this->_database_keys = $args; |
} |
if (isset($this->_database_keys)) { |
return $this->_database_keys; |
} |
global $_DB_DATAOBJECT; |
if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
$this->_connect(); |
} |
if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) { |
return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]); |
} |
$this->databaseStructure(); |
if (isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) { |
return array_keys($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]); |
} |
return array(); |
} |
/** |
* get/set an sequence key |
* |
* by default it returns the first key from keys() |
* set usage: $do->sequenceKey('id',true); |
* |
* override this to return array(false,false) if table has no real sequence key. |
* |
* @param string optional the key sequence/autoinc. key |
* @param boolean optional use native increment. default false |
* @param false|string optional native sequence name |
* @access private |
* @return array (column,use_native,sequence_name) |
*/ |
function sequenceKey() |
{ |
global $_DB_DATAOBJECT; |
// call setting |
if (!$this->_database) { |
$this->_connect(); |
} |
if (!isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database])) { |
$_DB_DATAOBJECT['SEQUENCE'][$this->_database] = array(); |
} |
$args = func_get_args(); |
if (count($args)) { |
$args[1] = isset($args[1]) ? $args[1] : false; |
$args[2] = isset($args[2]) ? $args[2] : false; |
$_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = $args; |
} |
if (isset($_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table])) { |
return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table]; |
} |
// end call setting (eg. $do->sequenceKeys(a,b,c); ) |
$keys = $this->keys(); |
if (!$keys) { |
return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] |
= array(false,false,false);; |
} |
$table = isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table]) ? |
$_DB_DATAOBJECT['INI'][$this->_database][$this->__table] : $this->table(); |
$dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype']; |
$usekey = $keys[0]; |
$seqname = false; |
if (!empty($_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table])) { |
$usekey = $_DB_DATAOBJECT['CONFIG']['sequence_'.$this->__table]; |
if (strpos($usekey,':') !== false) { |
list($usekey,$seqname) = explode(':',$usekey); |
} |
} |
// if the key is not an integer - then it's not a sequence or native |
if (!($table[$usekey] & DB_DATAOBJECT_INT)) { |
return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,false); |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'])) { |
$ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys']; |
if (is_string($ignore) && (strtoupper($ignore) == 'ALL')) { |
return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname); |
} |
if (is_string($ignore)) { |
$ignore = $_DB_DATAOBJECT['CONFIG']['ignore_sequence_keys'] = explode(',',$ignore); |
} |
if (in_array($this->__table,$ignore)) { |
return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname); |
} |
} |
$realkeys = $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]; |
// if you are using an old ini file - go back to old behaviour... |
if (is_numeric($realkeys[$usekey])) { |
$realkeys[$usekey] = 'N'; |
} |
// multiple unique primary keys without a native sequence... |
if (($realkeys[$usekey] == 'K') && (count($keys) > 1)) { |
return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array(false,false,$seqname); |
} |
// use native sequence keys... |
// technically postgres native here... |
// we need to get the new improved tabledata sorted out first. |
if ( in_array($dbtype , array( 'mysql', 'mysqli', 'mssql', 'ifx')) && |
($table[$usekey] & DB_DATAOBJECT_INT) && |
isset($realkeys[$usekey]) && ($realkeys[$usekey] == 'N') |
) { |
return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array($usekey,true,$seqname); |
} |
// if not a native autoinc, and we have not assumed all primary keys are sequence |
if (($realkeys[$usekey] != 'N') && |
!empty($_DB_DATAOBJECT['CONFIG']['dont_use_pear_sequences'])) { |
return array(false,false,false); |
} |
// I assume it's going to try and be a nextval DB sequence.. (not native) |
return $_DB_DATAOBJECT['SEQUENCE'][$this->_database][$this->__table] = array($usekey,false,$seqname); |
} |
/* =========================================================== */ |
/* Major Private Methods - the core part! */ |
/* =========================================================== */ |
/** |
* clear the cache values for this class - normally done on insert/update etc. |
* |
* @access private |
* @return void |
*/ |
function _clear_cache() |
{ |
global $_DB_DATAOBJECT; |
$class = get_class($this); |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("Clearing Cache for ".$class,1); |
} |
if (!empty($_DB_DATAOBJECT['CACHE'][$class])) { |
unset($_DB_DATAOBJECT['CACHE'][$class]); |
} |
} |
/** |
* backend wrapper for quoting, as MDB and DB do it differently... |
* |
* @access private |
* @return string quoted |
*/ |
function _quote($str) |
{ |
global $_DB_DATAOBJECT; |
return (empty($_DB_DATAOBJECT['CONFIG']['db_driver']) || |
($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) |
? $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->quoteSmart($str) |
: $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->quote($str); |
} |
/** |
* connects to the database |
* |
* |
* TODO: tidy this up - This has grown to support a number of connection options like |
* a) dynamic changing of ini file to change which database to connect to |
* b) multi data via the table_{$table} = dsn ini option |
* c) session based storage. |
* |
* @access private |
* @return true | PEAR::error |
*/ |
function _connect() |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
$this->_loadConfig(); |
} |
// is it already connected ? |
if ($this->_database_dsn_md5 && !empty($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
return $this->raiseError( |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->message, |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE |
); |
} |
if (!$this->_database) { |
$this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['database']; |
if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite') |
&& is_file($this->_database)) |
{ |
$this->_database = basename($this->_database); |
} |
} |
// theoretically we have a md5, it's listed in connections and it's not an error. |
// so everything is ok! |
return true; |
} |
// it's not currently connected! |
// try and work out what to use for the dsn ! |
$options= &$_DB_DATAOBJECT['CONFIG']; |
$dsn = isset($this->_database_dsn) ? $this->_database_dsn : null; |
if (!$dsn) { |
if (!$this->_database) { |
$this->_database = isset($options["table_{$this->__table}"]) ? $options["table_{$this->__table}"] : null; |
} |
if ($this->_database && !empty($options["database_{$this->_database}"])) { |
$dsn = $options["database_{$this->_database}"]; |
} else if (!empty($options['database'])) { |
$dsn = $options['database']; |
} |
} |
// if still no database... |
if (!$dsn) { |
return $this->raiseError( |
"No database name / dsn found anywhere", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG, PEAR_ERROR_DIE |
); |
} |
$this->_database_dsn_md5 = md5($dsn); |
if (!empty($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("USING CACHED CONNECTION", "CONNECT",3); |
} |
if (!$this->_database) { |
$this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["database"]; |
if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite') |
&& is_file($this->_database)) |
{ |
$this->_database = basename($this->_database); |
} |
} |
return true; |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug("NEW CONNECTION", "CONNECT",3); |
/* actualy make a connection */ |
$this->debug("{$dsn} {$this->_database_dsn_md5}", "CONNECT",3); |
} |
// Note this is verbose deliberatly! |
if (!isset($_DB_DATAOBJECT['CONFIG']['db_driver']) || |
($_DB_DATAOBJECT['CONFIG']['db_driver'] == 'DB')) { |
/* PEAR DB connect */ |
// this allows the setings of compatibility on DB |
$db_options = PEAR::getStaticProperty('DB','options'); |
require_once 'DB.php'; |
if ($db_options) { |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = DB::connect($dsn,$db_options); |
} else { |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = DB::connect($dsn); |
} |
} else { |
/* assumption is MDB */ |
require_once 'MDB2.php'; |
// this allows the setings of compatibility on MDB2 |
$db_options = PEAR::getStaticProperty('MDB2','options'); |
if ($db_options) { |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = MDB2::connect($dsn,$db_options); |
} else { |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5] = MDB2::connect($dsn); |
} |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug(serialize($_DB_DATAOBJECT['CONNECTIONS']), "CONNECT",5); |
} |
if (PEAR::isError($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
$this->debug($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->toString(), "CONNECT FAILED",5); |
return $this->raiseError( |
"Connect failed, turn on debugging to 5 see why", |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->code, PEAR_ERROR_DIE |
); |
} |
if (!$this->_database) { |
$this->_database = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn["database"]; |
if (($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype'] == 'sqlite') |
&& is_file($this->_database)) |
{ |
$this->_database = basename($this->_database); |
} |
} |
// Oracle need to optimize for portibility - not sure exactly what this does though :) |
$c = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
return true; |
} |
/** |
* sends query to database - this is the private one that must work |
* - internal functions use this rather than $this->query() |
* |
* @param string $string |
* @access private |
* @return mixed none or PEAR_Error |
*/ |
function _query($string) |
{ |
global $_DB_DATAOBJECT; |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
$options = &$_DB_DATAOBJECT['CONFIG']; |
$_DB_driver = empty($_DB_DATAOBJECT['CONFIG']['db_driver']) ? |
'DB': $_DB_DATAOBJECT['CONFIG']['db_driver']; |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug($string,$log="QUERY"); |
} |
if (strtoupper($string) == 'BEGIN') { |
if ($_DB_driver == 'DB') { |
$DB->autoCommit(false); |
} else { |
$DB->beginTransaction(); |
} |
// db backend adds begin anyway from now on.. |
return true; |
} |
if (strtoupper($string) == 'COMMIT') { |
$res = $DB->commit(); |
if ($_DB_driver == 'DB') { |
$DB->autoCommit(true); |
} |
return $res; |
} |
if (strtoupper($string) == 'ROLLBACK') { |
$DB->rollback(); |
if ($_DB_driver == 'DB') { |
$DB->autoCommit(true); |
} |
return true; |
} |
if (!empty($options['debug_ignore_updates']) && |
(strtolower(substr(trim($string), 0, 6)) != 'select') && |
(strtolower(substr(trim($string), 0, 4)) != 'show') && |
(strtolower(substr(trim($string), 0, 8)) != 'describe')) { |
$this->debug('Disabling Update as you are in debug mode'); |
return $this->raiseError("Disabling Update as you are in debug mode", null) ; |
} |
//if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 1) { |
// this will only work when PEAR:DB supports it. |
//$this->debug($DB->getAll('explain ' .$string,DB_DATAOBJECT_FETCHMODE_ASSOC), $log="sql",2); |
//} |
// some sim |
$t= explode(' ',microtime()); |
$_DB_DATAOBJECT['QUERYENDTIME'] = $time = $t[0]+$t[1]; |
$result = $DB->query($string); |
if (is_a($result,'DB_Error')) { |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug($result->toString(), "Query Error",1 ); |
} |
return $this->raiseError($result); |
} |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$t= explode(' ',microtime()); |
$_DB_DATAOBJECT['QUERYENDTIME'] = $t[0]+$t[1]; |
$this->debug('QUERY DONE IN '.($t[0]+$t[1]-$time)." seconds", 'query',1); |
} |
switch (strtolower(substr(trim($string),0,6))) { |
case 'insert': |
case 'update': |
case 'delete': |
if ($_DB_driver == 'DB') { |
// pear DB specific |
return $DB->affectedRows(); |
} |
return $result; |
} |
if (is_object($result)) { |
// lets hope that copying the result object is OK! |
$_DB_resultid = $GLOBALS['_DB_DATAOBJECT']['RESULTSEQ']++; |
$_DB_DATAOBJECT['RESULTS'][$_DB_resultid] = $result; |
$this->_DB_resultid = $_DB_resultid; |
} |
$this->N = 0; |
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) { |
$this->debug(serialize($result), 'RESULT',5); |
} |
if (method_exists($result, 'numrows')) { |
$DB->expectError(DB_ERROR_UNSUPPORTED); |
$this->N = $result->numrows(); |
if (is_a($this->N,'DB_Error')) { |
$this->N = true; |
} |
$DB->popExpect(); |
} |
} |
/** |
* Builds the WHERE based on the values of of this object |
* |
* @param mixed $keys |
* @param array $filter (used by update to only uses keys in this filter list). |
* @param array $negative_filter (used by delete to prevent deleting using the keys mentioned..) |
* @access private |
* @return string |
*/ |
function _build_condition($keys, $filter = array(),$negative_filter=array()) |
{ |
global $_DB_DATAOBJECT; |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); |
// if we dont have query vars.. - reset them. |
if (!isset($this->_query)) { |
$x = new DB_DataObject; |
$this->_query= $x->_query; |
} |
foreach($keys as $k => $v) { |
// index keys is an indexed array |
/* these filter checks are a bit suspicious.. |
- need to check that update really wants to work this way */ |
if ($filter) { |
if (!in_array($k, $filter)) { |
continue; |
} |
} |
if ($negative_filter) { |
if (in_array($k, $negative_filter)) { |
continue; |
} |
} |
if (!isset($this->$k)) { |
continue; |
} |
$kSql = $quoteIdentifiers |
? ( $DB->quoteIdentifier($this->__table) . '.' . $DB->quoteIdentifier($k) ) |
: "{$this->__table}.{$k}"; |
if (is_a($this->$k,'db_dataobject_cast')) { |
$dbtype = $DB->dsn["phptype"]; |
$value = $this->$k->toString($v,$DB); |
if (PEAR::isError($value)) { |
$this->raiseError($value->getMessage() ,DB_DATAOBJECT_ERROR_INVALIDARG); |
return false; |
} |
if ((strtolower($value) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) { |
$this->whereAdd(" $kSql IS NULL"); |
continue; |
} |
$this->whereAdd(" $kSql = $value"); |
continue; |
} |
if ((strtolower($this->$k) === 'null') && !($v & DB_DATAOBJECT_NOTNULL)) { |
$this->whereAdd(" $kSql IS NULL"); |
continue; |
} |
if ($v & DB_DATAOBJECT_STR) { |
$this->whereAdd(" $kSql = " . $this->_quote((string) ( |
($v & DB_DATAOBJECT_BOOL) ? |
// this is thanks to the braindead idea of postgres to |
// use t/f for boolean. |
(($this->$k == 'f') ? 0 : (int)(bool) $this->$k) : |
$this->$k |
)) ); |
continue; |
} |
if (is_numeric($this->$k)) { |
$this->whereAdd(" $kSql = {$this->$k}"); |
continue; |
} |
/* this is probably an error condition! */ |
$this->whereAdd(" $kSql = ".intval($this->$k)); |
} |
} |
/** |
* autoload Class relating to a table |
* (depreciated - use ::factory) |
* |
* @param string $table table |
* @access private |
* @return string classname on Success |
*/ |
function staticAutoloadTable($table) |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
$p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ? |
$_DB_DATAOBJECT['CONFIG']['class_prefix'] : ''; |
$class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)); |
$class = (class_exists($class)) ? $class : DB_DataObject::_autoloadClass($class); |
return $class; |
} |
/** |
* classic factory method for loading a table class |
* usage: $do = DB_DataObject::factory('person') |
* WARNING - this may emit a include error if the file does not exist.. |
* use @ to silence it (if you are sure it is acceptable) |
* eg. $do = @DB_DataObject::factory('person') |
* |
* table name will eventually be databasename/table |
* - and allow modular dataobjects to be written.. |
* (this also helps proxy creation) |
* |
* |
* @param string $table tablename (use blank to create a new instance of the same class.) |
* @access private |
* @return DataObject|PEAR_Error |
*/ |
function factory($table = '') { |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
if ($table === '') { |
if (is_a($this,'DB_DataObject') && strlen($this->__table)) { |
$table = $this->__table; |
} else { |
return DB_DataObject::raiseError( |
"factory did not recieve a table name", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
} |
} |
$p = isset($_DB_DATAOBJECT['CONFIG']['class_prefix']) ? |
$_DB_DATAOBJECT['CONFIG']['class_prefix'] : ''; |
$class = $p . preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)); |
$class = (class_exists($class)) ? $class : DB_DataObject::_autoloadClass($class); |
// proxy = full|light |
if (!$class && isset($_DB_DATAOBJECT['CONFIG']['proxy'])) { |
$proxyMethod = 'getProxy'.$_DB_DATAOBJECT['CONFIG']['proxy']; |
require_once 'DB/DataObject/Generator.php'; |
$d = new DB_DataObject; |
$d->__table = $table; |
$d->_connect(); |
$x = new DB_DataObject_Generator; |
return $x->$proxyMethod( $d->_database, $table); |
} |
if (!$class) { |
return DB_DataObject::raiseError( |
"factory could not find class $class from $table", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
} |
return new $class; |
} |
/** |
* autoload Class |
* |
* @param string $class Class |
* @access private |
* @return string classname on Success |
*/ |
function _autoloadClass($class) |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
$table = substr($class,strlen($_DB_DATAOBJECT['CONFIG']['class_prefix'])); |
// only include the file if it exists - and barf badly if it has parse errors :) |
if (!empty($_DB_DATAOBJECT['CONFIG']['proxy']) && empty($_DB_DATAOBJECT['CONFIG']['class_location'])) { |
return false; |
} |
if (strpos($_DB_DATAOBJECT['CONFIG']['class_location'],'%s') !== false) { |
$file = sprintf($_DB_DATAOBJECT['CONFIG']['class_location'], preg_replace('/[^A-Z0-9]/i','_',ucfirst($table))); |
} else { |
$file = $_DB_DATAOBJECT['CONFIG']['class_location'].'/'.preg_replace('/[^A-Z0-9]/i','_',ucfirst($table)).".php"; |
} |
if (!file_exists($file)) { |
$found = false; |
foreach(explode(PATH_SEPARATOR, ini_get('include_path')) as $p) { |
if (file_exists("$p/$file")) { |
$file = "$p/$file"; |
$found = true; |
break; |
} |
} |
if (!$found) { |
DB_DataObject::raiseError( |
"autoload:Could not find class {$class} using class_location value", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return false; |
} |
} |
include_once $file; |
if (!class_exists($class)) { |
DB_DataObject::raiseError( |
"autoload:Could not autoload {$class}", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return false; |
} |
return $class; |
} |
/** |
* Have the links been loaded? |
* if they have it contains a array of those variables. |
* |
* @access private |
* @var boolean | array |
*/ |
var $_link_loaded = false; |
/** |
* Get the links associate array as defined by the links.ini file. |
* |
* |
* Experimental... - |
* Should look a bit like |
* [local_col_name] => "related_tablename:related_col_name" |
* |
* |
* @return array|null |
* array = if there are links defined for this table. |
* empty array - if there is a links.ini file, but no links on this table |
* null - if no links.ini exists for this database (hence try auto_links). |
* @access public |
* @see DB_DataObject::getLinks(), DB_DataObject::getLink() |
*/ |
function links() |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
$this->_loadConfig(); |
} |
if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) { |
return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table]; |
} |
$this->databaseStructure(); |
// if there is no link data at all on the file! |
// we return null. |
if (!isset($_DB_DATAOBJECT['LINKS'][$this->_database])) { |
return null; |
} |
if (isset($_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table])) { |
return $_DB_DATAOBJECT['LINKS'][$this->_database][$this->__table]; |
} |
return array(); |
} |
/** |
* load related objects |
* |
* There are two ways to use this, one is to set up a <dbname>.links.ini file |
* into a static property named <dbname>.links and specifies the table joins, |
* the other highly dependent on naming columns 'correctly' :) |
* using colname = xxxxx_yyyyyy |
* xxxxxx = related table; (yyyyy = user defined..) |
* looks up table xxxxx, for value id=$this->xxxxx |
* stores it in $this->_xxxxx_yyyyy |
* you can change what object vars the links are stored in by |
* changeing the format parameter |
* |
* |
* @param string format (default _%s) where %s is the table name. |
* @author Tim White <tim@cyface.com> |
* @access public |
* @return boolean , true on success |
*/ |
function getLinks($format = '_%s') |
{ |
// get table will load the options. |
if ($this->_link_loaded) { |
return true; |
} |
$this->_link_loaded = false; |
$cols = $this->table(); |
$links = $this->links(); |
$loaded = array(); |
if ($links) { |
foreach($links as $key => $match) { |
list($table,$link) = explode(':', $match); |
$k = sprintf($format, str_replace('.', '_', $key)); |
// makes sure that '.' is the end of the key; |
if ($p = strpos($key,'.')) { |
$key = substr($key, 0, $p); |
} |
$this->$k = $this->getLink($key, $table, $link); |
if (is_object($this->$k)) { |
$loaded[] = $k; |
} |
} |
$this->_link_loaded = $loaded; |
return true; |
} |
// this is the autonaming stuff.. |
// it sends the column name down to getLink and lets that sort it out.. |
// if there is a links file then it is not used! |
// IT IS DEPRECIATED!!!! - USE |
if (!is_null($links)) { |
return false; |
} |
foreach (array_keys($cols) as $key) { |
if (!($p = strpos($key, '_'))) { |
continue; |
} |
// does the table exist. |
$k =sprintf($format, $key); |
$this->$k = $this->getLink($key); |
if (is_object($this->$k)) { |
$loaded[] = $k; |
} |
} |
$this->_link_loaded = $loaded; |
return true; |
} |
/** |
* return name from related object |
* |
* There are two ways to use this, one is to set up a <dbname>.links.ini file |
* into a static property named <dbname>.links and specifies the table joins, |
* the other is highly dependant on naming columns 'correctly' :) |
* |
* NOTE: the naming convention is depreciated!!! - use links.ini |
* |
* using colname = xxxxx_yyyyyy |
* xxxxxx = related table; (yyyyy = user defined..) |
* looks up table xxxxx, for value id=$this->xxxxx |
* stores it in $this->_xxxxx_yyyyy |
* |
* you can also use $this->getLink('thisColumnName','otherTable','otherTableColumnName') |
* |
* |
* @param string $row either row or row.xxxxx |
* @param string $table name of table to look up value in |
* @param string $link name of column in other table to match |
* @author Tim White <tim@cyface.com> |
* @access public |
* @return mixed object on success |
*/ |
function &getLink($row, $table = null, $link = false) |
{ |
// GUESS THE LINKED TABLE.. (if found - recursevly call self) |
if ($table === null) { |
$links = $this->links(); |
if (is_array($links)) { |
if ($links[$row]) { |
list($table,$link) = explode(':', $links[$row]); |
if ($p = strpos($row,".")) { |
$row = substr($row,0,$p); |
} |
return $r = &$this->getLink($row,$table,$link); |
} |
$this->raiseError( |
"getLink: $row is not defined as a link (normally this is ok)", |
DB_DATAOBJECT_ERROR_NODATA); |
return false; // technically a possible error condition? |
} |
// use the old _ method - this shouldnt happen if called via getLinks() |
if (!($p = strpos($row, '_'))) { |
return null; |
} |
$table = substr($row, 0, $p); |
return $r = &$this->getLink($row, $table); |
} |
if (!isset($this->$row)) { |
$this->raiseError("getLink: row not set $row", DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
// check to see if we know anything about this table.. |
$obj = $this->factory($table); |
if (!is_a($obj,'DB_DataObject')) { |
$this->raiseError( |
"getLink:Could not find class for row $row, table $table", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return false; |
} |
if ($link) { |
if ($obj->get($link, $this->$row)) { |
return $obj; |
} |
return false; |
} |
if ($obj->get($this->$row)) { |
return $obj; |
} |
return false; |
} |
/** |
* IS THIS SUPPORTED/USED ANYMORE???? |
*return a list of options for a linked table |
* |
* This is highly dependant on naming columns 'correctly' :) |
* using colname = xxxxx_yyyyyy |
* xxxxxx = related table; (yyyyy = user defined..) |
* looks up table xxxxx, for value id=$this->xxxxx |
* stores it in $this->_xxxxx_yyyyy |
* |
* @access public |
* @return array of results (empty array on failure) |
*/ |
function &getLinkArray($row, $table = null) |
{ |
$ret = array(); |
if (!$table) { |
$links = $this->links(); |
if (is_array($links)) { |
if (!isset($links[$row])) { |
// failed.. |
return $ret; |
} |
list($table,$link) = explode(':',$links[$row]); |
} else { |
if (!($p = strpos($row,'_'))) { |
return $ret; |
} |
$table = substr($row,0,$p); |
} |
} |
$c = $this->factory($table); |
if (!is_a($c,'DB_DataObject')) { |
$this->raiseError( |
"getLinkArray:Could not find class for row $row, table $table", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG |
); |
return $ret; |
} |
// if the user defined method list exists - use it... |
if (method_exists($c, 'listFind')) { |
$c->listFind($this->id); |
} else { |
$c->find(); |
} |
while ($c->fetch()) { |
$ret[] = $c; |
} |
return $ret; |
} |
/** |
* The JOIN condition |
* |
* @access private |
* @var string |
*/ |
var $_join = ''; |
/** |
* joinAdd - adds another dataobject to this, building a joined query. |
* |
* example (requires links.ini to be set up correctly) |
* // get all the images for product 24 |
* $i = new DataObject_Image(); |
* $pi = new DataObjects_Product_image(); |
* $pi->product_id = 24; // set the product id to 24 |
* $i->joinAdd($pi); // add the product_image connectoin |
* $i->find(); |
* while ($i->fetch()) { |
* // do stuff |
* } |
* // an example with 2 joins |
* // get all the images linked with products or productgroups |
* $i = new DataObject_Image(); |
* $pi = new DataObject_Product_image(); |
* $pgi = new DataObject_Productgroup_image(); |
* $i->joinAdd($pi); |
* $i->joinAdd($pgi); |
* $i->find(); |
* while ($i->fetch()) { |
* // do stuff |
* } |
* |
* |
* @param optional $obj object |array the joining object (no value resets the join) |
* If you use an array here it should be in the format: |
* array('local_column','remotetable:remote_column'); |
* if remotetable does not have a definition, you should |
* use @ to hide the include error message.. |
* |
* |
* @param optional $joinType string 'LEFT'|'INNER'|'RIGHT'|'' Inner is default, '' indicates |
* just select ... from a,b,c with no join and |
* links are added as where items. |
* |
* @param optional $joinAs string if you want to select the table as anther name |
* useful when you want to select multiple columsn |
* from a secondary table. |
* @param optional $joinCol string The column on This objects table to match (needed |
* if this table links to the child object in |
* multiple places eg. |
* user->friend (is a link to another user) |
* user->mother (is a link to another user..) |
* |
* @return none |
* @access public |
* @author Stijn de Reede <sjr@gmx.co.uk> |
*/ |
function joinAdd($obj = false, $joinType='INNER', $joinAs=false, $joinCol=false) |
{ |
global $_DB_DATAOBJECT; |
if ($obj === false) { |
$this->_join = ''; |
return; |
} |
// support for array as first argument |
// this assumes that you dont have a links.ini for the specified table. |
// and it doesnt exist as am extended dataobject!! - experimental. |
$ofield = false; // object field |
$tfield = false; // this field |
$toTable = false; |
if (is_array($obj)) { |
$tfield = $obj[0]; |
list($toTable,$ofield) = explode(':',$obj[1]); |
$obj = DB_DataObject::factory($toTable); |
if (!$obj || is_a($obj,'PEAR_Error')) { |
$obj = new DB_DataObject; |
$obj->__table = $toTable; |
} |
$obj->_connect(); |
// set the table items to nothing.. - eg. do not try and match |
// things in the child table...??? |
$items = array(); |
} |
if (!is_object($obj)) { |
$this->raiseError("joinAdd: called without an object", DB_DATAOBJECT_ERROR_NODATA,PEAR_ERROR_DIE); |
} |
/* make sure $this->_database is set. */ |
$this->_connect(); |
$DB = &$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
/* look up the links for obj table */ |
//print_r($obj->links()); |
if (!$ofield && ($olinks = $obj->links())) { |
foreach ($olinks as $k => $v) { |
/* link contains {this column} = {linked table}:{linked column} */ |
$ar = explode(':', $v); |
if ($ar[0] == $this->__table) { |
// you have explictly specified the column |
// and the col is listed here.. |
// not sure if 1:1 table could cause probs here.. |
if ($joinCol !== false) { |
$this->raiseError( |
"joinAdd: You cannot target a join column in the " . |
"'link from' table ({$obj->__table}). " . |
"Either remove the fourth argument to joinAdd() ". |
"({$joinCol}), or alter your links.ini file.", |
DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
$ofield = $k; |
$tfield = $ar[1]; |
break; |
} |
} |
} |
/* otherwise see if there are any links from this table to the obj. */ |
//print_r($this->links()); |
if (($ofield === false) && ($links = $this->links())) { |
foreach ($links as $k => $v) { |
/* link contains {this column} = {linked table}:{linked column} */ |
$ar = explode(':', $v); |
if ($ar[0] == $obj->__table) { |
if ($joinCol !== false) { |
if ($k == $joinCol) { |
$tfield = $k; |
$ofield = $ar[1]; |
break; |
} else { |
continue; |
} |
} else { |
$tfield = $k; |
$ofield = $ar[1]; |
break; |
} |
} |
} |
} |
/* did I find a conneciton between them? */ |
if ($ofield === false) { |
$this->raiseError( |
"joinAdd: {$obj->__table} has no link with {$this->__table}", |
DB_DATAOBJECT_ERROR_NODATA); |
return false; |
} |
$joinType = strtoupper($joinType); |
// we default to joining as the same name (this is remvoed later..) |
if ($joinAs === false) { |
$joinAs = $obj->__table; |
} |
$quoteIdentifiers = !empty($_DB_DATAOBJECT['CONFIG']['quote_identifiers']); |
// not sure how portable adding database prefixes is.. |
$objTable = $quoteIdentifiers ? |
$DB->quoteIdentifier($obj->__table) : |
$obj->__table ; |
// as far as we know only mysql supports database prefixes.. |
if ( |
in_array($DB->dsn['phptype'],array('mysql','mysqli')) && |
($obj->_database != $this->_database) && |
strlen($obj->_database) |
) |
{ |
// prefix database (quoted if neccessary..) |
$objTable = ($quoteIdentifiers |
? $DB->quoteIdentifier($obj->_database) |
: $obj->_database) |
. '.' . $objTable; |
} |
// nested (join of joined objects..) |
$appendJoin = ''; |
if ($obj->_join) { |
// postgres allows nested queries, with ()'s |
// not sure what the results are with other databases.. |
// may be unpredictable.. |
if (in_array($DB->dsn["phptype"],array('pgsql'))) { |
$objTable = "($objTable {$obj->_join})"; |
} else { |
$appendJoin = $obj->_join; |
} |
} |
$table = $this->__table; |
if ($quoteIdentifiers) { |
$joinAs = $DB->quoteIdentifier($joinAs); |
$table = $DB->quoteIdentifier($table); |
$ofield = $DB->quoteIdentifier($ofield); |
$tfield = $DB->quoteIdentifier($tfield); |
} |
// add database prefix if they are different databases |
$fullJoinAs = ''; |
$addJoinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->__table) : $obj->__table) != $joinAs; |
if ($addJoinAs) { |
$fullJoinAs = "AS {$joinAs}"; |
} else { |
// if |
if ( |
in_array($DB->dsn['phptype'],array('mysql','mysqli')) && |
($obj->_database != $this->_database) && |
strlen($this->_database) |
) |
{ |
$joinAs = ($quoteIdentifiers ? $DB->quoteIdentifier($obj->_database) : $obj->_database) . '.' . $joinAs; |
} |
} |
switch ($joinType) { |
case 'INNER': |
case 'LEFT': |
case 'RIGHT': // others??? .. cross, left outer, right outer, natural..? |
$this->_join .= "\n {$joinType} JOIN {$objTable} {$fullJoinAs}". |
" ON {$joinAs}.{$ofield}={$table}.{$tfield} {$appendJoin} "; |
break; |
case '': // this is just a standard multitable select.. |
$this->_join .= "\n , {$objTable} {$fullJoinAs} {$appendJoin}"; |
$this->whereAdd("{$joinAs}.{$ofield}={$table}.{$tfield}"); |
} |
// if obj only a dataobject - eg. no extended class has been defined.. |
// it obvioulsy cant work out what child elements might exist... |
// untill we get on the fly querying of tables.. |
if ( strtolower(get_class($obj)) == 'db_dataobject') { |
return true; |
} |
/* now add where conditions for anything that is set in the object */ |
$items = $obj->table(); |
// will return an array if no items.. |
// only fail if we where expecting it to work (eg. not joined on a array) |
if (!$items) { |
$this->raiseError( |
"joinAdd: No table definition for {$obj->__table}", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return false; |
} |
foreach($items as $k => $v) { |
if (!isset($obj->$k)) { |
continue; |
} |
$kSql = ($quoteIdentifiers ? $DB->quoteIdentifier($k) : $k); |
if ($v & DB_DATAOBJECT_STR) { |
$this->whereAdd("{$joinAs}.{$kSql} = " . $this->_quote((string) ( |
($v & DB_DATAOBJECT_BOOL) ? |
// this is thanks to the braindead idea of postgres to |
// use t/f for boolean. |
(($obj->$k == 'f') ? 0 : (int)(bool) $obj->$k) : |
$obj->$k |
))); |
continue; |
} |
if (is_numeric($obj->$k)) { |
$this->whereAdd("{$joinAs}.{$kSql} = {$obj->$k}"); |
continue; |
} |
/* this is probably an error condition! */ |
$this->whereAdd("{$joinAs}.{$kSql} = 0"); |
} |
if (!isset($this->_query)) { |
$this->raiseError( |
"joinAdd can not be run from a object that has had a query run on it, |
clone the object or create a new one and use setFrom()", |
DB_DATAOBJECT_ERROR_INVALIDARGS); |
return false; |
} |
// and finally merge the whereAdd from the child.. |
if (!$obj->_query['condition']) { |
return true; |
} |
$cond = preg_replace('/^\sWHERE/i','',$obj->_query['condition']); |
$this->whereAdd("($cond)"); |
return true; |
} |
/** |
* Copies items that are in the table definitions from an |
* array or object into the current object |
* will not override key values. |
* |
* |
* @param array | object $from |
* @param string $format eg. map xxxx_name to $object->name using 'xxxx_%s' (defaults to %s - eg. name -> $object->name |
* @access public |
* @return true on success or array of key=>setValue error message |
*/ |
function setFrom(&$from, $format = '%s', $checkEmpty=false) |
{ |
global $_DB_DATAOBJECT; |
$keys = $this->keys(); |
$items = $this->table(); |
if (!$items) { |
$this->raiseError( |
"setFrom:Could not find table definition for {$this->__table}", |
DB_DATAOBJECT_ERROR_INVALIDCONFIG); |
return; |
} |
$overload_return = array(); |
foreach (array_keys($items) as $k) { |
if (in_array($k,$keys)) { |
continue; // dont overwrite keys |
} |
if (!$k) { |
continue; // ignore empty keys!!! what |
} |
if (is_object($from) && isset($from->{sprintf($format,$k)})) { |
$kk = (strtolower($k) == 'from') ? '_from' : $k; |
if (method_exists($this,'set'.$kk)) { |
$ret = $this->{'set'.$kk}($from->{sprintf($format,$k)}); |
if (is_string($ret)) { |
$overload_return[$k] = $ret; |
} |
continue; |
} |
$this->$k = $from->{sprintf($format,$k)}; |
continue; |
} |
if (is_object($from)) { |
continue; |
} |
if (!isset($from[sprintf($format,$k)])) { |
continue; |
} |
$kk = (strtolower($k) == 'from') ? '_from' : $k; |
if (method_exists($this,'set'. $kk)) { |
$ret = $this->{'set'.$kk}($from[sprintf($format,$k)]); |
if (is_string($ret)) { |
$overload_return[$k] = $ret; |
} |
continue; |
} |
if (is_object($from[sprintf($format,$k)])) { |
continue; |
} |
if (is_array($from[sprintf($format,$k)])) { |
continue; |
} |
$ret = $this->fromValue($k,$from[sprintf($format,$k)]); |
if ($ret !== true) { |
$overload_return[$k] = 'Not A Valid Value'; |
} |
//$this->$k = $from[sprintf($format,$k)]; |
} |
if ($overload_return) { |
return $overload_return; |
} |
return true; |
} |
/** |
* Returns an associative array from the current data |
* (kind of oblivates the idea behind DataObjects, but |
* is usefull if you use it with things like QuickForms. |
* |
* you can use the format to return things like user[key] |
* by sending it $object->toArray('user[%s]') |
* |
* will also return links converted to arrays. |
* |
* @param string sprintf format for array |
* @param bool empty only return elemnts that have a value set. |
* |
* @access public |
* @return array of key => value for row |
*/ |
function toArray($format = '%s', $hideEmpty = false) |
{ |
global $_DB_DATAOBJECT; |
$ret = array(); |
$ar = isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]) ? |
array_merge($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid],$this->table()) : |
$this->table(); |
foreach($ar as $k=>$v) { |
if (!isset($this->$k)) { |
if (!$hideEmpty) { |
$ret[sprintf($format,$k)] = ''; |
} |
continue; |
} |
// call the overloaded getXXXX() method. - except getLink and getLinks |
if (method_exists($this,'get'.$k) && !in_array(strtolower($k),array('links','link'))) { |
$ret[sprintf($format,$k)] = $this->{'get'.$k}(); |
continue; |
} |
// should this call toValue() ??? |
$ret[sprintf($format,$k)] = $this->$k; |
} |
if (!$this->_link_loaded) { |
return $ret; |
} |
foreach($this->_link_loaded as $k) { |
$ret[sprintf($format,$k)] = $this->$k->toArray(); |
} |
return $ret; |
} |
/** |
* validate - override this to set up your validation rules |
* |
* validate the current objects values either just testing strings/numbers or |
* using the user defined validate{Row name}() methods. |
* will attempt to call $this->validate{column_name}() - expects true = ok false = ERROR |
* you can the use the validate Class from your own methods. |
* |
* This should really be in a extenal class - eg. DB_DataObject_Validate. |
* |
* @access public |
* @return array of validation results or true |
*/ |
function validate() |
{ |
require_once 'Validate.php'; |
$table = $this->table(); |
$ret = array(); |
$seq = $this->sequenceKey(); |
foreach($table as $key => $val) { |
// call user defined validation always... |
$method = "Validate" . ucfirst($key); |
if (method_exists($this, $method)) { |
$ret[$key] = $this->$method(); |
continue; |
} |
// if not null - and it's not set....... |
if (!isset($this->$key) && ($val & DB_DATAOBJECT_NOTNULL)) { |
// dont check empty sequence key values.. |
if (($key == $seq[0]) && ($seq[1] == true)) { |
continue; |
} |
$ret[$key] = false; |
continue; |
} |
if (is_string($this->$key) && (strtolower($this->$key) == 'null') && ($val & DB_DATAOBJECT_NOTNULL)) { |
$ret[$key] = false; |
continue; |
} |
// ignore things that are not set. ? |
if (!isset($this->$key)) { |
continue; |
} |
// if the string is empty.. assume it is ok.. |
if (!is_object($this->$key) && !is_array($this->$key) && !strlen((string) $this->$key)) { |
continue; |
} |
switch (true) { |
// todo: date time..... |
case ($val & DB_DATAOBJECT_STR): |
$ret[$key] = Validate::string($this->$key, VALIDATE_PUNCTUATION . VALIDATE_NAME); |
continue; |
case ($val & DB_DATAOBJECT_INT): |
$ret[$key] = Validate::number($this->$key, array('decimal'=>'.')); |
continue; |
} |
} |
foreach ($ret as $key => $val) { |
if ($val === false) { |
return $ret; |
} |
} |
return true; // everything is OK. |
} |
/** |
* Gets the DB object related to an object - so you can use funky peardb stuf with it :) |
* |
* @access public |
* @return object The DB connection |
*/ |
function &getDatabaseConnection() |
{ |
global $_DB_DATAOBJECT; |
if (($e = $this->_connect()) !== true) { |
return $e; |
} |
if (!isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
return false; |
} |
return $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]; |
} |
/** |
* Gets the DB result object related to the objects active query |
* - so you can use funky pear stuff with it - like pager for example.. :) |
* |
* @access public |
* @return object The DB result object |
*/ |
function &getDatabaseResult() |
{ |
global $_DB_DATAOBJECT; |
$this->_connect(); |
if (!isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) { |
return false; |
} |
return $_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]; |
} |
/** |
* Overload Extension support |
* - enables setCOLNAME/getCOLNAME |
* if you define a set/get method for the item it will be called. |
* otherwise it will just return/set the value. |
* NOTE this currently means that a few Names are NO-NO's |
* eg. links,link,linksarray, from, Databaseconnection,databaseresult |
* |
* note |
* - set is automatically called by setFrom. |
* - get is automatically called by toArray() |
* |
* setters return true on success. = strings on failure |
* getters return the value! |
* |
* this fires off trigger_error - if any problems.. pear_error, |
* has problems with 4.3.2RC2 here |
* |
* @access public |
* @return true? |
* @see overload |
*/ |
function _call($method,$params,&$return) { |
//$this->debug("ATTEMPTING OVERLOAD? $method"); |
// ignore constructors : - mm |
if (strtolower($method) == strtolower(get_class($this))) { |
return true; |
} |
$type = strtolower(substr($method,0,3)); |
$class = get_class($this); |
if (($type != 'set') && ($type != 'get')) { |
return false; |
} |
// deal with naming conflick of setFrom = this is messy ATM! |
if (strtolower($method) == 'set_from') { |
$return = $this->toValue('from',isset($params[0]) ? $params[0] : null); |
return true; |
} |
$element = substr($method,3); |
// dont you just love php's case insensitivity!!!! |
$array = array_keys(get_class_vars($class)); |
/* php5 version which segfaults on 5.0.3 */ |
if (class_exists('ReflectionClass')) { |
$reflection = new ReflectionClass($class); |
$array = array_keys($reflection->getdefaultProperties()); |
} |
if (!in_array($element,$array)) { |
// munge case |
foreach($array as $k) { |
$case[strtolower($k)] = $k; |
} |
if ((substr(phpversion(),0,1) == 5) && isset($case[strtolower($element)])) { |
trigger_error("PHP5 set/get calls should match the case of the variable",E_USER_WARNING); |
$element = strtolower($element); |
} |
// does it really exist? |
if (!isset($case[$element])) { |
return false; |
} |
// use the mundged case |
$element = $case[$element]; // real case ! |
} |
if ($type == 'get') { |
$return = $this->toValue($element,isset($params[0]) ? $params[0] : null); |
return true; |
} |
$return = $this->fromValue($element, $params[0]); |
return true; |
} |
/** |
* standard set* implementation. |
* |
* takes data and uses it to set dates/strings etc. |
* normally called from __call.. |
* |
* Current supports |
* date = using (standard time format, or unixtimestamp).... so you could create a method : |
* function setLastread($string) { $this->fromValue('lastread',strtotime($string)); } |
* |
* time = using strtotime |
* datetime = using same as date - accepts iso standard or unixtimestamp. |
* string = typecast only.. |
* |
* TODO: add formater:: eg. d/m/Y for date! ??? |
* |
* @param string column of database |
* @param mixed value to assign |
* |
* @return true| false (False on error) |
* @access public |
* @see DB_DataObject::_call |
*/ |
function fromValue($col,$value) |
{ |
$cols = $this->table(); |
// dont know anything about this col.. |
if (!isset($cols[$col])) { |
$this->$col = $value; |
return true; |
} |
//echo "FROM VALUE $col, {$cols[$col]}, $value\n"; |
switch (true) { |
// set to null and column is can be null... |
case ((strtolower($value) == 'null') && (!($cols[$col] & DB_DATAOBJECT_NOTNULL))): |
case (is_object($value) && is_a($value,'DB_DataObject_Cast')): |
$this->$col = $value; |
return true; |
// fail on setting null on a not null field.. |
case ((strtolower($value) == 'null') && ($cols[$col] & DB_DATAOBJECT_NOTNULL)): |
return false; |
case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)): |
// empty values get set to '' (which is inserted/updated as NULl |
if (!$value) { |
$this->$col = ''; |
} |
if (is_numeric($value)) { |
$this->$col = date('Y-m-d H:i:s', $value); |
return true; |
} |
// eak... - no way to validate date time otherwise... |
$this->$col = (string) $value; |
return true; |
case ($cols[$col] & DB_DATAOBJECT_DATE): |
// empty values get set to '' (which is inserted/updated as NULl |
if (!$value) { |
$this->$col = ''; |
return true; |
} |
if (is_numeric($value)) { |
$this->$col = date('Y-m-d',$value); |
return true; |
} |
// try date!!!! |
require_once 'Date.php'; |
$x = new Date($value); |
$this->$col = $x->format("%Y-%m-%d"); |
return true; |
case ($cols[$col] & DB_DATAOBJECT_TIME): |
// empty values get set to '' (which is inserted/updated as NULl |
if (!$value) { |
$this->$col = ''; |
} |
$guess = strtotime($value); |
if ($guess != -1) { |
$this->$col = date('H:i:s', $guess); |
return $return = true; |
} |
// otherwise an error in type... |
return false; |
case ($cols[$col] & DB_DATAOBJECT_STR): |
$this->$col = (string) $value; |
return true; |
// todo : floats numerics and ints... |
default: |
$this->$col = $value; |
return true; |
} |
} |
/** |
* standard get* implementation. |
* |
* with formaters.. |
* supported formaters: |
* date/time : %d/%m/%Y (eg. php strftime) or pear::Date |
* numbers : %02d (eg. sprintf) |
* NOTE you will get unexpected results with times like 0000-00-00 !!! |
* |
* |
* |
* @param string column of database |
* @param format foramt |
* |
* @return true Description |
* @access public |
* @see DB_DataObject::_call(),strftime(),Date::format() |
*/ |
function toValue($col,$format = null) |
{ |
if (is_null($format)) { |
return $this->$col; |
} |
$cols = $this->table(); |
switch (true) { |
case (($cols[$col] & DB_DATAOBJECT_DATE) && ($cols[$col] & DB_DATAOBJECT_TIME)): |
if (!$this->$col) { |
return ''; |
} |
$guess = strtotime($this->$col); |
if ($guess != -1) { |
return strftime($format, $guess); |
} |
// eak... - no way to validate date time otherwise... |
return $this->$col; |
case ($cols[$col] & DB_DATAOBJECT_DATE): |
if (!$this->$col) { |
return ''; |
} |
$guess = strtotime($this->$col); |
if ($guess != -1) { |
return strftime($format,$guess); |
} |
// try date!!!! |
require_once 'Date.php'; |
$x = new Date($this->$col); |
return $x->format($format); |
case ($cols[$col] & DB_DATAOBJECT_TIME): |
if (!$this->$col) { |
return ''; |
} |
$guess = strtotime($this->$col); |
if ($guess > -1) { |
return strftime($format, $guess); |
} |
// otherwise an error in type... |
return $this->$col; |
case ($cols[$col] & DB_DATAOBJECT_MYSQLTIMESTAMP): |
if (!$this->$col) { |
return ''; |
} |
require_once 'Date.php'; |
$x = new Date($this->$col); |
return $x->format($format); |
case ($cols[$col] & DB_DATAOBJECT_BOOLEAN): |
if ($cols[$col] & DB_DATAOBJECT_STR) { |
// it's a 't'/'f' ! |
return ($cols[$col] == 't'); |
} |
return (bool) $cols[$col]; |
default: |
return sprintf($format,$this->col); |
} |
} |
/* ----------------------- Debugger ------------------ */ |
/** |
* Debugger. - use this in your extended classes to output debugging information. |
* |
* Uses DB_DataObject::DebugLevel(x) to turn it on |
* |
* @param string $message - message to output |
* @param string $logtype - bold at start |
* @param string $level - output level |
* @access public |
* @return none |
*/ |
function debug($message, $logtype = 0, $level = 1) |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG']['debug']) || |
(is_numeric($_DB_DATAOBJECT['CONFIG']['debug']) && $_DB_DATAOBJECT['CONFIG']['debug'] < $level)) { |
return; |
} |
// this is a bit flaky due to php's wonderfull class passing around crap.. |
// but it's about as good as it gets.. |
$class = (isset($this) && is_a($this,'DB_DataObject')) ? get_class($this) : 'DB_DataObject'; |
if (!is_string($message)) { |
$message = print_r($message,true); |
} |
if (!is_numeric( $_DB_DATAOBJECT['CONFIG']['debug']) && is_callable( $_DB_DATAOBJECT['CONFIG']['debug'])) { |
return call_user_func($_DB_DATAOBJECT['CONFIG']['debug'], $class, $message, $logtype, $level); |
} |
if (!ini_get('html_errors')) { |
echo "$class : $logtype : $message\n"; |
flush(); |
return; |
} |
if (!is_string($message)) { |
$message = print_r($message,true); |
} |
echo "<code><B>$class: $logtype:</B> $message</code><BR>\n"; |
flush(); |
} |
/** |
* sets and returns debug level |
* eg. DB_DataObject::debugLevel(4); |
* |
* @param int $v level |
* @access public |
* @return none |
*/ |
function debugLevel($v = null) |
{ |
global $_DB_DATAOBJECT; |
if (empty($_DB_DATAOBJECT['CONFIG'])) { |
DB_DataObject::_loadConfig(); |
} |
if ($v !== null) { |
$r = isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0; |
$_DB_DATAOBJECT['CONFIG']['debug'] = $v; |
return $r; |
} |
return isset($_DB_DATAOBJECT['CONFIG']['debug']) ? $_DB_DATAOBJECT['CONFIG']['debug'] : 0; |
} |
/** |
* Last Error that has occured |
* - use $this->_lastError or |
* $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); |
* |
* @access public |
* @var object PEAR_Error (or false) |
*/ |
var $_lastError = false; |
/** |
* Default error handling is to create a pear error, but never return it. |
* if you need to handle errors you should look at setting the PEAR_Error callback |
* this is due to the fact it would wreck havoc on the internal methods! |
* |
* @param int $message message |
* @param int $type type |
* @param int $behaviour behaviour (die or continue!); |
* @access public |
* @return error object |
*/ |
function raiseError($message, $type = null, $behaviour = null) |
{ |
global $_DB_DATAOBJECT; |
if ($behaviour == PEAR_ERROR_DIE && !empty($_DB_DATAOBJECT['CONFIG']['dont_die'])) { |
$behaviour = null; |
} |
$error = &PEAR::getStaticProperty('DB_DataObject','lastError'); |
if (PEAR::isError($message)) { |
$error = $message; |
} else { |
require_once 'DB/DataObject/Error.php'; |
$error = PEAR::raiseError($message, $type, $behaviour, |
$opts=null, $userinfo=null, 'DB_DataObject_Error' |
); |
} |
// this will never work totally with PHP's object model. |
// as this is passed on static calls (like staticGet in our case) |
if (isset($this) && is_object($this) && is_subclass_of($this,'db_dataobject')) { |
$this->_lastError = $error; |
} |
$_DB_DATAOBJECT['LASTERROR'] = $error; |
// no checks for production here?....... |
DB_DataObject::debug($message,"ERROR",1); |
return $error; |
} |
/** |
* Define the global $_DB_DATAOBJECT['CONFIG'] as an alias to PEAR::getStaticProperty('DB_DataObject','options'); |
* |
* After Profiling DB_DataObject, I discoved that the debug calls where taking |
* considerable time (well 0.1 ms), so this should stop those calls happening. as |
* all calls to debug are wrapped with direct variable queries rather than actually calling the funciton |
* THIS STILL NEEDS FURTHER INVESTIGATION |
* |
* @access public |
* @return object an error object |
*/ |
function _loadConfig() |
{ |
global $_DB_DATAOBJECT; |
$_DB_DATAOBJECT['CONFIG'] = &PEAR::getStaticProperty('DB_DataObject','options'); |
} |
/** |
* Free global arrays associated with this object. |
* |
* Note: as we now store resultfields in a global, it is never freed, if you do alot of calls to find(), |
* memory will grow gradually. |
* |
* |
* @access public |
* @return none |
*/ |
function free() |
{ |
global $_DB_DATAOBJECT; |
if (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) { |
unset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]); |
} |
if (isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) { |
unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]); |
} |
// this is a huge bug in DB! |
if (isset($_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5])) { |
$_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->num_rows = array(); |
} |
} |
/* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/ |
function _get_table() { return $this->table(); } |
function _get_keys() { return $this->keys(); } |
} |
// technially 4.3.2RC1 was broken!! |
// looks like 4.3.3 may have problems too.... |
if (!defined('DB_DATAOBJECT_NO_OVERLOAD')) { |
if ((phpversion() != '4.3.2-RC1') && (version_compare( phpversion(), "4.3.1") > 0)) { |
if (version_compare( phpversion(), "5") < 0) { |
overload('DB_DataObject'); |
} |
$GLOBALS['_DB_DATAOBJECT']['OVERLOADED'] = true; |
} |
} |
/tags/Racine_livraison_narmer/api/pear/DB/sqlite.php |
---|
New file |
0,0 → 1,942 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's sqlite extension |
* for interacting with SQLite 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 Urs Gehrig <urs@circle.ch> |
* @author Mika Tuupola <tuupola@appelsiini.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 3.0 |
* @version CVS: $Id: sqlite.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 sqlite extension |
* for interacting with SQLite databases |
* |
* These methods overload the ones declared in DB_common. |
* |
* NOTICE: This driver needs PHP's track_errors ini setting to be on. |
* It is automatically turned on when connecting to the database. |
* Make sure your scripts don't turn it off. |
* |
* @category Database |
* @package DB |
* @author Urs Gehrig <urs@circle.ch> |
* @author Mika Tuupola <tuupola@appelsiini.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 3.0 |
* @version Release: 1.7.6 |
* @link http://pear.php.net/package/DB |
*/ |
class DB_sqlite extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'sqlite'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'sqlite'; |
/** |
* 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' => false, |
'numrows' => true, |
'pconnect' => true, |
'prepare' => false, |
'ssl' => false, |
'transactions' => false, |
); |
/** |
* A mapping of native error codes to DB error codes |
* |
* {@internal Error codes according to sqlite_exec. See the online |
* manual at http://sqlite.org/c_interface.html for info. |
* This error handling based on sqlite_exec is not yet implemented.}} |
* |
* @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(); |
/** |
* SQLite data types |
* |
* @link http://www.sqlite.org/datatypes.html |
* |
* @var array |
*/ |
var $keywords = array ( |
'BLOB' => '', |
'BOOLEAN' => '', |
'CHARACTER' => '', |
'CLOB' => '', |
'FLOAT' => '', |
'INTEGER' => '', |
'KEY' => '', |
'NATIONAL' => '', |
'NUMERIC' => '', |
'NVARCHAR' => '', |
'PRIMARY' => '', |
'TEXT' => '', |
'TIMESTAMP' => '', |
'UNIQUE' => '', |
'VARCHAR' => '', |
'VARYING' => '', |
); |
/** |
* The most recent error message from $php_errormsg |
* @var string |
* @access private |
*/ |
var $_lasterror = ''; |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_sqlite() |
{ |
$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 sqlite driver supports the following extra DSN options: |
* + mode The permissions for the database file, in four digit |
* chmod octal format (eg "0600"). |
* |
* Example of connecting to a database in read-only mode: |
* <code> |
* require_once 'DB.php'; |
* |
* $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400'; |
* $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. |
*/ |
function connect($dsn, $persistent = false) |
{ |
if (!PEAR::loadExtension('sqlite')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
if ($dsn['database']) { |
if (!file_exists($dsn['database'])) { |
if (!touch($dsn['database'])) { |
return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); |
} |
if (!isset($dsn['mode']) || |
!is_numeric($dsn['mode'])) |
{ |
$mode = 0644; |
} else { |
$mode = octdec($dsn['mode']); |
} |
if (!chmod($dsn['database'], $mode)) { |
return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); |
} |
if (!file_exists($dsn['database'])) { |
return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); |
} |
} |
if (!is_file($dsn['database'])) { |
return $this->sqliteRaiseError(DB_ERROR_INVALID); |
} |
if (!is_readable($dsn['database'])) { |
return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); |
} |
} else { |
return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); |
} |
$connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open'; |
// track_errors must remain on for simpleQuery() |
ini_set('track_errors', 1); |
$php_errormsg = ''; |
if (!$this->connection = @$connect_function($dsn['database'])) { |
return $this->raiseError(DB_ERROR_NODBSELECTED, |
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 = @sqlite_close($this->connection); |
$this->connection = null; |
return $ret; |
} |
// }}} |
// {{{ simpleQuery() |
/** |
* Sends a query to the database server |
* |
* NOTICE: This method needs PHP's track_errors ini setting to be on. |
* It is automatically turned on when connecting to the database. |
* Make sure your scripts don't turn it off. |
* |
* @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); |
$php_errormsg = ''; |
$result = @sqlite_query($query, $this->connection); |
$this->_lasterror = $php_errormsg ? $php_errormsg : ''; |
$this->result = $result; |
if (!$this->result) { |
return $this->sqliteRaiseError(null); |
} |
// sqlite_query() seems to allways return a resource |
// so cant use that. Using $ismanip instead |
if (!$ismanip) { |
$numRows = $this->numRows($result); |
if (is_object($numRows)) { |
// we've got PEAR_Error |
return $numRows; |
} |
return $result; |
} |
return DB_OK; |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal sqlite result pointer to the next available result |
* |
* @param resource $result the valid sqlite result resource |
* |
* @return bool 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) |
{ |
if ($rownum !== null) { |
if (!@sqlite_seek($this->result, $rownum)) { |
return null; |
} |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
$arr = @sqlite_fetch_array($result, SQLITE_ASSOC); |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @sqlite_fetch_array($result, SQLITE_NUM); |
} |
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) |
{ |
// XXX No native free? |
if (!is_resource($result)) { |
return false; |
} |
$result = null; |
return true; |
} |
// }}} |
// {{{ 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 = @sqlite_num_fields($result); |
if (!$cols) { |
return $this->sqliteRaiseError(); |
} |
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 = @sqlite_num_rows($result); |
if ($rows === null) { |
return $this->sqliteRaiseError(); |
} |
return $rows; |
} |
// }}} |
// {{{ affected() |
/** |
* 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 @sqlite_changes($this->connection); |
} |
// }}} |
// {{{ 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_sqlite::nextID(), DB_sqlite::createSequence() |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
} |
/** |
* 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_sqlite::nextID(), DB_sqlite::dropSequence() |
*/ |
function createSequence($seq_name) |
{ |
$seqname = $this->getSequenceName($seq_name); |
$query = 'CREATE TABLE ' . $seqname . |
' (id INTEGER UNSIGNED PRIMARY KEY) '; |
$result = $this->query($query); |
if (DB::isError($result)) { |
return($result); |
} |
$query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname |
BEGIN |
DELETE FROM $seqname WHERE id<LAST_INSERT_ROWID(); |
END "; |
$result = $this->query($query); |
if (DB::isError($result)) { |
return($result); |
} |
} |
// }}} |
// {{{ 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_sqlite::createSequence(), DB_sqlite::dropSequence() |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$seqname = $this->getSequenceName($seq_name); |
do { |
$repeat = 0; |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)"); |
$this->popErrorHandling(); |
if ($result === DB_OK) { |
$id = @sqlite_last_insert_rowid($this->connection); |
if ($id != 0) { |
return $id; |
} |
} elseif ($ondemand && DB::isError($result) && |
$result->getCode() == DB_ERROR_NOSUCHTABLE) |
{ |
$result = $this->createSequence($seq_name); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} else { |
$repeat = 1; |
} |
} |
} while ($repeat); |
return $this->raiseError($result); |
} |
// }}} |
// {{{ getDbFileStats() |
/** |
* Get the file stats for the current database |
* |
* Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size, |
* atime, mtime, ctime, blksize, blocks or a numeric key between |
* 0 and 12. |
* |
* @param string $arg the array key for stats() |
* |
* @return mixed an array on an unspecified key, integer on a passed |
* arg and false at a stats error |
*/ |
function getDbFileStats($arg = '') |
{ |
$stats = stat($this->dsn['database']); |
if ($stats == false) { |
return false; |
} |
if (is_array($stats)) { |
if (is_numeric($arg)) { |
if (((int)$arg <= 12) & ((int)$arg >= 0)) { |
return false; |
} |
return $stats[$arg ]; |
} |
if (array_key_exists(trim($arg), $stats)) { |
return $stats[$arg ]; |
} |
} |
return $stats; |
} |
// }}} |
// {{{ 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 |
* |
* @since Method available since Release 1.6.1 |
* @see DB_common::escapeSimple() |
*/ |
function escapeSimple($str) |
{ |
return @sqlite_escape_string($str); |
} |
// }}} |
// {{{ 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"; |
} |
// }}} |
// {{{ 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) { |
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; |
} |
// }}} |
// {{{ sqliteRaiseError() |
/** |
* 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_sqlite::errorNative(), DB_sqlite::errorCode() |
*/ |
function sqliteRaiseError($errno = null) |
{ |
$native = $this->errorNative(); |
if ($errno === null) { |
$errno = $this->errorCode($native); |
} |
$errorcode = @sqlite_last_error($this->connection); |
$userinfo = "$errorcode ** $this->last_query"; |
return $this->raiseError($errno, null, null, $userinfo, $native); |
} |
// }}} |
// {{{ errorNative() |
/** |
* Gets the DBMS' native error message produced by the last query |
* |
* {@internal This is used to retrieve more meaningfull error messages |
* because sqlite_last_error() does not provide adequate info.}} |
* |
* @return string the DBMS' error message |
*/ |
function errorNative() |
{ |
return $this->_lasterror; |
} |
// }}} |
// {{{ errorCode() |
/** |
* Determines PEAR::DB error code from the database's text error message |
* |
* @param string $errormsg the error message returned from the database |
* |
* @return integer the DB error number |
*/ |
function errorCode($errormsg) |
{ |
static $error_regexps; |
if (!isset($error_regexps)) { |
$error_regexps = array( |
'/^no such table:/' => DB_ERROR_NOSUCHTABLE, |
'/^no such index:/' => DB_ERROR_NOT_FOUND, |
'/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS, |
'/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT, |
'/is not unique/' => DB_ERROR_CONSTRAINT, |
'/columns .* are not unique/i' => DB_ERROR_CONSTRAINT, |
'/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT, |
'/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL, |
'/^no such column:/' => DB_ERROR_NOSUCHFIELD, |
'/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD, |
'/^near ".*": syntax error$/' => DB_ERROR_SYNTAX, |
'/[0-9]+ values for [0-9]+ 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 |
* |
* @param string $result a string containing the name of a table |
* @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() |
* @since Method available since Release 1.7.0 |
*/ |
function tableInfo($result, $mode = null) |
{ |
if (is_string($result)) { |
/* |
* Probably received a table name. |
* Create a result resource identifier. |
*/ |
$id = @sqlite_array_query($this->connection, |
"PRAGMA table_info('$result');", |
SQLITE_ASSOC); |
$got_string = true; |
} else { |
$this->last_query = ''; |
return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null, |
'This DBMS can not obtain tableInfo' . |
' from result sets'); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = count($id); |
$res = array(); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
if (strpos($id[$i]['type'], '(') !== false) { |
$bits = explode('(', $id[$i]['type']); |
$type = $bits[0]; |
$len = rtrim($bits[1],')'); |
} else { |
$type = $id[$i]['type']; |
$len = 0; |
} |
$flags = ''; |
if ($id[$i]['pk']) { |
$flags .= 'primary_key '; |
} |
if ($id[$i]['notnull']) { |
$flags .= 'not_null '; |
} |
if ($id[$i]['dflt_value'] !== null) { |
$flags .= 'default_' . rawurlencode($id[$i]['dflt_value']); |
} |
$flags = trim($flags); |
$res[$i] = array( |
'table' => $case_func($result), |
'name' => $case_func($id[$i]['name']), |
'type' => $type, |
'len' => $len, |
'flags' => $flags, |
); |
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; |
} |
} |
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 |
* @param array $args SQLITE DRIVER ONLY: a private array of arguments |
* used by the getSpecialQuery(). Do not use |
* this directly. |
* |
* @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, $args = array()) |
{ |
if (!is_array($args)) { |
return $this->raiseError('no key specified', null, null, null, |
'Argument has to be an array.'); |
} |
switch ($type) { |
case 'master': |
return 'SELECT * FROM sqlite_master;'; |
case 'tables': |
return "SELECT name FROM sqlite_master WHERE type='table' " |
. 'UNION ALL SELECT name FROM sqlite_temp_master ' |
. "WHERE type='table' ORDER BY name;"; |
case 'schema': |
return 'SELECT sql FROM (SELECT * FROM sqlite_master ' |
. 'UNION ALL SELECT * FROM sqlite_temp_master) ' |
. "WHERE type!='meta' " |
. 'ORDER BY tbl_name, type DESC, name;'; |
case 'schemax': |
case 'schema_x': |
/* |
* Use like: |
* $res = $db->query($db->getSpecialQuery('schema_x', |
* array('table' => 'table3'))); |
*/ |
return 'SELECT sql FROM (SELECT * FROM sqlite_master ' |
. 'UNION ALL SELECT * FROM sqlite_temp_master) ' |
. "WHERE tbl_name LIKE '{$args['table']}' " |
. "AND type!='meta' " |
. 'ORDER BY type DESC, name;'; |
case 'alter': |
/* |
* SQLite does not support ALTER TABLE; this is a helper query |
* to handle this. 'table' represents the table name, 'rows' |
* the news rows to create, 'save' the row(s) to keep _with_ |
* the data. |
* |
* Use like: |
* $args = array( |
* 'table' => $table, |
* 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT", |
* 'save' => "NULL, titel, content, datetime" |
* ); |
* $res = $db->query( $db->getSpecialQuery('alter', $args)); |
*/ |
$rows = strtr($args['rows'], $this->keywords); |
$q = array( |
'BEGIN TRANSACTION', |
"CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})", |
"INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}", |
"DROP TABLE {$args['table']}", |
"CREATE TABLE {$args['table']} ({$args['rows']})", |
"INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup", |
"DROP TABLE {$args['table']}_backup", |
'COMMIT', |
); |
/* |
* This is a dirty hack, since the above query will not get |
* executed with a single query call so here the query method |
* will be called directly and return a select instead. |
*/ |
foreach ($q as $query) { |
$this->query($query); |
} |
return "SELECT * FROM {$args['table']};"; |
default: |
return null; |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/oci8.php |
---|
New file |
0,0 → 1,1117 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's oci8 extension |
* for interacting with Oracle 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 James L. Pine <jlp@valinux.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: oci8.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 oci8 extension |
* for interacting with Oracle databases |
* |
* Definitely works with versions 8 and 9 of Oracle. |
* |
* These methods overload the ones declared in DB_common. |
* |
* Be aware... OCIError() only appears to return anything when given a |
* statement, so functions return the generic DB_ERROR instead of more |
* useful errors that have to do with feedback from the database. |
* |
* @category Database |
* @package DB |
* @author James L. Pine <jlp@valinux.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: 1.7.6 |
* @link http://pear.php.net/package/DB |
*/ |
class DB_oci8 extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'oci8'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'oci8'; |
/** |
* 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' => '5.0.0', |
'numrows' => 'subquery', |
'pconnect' => true, |
'prepare' => true, |
'ssl' => false, |
'transactions' => true, |
); |
/** |
* A mapping of native error codes to DB error codes |
* @var array |
*/ |
var $errorcode_map = array( |
1 => DB_ERROR_CONSTRAINT, |
900 => DB_ERROR_SYNTAX, |
904 => DB_ERROR_NOSUCHFIELD, |
913 => DB_ERROR_VALUE_COUNT_ON_ROW, |
921 => DB_ERROR_SYNTAX, |
923 => DB_ERROR_SYNTAX, |
942 => DB_ERROR_NOSUCHTABLE, |
955 => DB_ERROR_ALREADY_EXISTS, |
1400 => DB_ERROR_CONSTRAINT_NOT_NULL, |
1401 => DB_ERROR_INVALID, |
1407 => DB_ERROR_CONSTRAINT_NOT_NULL, |
1418 => DB_ERROR_NOT_FOUND, |
1476 => DB_ERROR_DIVZERO, |
1722 => DB_ERROR_INVALID_NUMBER, |
2289 => DB_ERROR_NOSUCHTABLE, |
2291 => DB_ERROR_CONSTRAINT, |
2292 => DB_ERROR_CONSTRAINT, |
2449 => 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; |
/** |
* Stores the $data passed to execute() in the oci8 driver |
* |
* Gets reset to array() when simpleQuery() is run. |
* |
* Needed in case user wants to call numRows() after prepare/execute |
* was used. |
* |
* @var array |
* @access private |
*/ |
var $_data = array(); |
/** |
* The result or statement handle from the most recently executed query |
* @var resource |
*/ |
var $last_stmt; |
/** |
* Is the given prepared statement a data manipulation query? |
* @var array |
* @access private |
*/ |
var $manip_query = array(); |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_oci8() |
{ |
$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. |
* |
* If PHP is at version 5.0.0 or greater: |
* + Generally, oci_connect() or oci_pconnect() are used. |
* + But if the new_link DSN option is set to true, oci_new_connect() |
* is used. |
* |
* When using PHP version 4.x, OCILogon() or OCIPLogon() are used. |
* |
* PEAR DB's oci8 driver supports the following extra DSN options: |
* + charset The character set to be used on the connection. |
* Only used if PHP is at version 5.0.0 or greater |
* and the Oracle server is at 9.2 or greater. |
* 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 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('oci8')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
if (function_exists('oci_connect')) { |
if (isset($dsn['new_link']) |
&& ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) |
{ |
$connect_function = 'oci_new_connect'; |
} else { |
$connect_function = $persistent ? 'oci_pconnect' |
: 'oci_connect'; |
} |
// Backwards compatibility with DB < 1.7.0 |
if (empty($dsn['database']) && !empty($dsn['hostspec'])) { |
$db = $dsn['hostspec']; |
} else { |
$db = $dsn['database']; |
} |
$char = empty($dsn['charset']) ? null : $dsn['charset']; |
$this->connection = @$connect_function($dsn['username'], |
$dsn['password'], |
$db, |
$char); |
$error = OCIError(); |
if (!empty($error) && $error['code'] == 12541) { |
// Couldn't find TNS listener. Try direct connection. |
$this->connection = @$connect_function($dsn['username'], |
$dsn['password'], |
null, |
$char); |
} |
} else { |
$connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; |
if ($dsn['hostspec']) { |
$this->connection = @$connect_function($dsn['username'], |
$dsn['password'], |
$dsn['hostspec']); |
} elseif ($dsn['username'] || $dsn['password']) { |
$this->connection = @$connect_function($dsn['username'], |
$dsn['password']); |
} |
} |
if (!$this->connection) { |
$error = OCIError(); |
$error = (is_array($error)) ? $error['message'] : null; |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
null, null, null, |
$error); |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
if (function_exists('oci_close')) { |
$ret = @oci_close($this->connection); |
} else { |
$ret = @OCILogOff($this->connection); |
} |
$this->connection = null; |
return $ret; |
} |
// }}} |
// {{{ simpleQuery() |
/** |
* Sends a query to the database server |
* |
* To determine how many rows of a result set get buffered using |
* ocisetprefetch(), see the "result_buffering" option in 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) |
{ |
$this->_data = array(); |
$this->last_parameters = array(); |
$this->last_query = $query; |
$query = $this->modifyQuery($query); |
$result = @OCIParse($this->connection, $query); |
if (!$result) { |
return $this->oci8RaiseError(); |
} |
if ($this->autocommit) { |
$success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS); |
} else { |
$success = @OCIExecute($result,OCI_DEFAULT); |
} |
if (!$success) { |
return $this->oci8RaiseError($result); |
} |
$this->last_stmt = $result; |
if (DB::isManip($query)) { |
return DB_OK; |
} else { |
@ocisetprefetch($result, $this->options['result_buffering']); |
return $result; |
} |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal oracle result pointer to the next available result |
* |
* @param a valid oci8 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) |
{ |
if ($rownum !== null) { |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
$moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS); |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && |
$moredata) |
{ |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS); |
} |
if (!$moredata) { |
return null; |
} |
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
$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 @OCIFreeStatement($result); |
} |
/** |
* Frees the internal resources associated with a prepared query |
* |
* @param resource $stmt the prepared statement's 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_oci8::prepare() |
*/ |
function freePrepared($stmt, $free_resource = true) |
{ |
if (!is_resource($stmt)) { |
return false; |
} |
if ($free_resource) { |
@ocifreestatement($stmt); |
} |
if (isset($this->prepare_types[(int)$stmt])) { |
unset($this->prepare_types[(int)$stmt]); |
unset($this->manip_query[(int)$stmt]); |
} else { |
return false; |
} |
return true; |
} |
// }}} |
// {{{ numRows() |
/** |
* Gets the number of rows in a result set |
* |
* Only works if the DB_PORTABILITY_NUMROWS portability option |
* is turned on. |
* |
* 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(), DB_common::setOption() |
*/ |
function numRows($result) |
{ |
// emulate numRows for Oracle. yuck. |
if ($this->options['portability'] & DB_PORTABILITY_NUMROWS && |
$result === $this->last_stmt) |
{ |
$countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')'; |
$save_query = $this->last_query; |
$save_stmt = $this->last_stmt; |
if (count($this->_data)) { |
$smt = $this->prepare('SELECT COUNT(*) FROM ('.$this->last_query.')'); |
$count = $this->execute($smt, $this->_data); |
} else { |
$count =& $this->query($countquery); |
} |
if (DB::isError($count) || |
DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) |
{ |
$this->last_query = $save_query; |
$this->last_stmt = $save_stmt; |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
return $row[0]; |
} |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ 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 = @OCINumCols($result); |
if (!$cols) { |
return $this->oci8RaiseError($result); |
} |
return $cols; |
} |
// }}} |
// {{{ prepare() |
/** |
* Prepares a query for multiple execution with execute(). |
* |
* With oci8, this is emulated. |
* |
* prepare() requires a generic query as string like <code> |
* INSERT INTO numbers VALUES (?, ?, ?) |
* </code>. The <kbd>?</kbd> characters are placeholders. |
* |
* Three types of placeholders can be used: |
* + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers |
* + <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) |
* |
* Use backslashes to escape placeholder characters if you don't want |
* them to be interpreted as placeholders. Example: <code> |
* "UPDATE foo SET col=? WHERE col='over \& under'" |
* </code> |
* |
* @param string $query the query to be prepared |
* |
* @return mixed DB statement resource on success. DB_Error on failure. |
* |
* @see DB_oci8::execute() |
*/ |
function prepare($query) |
{ |
$tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, |
PREG_SPLIT_DELIM_CAPTURE); |
$binds = count($tokens) - 1; |
$token = 0; |
$types = array(); |
$newquery = ''; |
foreach ($tokens as $key => $val) { |
switch ($val) { |
case '?': |
$types[$token++] = DB_PARAM_SCALAR; |
unset($tokens[$key]); |
break; |
case '&': |
$types[$token++] = DB_PARAM_OPAQUE; |
unset($tokens[$key]); |
break; |
case '!': |
$types[$token++] = DB_PARAM_MISC; |
unset($tokens[$key]); |
break; |
default: |
$tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); |
if ($key != $binds) { |
$newquery .= $tokens[$key] . ':bind' . $token; |
} else { |
$newquery .= $tokens[$key]; |
} |
} |
} |
$this->last_query = $query; |
$newquery = $this->modifyQuery($newquery); |
if (!$stmt = @OCIParse($this->connection, $newquery)) { |
return $this->oci8RaiseError(); |
} |
$this->prepare_types[(int)$stmt] = $types; |
$this->manip_query[(int)$stmt] = DB::isManip($query); |
return $stmt; |
} |
// }}} |
// {{{ execute() |
/** |
* Executes a DB statement prepared with prepare(). |
* |
* To determine how many rows of a result set get buffered using |
* ocisetprefetch(), see the "result_buffering" option in setOptions(). |
* This option was added in Release 1.7.0. |
* |
* @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 for non-array items or the |
* quantity of elements in the array. |
* |
* @return mixed returns an oic8 result resource for successful SELECT |
* queries, DB_OK for other successful queries. |
* A DB error object is returned on failure. |
* |
* @see DB_oci8::prepare() |
*/ |
function &execute($stmt, $data = array()) |
{ |
$data = (array)$data; |
$this->last_parameters = $data; |
$this->_data = $data; |
$types =& $this->prepare_types[(int)$stmt]; |
if (count($types) != count($data)) { |
$tmp =& $this->raiseError(DB_ERROR_MISMATCH); |
return $tmp; |
} |
$i = 0; |
foreach ($data as $key => $value) { |
if ($types[$i] == DB_PARAM_MISC) { |
/* |
* Oracle doesn't seem to have the ability to pass a |
* parameter along unchanged, so strip off quotes from start |
* and end, plus turn two single quotes to one single quote, |
* in order to avoid the quotes getting escaped by |
* Oracle and ending up in the database. |
*/ |
$data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); |
$data[$key] = str_replace("''", "'", $data[$key]); |
} elseif ($types[$i] == DB_PARAM_OPAQUE) { |
$fp = @fopen($data[$key], 'rb'); |
if (!$fp) { |
$tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); |
return $tmp; |
} |
$data[$key] = fread($fp, filesize($data[$key])); |
fclose($fp); |
} |
if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) { |
$tmp = $this->oci8RaiseError($stmt); |
return $tmp; |
} |
$i++; |
} |
if ($this->autocommit) { |
$success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS); |
} else { |
$success = @OCIExecute($stmt, OCI_DEFAULT); |
} |
if (!$success) { |
$tmp = $this->oci8RaiseError($stmt); |
return $tmp; |
} |
$this->last_stmt = $stmt; |
if ($this->manip_query[(int)$stmt]) { |
$tmp = DB_OK; |
} else { |
@ocisetprefetch($stmt, $this->options['result_buffering']); |
$tmp =& new DB_result($this, $stmt); |
} |
return $tmp; |
} |
// }}} |
// {{{ 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) |
{ |
$this->autocommit = (bool)$onoff;; |
return DB_OK; |
} |
// }}} |
// {{{ commit() |
/** |
* Commits the current transaction |
* |
* @return int DB_OK on success. A DB_Error object on failure. |
*/ |
function commit() |
{ |
$result = @OCICommit($this->connection); |
if (!$result) { |
return $this->oci8RaiseError(); |
} |
return DB_OK; |
} |
// }}} |
// {{{ rollback() |
/** |
* Reverts the current transaction |
* |
* @return int DB_OK on success. A DB_Error object on failure. |
*/ |
function rollback() |
{ |
$result = @OCIRollback($this->connection); |
if (!$result) { |
return $this->oci8RaiseError(); |
} |
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 ($this->last_stmt === false) { |
return $this->oci8RaiseError(); |
} |
$result = @OCIRowCount($this->last_stmt); |
if ($result === false) { |
return $this->oci8RaiseError($this->last_stmt); |
} |
return $result; |
} |
// }}} |
// {{{ modifyQuery() |
/** |
* Changes a query string for various DBMS specific reasons |
* |
* "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle. |
* |
* @param string $query the query string to modify |
* |
* @return string the modified query string |
* |
* @access protected |
*/ |
function modifyQuery($query) |
{ |
if (preg_match('/^\s*SELECT/i', $query) && |
!preg_match('/\sFROM\s/i', $query)) { |
$query .= ' FROM dual'; |
} |
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()) |
{ |
// Let Oracle return the name of the columns instead of |
// coding a "home" SQL parser |
if (count($params)) { |
$result = $this->prepare("SELECT * FROM ($query) " |
. 'WHERE NULL = NULL'); |
$tmp =& $this->execute($result, $params); |
} else { |
$q_fields = "SELECT * FROM ($query) WHERE NULL = NULL"; |
if (!$result = @OCIParse($this->connection, $q_fields)) { |
$this->last_query = $q_fields; |
return $this->oci8RaiseError(); |
} |
if (!@OCIExecute($result, OCI_DEFAULT)) { |
$this->last_query = $q_fields; |
return $this->oci8RaiseError($result); |
} |
} |
$ncols = OCINumCols($result); |
$cols = array(); |
for ( $i = 1; $i <= $ncols; $i++ ) { |
$cols[] = '"' . OCIColumnName($result, $i) . '"'; |
} |
$fields = implode(', ', $cols); |
// XXX Test that (tip by John Lim) |
//if (preg_match('/^\s*SELECT\s+/is', $query, $match)) { |
// // Introduce the FIRST_ROWS Oracle query optimizer |
// $query = substr($query, strlen($match[0]), strlen($query)); |
// $query = "SELECT /* +FIRST_ROWS */ " . $query; |
//} |
// Construct the query |
// more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2 |
// Perhaps this could be optimized with the use of Unions |
$query = "SELECT $fields FROM". |
" (SELECT rownum as linenum, $fields FROM". |
" ($query)". |
' WHERE rownum <= '. ($from + $count) . |
') WHERE linenum >= ' . ++$from; |
return $query; |
} |
// }}} |
// {{{ 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_oci8::createSequence(), DB_oci8::dropSequence() |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$seqname = $this->getSequenceName($seq_name); |
$repeat = 0; |
do { |
$this->expectError(DB_ERROR_NOSUCHTABLE); |
$result =& $this->query("SELECT ${seqname}.nextval FROM dual"); |
$this->popExpect(); |
if ($ondemand && DB::isError($result) && |
$result->getCode() == DB_ERROR_NOSUCHTABLE) { |
$repeat = 1; |
$result = $this->createSequence($seq_name); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
} else { |
$repeat = 0; |
} |
} while ($repeat); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
$arr = $result->fetchRow(DB_FETCHMODE_ORDERED); |
return $arr[0]; |
} |
/** |
* 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_oci8::nextID(), DB_oci8::dropSequence() |
*/ |
function createSequence($seq_name) |
{ |
return $this->query('CREATE SEQUENCE ' |
. $this->getSequenceName($seq_name)); |
} |
// }}} |
// {{{ 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_oci8::nextID(), DB_oci8::createSequence() |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DROP SEQUENCE ' |
. $this->getSequenceName($seq_name)); |
} |
// }}} |
// {{{ oci8RaiseError() |
/** |
* 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_oci8::errorNative(), DB_oci8::errorCode() |
*/ |
function oci8RaiseError($errno = null) |
{ |
if ($errno === null) { |
$error = @OCIError($this->connection); |
return $this->raiseError($this->errorCode($error['code']), |
null, null, null, $error['message']); |
} elseif (is_resource($errno)) { |
$error = @OCIError($errno); |
return $this->raiseError($this->errorCode($error['code']), |
null, null, null, $error['message']); |
} |
return $this->raiseError($this->errorCode($errno)); |
} |
// }}} |
// {{{ errorNative() |
/** |
* Gets the DBMS' native error code produced by the last query |
* |
* @return int the DBMS' error code. FALSE if the code could not be |
* determined |
*/ |
function errorNative() |
{ |
if (is_resource($this->last_stmt)) { |
$error = @OCIError($this->last_stmt); |
} else { |
$error = @OCIError($this->connection); |
} |
if (is_array($error)) { |
return $error['code']; |
} |
return false; |
} |
// }}} |
// {{{ tableInfo() |
/** |
* Returns information about a table or a result set |
* |
* NOTE: only supports 'table' and 'flags' if <var>$result</var> |
* is a table name. |
* |
* NOTE: flags won't contain index information. |
* |
* @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 ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$res = array(); |
if (is_string($result)) { |
/* |
* Probably received a table name. |
* Create a result resource identifier. |
*/ |
$result = strtoupper($result); |
$q_fields = 'SELECT column_name, data_type, data_length, ' |
. 'nullable ' |
. 'FROM user_tab_columns ' |
. "WHERE table_name='$result' ORDER BY column_id"; |
$this->last_query = $q_fields; |
if (!$stmt = @OCIParse($this->connection, $q_fields)) { |
return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if (!@OCIExecute($stmt, OCI_DEFAULT)) { |
return $this->oci8RaiseError($stmt); |
} |
$i = 0; |
while (@OCIFetch($stmt)) { |
$res[$i] = array( |
'table' => $case_func($result), |
'name' => $case_func(@OCIResult($stmt, 1)), |
'type' => @OCIResult($stmt, 2), |
'len' => @OCIResult($stmt, 3), |
'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '', |
); |
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; |
} |
$i++; |
} |
if ($mode) { |
$res['num_fields'] = $i; |
} |
@OCIFreeStatement($stmt); |
} else { |
if (isset($result->result)) { |
/* |
* Probably received a result object. |
* Extract the result resource identifier. |
*/ |
$result = $result->result; |
} |
$res = array(); |
if ($result === $this->last_stmt) { |
$count = @OCINumCols($result); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
$res[$i] = array( |
'table' => '', |
'name' => $case_func(@OCIColumnName($result, $i+1)), |
'type' => @OCIColumnType($result, $i+1), |
'len' => @OCIColumnSize($result, $i+1), |
'flags' => '', |
); |
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; |
} |
} |
} else { |
return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
} |
} |
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 'SELECT table_name FROM user_tables'; |
case 'synonyms': |
return 'SELECT synonym_name FROM user_synonyms'; |
default: |
return null; |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/QueryTool.php |
---|
New file |
0,0 → 1,63 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* Contains the DB_QueryTool 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_QueryTool |
* @author Wolfram Kriesing <wk@visionp.de> |
* @author Paolo Panto <wk@visionp.de> |
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: QueryTool.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
/** |
* require the DB_QueryTool_EasyJoin class |
*/ |
require_once 'DB/QueryTool/EasyJoin.php'; |
/** |
* MDB_QueryTool class |
* |
* This class should be extended; it's here to make it easy using the base |
* class of the package by its package name. |
* Since I tried to seperate the functionality a bit inside the |
* really working classes i decided to have this class here just to |
* provide the name, since the functionality inside the other |
* classes might be restructured a bit but this name always stays. |
* |
* @category Database |
* @package DB_QueryTool |
* @author Wolfram Kriesing <wk@visionp.de> |
* @copyright 2003-2005 Wolfram Kriesing |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
class DB_QueryTool extends DB_QueryTool_EasyJoin |
{ |
// {{{ DB_QueryTool() |
/** |
* call parent constructor |
* @param mixed $dsn DSN string, DSN array or DB object |
* @param array $options |
*/ |
function DB_QueryTool($dsn=false, $options=array()) |
{ |
parent::__construct($dsn, $options); |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/storage.php |
---|
New file |
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,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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: 1.7.6 |
* @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: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/mysql.php |
---|
New file |
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,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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: 1.7.6 |
* @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: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/odbc.php |
---|
New file |
0,0 → 1,883 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's odbc extension |
* for interacting with databases via ODBC connections |
* |
* 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: odbc.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 odbc extension |
* for interacting with databases via ODBC connections |
* |
* These methods overload the ones declared in DB_common. |
* |
* More info on ODBC errors could be found here: |
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp |
* |
* @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: 1.7.6 |
* @link http://pear.php.net/package/DB |
*/ |
class DB_odbc extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'odbc'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'sql92'; |
/** |
* 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 |
* |
* NOTE: The feature set of the following drivers are different than |
* the default: |
* + solid: 'transactions' = true |
* + navision: 'limit' = false |
* |
* @var array |
*/ |
var $features = array( |
'limit' => 'emulate', |
'new_link' => false, |
'numrows' => true, |
'pconnect' => true, |
'prepare' => false, |
'ssl' => false, |
'transactions' => false, |
); |
/** |
* A mapping of native error codes to DB error codes |
* @var array |
*/ |
var $errorcode_map = array( |
'01004' => DB_ERROR_TRUNCATED, |
'07001' => DB_ERROR_MISMATCH, |
'21S01' => DB_ERROR_VALUE_COUNT_ON_ROW, |
'21S02' => DB_ERROR_MISMATCH, |
'22001' => DB_ERROR_INVALID, |
'22003' => DB_ERROR_INVALID_NUMBER, |
'22005' => DB_ERROR_INVALID_NUMBER, |
'22008' => DB_ERROR_INVALID_DATE, |
'22012' => DB_ERROR_DIVZERO, |
'23000' => DB_ERROR_CONSTRAINT, |
'23502' => DB_ERROR_CONSTRAINT_NOT_NULL, |
'23503' => DB_ERROR_CONSTRAINT, |
'23504' => DB_ERROR_CONSTRAINT, |
'23505' => DB_ERROR_CONSTRAINT, |
'24000' => DB_ERROR_INVALID, |
'34000' => DB_ERROR_INVALID, |
'37000' => DB_ERROR_SYNTAX, |
'42000' => DB_ERROR_SYNTAX, |
'42601' => DB_ERROR_SYNTAX, |
'IM001' => DB_ERROR_UNSUPPORTED, |
'S0000' => DB_ERROR_NOSUCHTABLE, |
'S0001' => DB_ERROR_ALREADY_EXISTS, |
'S0002' => DB_ERROR_NOSUCHTABLE, |
'S0011' => DB_ERROR_ALREADY_EXISTS, |
'S0012' => DB_ERROR_NOT_FOUND, |
'S0021' => DB_ERROR_ALREADY_EXISTS, |
'S0022' => DB_ERROR_NOSUCHFIELD, |
'S1009' => DB_ERROR_INVALID, |
'S1090' => DB_ERROR_INVALID, |
'S1C00' => DB_ERROR_NOT_CAPABLE, |
); |
/** |
* The raw database connection created by PHP |
* @var resource |
*/ |
var $connection; |
/** |
* The DSN information for connecting to a database |
* @var array |
*/ |
var $dsn = array(); |
/** |
* The number of rows affected by a data manipulation query |
* @var integer |
* @access private |
*/ |
var $affected = 0; |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_odbc() |
{ |
$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 odbc driver supports the following extra DSN options: |
* + cursor The type of cursor to be used for this connection. |
* |
* @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('odbc')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
switch ($this->dbsyntax) { |
case 'access': |
case 'db2': |
case 'solid': |
$this->features['transactions'] = true; |
break; |
case 'navision': |
$this->features['limit'] = false; |
} |
/* |
* This is hear for backwards compatibility. Should have been using |
* 'database' all along, but prior to 1.6.0RC3 'hostspec' was used. |
*/ |
if ($dsn['database']) { |
$odbcdsn = $dsn['database']; |
} elseif ($dsn['hostspec']) { |
$odbcdsn = $dsn['hostspec']; |
} else { |
$odbcdsn = 'localhost'; |
} |
$connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect'; |
if (empty($dsn['cursor'])) { |
$this->connection = @$connect_function($odbcdsn, $dsn['username'], |
$dsn['password']); |
} else { |
$this->connection = @$connect_function($odbcdsn, $dsn['username'], |
$dsn['password'], |
$dsn['cursor']); |
} |
if (!is_resource($this->connection)) { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
null, null, null, |
$this->errorNative()); |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
$err = @odbc_close($this->connection); |
$this->connection = null; |
return $err; |
} |
// }}} |
// {{{ 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) |
{ |
$this->last_query = $query; |
$query = $this->modifyQuery($query); |
$result = @odbc_exec($this->connection, $query); |
if (!$result) { |
return $this->odbcRaiseError(); // XXX ERRORMSG |
} |
// Determine which queries that should return data, and which |
// should return an error code only. |
if (DB::isManip($query)) { |
$this->affected = $result; // For affectedRows() |
return DB_OK; |
} |
$this->affected = 0; |
return $result; |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal odbc 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 @odbc_next_result($result); |
} |
// }}} |
// {{{ 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) |
{ |
$arr = array(); |
if ($rownum !== null) { |
$rownum++; // ODBC first row is 1 |
if (version_compare(phpversion(), '4.2.0', 'ge')) { |
$cols = @odbc_fetch_into($result, $arr, $rownum); |
} else { |
$cols = @odbc_fetch_into($result, $rownum, $arr); |
} |
} else { |
$cols = @odbc_fetch_into($result, $arr); |
} |
if (!$cols) { |
return null; |
} |
if ($fetchmode !== DB_FETCHMODE_ORDERED) { |
for ($i = 0; $i < count($arr); $i++) { |
$colName = @odbc_field_name($result, $i+1); |
$a[$colName] = $arr[$i]; |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$a = array_change_key_case($a, CASE_LOWER); |
} |
$arr = $a; |
} |
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
$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 @odbc_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 = @odbc_num_fields($result); |
if (!$cols) { |
return $this->odbcRaiseError(); |
} |
return $cols; |
} |
// }}} |
// {{{ 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 (empty($this->affected)) { // In case of SELECT stms |
return 0; |
} |
$nrows = @odbc_num_rows($this->affected); |
if ($nrows == -1) { |
return $this->odbcRaiseError(); |
} |
return $nrows; |
} |
// }}} |
// {{{ numRows() |
/** |
* Gets the number of rows in a result set |
* |
* Not all ODBC drivers support this functionality. If they don't |
* a DB_Error object for DB_ERROR_UNSUPPORTED is returned. |
* |
* 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) |
{ |
$nrows = @odbc_num_rows($result); |
if ($nrows == -1) { |
return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED); |
} |
if ($nrows === false) { |
return $this->odbcRaiseError(); |
} |
return $nrows; |
} |
// }}} |
// {{{ quoteIdentifier() |
/** |
* Quotes a string so it can be safely used as a table or column name |
* |
* Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked |
* "Use ANSI quoted identifiers" when setting up the ODBC data source. |
* |
* @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) |
{ |
switch ($this->dsn['dbsyntax']) { |
case 'access': |
return '[' . $str . ']'; |
case 'mssql': |
case 'sybase': |
return '[' . str_replace(']', ']]', $str) . ']'; |
case 'mysql': |
case 'mysqli': |
return '`' . $str . '`'; |
default: |
return '"' . str_replace('"', '""', $str) . '"'; |
} |
} |
// }}} |
// {{{ quote() |
/** |
* @deprecated Deprecated in release 1.6.0 |
* @internal |
*/ |
function quote($str) |
{ |
return $this->quoteSmart($str); |
} |
// }}} |
// {{{ 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_odbc::createSequence(), DB_odbc::dropSequence() |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$seqname = $this->getSequenceName($seq_name); |
$repeat = 0; |
do { |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result = $this->query("update ${seqname} set id = id + 1"); |
$this->popErrorHandling(); |
if ($ondemand && DB::isError($result) && |
$result->getCode() == DB_ERROR_NOSUCHTABLE) { |
$repeat = 1; |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result = $this->createSequence($seq_name); |
$this->popErrorHandling(); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
$result = $this->query("insert into ${seqname} (id) values(0)"); |
} else { |
$repeat = 0; |
} |
} while ($repeat); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
$result = $this->query("select id from ${seqname}"); |
if (DB::isError($result)) { |
return $result; |
} |
$row = $result->fetchRow(DB_FETCHMODE_ORDERED); |
if (DB::isError($row || !$row)) { |
return $row; |
} |
return $row[0]; |
} |
/** |
* 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_odbc::nextID(), DB_odbc::dropSequence() |
*/ |
function createSequence($seq_name) |
{ |
return $this->query('CREATE TABLE ' |
. $this->getSequenceName($seq_name) |
. ' (id integer NOT NULL,' |
. ' PRIMARY KEY(id))'); |
} |
// }}} |
// {{{ 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_odbc::nextID(), DB_odbc::createSequence() |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
} |
// }}} |
// {{{ 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) |
{ |
if (!@odbc_autocommit($this->connection, $onoff)) { |
return $this->odbcRaiseError(); |
} |
return DB_OK; |
} |
// }}} |
// {{{ commit() |
/** |
* Commits the current transaction |
* |
* @return int DB_OK on success. A DB_Error object on failure. |
*/ |
function commit() |
{ |
if (!@odbc_commit($this->connection)) { |
return $this->odbcRaiseError(); |
} |
return DB_OK; |
} |
// }}} |
// {{{ rollback() |
/** |
* Reverts the current transaction |
* |
* @return int DB_OK on success. A DB_Error object on failure. |
*/ |
function rollback() |
{ |
if (!@odbc_rollback($this->connection)) { |
return $this->odbcRaiseError(); |
} |
return DB_OK; |
} |
// }}} |
// {{{ odbcRaiseError() |
/** |
* 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_odbc::errorNative(), DB_common::errorCode() |
*/ |
function odbcRaiseError($errno = null) |
{ |
if ($errno === null) { |
switch ($this->dbsyntax) { |
case 'access': |
if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { |
$this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD; |
} else { |
// Doing this in case mode changes during runtime. |
$this->errorcode_map['07001'] = DB_ERROR_MISMATCH; |
} |
$native_code = odbc_error($this->connection); |
// S1000 is for "General Error." Let's be more specific. |
if ($native_code == 'S1000') { |
$errormsg = odbc_errormsg($this->connection); |
static $error_regexps; |
if (!isset($error_regexps)) { |
$error_regexps = array( |
'/includes related records.$/i' => DB_ERROR_CONSTRAINT, |
'/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL, |
); |
} |
foreach ($error_regexps as $regexp => $code) { |
if (preg_match($regexp, $errormsg)) { |
return $this->raiseError($code, |
null, null, null, |
$native_code . ' ' . $errormsg); |
} |
} |
$errno = DB_ERROR; |
} else { |
$errno = $this->errorCode($native_code); |
} |
break; |
default: |
$errno = $this->errorCode(odbc_error($this->connection)); |
} |
} |
return $this->raiseError($errno, null, null, null, |
$this->errorNative()); |
} |
// }}} |
// {{{ errorNative() |
/** |
* Gets the DBMS' native error code and message produced by the last query |
* |
* @return string the DBMS' error code and message |
*/ |
function errorNative() |
{ |
if (!is_resource($this->connection)) { |
return @odbc_error() . ' ' . @odbc_errormsg(); |
} |
return @odbc_error($this->connection) . ' ' . @odbc_errormsg($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() |
* @since Method available since Release 1.7.0 |
*/ |
function tableInfo($result, $mode = null) |
{ |
if (is_string($result)) { |
/* |
* Probably received a table name. |
* Create a result resource identifier. |
*/ |
$id = @odbc_exec($this->connection, "SELECT * FROM $result"); |
if (!$id) { |
return $this->odbcRaiseError(); |
} |
$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->odbcRaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = @odbc_num_fields($id); |
$res = array(); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
$col = $i + 1; |
$res[$i] = array( |
'table' => $got_string ? $case_func($result) : '', |
'name' => $case_func(@odbc_field_name($id, $col)), |
'type' => @odbc_field_type($id, $col), |
'len' => @odbc_field_len($id, $col), |
'flags' => '', |
); |
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) { |
@odbc_free_result($id); |
} |
return $res; |
} |
// }}} |
// {{{ getSpecialQuery() |
/** |
* Obtains the query string needed for listing a given type of objects |
* |
* Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com. |
* |
* @param string $type the kind of objects you want to retrieve |
* |
* @return string the list of objects requested |
* |
* @access protected |
* @see DB_common::getListOf() |
* @since Method available since Release 1.7.0 |
*/ |
function getSpecialQuery($type) |
{ |
switch ($type) { |
case 'databases': |
if (!function_exists('odbc_data_source')) { |
return null; |
} |
$res = @odbc_data_source($this->connection, SQL_FETCH_FIRST); |
if (is_array($res)) { |
$out = array($res['server']); |
while($res = @odbc_data_source($this->connection, |
SQL_FETCH_NEXT)) |
{ |
$out[] = $res['server']; |
} |
return $out; |
} else { |
return $this->odbcRaiseError(); |
} |
break; |
case 'tables': |
case 'schema.tables': |
$keep = 'TABLE'; |
break; |
case 'views': |
$keep = 'VIEW'; |
break; |
default: |
return null; |
} |
/* |
* Removing non-conforming items in the while loop rather than |
* in the odbc_tables() call because some backends choke on this: |
* odbc_tables($this->connection, '', '', '', 'TABLE') |
*/ |
$res = @odbc_tables($this->connection); |
if (!$res) { |
return $this->odbcRaiseError(); |
} |
$out = array(); |
while ($row = odbc_fetch_array($res)) { |
if ($row['TABLE_TYPE'] != $keep) { |
continue; |
} |
if ($type == 'schema.tables') { |
$out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME']; |
} else { |
$out[] = $row['TABLE_NAME']; |
} |
} |
return $out; |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/fbsql.php |
---|
New file |
0,0 → 1,770 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's fbsql extension |
* for interacting with FrontBase 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 Frank M. Kromann <frank@frontbase.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: fbsql.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 fbsql extension |
* for interacting with FrontBase databases |
* |
* These methods overload the ones declared in DB_common. |
* |
* @category Database |
* @package DB |
* @author Frank M. Kromann <frank@frontbase.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: 1.7.6 |
* @link http://pear.php.net/package/DB |
* @since Class functional since Release 1.7.0 |
*/ |
class DB_fbsql extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'fbsql'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'fbsql'; |
/** |
* 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' => false, |
'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( |
22 => DB_ERROR_SYNTAX, |
85 => DB_ERROR_ALREADY_EXISTS, |
108 => DB_ERROR_SYNTAX, |
116 => DB_ERROR_NOSUCHTABLE, |
124 => DB_ERROR_VALUE_COUNT_ON_ROW, |
215 => DB_ERROR_NOSUCHFIELD, |
217 => DB_ERROR_INVALID_NUMBER, |
226 => DB_ERROR_NOSUCHFIELD, |
231 => DB_ERROR_INVALID, |
239 => DB_ERROR_TRUNCATED, |
251 => DB_ERROR_SYNTAX, |
266 => DB_ERROR_NOT_FOUND, |
357 => DB_ERROR_CONSTRAINT_NOT_NULL, |
358 => DB_ERROR_CONSTRAINT, |
360 => DB_ERROR_CONSTRAINT, |
361 => 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(); |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_fbsql() |
{ |
$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. |
* |
* @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('fbsql')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
$params = array( |
$dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', |
$dsn['username'] ? $dsn['username'] : null, |
$dsn['password'] ? $dsn['password'] : null, |
); |
$connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_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); |
} |
if ($dsn['database']) { |
if (!@fbsql_select_db($dsn['database'], $this->connection)) { |
return $this->fbsqlRaiseError(); |
} |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
$ret = @fbsql_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) |
{ |
$this->last_query = $query; |
$query = $this->modifyQuery($query); |
$result = @fbsql_query("$query;", $this->connection); |
if (!$result) { |
return $this->fbsqlRaiseError(); |
} |
// Determine which queries that should return data, and which |
// should return an error code only. |
if (DB::isManip($query)) { |
return DB_OK; |
} |
return $result; |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal fbsql 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 @fbsql_next_result($result); |
} |
// }}} |
// {{{ 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 (!@fbsql_data_seek($result, $rownum)) { |
return null; |
} |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
$arr = @fbsql_fetch_array($result, FBSQL_ASSOC); |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @fbsql_fetch_row($result); |
} |
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); |
} |
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 @fbsql_free_result($result); |
} |
// }}} |
// {{{ 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) |
{ |
if ($onoff) { |
$this->query("SET COMMIT TRUE"); |
} else { |
$this->query("SET COMMIT FALSE"); |
} |
} |
// }}} |
// {{{ commit() |
/** |
* Commits the current transaction |
* |
* @return int DB_OK on success. A DB_Error object on failure. |
*/ |
function commit() |
{ |
@fbsql_commit(); |
} |
// }}} |
// {{{ rollback() |
/** |
* Reverts the current transaction |
* |
* @return int DB_OK on success. A DB_Error object on failure. |
*/ |
function rollback() |
{ |
@fbsql_rollback(); |
} |
// }}} |
// {{{ 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 = @fbsql_num_fields($result); |
if (!$cols) { |
return $this->fbsqlRaiseError(); |
} |
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 = @fbsql_num_rows($result); |
if ($rows === null) { |
return $this->fbsqlRaiseError(); |
} |
return $rows; |
} |
// }}} |
// {{{ 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)) { |
$result = @fbsql_affected_rows($this->connection); |
} else { |
$result = 0; |
} |
return $result; |
} |
// }}} |
// {{{ 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_fbsql::createSequence(), DB_fbsql::dropSequence() |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$seqname = $this->getSequenceName($seq_name); |
do { |
$repeat = 0; |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result = $this->query('SELECT UNIQUE FROM ' . $seqname); |
$this->popErrorHandling(); |
if ($ondemand && DB::isError($result) && |
$result->getCode() == DB_ERROR_NOSUCHTABLE) { |
$repeat = 1; |
$result = $this->createSequence($seq_name); |
if (DB::isError($result)) { |
return $result; |
} |
} else { |
$repeat = 0; |
} |
} while ($repeat); |
if (DB::isError($result)) { |
return $this->fbsqlRaiseError(); |
} |
$result->fetchInto($tmp, DB_FETCHMODE_ORDERED); |
return $tmp[0]; |
} |
/** |
* 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_fbsql::nextID(), DB_fbsql::dropSequence() |
*/ |
function createSequence($seq_name) |
{ |
$seqname = $this->getSequenceName($seq_name); |
$res = $this->query('CREATE TABLE ' . $seqname |
. ' (id INTEGER NOT NULL,' |
. ' PRIMARY KEY(id))'); |
if ($res) { |
$res = $this->query('SET UNIQUE = 0 FOR ' . $seqname); |
} |
return $res; |
} |
// }}} |
// {{{ 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_fbsql::nextID(), DB_fbsql::createSequence() |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name) |
. ' RESTRICT'); |
} |
// }}} |
// {{{ 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 preg_replace('/^([\s(])*SELECT/i', |
"\\1SELECT TOP($count)", $query); |
} else { |
return preg_replace('/([\s(])*SELECT/i', |
"\\1SELECT TOP($from, $count)", $query); |
} |
} |
// }}} |
// {{{ 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 FrontBase'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) . "'"; |
} |
} |
// }}} |
// {{{ fbsqlRaiseError() |
/** |
* 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_fbsql::errorNative(), DB_common::errorCode() |
*/ |
function fbsqlRaiseError($errno = null) |
{ |
if ($errno === null) { |
$errno = $this->errorCode(fbsql_errno($this->connection)); |
} |
return $this->raiseError($errno, null, null, null, |
@fbsql_error($this->connection)); |
} |
// }}} |
// {{{ errorNative() |
/** |
* Gets the DBMS' native error code produced by the last query |
* |
* @return int the DBMS' error code |
*/ |
function errorNative() |
{ |
return @fbsql_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 = @fbsql_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->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = @fbsql_num_fields($id); |
$res = array(); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
$res[$i] = array( |
'table' => $case_func(@fbsql_field_table($id, $i)), |
'name' => $case_func(@fbsql_field_name($id, $i)), |
'type' => @fbsql_field_type($id, $i), |
'len' => @fbsql_field_len($id, $i), |
'flags' => @fbsql_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) { |
@fbsql_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 'SELECT "table_name" FROM information_schema.tables' |
. ' t0, information_schema.schemata t1' |
. ' WHERE t0.schema_pk=t1.schema_pk AND' |
. ' "table_type" = \'BASE TABLE\'' |
. ' AND "schema_name" = current_schema'; |
case 'views': |
return 'SELECT "table_name" FROM information_schema.tables' |
. ' t0, information_schema.schemata t1' |
. ' WHERE t0.schema_pk=t1.schema_pk AND' |
. ' "table_type" = \'VIEW\'' |
. ' AND "schema_name" = current_schema'; |
case 'users': |
return 'SELECT "user_name" from information_schema.users'; |
case 'functions': |
return 'SELECT "routine_name" FROM' |
. ' information_schema.psm_routines' |
. ' t0, information_schema.schemata t1' |
. ' WHERE t0.schema_pk=t1.schema_pk' |
. ' AND "routine_kind"=\'FUNCTION\'' |
. ' AND "schema_name" = current_schema'; |
case 'procedures': |
return 'SELECT "routine_name" FROM' |
. ' information_schema.psm_routines' |
. ' t0, information_schema.schemata t1' |
. ' WHERE t0.schema_pk=t1.schema_pk' |
. ' AND "routine_kind"=\'PROCEDURE\'' |
. ' AND "schema_name" = current_schema'; |
default: |
return null; |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/NestedSet/DB.php |
---|
New file |
0,0 → 1,135 |
<?php |
// |
// +----------------------------------------------------------------------+ |
// | PEAR :: DB_NestedSet_DB | |
// +----------------------------------------------------------------------+ |
// | 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: Daniel Khan <dk@webcluster.at> | |
// +----------------------------------------------------------------------+ |
// |
// $Id: DB.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
// |
require_once 'DB.php'; |
// {{{ DB_NestedSet_DB:: class |
/** |
* Wrapper class for PEAR::DB |
* |
* @author Daniel Khan <dk@webcluster.at> |
* @package DB_NestedSet |
* @version $Revision: 1.1 $ |
* @access public |
*/ |
// }}} |
class DB_NestedSet_DB extends DB_NestedSet { |
// {{{ properties |
/** |
* @var object Db object |
*/ |
var $db; |
// }}} |
// {{{ constructor |
/** |
* Constructor |
* |
* @param mixed $dsn DSN as PEAR dsn URI or dsn Array |
* @param array $params Database column fields which should be returned |
* |
*/ |
function DB_NestedSet_DB($dsn, $params = array()) |
{ |
$this->_debugMessage('DB_NestedSet_DB($dsn, $params = array())'); |
$this->DB_NestedSet($params); |
$this->db =& $this->_db_Connect($dsn); |
$this->db->setFetchMode(DB_FETCHMODE_ASSOC); |
} |
// }}} |
// {{{ destructor |
/** |
* Destructor |
*/ |
function _DB_NestedSet_DB() |
{ |
$this->_debugMessage('_DB_NestedSet_DB()'); |
$this->_DB_NestedSet(); |
$this->_db_Disconnect(); |
} |
// }}} |
// {{{ _db_Connect() |
/** |
* Connects to the db |
* |
* @return object DB The database object |
* @access private |
*/ |
function &_db_Connect($dsn) |
{ |
$this->_debugMessage('_db_Connect($dsn)'); |
if (is_object($this->db)) { |
return $this->db; |
} |
$db =& DB::connect($dsn); |
$this->_testFatalAbort($db, __FILE__, __LINE__); |
return $db; |
} |
// }}} |
function _numRows($res) { |
return $res->numRows(); |
} |
function _isDBError($err) { |
if(!DB::isError($err)) { |
return false; |
} |
return true; |
} |
function _quote($str) { |
return $this->db->quote($str); |
} |
// {{{ _db_Disconnect() |
/** |
* Disconnects from db |
* |
* @return void |
* @access private |
*/ |
function _db_Disconnect() |
{ |
$this->_debugMessage('_db_Disconnect()'); |
if (is_object($this->db)) { |
@$this->db->disconnect(); |
} |
return true; |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/NestedSet/TigraMenu.php |
---|
New file |
0,0 → 1,402 |
<?php |
// |
// +----------------------------------------------------------------------+ |
// | PEAR :: DB_NestedSet_TigraMenu | |
// +----------------------------------------------------------------------+ |
// | 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: Daniel Khan <dk@webcluster.at> | |
// +----------------------------------------------------------------------+ |
// |
// $Id: TigraMenu.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
// |
// {{{ DB_NestedSet_TigraMenu:: class |
/** |
* This class can be used to generate the data to build javascript popup menu |
* from a DB_NestedSet node array. |
* The Javascript part is done using the free available TigraMenu |
* available at http://www.softcomplex.com/products/tigra_menu/. |
* Currently version 1.0 is supported. |
* Parts of this class where taken ftom the TreemMenu driver by Jason Rust |
* |
* @author Daniel Khan <dk@webcluster.at> |
* @package DB_NestedSet |
* @version $Revision: 1.1 $ |
* @access public |
*/ |
// }}} |
class DB_NestedSet_TigraMenu extends DB_NestedSet_Output { |
// {{{{ properties |
/** |
* @var integer The depth of the current menu. |
* @access private |
*/ |
var $_levels = 1; |
/** |
* @var integer The level we started at |
* @access private |
*/ |
var $_levelOffset = false; |
/** |
* @var array The current menu structure |
* @access private |
*/ |
var $_structTigraMenu = false; |
/** |
* @var array The longest text for each level |
* @access private |
*/ |
var $_strlenByLevel = array(); |
// }}} |
// {{{ DB_NestedSet_TigraMenu |
/** |
* Constructor |
* |
* @param array $params A hash with parameters needed by the class |
* @see _createFromStructure() |
* @return bool |
**/ |
function &DB_NestedSet_TigraMenu($params) { |
$this->_menu_id = $params['menu_id']; |
$this->_structTigraMenu = $this->_createFromStructure($params); |
return true; |
} |
// }}} |
// {{{ _createFromStructure() |
/** |
* Creates the JavaScript array for TigraMenu |
* Initially this method was introduced for the TreeMenu driver by Jason Rust |
* |
* o 'structure' => the result from $nestedSet->getAllNodes(true) |
* o 'textField' => the field in the table that has the text for node |
* o 'linkField' => the field in the table that has the link for the node |
* |
* @access private |
* @return string The TigraMenu JavaScript array |
*/ |
function &_createFromStructure($params) |
{ |
// Basically we go through the array of nodes checking to see |
// if each node has children and if so recursing. The reason this |
// works is because the data from getAllNodes() is ordered by level |
// so a root node will always be first, and sub children will always |
// be after them. |
static $rootlevel; |
// always start at level 1 |
if (!isset($params['currentLevel'])) { |
$params['currentLevel'] = 1; |
} |
if (!isset($rootlevel)) { |
$rootlevel = $params['currentLevel']; |
} |
if (isset($params['tigraMenu'])) { |
$tigraMenu = $tigraMenu.$params['tigraMenu']; |
} |
if(!$this->_levelOffset) { |
$this->_levelOffset = $params['currentLevel']; |
} |
if($this->_levels < ($params['currentLevel']- $this->_levelOffset)) { |
$this->_levels = $params['currentLevel'] - $this->_levelOffset; |
} |
// have to use a while loop here because foreach works on a copy of the array and |
// the child nodes are passed by reference during the recursion so that the parent |
// will know when they have been hit. |
reset($params['structure']); |
while(list($key, $node) = each($params['structure'])) { |
// see if we've already been here before |
if (isset($node['hit']) || $node['level'] < $params['currentLevel']) { |
continue; |
} |
// mark that we've hit this node |
$params['structure'][$key]['hit'] = $node['hit'] = true; |
$tag = array( |
isset($node[$params['textField']]) ? "'".$node[$params['textField']]."'" : 'null', |
isset($node[$params['linkField']]) ? "'".$node[$params['linkField']]."'" : 'null' |
); |
if (!$this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset] || |
strlen($node[$params['textField']]) > $this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset]) { |
$this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset] = strlen($node[$params['textField']]); |
}; |
$tigraMenu = $tigraMenu.$this->_openSubMenu($tag); |
// see if it has children |
if (($node['r'] - 1) != $node['l']) { |
$children = array(); |
// harvest all the children |
$tempStructure = $params['structure']; |
foreach ($tempStructure as $childKey => $childNode) { |
if (!isset($childNode['hit']) && |
$node['rootid'] == $childNode['rootid'] && |
$node['l'] < $childNode['l'] && |
$node['r'] > $childNode['r'] && |
$childNode['level'] > $params['currentLevel']) { |
// important that we assign it by reference here, so that when the child |
// marks itself 'hit' the parent loops will know |
$children[] =& $params['structure'][$childKey]; |
} |
} |
$recurseParams = $params; |
$recurseParams['structure'] = $children; |
$recurseParams['currentLevel']++; |
$tigraMenu = $tigraMenu.$this->_createFromStructure($recurseParams); |
} |
$tigraMenu = $tigraMenu.$this->_closeSubMenu(); |
} |
return $tigraMenu; |
} |
// }}} |
// {{{ _openMenu() |
/** |
* Returns the string which opens the JavaScript menu |
* |
* @access private |
* @param int $menu_id ID of the menu needed to use more than one menu on a page |
* @return string The JavaScript piece |
*/ |
function _openMenu($menu_id=1) |
{ |
$str = false; |
$str = $str."var MENU_ITEMS".$menu_id." = new Array();\n"; |
$str = $str."MENU_ITEMS".$menu_id." = [\n"; |
return $str; |
} |
// }}} |
// {{{ _openSubMenu() |
/** |
* Returns the string which opens a submenu within the JavaScript menu |
* |
* @access private |
* @param array $tag Contains the content of the current item (name, link) |
* @return string The JavaScript piece |
*/ |
function _openSubMenu($tag) |
{ |
$rtag = implode(', ', $tag); |
return "\n[".$rtag.','; |
} |
// }}} |
// {{{ _closeMenu() |
/** |
* Closes the JavaScript array |
* |
* @access private |
* @return string The JavaScript piece |
*/ |
function _closeMenu() |
{ |
return '];'; |
} |
// }}} |
// {{{ _closeSubMenu() |
/** |
* Closes the JavaScript array of a submenu |
* |
* @access private |
* @return string The JavaScript piece |
*/ |
function _closeSubMenu() |
{ |
return "\n],"; |
} |
// }}} |
// {{{ _addStyles() |
/** |
* Creates the JavaScript code which sets the styles for each level |
* |
* @access private |
* @param int $menu_id ID of the menu needed to use more than one menu on a page |
* @param array $rootStyles Array of style attributes for the top items |
* @param array $childStyles Array of style attributes for the sub items |
* @return string The JavaScript piece |
*/ |
function _addStyles($menu_id, $rootStyles, $childStyles = false) |
{ |
if (!$childStyles) { |
$childStyles = $rootStyles; |
} |
$styles = array(); |
foreach ($rootStyles as $key => $val) { |
foreach ($val as $skey => $sval) { |
$styles["'$key'"][$skey][] = "'$sval'"; |
} |
} |
foreach ($childStyles as $key => $val) { |
foreach ($val as $skey => $sval) { |
for ($i = 1; $i <= $this->_levels; $i++) { |
$styles["'$key'"][$skey][] = "'$sval'"; |
} |
} |
} |
$menustyles = false; |
$menustyles = $menustyles . 'var MENU_STYLES'.$menu_id." = new Array();\n"; |
foreach ($styles as $key => $val) { |
$menustyles = $menustyles.'MENU_STYLES'.$menu_id."[$key] = [\n"; |
foreach ($val as $skey => $sval) { |
$menustyles = $menustyles . "'$skey', [".implode(', ', $sval)."],\n"; |
} |
$menustyles = $menustyles."];\n"; |
} |
return $menustyles; |
} |
// }}} |
// {{{ _addGeometry() |
/** |
* Creates the JavaScript code which sets the position and geometry of the menu |
* |
* @access private |
* @param int $menu_id ID of the menu needed to use more than one menu on a page |
* @param array $rootGeometry Array of geometry attributes for the top items |
* @param array $childGeometry Array of geometry attributes for the sub items |
* @return string The JavaScript piece |
*/ |
function _addGeometry($menu_id, $rootGeometry, $childGeometry = false) |
{ |
if (!$childGeometry) { |
$childGeometry = $rootGeometry; |
} |
$params = array(); |
$geometry = array(); |
foreach ($rootGeometry as $key => $val) { |
$geometry["'$key'"][] = $val; |
$incr = false; |
if (strpos($val, ',') !== false) { |
list($start, $interval) = explode(',',$val); |
$incr = true; |
} |
$ratio = false; |
if ($key == 'width' && strpos($val, '*') !== false) { |
$ratio = trim(str_replace('*','', $val)); |
} |
if ($incr) { |
$val = trim($interval); |
if ($key == 'left' && preg_match('/[+-]/', $interval)) { |
$val = $params[0]['width'] + trim($val); |
} |
} elseif ($incr) { |
$val = trim($start); |
} elseif ($ratio) { |
$val = $ratio * $this->_strlenByLevel[0]; |
} |
$geometry["'$key'"][0] = $val; |
$params[0][$key] = $val; |
} |
foreach($childGeometry as $key => $val) { |
$incr = false; |
if (strpos($val, ',') !== false) { |
list($start, $interval) = explode(',', $val); |
$incr = true; |
} |
$ratio = false; |
if ($key == 'width' && strpos($val, '*') !== false) { |
$ratio = trim(str_replace('*', '', $val)); |
} |
for ($i = 1; $i <= $this->_levels; $i++) { |
if ($incr && isset($lastval[$key])) { |
$val = trim($interval); |
if($key == 'block_left' && preg_match('/[+-]/', $interval)) { |
$val = $params[$i - 1]['width'] + trim($val); |
} |
} elseif($incr) { |
$val = trim($start); |
} elseif ($ratio) { |
$val = $ratio * $this->_strlenByLevel[$i]; |
if($val < $params[0]['width']) { |
$val = $params[0]['width']; |
} |
} |
$lastval[$key] = $val; |
$geometry["'$key'"][] = $val; |
$params[$i][$key] = $val; |
} |
} |
$pos = false; |
$pos = $pos . 'var MENU_POS'.$menu_id." = new Array();\n"; |
foreach ($geometry as $key => $val) { |
$pos = $pos . 'MENU_POS' . $menu_id . "[$key] = [" . implode(', ', $val) . "];\n"; |
} |
return $pos; |
} |
// }}} |
// {{{ printTree() |
/** |
* Print's the current tree using the output driver |
* |
* @access public |
*/ |
function printTree() |
{ |
if (!$options = $this->_getOptions('printTree')) { |
return PEAR::raiseError("TigraMenu::printTree() needs options. See TigraMenu::setOptions()", NESEO_ERROR_NO_OPTIONS, PEAR_ERROR_TRIGGER, E_USER_ERROR); |
} |
echo $this->_openMenu($options['menu_id']) . $this->_structTigraMenu .$this->_closeMenu(); |
echo "\n\n"; |
echo $this->_addStyles($options['menu_id'], $options['rootStyles'], $options['childStyles']); |
echo "\n\n"; |
echo $this->_addGeometry($options['menu_id'], $options['rootGeometry'], $options['childGeometry']); |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/NestedSet/Output.php |
---|
New file |
0,0 → 1,211 |
<?php |
// |
// +----------------------------------------------------------------------+ |
// | PEAR :: DB_NestedSet_Output | |
// +----------------------------------------------------------------------+ |
// | 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: Daniel Khan <dk@webcluster.at> | |
// | Jason Rust <jason@rustyparts.com> | |
// +----------------------------------------------------------------------+ |
// $Id: Output.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
// |
require_once 'PEAR.php'; |
// {{{ constants |
define('NESEO_ERROR_NO_METHOD', 'E1000'); |
define('NESEO_DRIVER_NOT_FOUND', 'E1100'); |
define('NESEO_ERROR_NO_OPTIONS', 'E2100'); |
// }}} |
// {{{ DB_NestedSet_Output:: class |
/** |
* DB_NestedSet_Output is a unified API for other output drivers |
* Status is beta |
* |
* At the moment PEAR::HTML_TreeMenu written by Jason Rust is supported |
* A driver for treemenu.org will follow soon. |
* |
* Usage example: |
* |
* require_once('DB_NestedSet/NestedSet/Output.php'); |
* $icon = 'folder.gif'; |
* $expandedIcon = 'folder-expanded.gif'; |
* // get data (important to fetch it as an array, using the true flag) |
* $data = $NeSe->getAllNodes(true); |
* // change the events for one of the elements |
* $data[35]['events'] = array('onexpand' => 'alert("we expanded!");'); |
* // add links to each item |
* foreach ($data as $a_data) { |
* $a_data['link'] = 'http://foo.com/foo.php?' . $a_data['id']; |
* } |
* $params = array( |
* 'structure' => $data, |
* 'options' => array( |
* 'icon' => $icon, |
* 'expandedIcon' => $expandedIcon, |
* ), |
* 'textField' => 'name', |
* 'linkField' => 'link', |
* ); |
* $menu =& DB_NestedSet_Output::factory('TreeMenu', $params); |
* $menu->printListbox(); |
* |
* @author Daniel Khan <dk@webcluster.at> |
* @package DB_NestedSet |
* @version $Revision: 1.1 $ |
* @access public |
* |
*/ |
// }}} |
class DB_NestedSet_Output { |
// {{{ properties |
/** |
* @var object The tree menu structure |
* @access private |
*/ |
var $_structTreeMenu = false; |
/** |
* @var array Array of options to be passed to the ouput methods |
* @access public |
*/ |
var $options = array(); |
// }}} |
// {{{ factory() |
/** |
* Returns a output driver object |
* |
* @param array $params A DB_NestedSet nodeset |
* @param string $driver (optional) The driver, such as TreeMenu (default) |
* |
* @access public |
* @return object The DB_NestedSet_Ouput object |
*/ |
function &factory ($params, $driver = 'TreeMenu') { |
$path = dirname(__FILE__).'/'.$driver.'.php'; |
if(is_dir($path) || !file_exists($path)) { |
PEAR::raiseError("The output driver '$driver' wasn't found", NESEO_DRIVER_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR); |
} |
require_once($path); |
$driverClass = 'DB_NestedSet_'.$driver; |
return new $driverClass($params); |
} |
// }}} |
// {{{ setOptions() |
/** |
* Set's options for a specific output group (printTree, printListbox) |
* This enables you to set specific options for each output method |
* |
* @param string $group Output group ATM 'printTree' or 'printListbox' |
* @param array $options Hash with options |
* |
* @access public |
* @return bool |
*/ |
function setOptions($group, $options) { |
$this->options[$group] = $options; |
return true; |
} |
// }}} |
// {{{ _getOptions() |
/** |
* Get's all option for a specific output group (printTree, printListbox) |
* |
* @param string $group Output group ATM 'printTree' or 'printListbox' |
* |
* @access private |
* @return array Options |
*/ |
function _getOptions($group) { |
if (!isset($this->options[$group])) { |
return array(); |
} |
return $this->options[$group]; |
} |
// }}} |
// {{{ printTree() |
/** |
* Print's the current tree using the output driver |
* Overriden by the driver class |
* |
* @access public |
*/ |
function printTree() { |
PEAR::raiseError("Method not available for this driver", NESEO_ERROR_NO_METHOD, PEAR_ERROR_TRIGGER, E_USER_ERROR); |
} |
// }}} |
// {{{ printListbox() |
/** |
* Print's a listbox representing the current tree |
* Overriden by the driver class |
* |
* @access public |
*/ |
function printListbox() { |
PEAR::raiseError("Method not available for this driver", NESEO_ERROR_NO_METHOD, PEAR_ERROR_TRIGGER, E_USER_ERROR); |
} |
// }}} |
// {{{ toHTML() |
/** |
* Returns the HTML for the DHTML-menu. This method can be |
* used instead of printMenu() to use the menu system |
* with a template system. |
* |
* @access public |
* @return string The HTML for the menu |
* @author Emanuel Zueger |
*/ |
function tree_toHTML() { |
PEAR::raiseError("Method not available for this driver", NESEO_ERROR_NO_METHOD, PEAR_ERROR_TRIGGER, E_USER_ERROR); |
} |
// }}} |
// {{{ listbox_toHTML() |
/** |
* Returns the HTML for the listbox. This method can be |
* used instead of printListbox() to use the menu system |
* with a template system. |
* |
* @access public |
* @return string The HTML for the listbox |
* @author Emanuel Zueger |
*/ |
function listbox_toHTML() { |
PEAR::raiseError("Method not available for this driver", NESEO_ERROR_NO_METHOD, PEAR_ERROR_TRIGGER, E_USER_ERROR); |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/NestedSet/Event.php |
---|
New file |
0,0 → 1,79 |
<?php |
/** |
// +----------------------------------------------------------------------+ |
// | PEAR :: DB_NestedSet_DB | |
// +----------------------------------------------------------------------+ |
// | 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: Daniel Khan <dk@webcluster.at> | |
// +----------------------------------------------------------------------+ |
// |
// $Id: Event.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
// |
// |
*/ |
/** |
* Poor mans event handler for DB_NestedSet |
* |
* Mostly for demo purposes or for extending it if |
* someone has ideas... |
* |
* @author Daniel Khan <dk@webcluster.at> |
* @package DB_NestedSet |
* @version $Revision: 1.1 $ |
* @access public |
*/ |
Class DB_NestedSetEvent extends PEAR { |
/** |
* Constructor |
* |
* @return void |
*/ |
function DB_NestedSetEvent() { |
$this->PEAR(); |
} |
/** |
* Destructor |
* |
* @return void |
*/ |
function _DB_NestedSetEvent() { |
$this->_PEAR(); |
} |
/** |
* Calls the event handler |
* |
* You may want to do a switch() here and call you methods |
* depending on the event |
* |
* @param string $event The Event that occured |
* @param object node $node A Reference to the node object which was subject to changes |
* @param array $eparams A associative array of params which may be needed by the handler |
* @return void |
* @access private |
*/ |
function callEvent($event, &$node, $eparams = array()) { |
echo "<br>Override callEvent() if you want to have custom event handlers<br>\n"; |
echo "Event $event was called with the following params:<br><br>\n"; |
echo "<PRE>"; |
print_r($eparams); |
echo "</PRE><br>\n"; |
} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/NestedSet/MDB.php |
---|
New file |
0,0 → 1,136 |
<?php |
// |
// +----------------------------------------------------------------------+ |
// | PEAR :: DB_NestedSet_MDB | |
// +----------------------------------------------------------------------+ |
// | 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: Daniel Khan <dk@webcluster.at> | |
// +----------------------------------------------------------------------+ |
// Thanks to Hans Lellelid for suggesting support for PEAR::MDB |
// and for his help in implementing this. |
// |
// $Id: MDB.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
// |
require_once 'MDB.php'; |
// {{{ DB_NestedSet_MDB:: class |
/** |
* Wrapper class for PEAR::MDB |
* |
* @author Daniel Khan <dk@webcluster.at> |
* @package DB_NestedSet |
* @version $Revision: 1.1 $ |
* @access public |
*/ |
// }}} |
class DB_NestedSet_MDB extends DB_NestedSet { |
// {{{ properties |
/** |
* @var object The MDB object |
*/ |
var $db; |
// }}} |
// {{{ constructor |
/** |
* Constructor |
* |
* @param mixed $dsn DSN as PEAR dsn URI or dsn Array |
* @param array $params Database column fields which should be returned |
* |
*/ |
function DB_NestedSet_MDB($dsn, $params = array()) |
{ |
$this->_debugMessage('DB_NestedSet_MDB($dsn, $params = array())'); |
$this->DB_NestedSet($params); |
$this->db =& $this->_db_Connect($dsn); |
$this->db->setFetchMode(MDB_FETCHMODE_ASSOC); |
} |
// }}} |
// {{{ destructor |
/** |
* Destructor |
*/ |
function _DB_NestedSet_MDB() |
{ |
$this->_debugMessage('_DB_NestedSet_MDB()'); |
$this->_DB_NestedSet(); |
$this->_db_Disconnect(); |
} |
// }}} |
// {{{ _db_Connect() |
/** |
* Connects to the db |
* |
* @return object DB The database object |
* @access private |
*/ |
function &_db_Connect($dsn) |
{ |
$this->_debugMessage('_db_Connect($dsn)'); |
if (is_object($this->db)) { |
return $this->db; |
} |
$db =& MDB::connect($dsn); |
$this->_testFatalAbort($db, __FILE__, __LINE__); |
return $db; |
} |
// }}} |
function _isDBError($err) { |
if(!MDB::isError($err)) { |
return false; |
} |
return true; |
} |
function _numRows($res) { |
return $this->db->numRows($res); |
} |
function _quote($str) { |
return $this->db->getTextValue($str); |
} |
// {{{ _db_Disconnect() |
/** |
* Disconnects from db |
* |
* @return void |
* @access private |
*/ |
function _db_Disconnect() |
{ |
$this->_debugMessage('_db_Disconnect()'); |
if (is_object($this->db)) { |
@$this->db->disconnect(); |
} |
return true; |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/NestedSet/TreeMenu.php |
---|
New file |
0,0 → 1,205 |
<?php |
// |
// +----------------------------------------------------------------------+ |
// | PEAR :: DB_NestedSet_TreeMenu | |
// +----------------------------------------------------------------------+ |
// | 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: Jason Rust <jrust@rustyparts.com> | |
// +----------------------------------------------------------------------+ |
// |
// $Id: TreeMenu.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
// |
require_once 'HTML/TreeMenu.php'; |
// {{{ DB_NestedSet_TreeMenu:: class |
/** |
* A helper class to translate the data from a nested set table into a HTML_TreeMenu object |
* so that it can be used to create a dynamic tree menu using the PEAR HTML_TreeMenu class. |
* |
* @see docs/TreeMenu_example.php |
* @author Jason Rust <jrust@rustyparts.com> |
* @package DB_NestedSet |
* @version $Revision: 1.1 $ |
* @access public |
*/ |
// }}} |
class DB_NestedSet_TreeMenu extends DB_NestedSet_Output { |
// {{{ properties |
/** |
* @var array The current menu structure |
* @access private |
*/ |
var $_structTreeMenu = false; |
// }}} |
// {{{ DB_NestedSet_TreeMenu |
function &DB_NestedSet_TreeMenu($params) { |
$this->_structTreeMenu =& $this->_createFromStructure($params); |
} |
// }}} |
// {{{ _createFromStructure() |
/** |
* <pre>Creates a HTML_TreeMenu structure based off of the results from getAllNodes() method |
* of the DB_NestedSet class. The needed parameters are: |
* o 'structure' => the result from $nestedSet->getAllNodes(true) |
* o 'textField' => the field in the table that has the text for node |
* o 'linkField' => the field in the table that has the link for the node |
* o 'options' => (optional) an array of any additional options to pass to the node when |
* Additionally these parameters may be added to the individual nodes to control their |
* behavior: |
* o 'ensureVisible' => (optional) whether or not the field should be forced as visible |
* creating it such as 'icon' or 'expandedIcon' |
* o 'events' => (optional) an array of any events to pass to the node when creating it |
* such as 'onclick' or 'onexpand'</pre> |
* </pre> |
* @access public |
* @return object A HTML_TreeMenu object |
*/ |
function &_createFromStructure($params) |
{ |
// Basically we go through the array of nodes checking to see |
// if each node has children and if so recursing. The reason this |
// works is because the data from getAllNodes() is ordered by level |
// so a root node will always be first, and sub children will always |
// be after them. |
if (!isset($params['treeMenu'])) { |
$treeMenu =& new HTML_TreeMenu(); |
} else { |
$treeMenu =& $params['treeMenu']; |
} |
// always start at level 1 |
if (!isset($params['currentLevel'])) { |
$params['currentLevel'] = 1; |
} |
// have to use a while loop here because foreach works on a copy of the array and |
// the child nodes are passed by reference during the recursion so that the parent |
// will know when they have been hit. |
reset($params['structure']); |
while(list($key, $node) = each($params['structure'])) { |
// see if we've already been here before |
if (isset($node['hit'])) { |
continue; |
} |
// mark that we've hit this node |
$params['structure'][$key]['hit'] = $node['hit'] = true; |
$tag = array( |
'text' => $node[$params['textField']], |
'link' => $node[$params['linkField']], |
'ensureVisible' => isset($node['ensureVisible']) ? $node['ensureVisible'] : false, |
); |
$options = isset($params['options']) ? array_merge($params['options'], $tag) : $tag; |
$events = isset($node['events']) ? $node['events'] : array(); |
$parentNode =& $treeMenu->addItem(new HTML_TreeNode($options, $events)); |
// see if it has children |
if (($node['r'] - 1) != $node['l']) { |
$children = array(); |
// harvest all the children |
$tempStructure = $params['structure']; |
foreach ($tempStructure as $childKey => $childNode) { |
if (!isset($childNode['hit']) && |
$childNode['l'] > $node['l'] && |
$childNode['r'] < $node['r'] && |
$childNode['rootid'] == $node['rootid']) { |
// important that we assign it by reference here, so that when the child |
// marks itself 'hit' the parent loops will know |
$children[] =& $params['structure'][$childKey]; |
} |
} |
$recurseParams = $params; |
$recurseParams['structure'] = $children; |
$recurseParams['treeMenu'] =& $parentNode; |
$recurseParams['currentLevel']++; |
$this->_createFromStructure($recurseParams); |
} |
} |
return $treeMenu; |
} |
// }}} |
// {{{ printTree() |
/** |
* Print's the current tree using the output driver |
* |
* @access public |
*/ |
function printTree() { |
$options = $this->_getOptions('printTree'); |
$tree =& new HTML_TreeMenu_DHTML($this->_structTreeMenu, $options); |
$tree->printMenu(); |
} |
// }}} |
// {{{ printListbox() |
/** |
* Print's a listbox representing the current tree |
* |
* @access public |
*/ |
function printListbox() { |
$options = $this->_getOptions('printListbox'); |
$listBox =& new HTML_TreeMenu_Listbox($this->_structTreeMenu, $options); |
$listBox->printMenu(); |
} |
// }}} |
// }}} |
// {{{ tree_toHTML() |
/** |
* Returns the HTML for the DHTML-menu. This method can be |
* used instead of printMenu() to use the menu system |
* with a template system. |
* |
* @access public |
* @return string The HTML for the menu |
* @Author Emanuel Zueger |
*/ |
function tree_toHTML() { |
$options = $this->_getOptions('toHTML'); |
$tree =& new HTML_TreeMenu_DHTML($this->_structTreeMenu, $options); |
return $tree->toHTML(); |
} |
// }}} |
// {{{ listbox_toHTML() |
/** |
* Returns the HTML for the listbox. This method can be |
* used instead of printListbox() to use the menu system |
* with a template system. |
* |
* @access public |
* @return string The HTML for the listbox |
* @author Emanuel Zueger |
*/ |
function listbox_toHTML() { |
$options = $this->_getOptions('toHTML'); |
$listBox =& new HTML_TreeMenu_Listbox($this->_structTreeMenu, $options); |
return $listBox->toHTML(); |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/DataObject/Generator.php |
---|
New file |
0,0 → 1,929 |
<?php |
/** |
* Generation tools for DB_DataObject |
* |
* 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_DataObject |
* @author Alan Knowles <alan@akbkhome.com> |
* @copyright 1997-2005 The PHP Group |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: Generator.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_DataObject |
*/ |
/** |
* |
* Config _$ptions |
* [DB_DataObject_Generator] |
* ; optional default = DB/DataObject.php |
* extends_location = |
* ; optional default = DB_DataObject |
* extends = |
* ; alter the extends field when updating a class (defaults to only replacing DB_DataObject) |
* generator_class_rewrite = ANY|specific_name // default is DB_DataObject |
* |
*/ |
/** |
* Needed classes |
*/ |
require_once 'DB/DataObject.php'; |
//require_once('Config.php'); |
/** |
* Generator class |
* |
* @package DB_DataObject |
*/ |
class DB_DataObject_Generator extends DB_DataObject |
{ |
/* =========================================================== */ |
/* Utility functions - for building db config files */ |
/* =========================================================== */ |
/** |
* Array of table names |
* |
* @var array |
* @access private |
*/ |
var $tables; |
/** |
* associative array table -> array of table row objects |
* |
* @var array |
* @access private |
*/ |
var $_definitions; |
/** |
* active table being output |
* |
* @var string |
* @access private |
*/ |
var $table; // active tablename |
/** |
* The 'starter' = call this to start the process |
* |
* @access public |
* @return none |
*/ |
function start() |
{ |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
$databases = array(); |
foreach($options as $k=>$v) { |
if (substr($k,0,9) == 'database_') { |
$databases[substr($k,9)] = $v; |
} |
} |
if (@$options['database']) { |
require_once 'DB.php'; |
$dsn = DB::parseDSN($options['database']); |
if (!isset($database[$dsn['database']])) { |
$databases[$dsn['database']] = $options['database']; |
} |
} |
foreach($databases as $databasename => $database) { |
if (!$database) { |
continue; |
} |
$this->debug("CREATING FOR $databasename\n"); |
$class = get_class($this); |
$t = new $class; |
$t->_database_dsn = $database; |
$t->_database = $databasename; |
$dsn = DB::parseDSN($database); |
if (($dsn['phptype'] == 'sqlite') && is_file($databasename)) { |
$t->_database = basename($t->_database); |
} |
$t->_createTableList(); |
foreach(get_class_methods($class) as $method) { |
if (substr($method,0,8 ) != 'generate') { |
continue; |
} |
$this->debug("calling $method"); |
$t->$method(); |
} |
} |
$this->debug("DONE\n\n"); |
} |
/** |
* Output File was config object, now just string |
* Used to generate the Tables |
* |
* @var string outputbuffer for table definitions |
* @access private |
*/ |
var $_newConfig; |
/** |
* Build a list of tables; |
* Currently this is very Mysql Specific - ideas for more generic stiff welcome |
* |
* @access private |
* @return none |
*/ |
function _createTableList() |
{ |
$this->_connect(); |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
$__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5]; |
// try getting a list of schema tables first. (postgres) |
$__DB->expectError(DB_ERROR_UNSUPPORTED); |
$this->tables = $__DB->getListOf('schema.tables'); |
$__DB->popExpect(); |
if (empty($this->tables) || is_a($this->tables , 'PEAR_Error')) { |
//if that fails fall back to clasic tables list. |
$this->tables = $__DB->getListOf('tables'); |
} |
if (is_a($this->tables , 'PEAR_Error')) { |
return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE); |
} |
// build views as well if asked to. |
if (!empty($options['build_views'])) { |
$views = $__DB->getListOf('views'); |
if (is_a($views,'PEAR_Error')) { |
return PEAR::raiseError( |
'Error getting Views (check the PEAR bug database for the fix to DB), ' . |
$views->toString(), |
null, |
PEAR_ERROR_DIE |
); |
} |
$this->tables = array_merge ($this->tables, $views); |
} |
// declare a temporary table to be filled with matching tables names |
$tmp_table = array(); |
foreach($this->tables as $table) { |
if (isset($options['generator_include_regex']) && |
!preg_match($options['generator_include_regex'],$table)) { |
continue; |
} else if (isset($options['generator_exclude_regex']) && |
preg_match($options['generator_exclude_regex'],$table)) { |
continue; |
} |
// postgres strip the schema bit from the |
if (!empty($options['generator_strip_schema'])) { |
$bits = explode('.', $table,2); |
$table = $bits[0]; |
if (count($bits) > 1) { |
$table = $bits[1]; |
} |
} |
$defs = $__DB->tableInfo($table); |
if (is_a($defs,'PEAR_Error')) { |
echo $defs->toString(); |
exit; |
} |
// cast all definitions to objects - as we deal with that better. |
foreach($defs as $def) { |
if (!is_array($def)) { |
continue; |
} |
$this->_definitions[$table][] = (object) $def; |
} |
// we find a matching table, just store it into a temporary array |
$tmp_table[] = $table; |
} |
// the temporary table array is now the right one (tables names matching |
// with regex expressions have been removed) |
$this->tables = $tmp_table; |
//print_r($this->_definitions); |
} |
/** |
* Auto generation of table data. |
* |
* it will output to db_oo_{database} the table definitions |
* |
* @access private |
* @return none |
*/ |
function generateDefinitions() |
{ |
$this->debug("Generating Definitions file: "); |
if (!$this->tables) { |
$this->debug("-- NO TABLES -- \n"); |
return; |
} |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
//$this->_newConfig = new Config('IniFile'); |
$this->_newConfig = ''; |
foreach($this->tables as $this->table) { |
$this->_generateDefinitionsTable(); |
} |
$this->_connect(); |
// dont generate a schema if location is not set |
// it's created on the fly! |
if (!@$options['schema_location'] && @!$options["ini_{$this->_database}"] ) { |
return; |
} |
$base = @$options['schema_location']; |
if (isset($options["ini_{$this->_database}"])) { |
$file = $options["ini_{$this->_database}"]; |
} else { |
$file = "{$base}/{$this->_database}.ini"; |
} |
if (!file_exists(dirname($file))) { |
require_once 'System.php'; |
System::mkdir(array('-p','-m',0755,dirname($file))); |
} |
$this->debug("Writing ini as {$file}\n"); |
touch($file); |
//print_r($this->_newConfig); |
$fh = fopen($file,'w'); |
fwrite($fh,$this->_newConfig); |
fclose($fh); |
//$ret = $this->_newConfig->writeInput($file,false); |
//if (PEAR::isError($ret) ) { |
// return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE); |
// } |
} |
/** |
* The table geneation part |
* |
* @access private |
* @return tabledef and keys array. |
*/ |
function _generateDefinitionsTable() |
{ |
global $_DB_DATAOBJECT; |
$defs = $this->_definitions[$this->table]; |
$this->_newConfig .= "\n[{$this->table}]\n"; |
$keys_out = "\n[{$this->table}__keys]\n"; |
$keys_out_primary = ''; |
$keys_out_secondary = ''; |
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) { |
echo "TABLE STRUCTURE FOR {$this->table}\n"; |
print_r($defs); |
} |
$DB = $this->getDatabaseConnection(); |
$dbtype = $DB->phptype; |
$ret = array( |
'table' => array(), |
'keys' => array(), |
); |
$ret_keys_primary = array(); |
$ret_keys_secondary = array(); |
foreach($defs as $t) { |
$n=0; |
switch (strtoupper($t->type)) { |
case 'INT': |
case 'INT2': // postgres |
case 'INT4': // postgres |
case 'INT8': // postgres |
case 'SERIAL4': // postgres |
case 'SERIAL8': // postgres |
case 'INTEGER': |
case 'TINYINT': |
case 'SMALLINT': |
case 'MEDIUMINT': |
case 'BIGINT': |
$type = DB_DATAOBJECT_INT; |
if ($t->len == 1) { |
$type += DB_DATAOBJECT_BOOL; |
} |
break; |
case 'REAL': |
case 'DOUBLE': |
case 'FLOAT': |
case 'FLOAT8': // double precision (postgres) |
case 'DECIMAL': |
case 'NUMERIC': |
case 'NUMBER': // oci8 |
$type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY... |
break; |
case 'YEAR': |
$type = DB_DATAOBJECT_INT; |
break; |
case 'BIT': |
case 'BOOL': |
case 'BOOLEAN': |
$type = DB_DATAOBJECT_BOOL; |
// postgres needs to quote '0' |
if ($dbtype == 'pgsql') { |
$type += DB_DATAOBJECT_STR; |
} |
break; |
case 'STRING': |
case 'CHAR': |
case 'VARCHAR': |
case 'VARCHAR2': |
case 'TINYTEXT': |
case 'ENUM': |
case 'SET': // not really but oh well |
case 'TIMESTAMPTZ': // postgres |
case 'BPCHAR': // postgres |
case 'INTERVAL': // postgres (eg. '12 days') |
case 'CIDR': // postgres IP net spec |
case 'INET': // postgres IP |
case 'MACADDR': // postgress network Mac address. |
$type = DB_DATAOBJECT_STR; |
break; |
case 'TEXT': |
case 'MEDIUMTEXT': |
case 'LONGTEXT': |
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT; |
break; |
case 'DATE': |
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE; |
break; |
case 'TIME': |
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME; |
break; |
case 'DATETIME': |
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME; |
break; |
case 'TIMESTAMP': // do other databases use this??? |
$type = ($dbtype == 'mysql') ? |
DB_DATAOBJECT_MYSQLTIMESTAMP : |
DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME; |
break; |
case 'TINYBLOB': |
case 'BLOB': /// these should really be ignored!!!??? |
case 'MEDIUMBLOB': |
case 'LONGBLOB': |
case 'BYTEA': // postgres blob support.. |
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB; |
break; |
} |
if (!strlen(trim($t->name))) { |
continue; |
} |
if (preg_match('/not_null/i',$t->flags)) { |
$type += DB_DATAOBJECT_NOTNULL; |
} |
$write_ini = true; |
if (in_array($t->name,array('null','yes','no','true','false'))) { |
echo "*****************************************************************\n". |
"** WARNING **\n". |
"** Found column '{$t->name}', which is invalid in an .ini file **\n". |
"** This line will not be writen to the file - you will have **\n". |
"** define the keys()/method manually. **\n". |
"*****************************************************************\n"; |
$write_ini = false; |
} else { |
$this->_newConfig .= "{$t->name} = $type\n"; |
} |
$ret['table'][$t->name] = $type; |
// i've no idea if this will work well on other databases? |
// only use primary key or nextval(), cause the setFrom blocks you setting all key items... |
// if no keys exist fall back to using unique |
//echo "\n{$t->name} => {$t->flags}\n"; |
if (preg_match("/(auto_increment|nextval\()/i",rawurldecode($t->flags))) { |
// native sequences = 2 |
if ($write_ini) { |
$keys_out_primary .= "{$t->name} = N\n"; |
} |
$ret_keys_primary[$t->name] = 'N'; |
} else if (preg_match("/(primary|unique)/i",$t->flags)) { |
// keys.. = 1 |
if ($write_ini) { |
$keys_out_secondary .= "{$t->name} = K\n"; |
} |
$ret_keys_secondary[$t->name] = 'K'; |
} |
} |
$this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary); |
$ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary; |
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) { |
print_r(array("dump for {$this->table}", $ret)); |
} |
return $ret; |
} |
/* |
* building the class files |
* for each of the tables output a file! |
*/ |
function generateClasses() |
{ |
//echo "Generating Class files: \n"; |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
$base = $options['class_location']; |
if (strpos($base,'%s') !== false) { |
$base = dirname($base); |
} |
if (!file_exists($base)) { |
require_once 'System.php'; |
System::mkdir(array('-p',$base)); |
} |
$class_prefix = $options['class_prefix']; |
if ($extends = @$options['extends']) { |
$this->_extends = $extends; |
$this->_extendsFile = $options['extends_location']; |
} |
foreach($this->tables as $this->table) { |
$this->table = trim($this->table); |
$this->classname = $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)); |
$i = ''; |
if (strpos($options['class_location'],'%s') !== false) { |
$outfilename = sprintf($options['class_location'], preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table))); |
} else { |
$outfilename = "{$base}/".preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)).".php"; |
} |
$oldcontents = ''; |
if (file_exists($outfilename)) { |
// file_get_contents??? |
$oldcontents = implode('',file($outfilename)); |
} |
$out = $this->_generateClassTable($oldcontents); |
$this->debug( "writing $this->classname\n"); |
$fh = fopen($outfilename, "w"); |
fputs($fh,$out); |
fclose($fh); |
} |
//echo $out; |
} |
/** |
* class being extended (can be overridden by [DB_DataObject_Generator] extends=xxxx |
* |
* @var string |
* @access private |
*/ |
var $_extends = 'DB_DataObject'; |
/** |
* line to use for require('DB/DataObject.php'); |
* |
* @var string |
* @access private |
*/ |
var $_extendsFile = "DB/DataObject.php"; |
/** |
* class being generated |
* |
* @var string |
* @access private |
*/ |
var $_className; |
/** |
* The table class geneation part - single file. |
* |
* @access private |
* @return none |
*/ |
function _generateClassTable($input = '') |
{ |
// title = expand me! |
$foot = ""; |
$head = "<?php\n/**\n * Table Definition for {$this->table}\n */\n"; |
// requires |
$head .= "require_once '{$this->_extendsFile}';\n\n"; |
// add dummy class header in... |
// class |
$head .= "class {$this->classname} extends {$this->_extends} \n{"; |
$body = "\n ###START_AUTOCODE\n"; |
$body .= " /* the code below is auto generated do not remove the above tag */\n\n"; |
// table |
$padding = (30 - strlen($this->table)); |
if ($padding < 2) $padding =2; |
$p = str_repeat(' ',$padding) ; |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
$var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var'; |
$body .= " {$var} \$__table = '{$this->table}'; {$p}// table name\n"; |
// if we are using the option database_{databasename} = dsn |
// then we should add var $_database = here |
// as database names may not always match.. |
if (isset($options["database_{$this->_database}"])) { |
$body .= " {$var} \$_database = '{$this->_database}'; {$p}// database name (used with database_{*} config)\n"; |
} |
$var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var'; |
if (!empty($options['generator_novars'])) { |
$var = '//'.$var; |
} |
$defs = $this->_definitions[$this->table]; |
// show nice information! |
$connections = array(); |
$sets = array(); |
foreach($defs as $t) { |
if (!strlen(trim($t->name))) { |
continue; |
} |
$padding = (30 - strlen($t->name)); |
if ($padding < 2) $padding =2; |
$p = str_repeat(' ',$padding) ; |
$body .=" {$var} \${$t->name}; {$p}// {$t->type}({$t->len}) {$t->flags}\n"; |
// can not do set as PEAR::DB table info doesnt support it. |
//if (substr($t->Type,0,3) == "set") |
// $sets[$t->Field] = "array".substr($t->Type,3); |
$body .= $this->derivedHookVar($t,$padding); |
} |
// THIS IS TOTALLY BORKED old FC creation |
// IT WILL BE REMOVED!!!!! in DataObjects 1.6 |
// grep -r __clone * to find all it's uses |
// and replace them with $x = clone($y); |
// due to the change in the PHP5 clone design. |
if ( substr(phpversion(),0,1) < 5) { |
$body .= "\n"; |
$body .= " /* ZE2 compatibility trick*/\n"; |
$body .= " function __clone() { return \$this;}\n"; |
} |
// simple creation tools ! (static stuff!) |
$body .= "\n"; |
$body .= " /* Static get */\n"; |
$body .= " function staticGet(\$k,\$v=NULL) { return DB_DataObject::staticGet('{$this->classname}',\$k,\$v); }\n"; |
// generate getter and setter methods |
$body .= $this->_generateGetters($input); |
$body .= $this->_generateSetters($input); |
/* |
theoretically there is scope here to introduce 'list' methods |
based up 'xxxx_up' column!!! for heiracitcal trees.. |
*/ |
// set methods |
//foreach ($sets as $k=>$v) { |
// $kk = strtoupper($k); |
// $body .=" function getSets{$k}() { return {$v}; }\n"; |
//} |
$body .= $this->derivedHookFunctions(); |
$body .= "\n /* the code above is auto generated do not remove the tag below */"; |
$body .= "\n ###END_AUTOCODE\n"; |
// stubs.. |
if (!empty($options['generator_add_validate_stubs'])) { |
foreach($defs as $t) { |
if (!strlen(trim($t->name))) { |
continue; |
} |
$validate_fname = 'validate' . ucfirst(strtolower($t->name)); |
// dont re-add it.. |
if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) { |
continue; |
} |
$body .= "\n function {$validate_fname}()\n {\n return false;\n }\n"; |
} |
} |
$foot .= "}\n"; |
$full = $head . $body . $foot; |
if (!$input) { |
return $full; |
} |
if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input)) { |
return $full; |
} |
if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) { |
return $full; |
} |
/* this will only replace extends DB_DataObject by default, |
unless use set generator_class_rewrite to ANY or a name*/ |
$class_rewrite = 'DB_DataObject'; |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
if (!($class_rewrite = @$options['generator_class_rewrite'])) { |
$class_rewrite = 'DB_DataObject'; |
} |
if ($class_rewrite == 'ANY') { |
$class_rewrite = '[a-z_]+'; |
} |
$input = preg_replace( |
'/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*\{(\n|\r\n)/si', |
"\nclass {$this->classname} extends {$this->_extends} \n{\n", |
$input); |
return preg_replace( |
'/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', |
$body,$input); |
} |
/** |
* hook to add extra methods to all classes |
* |
* called once for each class, use with $this->table and |
* $this->_definitions[$this->table], to get data out of the current table, |
* use it to add extra methods to the default classes. |
* |
* @access public |
* @return string added to class eg. functions. |
*/ |
function derivedHookFunctions() |
{ |
// This is so derived generator classes can generate functions |
// It MUST NOT be changed here!!! |
return ""; |
} |
/** |
* hook for var lines |
* called each time a var line is generated, override to add extra var |
* lines |
* |
* @param object t containing type,len,flags etc. from tableInfo call |
* @param int padding number of spaces |
* @access public |
* @return string added to class eg. functions. |
*/ |
function derivedHookVar(&$t,$padding) |
{ |
// This is so derived generator classes can generate variabels |
// It MUST NOT be changed here!!! |
return ""; |
} |
/** |
* getProxyFull - create a class definition on the fly and instantate it.. |
* |
* similar to generated files - but also evals the class definitoin code. |
* |
* |
* @param string database name |
* @param string table name of table to create proxy for. |
* |
* |
* @return object Instance of class. or PEAR Error |
* @access public |
*/ |
function getProxyFull($database,$table) { |
if ($err = $this->fillTableSchema($database,$table)) { |
return $err; |
} |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
$class_prefix = $options['class_prefix']; |
if ($extends = @$options['extends']) { |
$this->_extends = $extends; |
$this->_extendsFile = $options['extends_location']; |
} |
$classname = $this->classname = $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($this->table))); |
$out = $this->_generateClassTable(); |
//echo $out; |
eval('?>'.$out); |
return new $classname; |
} |
/** |
* fillTableSchema - set the database schema on the fly |
* |
* |
* |
* @param string database name |
* @param string table name of table to create schema info for |
* |
* @return none | PEAR::error() |
* @access public |
*/ |
function fillTableSchema($database,$table) { |
global $_DB_DATAOBJECT; |
$this->_database = $database; |
$this->_connect(); |
$table = trim($table); |
$__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5]; |
$defs = $__DB->tableInfo($table); |
if (PEAR::isError($defs)) { |
return $defs; |
} |
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) { |
$this->debug("getting def for $database/$table",'fillTable'); |
$this->debug(print_r($defs,true),'defs'); |
} |
// cast all definitions to objects - as we deal with that better. |
foreach($defs as $def) { |
if (is_array($def)) { |
$this->_definitions[$table][] = (object) $def; |
} |
} |
$this->table = trim($table); |
$ret = $this->_generateDefinitionsTable(); |
$_DB_DATAOBJECT['INI'][$database][$table] = $ret['table']; |
$_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys']; |
return false; |
} |
/** |
* Generate getter methods for class definition |
* |
* @param string $input Existing class contents |
* @return string |
* @access public |
*/ |
function _generateGetters($input) { |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
$getters = ''; |
// only generate if option is set to true |
if (empty($options['generate_getters'])) { |
return ''; |
} |
// remove auto-generated code from input to be able to check if the method exists outside of the auto-code |
$input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input); |
$getters .= "\n\n"; |
$defs = $this->_definitions[$this->table]; |
// loop through properties and create getter methods |
foreach ($defs = $defs as $t) { |
// build mehtod name |
$methodName = 'get' . ucfirst($t->name); |
if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) { |
continue; |
} |
$getters .= " /**\n"; |
$getters .= " * Getter for \${$t->name}\n"; |
$getters .= " *\n"; |
$getters .= (stristr($t->flags, 'multiple_key')) ? " * @return object\n" |
: " * @return {$t->type}\n"; |
$getters .= " * @access public\n"; |
$getters .= " */\n"; |
$getters .= (substr(phpversion(),0,1) > 4) ? ' public ' |
: ' '; |
$getters .= "function $methodName() {\n"; |
$getters .= " return \$this->{$t->name};\n"; |
$getters .= " }\n\n"; |
} |
return $getters; |
} |
/** |
* Generate setter methods for class definition |
* |
* @param string Existing class contents |
* @return string |
* @access public |
*/ |
function _generateSetters($input) { |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
$setters = ''; |
// only generate if option is set to true |
if (empty($options['generate_setters'])) { |
return ''; |
} |
// remove auto-generated code from input to be able to check if the method exists outside of the auto-code |
$input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input); |
$setters .= "\n"; |
$defs = $this->_definitions[$this->table]; |
// loop through properties and create setter methods |
foreach ($defs = $defs as $t) { |
// build mehtod name |
$methodName = 'set' . ucfirst($t->name); |
if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) { |
continue; |
} |
$setters .= " /**\n"; |
$setters .= " * Setter for \${$t->name}\n"; |
$setters .= " *\n"; |
$setters .= " * @param mixed input value\n"; |
$setters .= " * @access public\n"; |
$setters .= " */\n"; |
$setters .= (substr(phpversion(),0,1) > 4) ? ' public ' |
: ' '; |
$setters .= "function $methodName(\$value) {\n"; |
$setters .= " \$this->{$t->name} = \$value;\n"; |
$setters .= " }\n\n"; |
} |
return $setters; |
} |
} |
/tags/Racine_livraison_narmer/api/pear/DB/DataObject/Error.php |
---|
New file |
0,0 → 1,53 |
<?php |
/** |
* DataObjects error handler, loaded on demand... |
* |
* DB_DataObject_Error is a quick wrapper around pear error, so you can distinguish the |
* error code source. |
* |
* 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_DataObject |
* @author Alan Knowles <alan@akbkhome.com> |
* @copyright 1997-2005 The PHP Group |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: Error.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_DataObject |
*/ |
class DB_DataObject_Error extends PEAR_Error |
{ |
/** |
* DB_DataObject_Error constructor. |
* |
* @param mixed $code DB error code, or string with error message. |
* @param integer $mode what "error mode" to operate in |
* @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER |
* @param mixed $debuginfo additional debug info, such as the last query |
* |
* @access public |
* |
* @see PEAR_Error |
*/ |
function DB_DataObject_Error($message = '', $code = DB_ERROR, $mode = PEAR_ERROR_RETURN, |
$level = E_USER_NOTICE) |
{ |
$this->PEAR_Error('DB_DataObject Error: ' . $message, $code, $mode, $level); |
} |
// todo : - support code -> message handling, and translated error messages... |
} |
/tags/Racine_livraison_narmer/api/pear/DB/DataObject/Cast.php |
---|
New file |
0,0 → 1,546 |
<?php |
/** |
* Prototype Castable Object.. for DataObject queries |
* |
* Storage for Data that may be cast into a variety of formats. |
* |
* 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_DataObject |
* @author Alan Knowles <alan@akbkhome.com> |
* @copyright 1997-2005 The PHP Group |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: Cast.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_DataObject |
*/ |
/** |
* |
* Common usages: |
* // blobs |
* $data = DB_DataObject_Cast::blob($somefile); |
* $data = DB_DataObject_Cast::string($somefile); |
* $dataObject->someblobfield = $data |
* |
* // dates? |
* $d1 = new DB_DataObject_Cast::date('12/12/2000'); |
* $d2 = new DB_DataObject_Cast::date(2000,12,30); |
* $d3 = new DB_DataObject_Cast::date($d1->year, $d1->month+30, $d1->day+30); |
* |
* // time, datetime.. ????????? |
* |
* // raw sql???? |
* $data = DB_DataObject_Cast::sql('cast("123123",datetime)'); |
* $data = DB_DataObject_Cast::sql('NULL'); |
* |
* // int's/string etc. are proably pretty pointless..!!!! |
* |
* |
* inside DB_DataObject, |
* if (is_a($v,'db_dataobject_class')) { |
* $value .= $v->toString(DB_DATAOBJECT_INT,'mysql'); |
* } |
* |
* |
* |
* |
*/ |
class DB_DataObject_Cast { |
/** |
* Type of data Stored in the object.. |
* |
* @var string (date|blob|.....?) |
* @access public |
*/ |
var $type; |
/** |
* Data For date representation |
* |
* @var int day/month/year |
* @access public |
*/ |
var $day; |
var $month; |
var $year; |
/** |
* Generic Data.. |
* |
* @var string |
* @access public |
*/ |
var $value; |
/** |
* Blob consructor |
* |
* create a Cast object from some raw data.. (binary) |
* |
* |
* @param string (with binary data!) |
* |
* @return object DB_DataObject_Cast |
* @access public |
*/ |
function blob($value) { |
$r = new DB_DataObject_Cast; |
$r->type = 'blob'; |
$r->value = $value; |
return $r; |
} |
/** |
* String consructor (actually use if for ints and everything else!!! |
* |
* create a Cast object from some string (not binary) |
* |
* |
* @param string (with binary data!) |
* |
* @return object DB_DataObject_Cast |
* @access public |
*/ |
function string($value) { |
$r = new DB_DataObject_Cast; |
$r->type = 'string'; |
$r->value = $value; |
return $r; |
} |
/** |
* SQL constructor (for raw SQL insert) |
* |
* create a Cast object from some sql |
* |
* @param string (with binary data!) |
* |
* @return object DB_DataObject_Cast |
* @access public |
*/ |
function sql($value) |
{ |
$r = new DB_DataObject_Cast; |
$r->type = 'sql'; |
$r->value = $value; |
return $r; |
} |
/** |
* Date Constructor |
* |
* create a Cast object from some string (not binary) |
* NO VALIDATION DONE, although some crappy re-calcing done! |
* |
* @param vargs... accepts |
* dd/mm |
* dd/mm/yyyy |
* yyyy-mm |
* yyyy-mm-dd |
* array(yyyy,dd) |
* array(yyyy,dd,mm) |
* |
* |
* |
* @return object DB_DataObject_Cast |
* @access public |
*/ |
function date() |
{ |
$args = func_get_args(); |
switch(count($args)) { |
case 0: // no args = today! |
$bits = explode('-',date('Y-m-d')); |
break; |
case 1: // one arg = a string |
if (strpos($args[0],'/') !== false) { |
$bits = array_reverse(explode('/',$args[0])); |
} else { |
$bits = explode('-',$args[0]); |
} |
break; |
default: // 2 or more.. |
$bits = $args; |
} |
if (count($bits) == 1) { // if YYYY set day = 1st.. |
$bits[] = 1; |
} |
if (count($bits) == 2) { // if YYYY-DD set day = 1st.. |
$bits[] = 1; |
} |
// if year < 1970 we cant use system tools to check it... |
// so we make a few best gueses.... |
// basically do date calculations for the year 2000!!! |
// fix me if anyone has more time... |
if (($bits[0] < 1975) || ($bits[0] > 2030)) { |
$oldyear = $bits[0]; |
$bits = explode('-',date('Y-m-d',mktime(1,1,1,$bits[1],$bits[2],2000))); |
$bits[0] = ($bits[0] - 2000) + $oldyear; |
} else { |
// now mktime |
$bits = explode('-',date('Y-m-d',mktime(1,1,1,$bits[1],$bits[2],$bits[0]))); |
} |
$r = new DB_DataObject_Cast; |
$r->type = 'date'; |
list($r->year,$r->month,$r->day) = $bits; |
return $r; |
} |
/** |
* Data For time representation ** does not handle timezones!! |
* |
* @var int hour/minute/second |
* @access public |
*/ |
var $hour; |
var $minute; |
var $second; |
/** |
* DateTime Constructor |
* |
* create a Cast object from a Date/Time |
* Maybe should accept a Date object.! |
* NO VALIDATION DONE, although some crappy re-calcing done! |
* |
* @param vargs... accepts |
* noargs (now) |
* yyyy-mm-dd HH:MM:SS (Iso) |
* array(yyyy,mm,dd,HH,MM,SS) |
* |
* |
* @return object DB_DataObject_Cast |
* @access public |
* @author therion 5 at hotmail |
*/ |
function dateTime() |
{ |
$args = func_get_args(); |
switch(count($args)) { |
case 0: // no args = now! |
$datetime = date('Y-m-d G:i:s', mktime()); |
case 1: |
// continue on from 0 args. |
if (!isset($datetime)) { |
$datetime = $args[0]; |
} |
$parts = explode(' ', $datetime); |
$bits = explode('-', $parts[0]); |
$bits = array_merge($bits, explode(':', $parts[1])); |
break; |
default: // 2 or more.. |
$bits = $args; |
} |
if (count($bits) != 6) { |
// PEAR ERROR? |
return false; |
} |
$r = DB_DataObject_Cast::date($bits[0], $bits[1], $bits[2]); |
if (!$r) { |
return $r; // pass thru error (False) - doesnt happen at present! |
} |
// change the type! |
$r->type = 'datetime'; |
// should we mathematically sort this out.. |
// (or just assume that no-one's dumb enough to enter 26:90:90 as a time! |
$r->hour = $bits[3]; |
$r->minute = $bits[4]; |
$r->second = $bits[5]; |
return $r; |
} |
/** |
* time Constructor |
* |
* create a Cast object from a Date/Time |
* Maybe should accept a Date object.! |
* NO VALIDATION DONE, and no-recalcing done! |
* |
* @param vargs... accepts |
* noargs (now) |
* HH:MM:SS (Iso) |
* array(HH,MM,SS) |
* |
* |
* @return object DB_DataObject_Cast |
* @access public |
* @author therion 5 at hotmail |
*/ |
function time() |
{ |
$args = func_get_args(); |
switch (count($args)) { |
case 0: // no args = now! |
$time = date('G:i:s', mktime()); |
case 1: |
// continue on from 0 args. |
if (!isset($time)) { |
$time = $args[0]; |
} |
$bits = explode(':', $time); |
break; |
default: // 2 or more.. |
$bits = $args; |
} |
if (count($bits) != 3) { |
return false; |
} |
// now take data from bits into object fields |
$r = new DB_DataObject_Cast; |
$r->type = 'time'; |
$r->hour = $bits[0]; |
$r->minute = $bits[1]; |
$r->second = $bits[2]; |
return $r; |
} |
/** |
* get the string to use in the SQL statement for this... |
* |
* |
* @param int $to Type (DB_DATAOBJECT_* |
* @param object $db DB Connection Object |
* |
* |
* @return string |
* @access public |
*/ |
function toString($to=false,$db) |
{ |
// if $this->type is not set, we are in serious trouble!!!! |
// values for to: |
$method = 'toStringFrom'.$this->type; |
return $this->$method($to,$db); |
} |
/** |
* get the string to use in the SQL statement from a blob of binary data |
* ** Suppots only blob->postgres::bytea |
* |
* @param int $to Type (DB_DATAOBJECT_* |
* @param object $db DB Connection Object |
* |
* |
* @return string |
* @access public |
*/ |
function toStringFromBlob($to,$db) |
{ |
// first weed out invalid casts.. |
// in blobs can only be cast to blobs.! |
// perhaps we should support TEXT fields??? |
if (!($to & DB_DATAOBJECT_BLOB)) { |
return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::blob to something other than a blob!'); |
} |
switch ($db->dsn["phptype"]) { |
case 'pgsql': |
return "'".pg_escape_bytea($this->value)."'::bytea"; |
case 'mysql': |
return "'".mysql_real_escape_string($this->value,$db->connection)."'"; |
case 'mysqli': |
// this is funny - the parameter order is reversed ;) |
return "'".mysqli_real_escape_string($db->connection, $this->value)."'"; |
default: |
return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet"); |
} |
} |
/** |
* get the string to use in the SQL statement for a blob from a string! |
* ** Suppots only string->postgres::bytea |
* |
* |
* @param int $to Type (DB_DATAOBJECT_* |
* @param object $db DB Connection Object |
* |
* |
* @return string |
* @access public |
*/ |
function toStringFromString($to,$db) |
{ |
// first weed out invalid casts.. |
// in blobs can only be cast to blobs.! |
// perhaps we should support TEXT fields??? |
// |
if (!($to & DB_DATAOBJECT_BLOB)) { |
return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a blob!'. |
' (why not just use native features)'); |
} |
switch ($db->dsn['phptype']) { |
case 'pgsql': |
return "'".pg_escape_string($this->value)."'::bytea"; |
case 'mysql': |
return "'".mysql_real_escape_string($this->value,$db->connection)."'"; |
case 'mysqli': |
return "'".mysqli_real_escape_string($db->connection, $this->value)."'"; |
default: |
return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet"); |
} |
} |
/** |
* get the string to use in the SQL statement for a date |
* |
* |
* |
* @param int $to Type (DB_DATAOBJECT_* |
* @param object $db DB Connection Object |
* |
* |
* @return string |
* @access public |
*/ |
function toStringFromDate($to,$db) |
{ |
// first weed out invalid casts.. |
// in blobs can only be cast to blobs.! |
// perhaps we should support TEXT fields??? |
// |
if (($to !== false) && !($to & DB_DATAOBJECT_DATE)) { |
return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a date!'. |
' (why not just use native features)'); |
} |
return "'{$this->year}-{$this->month}-{$this->day}'"; |
} |
/** |
* get the string to use in the SQL statement for a datetime |
* |
* |
* |
* @param int $to Type (DB_DATAOBJECT_* |
* @param object $db DB Connection Object |
* |
* |
* @return string |
* @access public |
* @author therion 5 at hotmail |
*/ |
function toStringFromDateTime($to,$db) |
{ |
// first weed out invalid casts.. |
// in blobs can only be cast to blobs.! |
// perhaps we should support TEXT fields??? |
if (($to !== false) && |
!($to & (DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME))) { |
return PEAR::raiseError('Invalid Cast from a ' . |
' DB_DataObject_Cast::dateTime to something other than a datetime!' . |
' (try using native features)'); |
} |
return "'{$this->year}-{$this->month}-{$this->day} {$this->hour}:{$this->minute}:{$this->second}'"; |
} |
/** |
* get the string to use in the SQL statement for a time |
* |
* |
* |
* @param int $to Type (DB_DATAOBJECT_* |
* @param object $db DB Connection Object |
* |
* |
* @return string |
* @access public |
* @author therion 5 at hotmail |
*/ |
function toStringFromTime($to,$db) |
{ |
// first weed out invalid casts.. |
// in blobs can only be cast to blobs.! |
// perhaps we should support TEXT fields??? |
if (($to !== false) && !($to & DB_DATAOBJECT_TIME)) { |
return PEAR::raiseError('Invalid Cast from a' . |
' DB_DataObject_Cast::time to something other than a time!'. |
' (try using native features)'); |
} |
return "'{$this->hour}:{$this->minute}:{$this->second}'"; |
} |
/** |
* get the string to use in the SQL statement for a raw sql statement. |
* |
* @param int $to Type (DB_DATAOBJECT_* |
* @param object $db DB Connection Object |
* |
* |
* @return string |
* @access public |
*/ |
function toStringFromSql($to,$db) |
{ |
return $this->value; |
} |
} |
/tags/Racine_livraison_narmer/api/pear/DB/DataObject/createTables.php |
---|
New file |
0,0 → 1,55 |
#!/usr/bin/php -q |
<?php |
// +----------------------------------------------------------------------+ |
// | PHP Version 4 | |
// +----------------------------------------------------------------------+ |
// | Copyright (c) 1997-2003 The PHP Group | |
// +----------------------------------------------------------------------+ |
// | This source file is subject to version 2.02 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. | |
// +----------------------------------------------------------------------+ |
// | Author: Alan Knowles <alan@akbkhome.com> |
// +----------------------------------------------------------------------+ |
// |
// $Id: createTables.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
// |
// since this version doesnt use overload, |
// and I assume anyone using custom generators should add this.. |
define('DB_DATAOBJECT_NO_OVERLOAD',1); |
require_once 'DB/DataObject/Generator.php'; |
if (!ini_get('register_argc_argv')) { |
PEAR::raiseError("\nERROR: You must turn register_argc_argv On in you php.ini file for this to work\neg.\n\nregister_argc_argv = On\n\n", null, PEAR_ERROR_DIE); |
exit; |
} |
if (!@$_SERVER['argv'][1]) { |
PEAR::raiseError("\nERROR: createTable.php usage:\n\nC:\php\pear\DB\DataObjects\createTable.php example.ini\n\n", null, PEAR_ERROR_DIE); |
exit; |
} |
$config = parse_ini_file($_SERVER['argv'][1], true); |
foreach($config as $class=>$values) { |
$options = &PEAR::getStaticProperty($class,'options'); |
$options = $values; |
} |
$options = &PEAR::getStaticProperty('DB_DataObject','options'); |
if (empty($options)) { |
PEAR::raiseError("\nERROR: could not read ini file\n\n", null, PEAR_ERROR_DIE); |
exit; |
} |
set_time_limit(0); |
DB_DataObject::debugLevel(1); |
$generator = new DB_DataObject_Generator; |
$generator->start(); |
/tags/Racine_livraison_narmer/api/pear/DB/msql.php |
---|
New file |
0,0 → 1,810 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's msql extension |
* for interacting with Mini SQL databases |
* |
* PHP's mSQL extension did weird things with NULL values prior to PHP |
* 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds |
* those versions. |
* |
* 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 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: msql.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 msql extension |
* for interacting with Mini SQL databases |
* |
* These methods overload the ones declared in DB_common. |
* |
* PHP's mSQL extension did weird things with NULL values prior to PHP |
* 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds |
* those versions. |
* |
* @category Database |
* @package DB |
* @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: 1.7.6 |
* @link http://pear.php.net/package/DB |
* @since Class not functional until Release 1.7.0 |
*/ |
class DB_msql extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'msql'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'msql'; |
/** |
* 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' => 'emulate', |
'new_link' => false, |
'numrows' => true, |
'pconnect' => true, |
'prepare' => false, |
'ssl' => false, |
'transactions' => false, |
); |
/** |
* 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(); |
/** |
* The query result resource created by PHP |
* |
* Used to make affectedRows() work. Only contains the result for |
* data manipulation queries. Contains false for other queries. |
* |
* @var resource |
* @access private |
*/ |
var $_result; |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_msql() |
{ |
$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. |
* |
* Example of how to connect: |
* <code> |
* require_once 'DB.php'; |
* |
* // $dsn = 'msql://hostname/dbname'; // use a TCP connection |
* $dsn = 'msql:///dbname'; // use a socket |
* $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. |
*/ |
function connect($dsn, $persistent = false) |
{ |
if (!PEAR::loadExtension('msql')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
$params = array(); |
if ($dsn['hostspec']) { |
$params[] = $dsn['port'] |
? $dsn['hostspec'] . ',' . $dsn['port'] |
: $dsn['hostspec']; |
} |
$connect_function = $persistent ? 'msql_pconnect' : 'msql_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 = @msql_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 (!@msql_select_db($dsn['database'], $this->connection)) { |
return $this->msqlRaiseError(); |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
$ret = @msql_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) |
{ |
$this->last_query = $query; |
$query = $this->modifyQuery($query); |
$result = @msql_query($query, $this->connection); |
if (!$result) { |
return $this->msqlRaiseError(); |
} |
// Determine which queries that should return data, and which |
// should return an error code only. |
if (DB::isManip($query)) { |
$this->_result = $result; |
return DB_OK; |
} else { |
$this->_result = false; |
return $result; |
} |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal msql 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. |
* |
* PHP's mSQL extension did weird things with NULL values prior to PHP |
* 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds |
* those versions. |
* |
* @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 (!@msql_data_seek($result, $rownum)) { |
return null; |
} |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
$arr = @msql_fetch_array($result, MSQL_ASSOC); |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @msql_fetch_row($result); |
} |
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); |
} |
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 @msql_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 = @msql_num_fields($result); |
if (!$cols) { |
return $this->msqlRaiseError(); |
} |
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 = @msql_num_rows($result); |
if ($rows === false) { |
return $this->msqlRaiseError(); |
} |
return $rows; |
} |
// }}} |
// {{{ affected() |
/** |
* 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 (!$this->_result) { |
return 0; |
} |
return msql_affected_rows($this->_result); |
} |
// }}} |
// {{{ 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_msql::createSequence(), DB_msql::dropSequence() |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$seqname = $this->getSequenceName($seq_name); |
$repeat = false; |
do { |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result =& $this->query("SELECT _seq FROM ${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 |
* |
* Also creates a new table to associate the sequence with. Uses |
* a separate table to ensure portability with other drivers. |
* |
* @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_msql::nextID(), DB_msql::dropSequence() |
*/ |
function createSequence($seq_name) |
{ |
$seqname = $this->getSequenceName($seq_name); |
$res = $this->query('CREATE TABLE ' . $seqname |
. ' (id INTEGER NOT NULL)'); |
if (DB::isError($res)) { |
return $res; |
} |
$res = $this->query("CREATE SEQUENCE ON ${seqname}"); |
return $res; |
} |
// }}} |
// {{{ 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_msql::nextID(), DB_msql::createSequence() |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
} |
// }}} |
// {{{ quoteIdentifier() |
/** |
* mSQL does not support delimited identifiers |
* |
* @param string $str the identifier name to be quoted |
* |
* @return object a DB_Error object |
* |
* @see DB_common::quoteIdentifier() |
* @since Method available since Release 1.7.0 |
*/ |
function quoteIdentifier($str) |
{ |
return $this->raiseError(DB_ERROR_UNSUPPORTED); |
} |
// }}} |
// {{{ 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.7.0 |
*/ |
function escapeSimple($str) |
{ |
return addslashes($str); |
} |
// }}} |
// {{{ msqlRaiseError() |
/** |
* 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_msql::errorNative(), DB_msql::errorCode() |
*/ |
function msqlRaiseError($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 |
* |
* @return string the DBMS' error message |
*/ |
function errorNative() |
{ |
return @msql_error(); |
} |
// }}} |
// {{{ errorCode() |
/** |
* Determines PEAR::DB error code from the database's text error message |
* |
* @param string $errormsg the error message returned from the database |
* |
* @return integer the error number from a DB_ERROR* constant |
*/ |
function errorCode($errormsg) |
{ |
static $error_regexps; |
if (!isset($error_regexps)) { |
$error_regexps = array( |
'/^Access to database denied/i' |
=> DB_ERROR_ACCESS_VIOLATION, |
'/^Bad index name/i' |
=> DB_ERROR_ALREADY_EXISTS, |
'/^Bad order field/i' |
=> DB_ERROR_SYNTAX, |
'/^Bad type for comparison/i' |
=> DB_ERROR_SYNTAX, |
'/^Can\'t perform LIKE on/i' |
=> DB_ERROR_SYNTAX, |
'/^Can\'t use TEXT fields in LIKE comparison/i' |
=> DB_ERROR_SYNTAX, |
'/^Couldn\'t create temporary table/i' |
=> DB_ERROR_CANNOT_CREATE, |
'/^Error creating table file/i' |
=> DB_ERROR_CANNOT_CREATE, |
'/^Field .* cannot be null$/i' |
=> DB_ERROR_CONSTRAINT_NOT_NULL, |
'/^Index (field|condition) .* cannot be null$/i' |
=> DB_ERROR_SYNTAX, |
'/^Invalid date format/i' |
=> DB_ERROR_INVALID_DATE, |
'/^Invalid time format/i' |
=> DB_ERROR_INVALID, |
'/^Literal value for .* is wrong type$/i' |
=> DB_ERROR_INVALID_NUMBER, |
'/^No Database Selected/i' |
=> DB_ERROR_NODBSELECTED, |
'/^No value specified for field/i' |
=> DB_ERROR_VALUE_COUNT_ON_ROW, |
'/^Non unique value for unique index/i' |
=> DB_ERROR_CONSTRAINT, |
'/^Out of memory for temporary table/i' |
=> DB_ERROR_CANNOT_CREATE, |
'/^Permission denied/i' |
=> DB_ERROR_ACCESS_VIOLATION, |
'/^Reference to un-selected table/i' |
=> DB_ERROR_SYNTAX, |
'/^syntax error/i' |
=> DB_ERROR_SYNTAX, |
'/^Table .* exists$/i' |
=> DB_ERROR_ALREADY_EXISTS, |
'/^Unknown database/i' |
=> DB_ERROR_NOSUCHDB, |
'/^Unknown field/i' |
=> DB_ERROR_NOSUCHFIELD, |
'/^Unknown (index|system variable)/i' |
=> DB_ERROR_NOT_FOUND, |
'/^Unknown table/i' |
=> DB_ERROR_NOSUCHTABLE, |
'/^Unqualified field/i' |
=> DB_ERROR_SYNTAX, |
); |
} |
foreach ($error_regexps as $regexp => $code) { |
if (preg_match($regexp, $errormsg)) { |
return $code; |
} |
} |
return DB_ERROR; |
} |
// }}} |
// {{{ 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::setOption() |
*/ |
function tableInfo($result, $mode = null) |
{ |
if (is_string($result)) { |
/* |
* Probably received a table name. |
* Create a result resource identifier. |
*/ |
$id = @msql_query("SELECT * FROM $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->raiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = @msql_num_fields($id); |
$res = array(); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
$tmp = @msql_fetch_field($id); |
$flags = ''; |
if ($tmp->not_null) { |
$flags .= 'not_null '; |
} |
if ($tmp->unique) { |
$flags .= 'unique_key '; |
} |
$flags = trim($flags); |
$res[$i] = array( |
'table' => $case_func($tmp->table), |
'name' => $case_func($tmp->name), |
'type' => $tmp->type, |
'len' => msql_field_len($id, $i), |
'flags' => $flags, |
); |
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) { |
@msql_free_result($id); |
} |
return $res; |
} |
// }}} |
// {{{ getSpecialQuery() |
/** |
* Obtain a list of a given type of objects |
* |
* @param string $type the kind of objects you want to retrieve |
* |
* @return array the array containing the list of objects requested |
* |
* @access protected |
* @see DB_common::getListOf() |
*/ |
function getSpecialQuery($type) |
{ |
switch ($type) { |
case 'databases': |
$id = @msql_list_dbs($this->connection); |
break; |
case 'tables': |
$id = @msql_list_tables($this->dsn['database'], |
$this->connection); |
break; |
default: |
return null; |
} |
if (!$id) { |
return $this->msqlRaiseError(); |
} |
$out = array(); |
while ($row = @msql_fetch_row($id)) { |
$out[] = $row[0]; |
} |
return $out; |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/QueryTool/EasyJoin.php |
---|
New file |
0,0 → 1,135 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* Contains the DB_QueryTool_EasyJoin 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_QueryTool |
* @author Wolfram Kriesing <wk@visionp.de> |
* @author Paolo Panto <wk@visionp.de> |
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: EasyJoin.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
/** |
* require the DB_QueryTool_Query class |
*/ |
require_once 'DB/QueryTool/Query.php'; |
/** |
* DB_QueryTool_EasyJoin class |
* |
* @category Database |
* @package DB_QueryTool |
* @author Wolfram Kriesing <wk@visionp.de> |
* @copyright 2003-2005 Wolfram Kriesing |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
class DB_QueryTool_EasyJoin extends DB_QueryTool_Query |
{ |
// {{{ class vars |
/** |
* This is the regular expression that shall be used to find a table's shortName |
* in a column name, the string found by using this regular expression will be removed |
* from the column name and it will be checked if it is a table name |
* i.e. the default '/_id$/' would find the table name 'user' from the column name 'user_id' |
* |
* @var string regexp |
*/ |
var $_tableNamePreg = '/_id$/'; |
/** |
* This is to find the column name that is referred by it, so the default find |
* from 'user_id' the column 'id' which will be used to refer to the 'user' table |
* |
* @var string regexp |
*/ |
var $_columnNamePreg = '/^.*_/'; |
// }}} |
// {{{ __construct() |
/** |
* call parent constructor |
* @param mixed $dsn DSN string, DSN array or DB object |
* @param array $options |
*/ |
function __construct($dsn=false, $options=array()) |
{ |
parent::DB_QueryTool_Query($dsn, $options); |
} |
// }}} |
// {{{ autoJoin() |
/** |
* Join the given tables, using the column names, to find out how to join the tables; |
* i.e., if table1 has a column named "table2_id", this method will join |
* "WHERE table1.table2_id=table2.id". |
* All joins made here are only concatenated via AND. |
* @param array $tables |
*/ |
function autoJoin($tables) |
{ |
// FIXXME if $tables is empty autoJoin all available tables that have a relation |
// to $this->table, starting to search in $this->table |
settype($tables, 'array'); |
// add this->table to the tables array, so we go thru the current table first |
$tables = array_merge(array($this->table), $tables); |
$shortNameIndexed = $this->getTableSpec(true, $tables); |
$nameIndexed = $this->getTableSpec(false, $tables); |
//print_r($shortNameIndexed); |
//print_r($tables); print '<br><br>'; |
if (sizeof($shortNameIndexed) != sizeof($tables)) { |
$this->_errorLog("autoJoin-ERROR: not all the tables are in the tableSpec!<br />"); |
} |
$joinTables = array(); |
$joinConditions = array(); |
foreach ($tables as $aTable) { // go through $this->table and all the given tables |
if ($metadata = $this->metadata($aTable)) |
foreach ($metadata as $aCol => $x) { // go through each row to check which might be related to $aTable |
$possibleTableShortName = preg_replace($this->_tableNamePreg, '' , $aCol); |
$possibleColumnName = preg_replace($this->_columnNamePreg, '' , $aCol); |
//print "$aTable.$aCol .... possibleTableShortName=$possibleTableShortName .... possibleColumnName=$possibleColumnName<br />"; |
if (isset($shortNameIndexed[$possibleTableShortName])) { |
// are the tables given in the tableSpec? |
if (!$shortNameIndexed[$possibleTableShortName]['name'] || |
!$nameIndexed[$aTable]['name']) { |
// its an error of the developer, so log the error, dont show it to the end user |
$this->_errorLog("autoJoin-ERROR: '$aTable' is not given in the tableSpec!<br />"); |
} else { |
// do only join different table.col combination, |
// we should not join stuff like 'question.question=question.question' |
// this would be quite stupid, but it used to be :-( |
if ($shortNameIndexed[$possibleTableShortName]['name'] != $aTable || |
$possibleColumnName != $aCol |
) { |
$where = $shortNameIndexed[$possibleTableShortName]['name'].".$possibleColumnName=$aTable.$aCol"; |
$this->addJoin($nameIndexed[$aTable]['name'], $where); |
$this->addJoin($shortNameIndexed[$possibleTableShortName]['name'], $where); |
} |
} |
} |
} |
} |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/QueryTool/Query.php |
---|
New file |
0,0 → 1,2395 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* Contains the DB_QueryTool_Query 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_QueryTool |
* @author Wolfram Kriesing <wk@visionp.de> |
* @author Paolo Panto <wk@visionp.de> |
* @author Lorenzo Alberton <l dot alberton at quipo dot it> |
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: Query.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
/** |
* require the PEAR and DB classes |
*/ |
require_once 'PEAR.php'; |
require_once 'DB.php'; |
/** |
* DB_QueryTool_Query class |
* |
* This class should be extended |
* |
* @category Database |
* @package DB_QueryTool |
* @author Wolfram Kriesing <wk@visionp.de> |
* @author Paolo Panto <wk@visionp.de> |
* @author Lorenzo Alberton <l dot alberton at quipo dot it> |
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
class DB_QueryTool_Query |
{ |
// {{{ class vars |
/** |
* @var string the name of the primary column |
*/ |
var $primaryCol = 'id'; |
/** |
* @var string the current table the class works on |
*/ |
var $table = ''; |
/** |
* @var string the name of the sequence for this table |
*/ |
var $sequenceName = null; |
/** |
* @var object the db-object, a PEAR::DB instance |
*/ |
var $db = null; |
/** |
* @var string the where condition |
* @access private |
*/ |
var $_where = ''; |
/** |
* @var string the order condition |
* @access private |
*/ |
var $_order = ''; |
/** |
* @var string the having definition |
* @access private |
*/ |
var $_having = ''; |
/** |
* @var array contains the join content |
* the key is the join type, for now we have 'default' and 'left' |
* inside each key 'table' contains the table |
* key 'where' contains the where clause for the join |
* @access private |
*/ |
var $_join = array(); |
/** |
* @var string which column to index the result by |
* @access private |
*/ |
var $_index = null; |
/** |
* @var string the group-by clause |
* @access private |
*/ |
var $_group = ''; |
/** |
* @var array the limit |
* @access private |
*/ |
var $_limit = array(); |
/** |
* @var string type of result to return |
* @access private |
*/ |
var $_resultType = 'none'; |
/** |
* @var array the metadata temporary saved |
* @access private |
*/ |
var $_metadata = array(); |
/** |
* @var string |
* @access private |
*/ |
var $_lastQuery = null; |
/** |
* @var string the rows that shall be selected |
* @access private |
*/ |
var $_select = '*'; |
/** |
* @var string the rows that shall not be selected |
* @access private |
*/ |
var $_dontSelect = ''; |
/** |
* @var array this array saves different modes in which this class works |
* i.e. 'raw' means no quoting before saving/updating data |
* @access private |
*/ |
var $options = array( |
'raw' => false, |
'verbose' => true, // set this to false in a productive environment |
// it will produce error-logs if set to true |
'useCache' => false, |
'logFile' => false, |
); |
/** |
* this array contains information about the tables |
* those are |
* - 'name' => the real table name |
* - 'shortName' => the short name used, so that when moving the table i.e. |
* onto a provider's db and u have to rename the tables to |
* longer names this name will be relevant, i.e. when |
* autoJoining, i.e. a table name on your local machine is: |
* 'user' but online it has to be 'applName_user' then the |
* shortName will be used to determine if a column refers to |
* another table, if the colName is 'user_id', it knows the |
* shortName 'user' refers to the table 'applName_user' |
*/ |
var $tableSpec = array(); |
/** |
* this is the regular expression that shall be used to find a table's shortName |
* in a column name, the string found by using this regular expression will be removed |
* from the column name and it will be checked if it is a table name |
* i.e. the default '/_id$/' would find the table name 'user' from the column name 'user_id' |
*/ |
var $_tableNameToShortNamePreg = '/^.*_/'; |
/** |
* @var array this array caches queries that have already been built once |
* to reduce the execution time |
*/ |
var $_queryCache = array(); |
/** |
* The object that contains the log-instance |
*/ |
var $_logObject = null; |
/** |
* Some internal data the logging needs |
*/ |
var $_logData = array(); |
// }}} |
// {{{ __construct() |
/** |
* this is the constructor, as it will be implemented in ZE2 (php5) |
* |
* @version 2002/04/02 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param object db-object |
*/ |
/* |
function __construct($dsn=false, $options=array()) |
{ |
if (!isset($options['autoConnect'])) { |
$autoConnect = true; |
} else { |
$autoConnect = $options['autoConnect']; |
} |
if (isset($options['errorCallback'])) { |
$this->setErrorCallback($options['errorCallback']); |
} |
if (isset($options['errorSetCallback'])) { |
$this->setErrorSetCallback($options['errorSetCallback']); |
} |
if (isset($options['errorLogCallback'])) { |
$this->setErrorLogCallback($options['errorLogCallback']); |
} |
if ($autoConnect && $dsn) { |
$this->connect($dsn, $options); |
} |
//we would need to parse the dsn first ... i dont feel like now :-) |
// oracle has all column names in upper case |
//FIXXXME make the class work only with upper case when we work with oracle |
//if ($this->db->phptype=='oci8' && !$this->primaryCol) { |
// $this->primaryCol = 'ID'; |
//} |
if ($this->sequenceName == null) { |
$this->sequenceName = $this->table; |
} |
} |
*/ |
// }}} |
// {{{ DB_QueryTool_Query() |
/** |
* @version 2002/04/02 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param mixed $dsn DSN string, DSN array or DB object |
* @param array $options |
*/ |
function DB_QueryTool_Query($dsn=false, $options=array()) |
{ |
//$this->__construct($dsn, $options); |
if (!isset($options['autoConnect'])) { |
$autoConnect = true; |
} else { |
$autoConnect = $options['autoConnect']; |
unset($options['autoConnect']); |
} |
if (isset($options['errorCallback'])) { |
$this->setErrorCallback($options['errorCallback']); |
unset($options['errorCallback']); |
} |
if (isset($options['errorSetCallback'])) { |
$this->setErrorSetCallback($options['errorSetCallback']); |
unset($options['errorSetCallback']); |
} |
if (isset($options['errorLogCallback'])) { |
$this->setErrorLogCallback($options['errorLogCallback']); |
unset($options['errorLogCallback']); |
} |
if ($autoConnect && $dsn) { |
$this->connect($dsn, $options); |
} |
if (is_null($this->sequenceName)) { |
$this->sequenceName = $this->table; |
} |
} |
// }}} |
// {{{ connect() |
/** |
* use this method if you want to connect manually |
* @param mixed $dsn DSN string, DSN array or MDB object |
* @param array $options |
*/ |
function connect($dsn, $options=array()) |
{ |
if (is_object($dsn)) { |
$res = $this->db =& $dsn; |
} else { |
$res = $this->db = DB::connect($dsn, $options); |
} |
if (DB::isError($res)) { |
// FIXXME what shall we do here? |
$this->_errorLog($res->getUserInfo()); |
} else { |
$this->db->setFetchMode(DB_FETCHMODE_ASSOC); |
} |
} |
// }}} |
// {{{ getDbInstance() |
/** |
* @return reference to current DB instance |
*/ |
function &getDbInstance() |
{ |
return $this->db; |
} |
// }}} |
// {{{ setDbInstance() |
/** |
* Setup using an existing connection. |
* this also sets the DB_FETCHMODE_ASSOC since this class |
* needs this to be set! |
* |
* @param object a reference to an existing DB-object |
* @return void |
*/ |
function setDbInstance(&$dbh) |
{ |
$this->db =& $dbh; |
$this->db->setFetchMode(DB_FETCHMODE_ASSOC); |
} |
// }}} |
// {{{ get() |
/** |
* get the data of a single entry |
* if the second parameter is only one column the result will be returned |
* directly not as an array! |
* |
* @version 2002/03/05 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param integer the id of the element to retrieve |
* @param string if this is given only one row shall be returned, directly, not an array |
* @return mixed (1) an array of the retrieved data |
* (2) if the second parameter is given and its only one column, |
* only this column's data will be returned |
* (3) false in case of failure |
*/ |
function get($id, $column='') |
{ |
$table = $this->table; |
$getMethod = 'getRow'; |
if ($column && !strpos($column, ',')) { // if only one column shall be selected |
$getMethod = 'getOne'; |
} |
// we dont use 'setSelect' here, since this changes the setup of the class, we |
// build the query directly |
// if $column is '' then _buildSelect selects '*' anyway, so that's the same behaviour as before |
$query['select'] = $this->_buildSelect($column); |
$query['where'] = $this->_buildWhere($this->table.'.'.$this->primaryCol.'='.$id); |
$queryString = $this->_buildSelectQuery($query); |
return $this->returnResult($this->execute($queryString,$getMethod)); |
} |
// }}} |
// {{{ getMultiple() |
/** |
* gets the data of the given ids |
* |
* @version 2002/04/23 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param array this is an array of ids to retreive |
* @param string the column to search in for |
* @return mixed an array of the retreived data, or false in case of failure |
* when failing an error is set in $this->_error |
*/ |
function getMultiple($ids, $column='') |
{ |
$col = $this->primaryCol; |
if ($column) { |
$col = $column; |
} |
// FIXXME if $ids has no table.col syntax and we are using joins, the table better be put in front!!! |
$ids = $this->_quoteArray($ids); |
$query['where'] = $this->_buildWhere($col.' IN ('.implode(',', $ids).')'); |
$queryString = $this->_buildSelectQuery($query); |
return $this->returnResult($this->execute($queryString)); |
} |
// }}} |
// {{{ getAll() |
/** |
* get all entries from the DB |
* for sorting use setOrder!!!, the last 2 parameters are deprecated |
* |
* @version 2002/03/05 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param int to start from |
* @param int the number of rows to show |
* @param string the DB-method to use, i dont know if we should leave this param here ... |
* @return mixed an array of the retreived data, or false in case of failure |
* when failing an error is set in $this->_error |
*/ |
function getAll($from=0,$count=0,$method='getAll') |
{ |
$query = array(); |
if ($count) { |
$query = array('limit' => array($from, $count)); |
} |
return $this->returnResult($this->execute($this->_buildSelectQuery($query), $method)); |
} |
// }}} |
// {{{ getCol() |
/** |
* this method only returns one column, so the result will be a one dimensional array |
* this does also mean that using setSelect() should be set to *one* column, the one you want to |
* have returned a most common use case for this could be: |
* $table->setSelect('id'); |
* $ids = $table->getCol(); |
* OR |
* $ids = $table->getCol('id'); |
* so ids will be an array with all the id's |
* |
* @version 2003/02/25 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the column that shall be retreived |
* @param int to start from |
* @param int the number of rows to show |
* @return mixed an array of the retreived data, or false in case of failure |
* when failing an error is set in $this->_error |
*/ |
function getCol($column=null, $from=0, $count=0) |
{ |
$query = array(); |
if (!is_null($column)) { |
// by using _buildSelect() i can be sure that the table name will not be ambigious |
// i.e. in a join, where all the joined tables have a col 'id' |
// _buildSelect() will put the proper table name in front in case there is none |
$query['select'] = $this->_buildSelect($column); |
} |
if ($count) { |
$query['limit'] = array($from,$count); |
} |
return $this->returnResult($this->execute($this->_buildSelectQuery($query), 'getCol')); |
} |
// }}} |
// {{{ getCount() |
/** |
* get the number of entries |
* |
* @version 2002/04/02 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param |
* @return mixed an array of the retreived data, or false in case of failure |
* when failing an error is set in $this->_error |
*/ |
function getCount() |
{ |
/* the following query works on mysql |
SELECT count(DISTINCT image.id) FROM image2tree |
RIGHT JOIN image ON image.id = image2tree.image_id |
the reason why this is needed - i just wanted to get the number of rows that do exist if the result is grouped by image.id |
the following query is what i tried first, but that returns the number of rows that have been grouped together |
for each image.id |
SELECT count(*) FROM image2tree |
RIGHT JOIN image ON image.id = image2tree.image_id GROUP BY image.id |
so that's why we do the following, i am not sure if that is standard SQL and absolutley correct!!! |
*/ |
//FIXXME see comment above if this is absolutely correct!!! |
if ($group = $this->_buildGroup()) { |
$query['select'] = 'COUNT(DISTINCT '.$group.')'; |
$query['group'] = ''; |
} else { |
$query['select'] = 'COUNT(*)'; |
} |
$query['order'] = ''; // order is not of importance and might freak up the special group-handling up there, since the order-col is not be known |
/*# FIXXME use the following line, but watch out, then it has to be used in every method, or this |
# value will be used always, simply try calling getCount and getAll afterwards, getAll will return the count :-) |
# if getAll doesn't use setSelect!!! |
*/ |
//$this->setSelect('count(*)'); |
$queryString = $this->_buildSelectQuery($query, true); |
return ($res = $this->execute($queryString, 'getOne')) ? $res : 0; |
} |
// }}} |
// {{{ getDefaultValues() |
/** |
* return an empty element where all the array elements do already exist |
* corresponding to the columns in the DB |
* |
* @version 2002/04/05 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @return array an empty, or pre-initialized element |
*/ |
function getDefaultValues() |
{ |
$ret = array(); |
// here we read all the columns from the DB and initialize them |
// with '' to prevent PHP-warnings in case we use error_reporting=E_ALL |
foreach ($this->metadata() as $aCol=>$x) { |
$ret[$aCol] = ''; |
} |
return $ret; |
} |
// }}} |
// {{{ getEmptyElement() |
/** |
* this is just for BC |
* @deprecated |
*/ |
function getEmptyElement() |
{ |
$this->getDefaultValues(); |
} |
// }}} |
// {{{ getQueryString() |
/** |
* Render the current query and return it as a string. |
* |
* @return string the current query |
*/ |
function getQueryString() |
{ |
$ret = $this->_buildSelectQuery(); |
if (is_string($ret)) { |
$ret = trim($ret); |
} |
return $ret; |
} |
// }}} |
// {{{ save() |
/** |
* save data, calls either update or add |
* if the primaryCol is given in the data this method knows that the |
* data passed to it are meant to be updated (call 'update'), otherwise it will |
* call the method 'add'. |
* If you dont like this behaviour simply stick with the methods 'add' |
* and 'update' and ignore this one here. |
* This method is very useful when you have validation checks that have to |
* be done for both adding and updating, then you can simply overwrite this |
* method and do the checks in here, and both cases will be validated first. |
* |
* @version 2002/03/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param array contains the new data that shall be saved in the DB |
* @return mixed the data returned by either add or update-method |
*/ |
function save($data) |
{ |
if (!empty($data[$this->primaryCol])) { |
return $this->update($data); |
} |
return $this->add($data); |
} |
// }}} |
// {{{ update() |
/** |
* update the member data of a data set |
* |
* @version 2002/03/06 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param array contains the new data that shall be saved in the DB |
* the id has to be given in the field with the key 'ID' |
* @return mixed true on success, or false otherwise |
*/ |
function update($newData) |
{ |
$query = array(); |
// do only set the 'where' part in $query, if a primary column is given |
// if not the default 'where' clause is used |
if (isset($newData[$this->primaryCol])) { |
$query['where'] = $this->primaryCol.'='.$newData[$this->primaryCol]; |
} |
$newData = $this->_checkColumns($newData, 'update'); |
$values = array(); |
$raw = $this->getOption('raw'); |
foreach ($newData as $key => $aData) { // quote the data |
//$values[] = "{$this->table}.$key=". ($raw ? $aData : $this->db->quote($aData)); |
$values[] = "$key=". ($raw ? $aData : $this->db->quote($aData)); |
} |
$query['set'] = implode(',', $values); |
//FIXXXME _buildUpdateQuery() seems to take joins into account, whcih is bullshit here |
$updateString = $this->_buildUpdateQuery($query); |
#print '$updateString = '.$updateString; |
return $this->execute($updateString, 'query') ? true : false; |
} |
// }}} |
// {{{ add() |
/** |
* add a new member in the DB |
* |
* @version 2002/04/02 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param array contains the new data that shall be saved in the DB |
* @return mixed the inserted id on success, or false otherwise |
*/ |
function add($newData) |
{ |
// if no primary col is given, get next sequence value |
if (empty($newData[$this->primaryCol])) { |
if ($this->primaryCol) { // do only use the sequence if a primary column is given |
// otherwise the data are written as given |
$id = $this->db->nextId($this->sequenceName); |
$newData[$this->primaryCol] = (int)$id; |
} else { |
// if no primary col is given return true on success |
$id = true; |
} |
} else { |
$id = $newData[$this->primaryCol]; |
} |
//unset($newData[$this->primaryCol]); |
$newData = $this->_checkColumns($newData, 'add'); |
$newData = $this->_quoteArray($newData); |
$query = sprintf( 'INSERT INTO %s (%s) VALUES (%s)', |
$this->table, |
implode(', ', array_keys($newData)), |
implode(', ', $newData) |
); |
return $this->execute($query, 'query') ? $id : false; |
} |
// }}} |
// {{{ addMultiple() |
/** |
* adds multiple new members in the DB |
* |
* @version 2002/07/17 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param array contains an array of new data that shall be saved in the DB |
* the key-value pairs have to be the same for all the data!!! |
* @return mixed the inserted ids on success, or false otherwise |
*/ |
function addMultiple($data) |
{ |
if (!sizeof($data)) { |
return false; |
} |
// the inserted ids which will be returned or if no primaryCol is given |
// we return true by default |
$retIds = $this->primaryCol ? array() : true; |
$allData = array(); // each row that will be inserted |
foreach ($data as $key => $aData) { |
$aData = $this->_checkColumns($aData, 'add'); |
$aData = $this->_quoteArray($aData); |
if (empty($aData[$this->primaryCol])) { |
if ($this->primaryCol) { // do only use the sequence if a primary column is given |
// otherwise the data are written as given |
$retIds[] = $id = (int)$this->db->nextId($this->sequenceName); |
$aData[$this->primaryCol] = $id; |
} |
} else { |
$retIds[] = $aData[$this->primaryCol]; |
} |
$allData[] = '('.implode(', ', $aData).')'; |
} |
$query = sprintf( 'INSERT INTO %s (%s) VALUES %s', |
$this->table, |
implode(', ', array_keys($aData)), // use the keys of the last element built |
implode(', ', $allData) |
); |
return $this->execute($query, 'query') ? $retIds : false; |
} |
// }}} |
// {{{ remove() |
/** |
* removes a member from the DB |
* |
* @version 2002/04/08 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param mixed integer/string - the value of the column that shall be removed |
* array - multiple columns that shall be matched, the second parameter will be ignored |
* @param string the column to match the data against, only if $data is not an array |
* @return boolean |
*/ |
function remove($data, $whereCol='') |
{ |
$raw = $this->getOption('raw'); |
if (is_array($data)) { |
//FIXXME check $data if it only contains columns that really exist in the table |
$wheres = array(); |
foreach ($data as $key => $val) { |
$wheres[] = $key.'='. ($raw ? $val : $this->db->quote($val)); |
} |
$whereClause = implode(' AND ',$wheres); |
} else { |
if (empty($whereCol)) { |
$whereCol = $this->primaryCol; |
} |
$whereClause = $whereCol.'='. ($raw ? $data : $this->db->quote($data)); |
} |
$query = sprintf( 'DELETE FROM %s WHERE %s', |
$this->table, |
$whereClause |
); |
return $this->execute($query, 'query') ? true : false; |
// i think this method should return the ID's that it removed, this way we could simply use the result |
// for further actions that depend on those id ... or? make stuff easier, see ignaz::imail::remove |
} |
// }}} |
// {{{ removeAll() |
/** |
* empty a table |
* |
* @version 2002/06/17 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @return |
*/ |
function removeAll() |
{ |
$query = 'DELETE FROM '.$this->table; |
return $this->execute($query, 'query') ? true : false; |
} |
// }}} |
// {{{ removeMultiple() |
/** |
* remove the datasets with the given ids |
* |
* @version 2002/04/24 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param array the ids to remove |
* @return |
*/ |
function removeMultiple($ids, $colName='') |
{ |
if (empty($colName)) { |
$colName = $this->primaryCol; |
} |
$ids = $this->_quoteArray($ids); |
$query = sprintf( 'DELETE FROM %s WHERE %s IN (%s)', |
$this->table, |
$colName, |
implode(',', $ids) |
); |
return $this->execute($query, 'query') ? true : false; |
} |
// }}} |
// {{{ removePrimary() |
/** |
* removes a member from the DB and calls the remove methods of the given objects |
* so all rows in another table that refer to this table are erased too |
* |
* @version 2002/04/08 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param integer the value of the primary key |
* @param string the column name of the tables with the foreign keys |
* @param object just for convinience, so nobody forgets to call this method |
* with at least one object as a parameter |
* @return boolean |
*/ |
function removePrimary($id, $colName, $atLeastOneObject) |
{ |
$argCounter = 2; // we have 2 parameters that need to be given at least |
// func_get_arg returns false and a warning if there are no more parameters, so |
// we suppress the warning and check for false |
while ($object = @func_get_arg($argCounter++)) { |
//FIXXXME let $object also simply be a table name |
if (!$object->remove($id, $colName)) { |
//FIXXXME do this better |
$this->_errorSet("Error removing '$colName=$id' from table {$object->table}."); |
return false; |
} |
} |
return ($this->remove($id) ? true : false); |
} |
// }}} |
// {{{ setLimit() |
/** |
* @param integer $from |
* @param integer $count |
*/ |
function setLimit($from=0, $count=0) |
{ |
if ($from==0 && $count==0) { |
$this->_limit = array(); |
} else { |
$this->_limit = array($from, $count); |
} |
} |
// }}} |
// {{{ getLimit() |
/** |
* @return array |
*/ |
function getLimit() |
{ |
return $this->_limit; |
} |
// }}} |
// {{{ setWhere() |
/** |
* sets the where condition which is used for the current instance |
* |
* @version 2002/04/16 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the where condition, this can be complete like 'X=7 AND Y=8' |
*/ |
function setWhere($whereCondition='') |
{ |
$this->_where = $whereCondition; |
//FIXXME parse the where condition and replace ambigious column names, such as "name='Deutschland'" with "country.name='Deutschland'" |
// then the users dont have to write that explicitly and can use the same name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix'); |
} |
// }}} |
// {{{ getWhere() |
/** |
* gets the where condition which is used for the current instance |
* |
* @version 2002/04/22 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @return string the where condition, this can be complete like 'X=7 AND Y=8' |
*/ |
function getWhere() |
{ |
return $this->_where; |
} |
// }}} |
// {{{ addWhere() |
/** |
* only adds a string to the where clause |
* |
* @version 2002/07/22 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the where clause to add to the existing one |
* @param string the condition for how to concatenate the new where clause |
* to the existing one |
*/ |
function addWhere($where, $condition='AND') |
{ |
if ($this->getWhere()) { |
$where = $this->getWhere().' '.$condition.' '.$where; |
} |
$this->setWhere($where); |
} |
// }}} |
// {{{ addWhereSearch() |
/** |
* add a where-like clause which works like a search for the given string |
* i.e. calling it like this: |
* $this->addWhereSearch('name', 'otto hans') |
* produces a where clause like this one |
* LOWER(name) LIKE "%otto%hans%" |
* so the search finds the given string |
* |
* @version 2002/08/14 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the column to search in for |
* @param string the string to search for |
*/ |
function addWhereSearch($column, $string, $condition='AND') |
{ |
// if the column doesn't contain a tablename use the current table name |
// in case it is a defined column to prevent ambiguous rows |
if (strpos($column, '.') === false) { |
$meta = $this->metadata(); |
if (isset($meta[$column])) { |
$column = $this->table.".$column"; |
} |
} |
$string = $this->db->quote('%'.str_replace(' ', '%', strtolower($string)).'%'); |
$this->addWhere("LOWER($column) LIKE $string", $condition); |
} |
// }}} |
// {{{ setOrder() |
/** |
* sets the order condition which is used for the current instance |
* |
* @version 2002/05/16 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the where condition, this can be complete like 'X=7 AND Y=8' |
* @param boolean sorting order (TRUE => ASC, FALSE => DESC) |
*/ |
function setOrder($orderCondition='', $desc=false) |
{ |
$this->_order = $orderCondition .($desc ? ' DESC' : ''); |
} |
// }}} |
// {{{ addOrder() |
/** |
* Add a order parameter to the query. |
* |
* @version 2003/05/28 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the where condition, this can be complete like 'X=7 AND Y=8' |
* @param boolean sorting order (TRUE => ASC, FALSE => DESC) |
*/ |
function addOrder($orderCondition='', $desc=false) |
{ |
$order = $orderCondition .($desc ? ' DESC' : ''); |
if ($this->_order) { |
$this->_order = $this->_order.','.$order; |
} else { |
$this->_order = $order; |
} |
} |
// }}} |
// {{{ getOrder() |
/** |
* gets the order condition which is used for the current instance |
* |
* @version 2002/05/16 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @return string the order condition, this can be complete like 'ID,TIMESTAMP DESC' |
*/ |
function getOrder() |
{ |
return $this->_order; |
} |
// }}} |
// {{{ setHaving() |
/** |
* sets the having definition |
* |
* @version 2003/06/05 |
* @access public |
* @author Johannes Schaefer <johnschaefer@gmx.de> |
* @param string the having definition |
*/ |
function setHaving($having='') |
{ |
$this->_having = $having; |
} |
// }}} |
// {{{ getHaving() |
/** |
* gets the having definition which is used for the current instance |
* |
* @version 2003/06/05 |
* @access public |
* @author Johannes Schaefer <johnschaefer@gmx.de> |
* @return string the having definition |
*/ |
function getHaving() |
{ |
return $this->_having; |
} |
// }}} |
// {{{ addHaving() |
/** |
* Extend the current having clause. This is very useful, when you are building |
* this clause from different places and don't want to overwrite the currently |
* set having clause, but extend it. |
* |
* @param string this is a having clause, i.e. 'column' or 'table.column' or 'MAX(column)' |
* @param string the connection string, which usually stays the default, which is ',' (a comma) |
*/ |
function addHaving($what='*', $connectString=' AND ') |
{ |
if ($this->_having) { |
$this->_having = $this->_having.$connectString.$what; |
} else { |
$this->_having = $what; |
} |
} |
// }}} |
// {{{ setJoin() |
/** |
* sets a join-condition |
* |
* @version 2002/06/10 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param mixed either a string or an array that contains |
* the table(s) to join on the current table |
* @param string the where clause for the join |
*/ |
function setJoin($table=null, $where=null, $joinType='default') |
{ |
//FIXXME make it possible to pass a table name as a string like this too 'user u' |
// where u is the string that can be used to refer to this table in a where/order |
// or whatever condition |
// this way it will be possible to join tables with itself, like setJoin(array('user u','user u1')) |
// this wouldnt work yet, but for doing so we would need to change the _build methods too!!! |
// because they use getJoin('tables') and this simply returns all the tables in use |
// but don't take care of the mentioned syntax |
if (is_null($table) || is_null($where)) { // remove the join if not sufficient parameters are given |
$this->_join[$joinType] = array(); |
return; |
} |
/* this causes problems if we use the order-by, since it doenst know the name to order it by ... :-) |
// replace the table names with the internal name used for the join |
// this way we can also join one table multiple times if it will be implemented one day |
$this->_join[$table] = preg_replace('/'.$table.'/','j1',$where); |
*/ |
$this->_join[$joinType][$table] = $where; |
} |
// }}} |
// {{{ setJoin() |
/** |
* if you do a left join on $this->table you will get all entries |
* from $this->table, also if there are no entries for them in the joined table |
* if both parameters are not given the left-join will be removed |
* NOTE: be sure to only use either a right or a left join |
* |
* @version 2002/07/22 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the table(s) to be left-joined |
* @param string the where clause for the join |
*/ |
function setLeftJoin($table=null, $where=null) |
{ |
$this->setJoin($table, $where, 'left'); |
} |
// }}} |
// {{{ addLeftJoin() |
/** |
* @param string the table to be left-joined |
* @param string the where clause for the join |
* @param string the join type |
*/ |
function addLeftJoin($table, $where, $type='left') |
{ |
// init value, to prevent E_ALL-warning |
if (!isset($this->_join[$type]) || !$this->_join[$type]) { |
$this->_join[$type] = array(); |
} |
$this->_join[$type][$table] = $where; |
} |
// }}} |
// {{{ setRightJoin() |
/** |
* see setLeftJoin for further explaination on what a left/right join is |
* NOTE: be sure to only use either a right or a left join |
//FIXXME check if the above sentence is necessary and if sql doesnt allow the use of both |
* |
* @see setLeftJoin() |
* @version 2002/09/04 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the table(s) to be right-joined |
* @param string the where clause for the join |
*/ |
function setRightJoin($table=null, $where=null) |
{ |
$this->setJoin($table, $where, 'right'); |
} |
// }}} |
// {{{ getJoin() |
/** |
* gets the join-condition |
* |
* @access public |
* @param string [null|''|'table'|'tables'|'right'|'left'] |
* @return array gets the join parameters |
*/ |
function getJoin($what=null) |
{ |
// if the user requests all the join data or if the join is empty, return it |
if (is_null($what) || empty($this->_join)) { |
return $this->_join; |
} |
$ret = array(); |
switch (strtolower($what)) { |
case 'table': |
case 'tables': |
foreach ($this->_join as $aJoin) { |
if (count($aJoin)) { |
$ret = array_merge($ret, array_keys($aJoin)); |
} |
} |
break; |
case 'right': // return right-join data only |
case 'left': // return left join data only |
if (count($this->_join[$what])) { |
$ret = array_merge($ret, $this->_join[$what]); |
} |
break; |
} |
return $ret; |
} |
// }}} |
// {{{ addJoin() |
/** |
* adds a table and a where clause that shall be used for the join |
* instead of calling |
* setJoin(array(table1,table2),'<where clause1> AND <where clause2>') |
* you can also call |
* setJoin(table1,'<where clause1>') |
* addJoin(table2,'<where clause2>') |
* or where it makes more sense is to build a query which is made out of a |
* left join and a standard join |
* setLeftJoin(table1,'<where clause1>') |
* // results in ... FROM $this->table LEFT JOIN table ON <where clause1> |
* addJoin(table2,'<where clause2>') |
* // results in ... FROM $this->table,table2 LEFT JOIN table ON <where clause1> WHERE <where clause2> |
* |
* @access public |
* @param string the table to be joined |
* @param string the where clause for the join |
* @param string the join type |
*/ |
function addJoin($table, $where, $type='default') |
{ |
if ($table == $this->table) { |
return; //skip. Self joins are not supported. |
} |
// init value, to prevent E_ALL-warning |
if (!isset($this->_join[$type]) || !$this->_join[$type]) { |
$this->_join[$type] = array(); |
} |
$this->_join[$type][$table] = $where; |
} |
// }}} |
// {{{ setTable() |
/** |
* sets the table this class is currently working on |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the table name |
*/ |
function setTable($table) |
{ |
$this->table = $table; |
} |
// }}} |
// {{{ getTable() |
/** |
* gets the table this class is currently working on |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @return string the table name |
*/ |
function getTable() |
{ |
return $this->table; |
} |
// }}} |
// {{{ setGroup() |
/** |
* sets the group-by condition |
* |
* @version 2002/07/22 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the group condition |
*/ |
function setGroup($group='') |
{ |
$this->_group = $group; |
//FIXXME parse the condition and replace ambigious column names, such as "name='Deutschland'" with "country.name='Deutschland'" |
// then the users dont have to write that explicitly and can use the same name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix'); |
} |
// }}} |
// {{{ getGroup() |
/** |
* gets the group condition which is used for the current instance |
* |
* @version 2002/07/22 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @return string the group condition |
*/ |
function getGroup() |
{ |
return $this->_group; |
} |
// }}} |
// {{{ setSelect() |
/** |
* limit the result to return only the columns given in $what |
* @param string fields that shall be selected |
*/ |
function setSelect($what='*') |
{ |
$this->_select = $what; |
} |
// }}} |
// {{{ addSelect() |
/** |
* add a string to the select part of the query |
* |
* add a string to the select-part of the query and connects it to an existing |
* string using the $connectString, which by default is a comma. |
* (SELECT xxx FROM - xxx is the select-part of a query) |
* |
* @version 2003/01/08 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the string that shall be added to the select-part |
* @param string the string to connect the new string with the existing one |
* @return void |
*/ |
function addSelect($what='*', $connectString=',') |
{ |
// if the select string is not empty add the string, otherwise simply set it |
if ($this->_select) { |
$this->_select = $this->_select.$connectString.$what; |
} else { |
$this->_select = $what; |
} |
} |
// }}} |
// {{{ getSelect() |
/** |
* @return string |
*/ |
function getSelect() |
{ |
return $this->_select; |
} |
// }}} |
// {{{ setDontSelect() |
/** |
* @param string |
*/ |
function setDontSelect($what='') |
{ |
$this->_dontSelect = $what; |
} |
// }}} |
// {{{ getDontSelect() |
/** |
* @return string |
*/ |
function getDontSelect() |
{ |
return $this->_dontSelect; |
} |
// }}} |
// {{{ reset() |
/** |
* reset all the set* settings; with no parameter given, it resets them all |
* |
* @version 2002/09/16 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @return void |
*/ |
function reset($what=array()) |
{ |
if (!sizeof($what)) { |
$what = array( |
'select', |
'dontSelect', |
'group', |
'having', |
'limit', |
'where', |
'index', |
'order', |
'join', |
'leftJoin', |
'rightJoin' |
); |
} |
foreach ($what as $aReset) { |
$this->{'set'.ucfirst($aReset)}(); |
} |
} |
// }}} |
// {{{ setOption() |
/** |
* set mode the class shall work in |
* currently we have the modes: |
* 'raw' does not quote the data before building the query |
* |
* @version 2002/09/17 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the mode to be set |
* @param mixed the value of the mode |
* @return void |
*/ |
function setOption($option, $value) |
{ |
$this->options[strtolower($option)] = $value; |
} |
// }}} |
// {{{ getOption() |
/** |
* @return string |
*/ |
function getOption($option) |
{ |
return $this->options[strtolower($option)]; |
} |
// }}} |
// {{{ _quoteArray() |
/** |
* quotes all the data in this array if we are not in raw mode! |
* @param array |
*/ |
function _quoteArray($data) |
{ |
if (!$this->getOption('raw')) { |
foreach ($data as $key => $val) { |
$data[$key] = $this->db->quote($val); |
} |
} |
return $data; |
} |
// }}} |
// {{{ _checkColumns() |
/** |
* checks if the columns which are given as the array's indexes really exist |
* if not it will be unset anyway |
* |
* @version 2002/04/16 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the actual message, first word should always be the method name, |
* to build the message like this: className::methodname |
* @param integer the line number |
*/ |
function _checkColumns($newData, $method='unknown') |
{ |
if (!$meta = $this->metadata()) { // if no metadata available, return data as given |
return $newData; |
} |
foreach ($newData as $colName => $x) { |
if (!isset($meta[$colName])) { |
$this->_errorLog("$method, column {$this->table}.$colName doesnt exist, value was removed before '$method'",__LINE__); |
unset($newData[$colName]); |
} else { |
// if the current column exists, check the length too, not to write content that is too long |
// prevent DB-errors here |
// do only check the data length if this field is given |
if (isset($meta[$colName]['len']) && ($meta[$colName]['len'] != -1) && |
($oldLength=strlen($newData[$colName])) > $meta[$colName]['len'] |
) { |
$this->_errorLog("_checkColumns, had to trim column '$colName' from $oldLength to ". |
$meta[$colName]['DATA_LENGTH'].' characters.', __LINE__); |
$newData[$colName] = substr($newData[$colName], 0, $meta[$colName]['len']); |
} |
} |
} |
return $newData; |
} |
// }}} |
// {{{ debug() |
/** |
* overwrite this method and i.e. print the query $string |
* to see the final query |
* |
* @param string the query mostly |
*/ |
function debug($string){} |
// |
// |
// ONLY ORACLE SPECIFIC, not very nice since it is DB dependent, but we need it!!! |
// |
// |
// }}} |
// {{{ metadata() |
/** |
* !!!! query COPIED FROM db_oci8.inc - from PHPLIB !!!! |
* |
* @access public |
* @see |
* @version 2001/09 |
* @author PHPLIB |
* @param |
* @return |
*/ |
function metadata($table='') |
{ |
// is there an alias in the table name, then we have something like this: 'user ua' |
// cut of the alias and return the table name |
if (strpos($table, ' ') !== false) { |
$split = explode(' ', trim($table)); |
$table = $split[0]; |
} |
$full = false; |
if (empty($table)) { |
$table = $this->table; |
} |
// to prevent multiple selects for the same metadata |
if (isset($this->_metadata[$table])) { |
return $this->_metadata[$table]; |
} |
// FIXXXME use oci8 implementation of newer PEAR::DB-version |
if ($this->db->phptype == 'oci8') { |
$count = 0; |
$id = 0; |
$res = array(); |
//# This is a RIGHT OUTER JOIN: "(+)", if you want to see, what |
//# this query results try the following: |
//// $table = new Table; $this->db = new my_DB_Sql; // you have to make |
//// // your own class |
//// $table->show_results($this->db->query(see query vvvvvv)) |
//// |
$res = $this->db->getAll("SELECT T.column_name,T.table_name,T.data_type,". |
"T.data_length,T.data_precision,T.data_scale,T.nullable,". |
"T.char_col_decl_length,I.index_name". |
" FROM ALL_TAB_COLUMNS T,ALL_IND_COLUMNS I". |
" WHERE T.column_name=I.column_name (+)". |
" AND T.table_name=I.table_name (+)". |
" AND T.table_name=UPPER('$table') ORDER BY T.column_id"); |
if (DB::isError($res)) { |
//$this->_errorSet($res->getMessage()); |
// i think we only need to log here, since this method is never used |
// directly for the user's functionality, which means if it fails it |
// is most probably an appl error |
$this->_errorLog($res->getUserInfo()); |
return false; |
} |
foreach ($res as $key=>$val) { |
$res[$key]['name'] = $val['COLUMN_NAME']; |
} |
} else { |
if (!is_object($this->db)) { |
return false; |
} |
$res = $this->db->tableinfo($table); |
if (DB::isError($res)) { |
$this->_errorSet($res->getUserInfo()); |
return false; |
} |
} |
$ret = array(); |
foreach ($res as $key => $val) { |
$ret[$val['name']] = $val; |
} |
$this->_metadata[$table] = $ret; |
return $ret; |
} |
// |
// methods for building the query |
// |
// }}} |
// {{{ _buildFrom() |
/** |
* build the from string |
* |
* @access private |
* @return string the string added after FROM |
*/ |
function _buildFrom() |
{ |
$from = $this->table; |
$join = $this->getJoin(); |
if (!$join) { // no join set |
return $from; |
} |
// handle the standard join thingy |
if (isset($join['default']) && count($join['default'])) { |
$from .= ','.implode(',',array_keys($join['default'])); |
} |
// handle left/right joins |
foreach (array('left', 'right') as $joinType) { |
if (isset($join[$joinType]) && count($join[$joinType])) { |
foreach($join[$joinType] as $table => $condition) { |
// replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME |
// since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange |
// FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column' |
$regExp = '/_('.$table.')_([^\s]+)/'; |
$where = preg_replace($regExp, '$1.$2', $condition); |
// add the table name before any column that has no table prefix |
// since this might cause "unambiguous column" errors |
if ($meta = $this->metadata()) { |
foreach ($meta as $aCol=>$x) { |
// this covers the LIKE,IN stuff: 'name LIKE "%you%"' 'id IN (2,3,4,5)' |
$condition = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $condition); |
// replace also the column names which are behind a '=' |
// and do this also if the aCol is at the end of the where clause |
// that's what the $ is for |
$condition = preg_replace('/=\s*'.$aCol.'(\s|$)/', "={$this->table}.$aCol ", $condition); |
// replace if colName is first and possibly also if at the beginning of the where-string |
$condition = preg_replace('/(^\s*|\s+)'.$aCol.'\s*=/', "$1{$this->table}.$aCol=", $condition); |
} |
} |
$from .= ' '.strtoupper($joinType).' JOIN '.$table.' ON '.$condition; |
} |
} |
} |
return $from; |
} |
// }}} |
// {{{ getTableShortName() |
/** |
* this method gets the short name for a table |
* |
* get the short name for a table, this is needed to properly build the |
* 'AS' parts in the select query |
* @param string the real table name |
* @return string the table's short name |
*/ |
function getTableShortName($table) |
{ |
$tableSpec = $this->getTableSpec(false); |
if (isset($tableSpec[$table]['shortName']) && $tableSpec[$table]['shortName']) { |
//print "$table ... ".$tableSpec[$table]['shortName'].'<br>'; |
return $tableSpec[$table]['shortName']; |
} |
$possibleTableShortName = preg_replace($this->_tableNameToShortNamePreg, '', $table); |
//print "$table ... $possibleTableShortName<br>"; |
return $possibleTableShortName; |
} |
// }}} |
// {{{ getTableSpec() |
/** |
* gets the tableSpec either indexed by the short name or the name |
* returns the array for the tables given as parameter or if no |
* parameter given for all tables that exist in the tableSpec |
* |
* @param array table names (not the short names!) |
* @param boolean if true the table is returned indexed by the shortName |
* otherwise indexed by the name |
* @return array the tableSpec indexed |
*/ |
function getTableSpec($shortNameIndexed=true, $tables=array()) |
{ |
$newSpec = array(); |
foreach ($this->tableSpec as $aSpec) { |
if (sizeof($tables)==0 || in_array($aSpec['name'],$tables)) { |
if ($shortNameIndexed) { |
$newSpec[$aSpec['shortName']] = $aSpec; |
} else { |
$newSpec[$aSpec['name']] = $aSpec; |
} |
} |
} |
return $newSpec; |
} |
// }}} |
// {{{ _buildSelect() |
/** |
* build the 'SELECT <what> FROM ... 'for a select |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string if given use this string |
* @return string the what-clause |
*/ |
function _buildSelect($what=null) |
{ |
// what has preference, that means if what is set it is used |
// this is only because the methods like 'get' pass an individually built value, which |
// is supposed to be used, but usually it's generically build using the 'getSelect' values |
if (empty($what) && $this->getSelect()) { |
$what = $this->getSelect(); |
} |
// |
// replace all the '*' by the real column names, and take care of the dontSelect-columns! |
// |
$dontSelect = $this->getDontSelect(); |
$dontSelect = $dontSelect ? explode(',', $dontSelect) : array(); // make sure dontSelect is an array |
// here we will replace all the '*' and 'table.*' by all the columns that this table |
// contains. we do this so we can easily apply the 'dontSelect' values. |
// and so we can also handle queries like: 'SELECT *,count() FROM ' and 'SELECT table.*,x FROM ' too |
if (strpos($what, '*') !== false) { |
// subpattern 1 get all the table names, that are written like this: 'table.*' including '*' |
// for '*' the tablename will be '' |
preg_match_all('/([^,]*)(\.)?\*\s*(,|$)/U', $what, $res); |
//print "$what ... ";print_r($res);print "<br>"; |
$selectAllFromTables = array_unique($res[1]); // make the table names unique, so we do it all just once for each table |
$tables = array(); |
if (in_array('', $selectAllFromTables)) { // was there a '*' ? |
// get all the tables that we need to process, depending on if joined or not |
$tables = $this->getJoin() ? |
array_merge($this->getJoin('tables'), array($this->table)) : // get the joined tables and this->table |
array($this->table); // create an array with only this->table |
} else { |
$tables = $selectAllFromTables; |
} |
$cols = array(); |
foreach ($tables as $aTable) { // go thru all the tables and get all columns for each, and handle 'dontSelect' |
if ($meta = $this->metadata($aTable)) { |
foreach ($meta as $colName => $x) { |
// handle the dontSelect's |
if (in_array($colName, $dontSelect) || in_array("$aTable.$colName", $dontSelect)) { |
continue; |
} |
// build the AS clauses |
// put " around them to enable use of reserved words, i.e. SELECT table.option as option FROM... |
// and 'option' actually is a reserved word, at least in mysql |
// put double quotes around them, since pgsql doesnt work with single quotes |
// but don't do this for ibase because it doesn't work! |
if ($aTable == $this->table) { |
if ($this->db->phptype == 'ibase') { |
$cols[$aTable][] = $this->table. '.' .$colName . ' AS '. $colName; |
} else { |
$cols[$aTable][] = $this->table. '.' .$colName . ' AS "'. $colName .'"'; |
} |
} else { |
$cols[$aTable][] = "$aTable.$colName AS \"_".$this->getTableShortName($aTable)."_$colName\""; |
} |
} |
} |
} |
// put the extracted select back in the $what |
// that means replace 'table.*' by the i.e. 'table.id AS _table_id' |
// or if it is the table of this class replace 'table.id AS id' |
if (in_array('', $selectAllFromTables)) { |
$allCols = array(); |
foreach ($cols as $aTable) { |
$allCols[] = implode(',', $aTable); |
} |
$what = preg_replace('/(^|,)\*($|,)/', '$1'.implode(',',$allCols).'$2', $what); |
// remove all the 'table.*' since we have selected all anyway (because there was a '*' in the select) |
$what = preg_replace('/[^,]*(\.)?\*\s*(,|$)/U', '', $what); |
} else { |
foreach ($cols as $tableName => $aTable) { |
if (is_array($aTable) && sizeof($aTable)) { |
// replace all the 'table.*' by their select of each column |
$what = preg_replace('/(^|,)\s*'.$tableName.'\.\*\s*($|,)/', '$1'.implode(',',$aTable).'$2', $what); |
} |
} |
} |
} |
if ($this->getJoin()) { |
// replace all 'column' by '$this->table.column' to prevent ambigious errors |
$metadata = $this->metadata(); |
if (is_array($metadata)) { |
foreach ($metadata as $aCol => $x) { |
// handle ',id as xid,MAX(id),id' etc. |
// FIXXME do this better!!! |
$what = preg_replace( "/(^|,|\()(\s*)$aCol(\)|\s|,|as|$)/i", |
// $2 is actually just to keep the spaces, is not really |
// necessary, but this way the test works independent of this functionality here |
"$1$2{$this->table}.$aCol$3", |
$what); |
} |
} |
// replace all 'joinedTable.columnName' by '_joinedTable_columnName' |
// this actually only has an effect if there was no 'table.*' for 'table' |
// if that was there, then it has already been done before |
foreach ($this->getJoin('tables') as $aTable) { |
if ($meta = $this->metadata($aTable)) { |
foreach ($meta as $aCol=>$x) { |
// dont put the 'AS' behind it if there is already one |
if (preg_match("/$aTable.$aCol\s*as/i",$what)) { |
continue; |
} |
// this covers a ' table.colName ' surrounded by spaces, and replaces it by ' table.colName AS _table_colName' |
$what = preg_replace('/\s'.$aTable.'.'.$aCol.'\s/', " $aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol ", $what); |
// replace also the column names which are behind a ',' |
// and do this also if the aCol is at the end that's what the $ is for |
$what = preg_replace('/,\s*'.$aTable.'.'.$aCol.'(,|\s|$)/', ",$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol$1", $what); |
// replace if colName is first and possibly also if at the beginning of the where-string |
$what = preg_replace('/(^\s*|\s+)'.$aTable.'.'.$aCol.'\s*,/', "$1$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol,", $what); |
} |
} |
} |
} |
return $what; |
} |
// }}} |
// {{{ _buildWhere() |
/** |
* Build WHERE clause |
* |
* @param string $where WHERE clause |
* @return string $where WHERE clause after processing |
* @access private |
*/ |
function _buildWhere($where='') |
{ |
$where = trim($where); |
$originalWhere = $this->getWhere(); |
if ($originalWhere) { |
if (!empty($where)) { |
$where = $originalWhere.' AND '.$where; |
} else { |
$where = $originalWhere; |
} |
} |
$where = trim($where); |
if ($join = $this->getJoin()) { // is join set? |
// only those where conditions in the default-join have to be added here |
// left-join conditions are added behind 'ON', the '_buildJoin()' does that |
if (isset($join['default']) && count($join['default'])) { |
// we have to add this join-where clause here |
// since at least in mysql a query like: select * from tableX JOIN tableY ON ... |
// doesnt work, may be that's even SQL-standard... |
if (!empty($where)) { |
$where = implode(' AND ', $join['default']).' AND '.$where; |
} else { |
$where = implode(' AND ', $join['default']); |
} |
} |
// replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME |
// since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange |
// FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column' |
$regExp = '/_('.implode('|', $this->getJoin('tables')).')_([^\s]+)/'; |
$where = preg_replace($regExp, '$1.$2', $where); |
// add the table name before any column that has no table prefix |
// since this might cause "unambigious column" errors |
if ($meta = $this->metadata()) { |
foreach ($meta as $aCol => $x) { |
// this covers the LIKE,IN stuff: 'name LIKE "%you%"' 'id IN (2,3,4,5)' |
$where = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $where); |
// replace also the column names which are behind a '=' |
// and do this also if the aCol is at the end of the where clause |
// that's what the $ is for |
$where = preg_replace('/([=<>])\s*'.$aCol.'(\s|$)/', "$1{$this->table}.$aCol ", $where); |
// replace if colName is first and possibly also if at the beginning of the where-string |
$where = preg_replace('/(^\s*|\s+)'.$aCol.'\s*([=<>])/', "$1{$this->table}.$aCol$2", $where); |
} |
} |
} |
return $where; |
} |
// }}} |
// {{{ _buildOrder() |
/** |
* |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param |
* @return |
*/ |
function _buildOrder() |
{ |
$order = $this->getOrder(); |
// replace 'column' by '$this->table.column' if the column is defined for $this->table |
if ($meta = $this->metadata()) { |
foreach ($meta as $aCol=>$x) { |
$order = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U', "$1{$this->table}.$aCol$2", $order); |
} |
} |
return $order; |
} |
// }}} |
// {{{ _buildGroup() |
/** |
* Build the group-clause, replace 'column' by 'table.column'. |
* |
* @access public |
* @param void |
* @return string the rendered group clause |
*/ |
function _buildGroup() |
{ |
$group = $this->getGroup(); |
// replace 'column' by '$this->table.column' if the column is defined for $this->table |
if ($meta = $this->metadata()) { |
foreach ($meta as $aCol => $x) { |
$group = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U', "$1{$this->table}.$aCol$2", $group); |
} |
} |
return $group; |
} |
// }}} |
// {{{ _buildHaving() |
/** |
* |
* @version 2003/06/05 |
* @access public |
* @author Johannes Schaefer <johnschaefer@gmx.de> |
* @param |
* @return string the having clause |
*/ |
function _buildHaving() |
{ |
$having = $this->getHaving(); |
// replace 'column' by '$this->table.column' if the column is defined for $this->table |
if ($meta = $this->metadata()) { |
foreach ($meta as $aCol => $x) { |
$having = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U',"$1{$this->table}.$aCol$2",$having); |
} |
} |
return $having; |
} |
// }}} |
// {{{ _buildHaving() |
/** |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param array this array contains the elements of the query, |
* indexed by their key, which are: 'select','from','where', etc. |
* @param boolean whether this method is called via getCount() or not. |
* @return |
*/ |
function _buildSelectQuery($query=array(), $isCalledViaGetCount = false) |
{ |
/*FIXXXME finish this |
$cacheKey = md5(serialize(????)); |
if (isset($this->_queryCache[$cacheKey])) { |
$this->_errorLog('using cached query',__LINE__); |
return $this->_queryCache[$cacheKey]; |
} |
*/ |
$where = isset($query['where']) ? $query['where'] : $this->_buildWhere(); |
if ($where) { |
$where = 'WHERE '.$where; |
} |
$order = isset($query['order']) ? $query['order'] : $this->_buildOrder(); |
if ($order) { |
$order = 'ORDER BY '.$order; |
} |
$group = isset($query['group']) ? $query['group'] : $this->_buildGroup(); |
if ($group) { |
$group = 'GROUP BY '.$group; |
} |
$having = isset($query['having']) ? $query['having'] : $this->_buildHaving(); |
if ($having) { |
$having = 'HAVING '.$having; |
} |
$queryString = sprintf( 'SELECT %s FROM %s %s %s %s %s', |
isset($query['select']) ? $query['select'] : $this->_buildSelect(), |
isset($query['from']) ? $query['from'] : $this->_buildFrom(), |
$where, |
$group, |
$having, |
$order |
); |
// $query['limit'] has preference! |
$limit = isset($query['limit']) ? $query['limit'] : $this->_limit; |
if (!$isCalledViaGetCount && @$limit[1]) { // is there a count set? |
$queryString=$this->db->modifyLimitQuery($queryString,$limit[0],$limit[1]); |
if (DB::isError($queryString)) { |
$this->_errorSet('DB_QueryTool::db::modifyLimitQuery failed '.$queryString->getMessage()); |
$this->_errorLog($queryString->getUserInfo()); |
return false; |
} |
} |
// $this->_queryCache[$cacheKey] = $queryString; |
return $queryString; |
} |
// }}} |
// {{{ _buildUpdateQuery() |
/** |
* this simply builds an update query. |
* |
* @param array the parameter array might contain the following indexes |
* 'where' the where clause to be added, i.e. |
* UPDATE table SET x=1 WHERE y=0 |
* here the 'where' part simply would be 'y=0' |
* 'set' the actual data to be updated |
* in the example above, that would be 'x=1' |
* @return string the resulting query |
*/ |
function _buildUpdateQuery($query=array()) |
{ |
$where = isset($query['where']) ? $query['where'] : $this->_buildWhere(); |
if ($where) { |
$where = 'WHERE '.$where; |
} |
$updateString = sprintf('UPDATE %s SET %s %s', |
$this->table, |
$query['set'], |
$where |
); |
return $updateString; |
} |
// }}} |
// {{{ execute() |
/** |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param |
* @param string query method |
* @return boolean |
*/ |
function execute($query=null, $method='getAll') |
{ |
$this->writeLog(); |
if (is_null($query)) { |
$query = $this->_buildSelectQuery(); |
} |
$this->writeLog('query built: '.$query); |
// FIXXME on ORACLE this doesnt work, since we return joined columns as _TABLE_COLNAME and the _ in front |
// doesnt work on oracle, add a letter before it!!! |
$this->_lastQuery = $query; |
$this->debug($query); |
$this->writeLog('start query'); |
if (DB::isError($res = $this->db->$method($query))) { |
$this->writeLog('end query (failed)'); |
if ($this->getOption('verbose')) { |
$this->_errorSet($res->getMessage()); |
} else { |
$this->_errorLog($res->getMessage()); |
} |
$this->_errorLog($res->getUserInfo(), __LINE__); |
return false; |
} else { |
$this->writeLog('end query'); |
} |
$res = $this->_makeIndexed($res); |
return $res; |
} |
// }}} |
// {{{ writeLog() |
/** |
* Write events to the logfile. |
* |
* It does some additional work, like time measuring etc. to |
* see some additional info |
* |
*/ |
function writeLog($text='START') |
{ |
//its still really a quicky.... 'refactor' (nice word) that |
if (!isset($this->options['logfile'])) { |
return; |
} |
include_once 'Log.php'; |
if (!class_exists('Log')) { |
return; |
} |
if (!$this->_logObject) { |
$this->_logObject =& Log::factory('file', $this->options['logfile']); |
} |
if ($text==='start query' || $text==='end query') { |
$bytesSent = $this->db->getAll("SHOW STATUS like 'Bytes_sent'"); |
$bytesSent = $bytesSent[0]['Value']; |
} |
if ($text==='START') { |
$startTime = split(' ', microtime()); |
$this->_logData['startTime'] = $startTime[1] + $startTime[0]; |
} |
if ($text==='start query') { |
$this->_logData['startBytesSent'] = $bytesSent; |
$startTime = split(' ', microtime()); |
$this->_logData['startQueryTime'] = $startTime[1] + $startTime[0]; |
return; |
} |
if ($text==='end query') { |
$text .= ' result size: '.((int)$bytesSent-(int)$this->_logData['startBytesSent']).' bytes'; |
$endTime = split(' ', microtime()); |
$endTime = $endTime[1] + $endTime[0]; |
$text .= ', took: '.(($endTime - $this->_logData['startQueryTime'])).' seconds'; |
} |
if (strpos($text, 'query built')===0) { |
$endTime = split(' ', microtime()); |
$endTime = $endTime[1] + $endTime[0]; |
$this->writeLog('query building took: '.(($endTime - $this->_logData['startTime'])).' seconds'); |
} |
$this->_logObject->log($text); |
if (strpos($text, 'end query')===0) { |
$endTime = split(' ', microtime()); |
$endTime = $endTime[1] + $endTime[0]; |
$text = 'time over all: '.(($endTime - $this->_logData['startTime'])).' seconds'; |
$this->_logObject->log($text); |
} |
} |
// }}} |
// {{{ returnResult() |
/** |
* Return the chosen result type |
* |
* @version 2004/04/28 |
* @access public |
* @param object reference |
* @return mixed |
*/ |
function returnResult(&$result) |
{ |
if ($this->_resultType == 'none') { |
return $result; |
} |
if ($result === false) { |
return false; |
} |
//what about allowing other (custom) result types? |
switch (strtolower($this->_resultType)) { |
case 'object': return new DB_QueryTool_Result_Object($result); |
case 'array': |
default: return new DB_QueryTool_Result($result); |
} |
} |
// }}} |
// {{{ _makeIndexed() |
/** |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param mixed |
* @return mixed |
*/ |
function &_makeIndexed(&$data) |
{ |
// we can only return an indexed result if the result has a number of columns |
if (is_array($data) && sizeof($data) && $key = $this->getIndex()) { |
// build the string to evaluate which might be made up out of multiple indexes of a result-row |
$evalString = '$val[\''.implode('\'].\',\'.$val[\'',explode(',',$key)).'\']'; //" |
$indexedData = array(); |
//FIXXME actually we also need to check ONCE if $val is an array, so to say if $data is 2-dimensional |
foreach ($data as $val) { |
eval("\$keyValue = $evalString;"); // get the actual real (string-)key (string if multiple cols are used as index) |
$indexedData[$keyValue] = $val; |
} |
unset($data); |
return $indexedData; |
} |
return $data; |
} |
// }}} |
// {{{ setIndex() |
/** |
* format the result to be indexed by $key |
* NOTE: be careful, when using this you should be aware, that if you |
* use an index which's value appears multiple times you may loose data |
* since a key cant exist multiple times!! |
* the result for a result to be indexed by a key(=columnName) |
* (i.e. 'relationtoMe') which's values are 'brother' and 'sister' |
* or alike normally returns this: |
* $res['brother'] = array('name'=>'xxx') |
* $res['sister'] = array('name'=>'xxx') |
* but if the column 'relationtoMe' contains multiple entries for 'brother' |
* then the returned dataset will only contain one brother, since the |
* value from the column 'relationtoMe' is used |
* and which 'brother' you get depends on a lot of things, like the sortorder, |
* how the db saves the data, and whatever else |
* |
* you can also set indexes which depend on 2 columns, simply pass the parameters like |
* 'table1.id,table2.id' it will be used as a string for indexing the result |
* and the index will be built using the 2 values given, so a possible |
* index might be '1,2' or '2108,29389' this way you can access data which |
* have 2 primary keys. Be sure to remember that the index is a string! |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param |
* @return |
*/ |
function setIndex($key=null) |
{ |
if ($this->getJoin()) { // is join set? |
// replace TABLENAME.COLUMNNAME by _TABLENAME_COLUMNNAME |
// since this is only the result-keys can be used for indexing :-) |
$regExp = '/('.implode('|', $this->getJoin('tables')).')\.([^\s]+)/'; |
$key = preg_replace($regExp, '_$1_$2', $key); |
// remove the table name if it is in front of '<$this->table>.columnname' |
// since the key doesnt contain it neither |
if ($meta = $this->metadata()) { |
foreach ($meta as $aCol => $x) { |
$key = preg_replace('/'.$this->table.'\.'.$aCol.'/', $aCol, $key); |
} |
} |
} |
$this->_index = $key; |
} |
// }}} |
// {{{ getIndex() |
/** |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param |
* @return |
*/ |
function getIndex() |
{ |
return $this->_index; |
} |
// }}} |
// {{{ useResult() |
/** |
* Choose the type of the returned result |
* |
* @version 2004/04/28 |
* @access public |
* @param string $type ['array' | 'object' | 'none'] |
* For BC reasons, $type=true is equal to 'array', |
* $type=false is equal to 'none' |
*/ |
function useResult($type='array') |
{ |
if ($type === true) { |
$type = 'array'; |
} elseif ($type === false) { |
$type = 'none'; |
} |
switch (strtolower($type)) { |
case 'array': |
$this->_resultType = 'array'; |
require_once 'DB/QueryTool/Result.php'; |
break; |
case 'object': |
$this->_resultType = 'object'; |
require_once 'DB/QueryTool/Result/Object.php'; |
break; |
default: |
$this->_resultType = 'none'; |
} |
} |
// }}} |
// {{{ setErrorCallback() |
/** |
* set both callbacks |
* @param string |
*/ |
function setErrorCallback($param='') |
{ |
$this->setErrorLogCallback($param); |
$this->setErrorSetCallback($param); |
} |
// }}} |
// {{{ setErrorLogCallback() |
/** |
* @param string |
*/ |
function setErrorLogCallback($param='') |
{ |
$errorLogCallback = &PEAR::getStaticProperty('DB_QueryTool','_errorLogCallback'); |
$errorLogCallback = $param; |
} |
// }}} |
// {{{ setErrorSetCallback() |
/** |
* @param string |
*/ |
function setErrorSetCallback($param='') |
{ |
$errorSetCallback = &PEAR::getStaticProperty('DB_QueryTool','_errorSetCallback'); |
$errorSetCallback = $param; |
} |
// }}} |
// {{{ _errorLog() |
/** |
* sets error log and adds additional info |
* |
* @version 2002/04/16 |
* @access public |
* @author Wolfram Kriesing <wk@visionp.de> |
* @param string the actual message, first word should always be the method name, |
* to build the message like this: className::methodname |
* @param integer the line number |
*/ |
function _errorLog($msg, $line='unknown') |
{ |
$this->_errorHandler('log', $msg, $line); |
/* |
if ($this->getOption('verbose') == true) |
{ |
$this->_errorLog(get_class($this)."::$msg ($line)"); |
return; |
} |
if ($this->_errorLogCallback) |
call_user_func($this->_errorLogCallback, $msg); |
*/ |
} |
// }}} |
// {{{ _errorSet() |
/** |
* @param string |
* @param string |
*/ |
function _errorSet($msg, $line='unknown') |
{ |
$this->_errorHandler('set', $msg, $line); |
} |
// }}} |
// {{{ _errorHandler() |
/** |
* @param |
* @param string |
* @param string |
*/ |
function _errorHandler($logOrSet, $msg, $line='unknown') |
{ |
/* what did i do this for? |
if ($this->getOption('verbose') == true) |
{ |
$this->_errorHandler($logOrSet, get_class($this)."::$msg ($line)"); |
return; |
} |
*/ |
$msg = get_class($this)."::$msg ($line)"; |
$logOrSet = ucfirst($logOrSet); |
$callback = &PEAR::getStaticProperty('DB_QueryTool','_error'.$logOrSet.'Callback'); |
//var_dump($callback); |
//if ($callback) |
// call_user_func($callback, $msg); |
// else |
// ????? |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/QueryTool/Result/Object.php |
---|
New file |
0,0 → 1,89 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* Contains the DB_QueryTool_Result_Row and DB_QueryTool_Result_Object classes |
* |
* 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_QueryTool |
* @author Roman Dostovalov, Com-tec-so S.A.<roman.dostovalov@ctco.lv> |
* @copyright 2004-2005 Roman Dostovalov |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: Object.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
/** |
* Include parent class |
*/ |
require_once 'DB/QueryTool/Result.php'; |
/** |
* DB_QueryTool_Result_Row class |
* |
* @category Database |
* @package DB_QueryTool |
* @author Roman Dostovalov, Com-tec-so S.A.<roman.dostovalov@ctco.lv> |
* @copyright 2004-2005 Roman Dostovalov |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
class DB_QueryTool_Result_Row |
{ |
/** |
* create object properties from the array |
* @param array |
*/ |
function DB_QueryTool_Result_Row($arr) |
{ |
foreach ($arr as $key => $value) { |
$this->$key = $value; |
} |
} |
} |
// ----------------------------------------------------------------------------- |
/** |
* DB_QueryTool_Result_Object class |
* |
* @category Database |
* @package DB_QueryTool |
* @author Roman Dostovalov, Com-tec-so S.A.<roman.dostovalov@ctco.lv> |
* @copyright 2004-2005 Roman Dostovalov |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
class DB_QueryTool_Result_Object extends DB_QueryTool_Result |
{ |
// {{{ fetchRow |
/** |
* This function emulates PEAR::DB fetchRow() method |
* With this function DB_QueryTool can transparently replace PEAR::DB |
* |
* @todo implement fetchmode support? |
* @access public |
* @return void |
*/ |
function fetchRow() |
{ |
$arr = $this->getNext(); |
if (!PEAR::isError($arr)) { |
$row = new DB_QueryTool_Result_Row($arr); |
return $row; |
} |
return false; |
} |
// }}} |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/QueryTool/Result.php |
---|
New file |
0,0 → 1,261 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* Contains the DB_QueryTool_Result 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_QueryTool |
* @author Wolfram Kriesing <wk@visionp.de> |
* @author Paolo Panto <wk@visionp.de> |
* @author Lorenzo Alberton <l dot alberton at quipo dot it> |
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @version CVS: $Id: Result.php,v 1.1 2006-12-14 15:04:29 jp_milcent Exp $ |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
/** |
* DB_QueryTool_Result class |
* |
* This result actually contains the 'data' itself, the number of rows |
* returned and some additional info |
* using ZE2 you can also get retrieve data from the result doing the following: |
* <DB_QueryTool_Common-instance>->getAll()->getCount() |
* or |
* <DB_QueryTool_Common-instance>->getAll()->getData() |
* |
* @category Database |
* @package DB_QueryTool |
* @author Wolfram Kriesing <wk@visionp.de> |
* @author Paolo Panto <wk@visionp.de> |
* @author Lorenzo Alberton <l dot alberton at quipo dot it> |
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton |
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
* @link http://pear.php.net/package/DB_QueryTool |
*/ |
class DB_QueryTool_Result |
{ |
// {{{ class vars |
/** |
* @var array |
*/ |
var $_data = array(); |
/** |
* @var array |
*/ |
var $_dataKeys = array(); |
/** |
* @var integer |
*/ |
var $_count = 0; |
/** |
* the counter for the methods getFirst, getNext |
* @var array |
*/ |
var $_counter = null; |
// }}} |
// {{{ DB_QueryTool_Result() |
/** |
* create a new instance of result with the data returned by the query |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wolfram@kriesing.de> |
* @param array the data returned by the result |
*/ |
function DB_QueryTool_Result($data) |
{ |
if (!count($data)) { |
$this->_count = 0; |
} else { |
list($firstElement) = $data; |
if (is_array($firstElement)) { // is the array a collection of rows? |
$this->_count = sizeof($data); |
} else { |
if (sizeof($data) > 0) { |
$this->_count = 1; |
} else { |
$this->_count = 0; |
} |
} |
} |
$this->_data = $data; |
} |
// }}} |
// {{{ numRows |
/** |
* return the number of rows returned. This is an alias for getCount(). |
* |
* @access public |
* @return integer |
*/ |
function numRows() |
{ |
return $this->_count; |
} |
// }}} |
// {{{ getCount() |
/** |
* return the number of rows returned |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wolfram@kriesing.de> |
* @return integer the number of rows returned |
*/ |
function getCount() |
{ |
return $this->_count; |
} |
// }}} |
// {{{ getData() |
/** |
* get all the data returned |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wolfram@kriesing.de> |
* @param string $key |
* @return mixed array or PEAR_Error |
*/ |
function getData($key=null) |
{ |
if (is_null($key)) { |
return $this->_data; |
} |
if ($this->_data[$key]) { |
return $this->_data[$key]; |
} |
return new PEAR_Error("there is no element with the key '$key'!"); |
} |
// }}} |
// {{{ getFirst() |
/** |
* get the first result set |
* we are not using next, current, and reset, since those ignore keys |
* which are empty or 0 |
* |
* @version 2002/07/11 |
* @access public |
* @author Wolfram Kriesing <wolfram@kriesing.de> |
* @return mixed |
*/ |
function getFirst() |
{ |
if ($this->getCount() > 0) { |
$this->_dataKeys = array_keys($this->_data); |
$this->_counter = 0; |
return $this->_data[$this->_dataKeys[$this->_counter]]; |
} |
return new PEAR_Error('There are no elements!'); |
} |
// }}} |
// {{{ getNext() |
/** |
* Get next result set. If getFirst() has never been called before, |
* it calls that method. |
* @return mixed |
* @access public |
*/ |
function getNext() |
{ |
if (!$this->initDone()) { |
return $this->getFirst(); |
} |
if ($this->hasMore()) { |
$this->_counter++; |
return $this->_data[$this->_dataKeys[$this->_counter]]; |
} |
return new PEAR_Error('there are no more elements!'); |
} |
// }}} |
// {{{ hasMore() |
/** |
* check if there are other rows |
* |
* @return boolean |
* @access public |
*/ |
function hasMore() |
{ |
if ($this->_counter+1 < $this->getCount()) { |
return true; |
} |
return false; |
} |
// }}} |
// {{{ fetchRow |
/** |
* This function emulates PEAR::DB fetchRow() method. |
* With this method, DB_QueryTool can transparently replace PEAR_DB |
* |
* @todo implement fetchmode support? |
* @access public |
* @return void |
*/ |
function fetchRow() |
{ |
if ($this->hasMore()) { |
$arr = $this->getNext(); |
if (!PEAR::isError($arr)) { |
return $arr; |
} |
} |
return false; |
} |
// }}} |
// {{{ initDone |
/** |
* Helper method. Check if $this->_dataKeys has been initialized |
* |
* @return boolean |
* @access private |
*/ |
function initDone() |
{ |
return ( |
isset($this->_dataKeys) && |
is_array($this->_dataKeys) && |
count($this->_dataKeys) |
); |
} |
// }}} |
#TODO |
#function getPrevious() |
#function getLast() |
} |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/dbase.php |
---|
New file |
0,0 → 1,510 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's dbase extension |
* for interacting with dBase 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 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: dbase.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 dbase extension |
* for interacting with dBase databases |
* |
* These methods overload the ones declared in DB_common. |
* |
* @category Database |
* @package DB |
* @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: 1.7.6 |
* @link http://pear.php.net/package/DB |
*/ |
class DB_dbase extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'dbase'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'dbase'; |
/** |
* 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' => false, |
'new_link' => false, |
'numrows' => true, |
'pconnect' => false, |
'prepare' => false, |
'ssl' => false, |
'transactions' => false, |
); |
/** |
* 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(); |
/** |
* A means of emulating result resources |
* @var array |
*/ |
var $res_row = array(); |
/** |
* The quantity of results so far |
* |
* For emulating result resources. |
* |
* @var integer |
*/ |
var $result = 0; |
/** |
* Maps dbase data type id's to human readable strings |
* |
* The human readable values are based on the output of PHP's |
* dbase_get_header_info() function. |
* |
* @var array |
* @since Property available since Release 1.7.0 |
*/ |
var $types = array( |
'C' => 'character', |
'D' => 'date', |
'L' => 'boolean', |
'M' => 'memo', |
'N' => 'number', |
); |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_dbase() |
{ |
$this->DB_common(); |
} |
// }}} |
// {{{ connect() |
/** |
* Connect to the database and create it if it doesn't exist |
* |
* Don't call this method directly. Use DB::connect() instead. |
* |
* PEAR DB's dbase driver supports the following extra DSN options: |
* + mode An integer specifying the read/write mode to use |
* (0 = read only, 1 = write only, 2 = read/write). |
* Available since PEAR DB 1.7.0. |
* + fields An array of arrays that PHP's dbase_create() function needs |
* to create a new database. This information is used if the |
* dBase file specified in the "database" segment of the DSN |
* does not exist. For more info, see the PHP manual's |
* {@link http://php.net/dbase_create dbase_create()} page. |
* Available since PEAR DB 1.7.0. |
* |
* Example of how to connect and establish a new dBase file if necessary: |
* <code> |
* require_once 'DB.php'; |
* |
* $dsn = array( |
* 'phptype' => 'dbase', |
* 'database' => '/path/and/name/of/dbase/file', |
* 'mode' => 2, |
* 'fields' => array( |
* array('a', 'N', 5, 0), |
* array('b', 'C', 40), |
* array('c', 'C', 255), |
* array('d', 'C', 20), |
* ), |
* ); |
* $options = array( |
* 'debug' => 2, |
* '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. |
*/ |
function connect($dsn, $persistent = false) |
{ |
if (!PEAR::loadExtension('dbase')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
/* |
* Turn track_errors on for entire script since $php_errormsg |
* is the only way to find errors from the dbase extension. |
*/ |
ini_set('track_errors', 1); |
$php_errormsg = ''; |
if (!file_exists($dsn['database'])) { |
$this->dsn['mode'] = 2; |
if (empty($dsn['fields']) || !is_array($dsn['fields'])) { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
null, null, null, |
'the dbase file does not exist and ' |
. 'it could not be created because ' |
. 'the "fields" element of the DSN ' |
. 'is not properly set'); |
} |
$this->connection = @dbase_create($dsn['database'], |
$dsn['fields']); |
if (!$this->connection) { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
null, null, null, |
'the dbase file does not exist and ' |
. 'the attempt to create it failed: ' |
. $php_errormsg); |
} |
} else { |
if (!isset($this->dsn['mode'])) { |
$this->dsn['mode'] = 0; |
} |
$this->connection = @dbase_open($dsn['database'], |
$this->dsn['mode']); |
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 = @dbase_close($this->connection); |
$this->connection = null; |
return $ret; |
} |
// }}} |
// {{{ &query() |
function &query($query = null) |
{ |
// emulate result resources |
$this->res_row[(int)$this->result] = 0; |
$tmp =& new DB_result($this, $this->result++); |
return $tmp; |
} |
// }}} |
// {{{ 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) { |
$rownum = $this->res_row[(int)$result]++; |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
$arr = @dbase_get_record_with_names($this->connection, $rownum); |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @dbase_get_record($this->connection, $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); |
} |
return DB_OK; |
} |
// }}} |
// {{{ 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($foo) |
{ |
return @dbase_numfields($this->connection); |
} |
// }}} |
// {{{ 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($foo) |
{ |
return @dbase_numrecords($this->connection); |
} |
// }}} |
// {{{ 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 = <samp>T</samp> if true or |
* <samp>F</samp> if false. Use the <kbd>Logical</kbd> |
* data type. |
* + integer or double = the unquoted number |
* + other (including strings and numeric strings) = |
* the data with single quotes escaped by preceeding |
* single quotes then the whole string is 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 ? 'T' : 'F'; |
} elseif (is_null($in)) { |
return 'NULL'; |
} else { |
return "'" . $this->escapeSimple($in) . "'"; |
} |
} |
// }}} |
// {{{ tableInfo() |
/** |
* Returns information about the current database |
* |
* @param mixed $result THIS IS UNUSED IN DBASE. The current database |
* is examined regardless of what is provided here. |
* @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() |
* @since Method available since Release 1.7.0 |
*/ |
function tableInfo($result = null, $mode = null) |
{ |
if (function_exists('dbase_get_header_info')) { |
$id = @dbase_get_header_info($this->connection); |
if (!$id && $php_errormsg) { |
return $this->raiseError(DB_ERROR, |
null, null, null, |
$php_errormsg); |
} |
} else { |
/* |
* This segment for PHP 4 is loosely based on code by |
* Hadi Rusiah <deegos@yahoo.com> in the comments on |
* the dBase reference page in the PHP manual. |
*/ |
$db = @fopen($this->dsn['database'], 'r'); |
if (!$db) { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
null, null, null, |
$php_errormsg); |
} |
$id = array(); |
$i = 0; |
$line = fread($db, 32); |
while (!feof($db)) { |
$line = fread($db, 32); |
if (substr($line, 0, 1) == chr(13)) { |
break; |
} else { |
$pos = strpos(substr($line, 0, 10), chr(0)); |
$pos = ($pos == 0 ? 10 : $pos); |
$id[$i] = array( |
'name' => substr($line, 0, $pos), |
'type' => $this->types[substr($line, 11, 1)], |
'length' => ord(substr($line, 16, 1)), |
'precision' => ord(substr($line, 17, 1)), |
); |
} |
$i++; |
} |
fclose($db); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$res = array(); |
$count = count($id); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
$res[$i] = array( |
'table' => $this->dsn['database'], |
'name' => $case_func($id[$i]['name']), |
'type' => $id[$i]['type'], |
'len' => $id[$i]['length'], |
'flags' => '' |
); |
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; |
} |
} |
return $res; |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/mysqli.php |
---|
New file |
0,0 → 1,1076 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's mysqli 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 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: mysqli.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 mysqli extension |
* for interacting with MySQL databases |
* |
* This is for MySQL versions 4.1 and above. Requires PHP 5. |
* |
* Note that persistent connections no longer exist. |
* |
* These methods overload the ones declared in DB_common. |
* |
* @category Database |
* @package DB |
* @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: 1.7.6 |
* @link http://pear.php.net/package/DB |
* @since Class functional since Release 1.6.3 |
*/ |
class DB_mysqli extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'mysqli'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'mysqli'; |
/** |
* 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' => false, |
'numrows' => true, |
'pconnect' => false, |
'prepare' => false, |
'ssl' => true, |
'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 = ''; |
/** |
* Array for converting MYSQLI_*_FLAG constants to text values |
* @var array |
* @access public |
* @since Property available since Release 1.6.5 |
*/ |
var $mysqli_flags = array( |
MYSQLI_NOT_NULL_FLAG => 'not_null', |
MYSQLI_PRI_KEY_FLAG => 'primary_key', |
MYSQLI_UNIQUE_KEY_FLAG => 'unique_key', |
MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key', |
MYSQLI_BLOB_FLAG => 'blob', |
MYSQLI_UNSIGNED_FLAG => 'unsigned', |
MYSQLI_ZEROFILL_FLAG => 'zerofill', |
MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment', |
MYSQLI_TIMESTAMP_FLAG => 'timestamp', |
MYSQLI_SET_FLAG => 'set', |
// MYSQLI_NUM_FLAG => 'numeric', // unnecessary |
// MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie |
MYSQLI_GROUP_FLAG => 'group_by' |
); |
/** |
* Array for converting MYSQLI_TYPE_* constants to text values |
* @var array |
* @access public |
* @since Property available since Release 1.6.5 |
*/ |
var $mysqli_types = array( |
MYSQLI_TYPE_DECIMAL => 'decimal', |
MYSQLI_TYPE_TINY => 'tinyint', |
MYSQLI_TYPE_SHORT => 'int', |
MYSQLI_TYPE_LONG => 'int', |
MYSQLI_TYPE_FLOAT => 'float', |
MYSQLI_TYPE_DOUBLE => 'double', |
// MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it |
MYSQLI_TYPE_TIMESTAMP => 'timestamp', |
MYSQLI_TYPE_LONGLONG => 'bigint', |
MYSQLI_TYPE_INT24 => 'mediumint', |
MYSQLI_TYPE_DATE => 'date', |
MYSQLI_TYPE_TIME => 'time', |
MYSQLI_TYPE_DATETIME => 'datetime', |
MYSQLI_TYPE_YEAR => 'year', |
MYSQLI_TYPE_NEWDATE => 'date', |
MYSQLI_TYPE_ENUM => 'enum', |
MYSQLI_TYPE_SET => 'set', |
MYSQLI_TYPE_TINY_BLOB => 'tinyblob', |
MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob', |
MYSQLI_TYPE_LONG_BLOB => 'longblob', |
MYSQLI_TYPE_BLOB => 'blob', |
MYSQLI_TYPE_VAR_STRING => 'varchar', |
MYSQLI_TYPE_STRING => 'char', |
MYSQLI_TYPE_GEOMETRY => 'geometry', |
); |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_mysqli() |
{ |
$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 mysqli driver supports the following extra DSN options: |
* + When the 'ssl' $option passed to DB::connect() is true: |
* + key The path to the key file. |
* + cert The path to the certificate file. |
* + ca The path to the certificate authority file. |
* + capath The path to a directory that contains trusted SSL |
* CA certificates in pem format. |
* + cipher The list of allowable ciphers for SSL encryption. |
* |
* Example of how to connect using SSL: |
* <code> |
* require_once 'DB.php'; |
* |
* $dsn = array( |
* 'phptype' => 'mysqli', |
* 'username' => 'someuser', |
* 'password' => 'apasswd', |
* 'hostspec' => 'localhost', |
* 'database' => 'thedb', |
* 'key' => 'client-key.pem', |
* 'cert' => 'client-cert.pem', |
* 'ca' => 'cacert.pem', |
* 'capath' => '/path/to/ca/dir', |
* 'cipher' => 'AES', |
* ); |
* |
* $options = array( |
* 'ssl' => true, |
* ); |
* |
* $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. |
*/ |
function connect($dsn, $persistent = false) |
{ |
if (!PEAR::loadExtension('mysqli')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
$ini = ini_get('track_errors'); |
ini_set('track_errors', 1); |
$php_errormsg = ''; |
if ($this->getOption('ssl') === true) { |
$init = mysqli_init(); |
mysqli_ssl_set( |
$init, |
empty($dsn['key']) ? null : $dsn['key'], |
empty($dsn['cert']) ? null : $dsn['cert'], |
empty($dsn['ca']) ? null : $dsn['ca'], |
empty($dsn['capath']) ? null : $dsn['capath'], |
empty($dsn['cipher']) ? null : $dsn['cipher'] |
); |
if ($this->connection = @mysqli_real_connect( |
$init, |
$dsn['hostspec'], |
$dsn['username'], |
$dsn['password'], |
$dsn['database'], |
$dsn['port'], |
$dsn['socket'])) |
{ |
$this->connection = $init; |
} |
} else { |
$this->connection = @mysqli_connect( |
$dsn['hostspec'], |
$dsn['username'], |
$dsn['password'], |
$dsn['database'], |
$dsn['port'], |
$dsn['socket'] |
); |
} |
ini_set('track_errors', $ini); |
if (!$this->connection) { |
if (($err = @mysqli_connect_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']) { |
$this->_db = $dsn['database']; |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
$ret = @mysqli_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->_db) { |
if (!@mysqli_select_db($this->connection, $this->_db)) { |
return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); |
} |
} |
if (!$this->autocommit && $ismanip) { |
if ($this->transaction_opcount == 0) { |
$result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0'); |
$result = @mysqli_query($this->connection, 'BEGIN'); |
if (!$result) { |
return $this->mysqliRaiseError(); |
} |
} |
$this->transaction_opcount++; |
} |
$result = @mysqli_query($this->connection, $query); |
if (!$result) { |
return $this->mysqliRaiseError(); |
} |
if (is_object($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 resource $result a valid sql result resource |
* @return false |
* @access public |
*/ |
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 (!@mysqli_data_seek($result, $rownum)) { |
return null; |
} |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
$arr = @mysqli_fetch_array($result, MYSQLI_ASSOC); |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @mysqli_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 @mysqli_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 = @mysqli_num_fields($result); |
if (!$cols) { |
return $this->mysqliRaiseError(); |
} |
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 = @mysqli_num_rows($result); |
if ($rows === null) { |
return $this->mysqliRaiseError(); |
} |
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 (!@mysqli_select_db($this->connection, $this->_db)) { |
return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); |
} |
} |
$result = @mysqli_query($this->connection, 'COMMIT'); |
$result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->mysqliRaiseError(); |
} |
} |
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 (!@mysqli_select_db($this->connection, $this->_db)) { |
return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); |
} |
} |
$result = @mysqli_query($this->connection, 'ROLLBACK'); |
$result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->mysqliRaiseError(); |
} |
} |
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 @mysqli_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_mysqli::createSequence(), DB_mysqli::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 = @mysqli_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 |
// 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) { |
return $this->mysqliRaiseError(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); |
// Since createSequence initializes the ID to be 1, |
// we do not need to retrieve the ID again (or we will get 2) |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} else { |
// First ID of a newly created sequence is 1 |
return 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); |
} |
/** |
* 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_mysqli::nextID(), DB_mysqli::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 |
return $this->query("INSERT INTO ${seqname} (id) VALUES (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->mysqliRaiseError(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 . '`'; |
} |
// }}} |
// {{{ 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) |
{ |
return @mysqli_real_escape_string($this->connection, $str); |
} |
// }}} |
// {{{ 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"; |
} |
} |
// }}} |
// {{{ mysqliRaiseError() |
/** |
* 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_mysqli::errorNative(), DB_common::errorCode() |
*/ |
function mysqliRaiseError($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(mysqli_errno($this->connection)); |
} |
return $this->raiseError($errno, null, null, null, |
@mysqli_errno($this->connection) . ' ** ' . |
@mysqli_error($this->connection)); |
} |
// }}} |
// {{{ errorNative() |
/** |
* Gets the DBMS' native error code produced by the last query |
* |
* @return int the DBMS' error code |
*/ |
function errorNative() |
{ |
return @mysqli_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::setOption() |
*/ |
function tableInfo($result, $mode = null) |
{ |
if (is_string($result)) { |
/* |
* Probably received a table name. |
* Create a result resource identifier. |
*/ |
$id = @mysqli_query($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_a($id, 'mysqli_result')) { |
return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = @mysqli_num_fields($id); |
$res = array(); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
$tmp = @mysqli_fetch_field($id); |
$flags = ''; |
foreach ($this->mysqli_flags as $const => $means) { |
if ($tmp->flags & $const) { |
$flags .= $means . ' '; |
} |
} |
if ($tmp->def) { |
$flags .= 'default_' . rawurlencode($tmp->def); |
} |
$flags = trim($flags); |
$res[$i] = array( |
'table' => $case_func($tmp->table), |
'name' => $case_func($tmp->name), |
'type' => isset($this->mysqli_types[$tmp->type]) |
? $this->mysqli_types[$tmp->type] |
: 'unknown', |
'len' => $tmp->max_length, |
'flags' => $flags, |
); |
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) { |
@mysqli_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: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/ldap.php |
---|
New file |
0,0 → 1,994 |
<?php |
// |
// Pear DB LDAP - Database independent query interface definition |
// for PHP's LDAP extension. |
// |
// Copyright (c) 2002-2003 Ludovico Magnocavallo <ludo@sumatrasolutions.com> |
// |
// This library is free software; you can redistribute it and/or |
// modify it under the terms of the GNU Lesser General Public |
// License as published by the Free Software Foundation; either |
// version 2.1 of the License, or (at your option) any later version. |
// |
// This library is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// Lesser General Public License for more details. |
// |
// You should have received a copy of the GNU Lesser General Public |
// License along with this library; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// Contributors |
// - Piotr Roszatycki <dexter@debian.org> |
// DB_ldap::base() method, support for LDAP sequences, various fixes |
// - Aaron Spencer Hawley <aaron dot hawley at uvm dot edu> |
// fix to use port number if present in DB_ldap->connect() |
// |
// $Id: ldap.php,v 1.1 2006-12-14 15:04:28 jp_milcent Exp $ |
// |
require_once 'DB.php'; |
require_once 'DB/common.php'; |
define("DB_ERROR_BIND_FAILED", -26); |
define("DB_ERROR_UNKNOWN_LDAP_ACTION", -27); |
/** |
* LDAP result class |
* |
* LDAP_result extends DB_result to provide specific LDAP |
* result methods. |
* |
* @version 1.0 |
* @author Ludovico Magnocavallo <ludo@sumatrasolutions.com> |
* @package DB |
*/ |
class LDAP_result extends DB_result |
{ |
// {{{ properties |
/** |
* data returned from ldap_entries() |
* @access private |
*/ |
var $_entries = null; |
/** |
* result rows as hash of records |
* @access private |
*/ |
var $_recordset = null; |
/** |
* current record as hash |
* @access private |
*/ |
var $_record = null; |
// }}} |
// {{{ constructor |
/** |
* class constructor, calls DB_result constructor |
* @param ref $dbh reference to the db instance |
* @param resource $result ldap command result |
*/ |
function LDAP_result(&$dbh, $result) |
{ |
$this->DB_result($dbh, $result); |
} |
/** |
* fetch rows of data into $this->_recordset |
* |
* called once as soon as something needs to be returned |
* @access private |
* @param resource $result ldap command result |
* @return boolean true |
*/ |
function getRows() { |
if ($this->_recordset === null) { |
// begin processing result into recordset |
$this->_entries = ldap_get_entries($this->dbh->connection, $this->result); |
$this->row_counter = $this->_entries['count']; |
$i = 1; |
$rs_template = array(); |
if (count($this->dbh->attributes) > 0) { |
reset($this->dbh->attributes); |
while (list($a_index, $a_name) = each($this->dbh->attributes)) $rs_template[$a_name] = ''; |
} |
while (list($entry_idx, $entry) = each($this->_entries)) { |
// begin first loop, iterate through entries |
if (!empty($this->dbh->limit_from) && ($i < $this->dbh->limit_from)) continue; |
if (!empty($this->dbh->limit_count) && ($i > $this->dbh->limit_count)) break; |
$rs = $rs_template; |
if (!is_array($entry)) continue; |
while (list($attr, $attr_values) = each($entry)) { |
// begin second loop, iterate through attributes |
if (is_int($attr) || $attr == 'count') continue; |
if (is_string($attr_values)) $rs[$attr] = $attr_values; |
else { |
$value = ''; |
while (list($value_idx, $attr_value) = each($attr_values)) { |
// begin third loop, iterate through attribute values |
if (!is_int($value_idx)) continue; |
if (empty($value)) $value = $attr_value; |
else { |
if (is_array($value)) $value[] = $attr_value; |
else $value = array($value, $attr_value); |
} |
// else $value .= "\n$attr_value"; |
// end third loop |
} |
$rs[$attr] = $value; |
} |
// end second loop |
} |
reset($rs); |
$this->_recordset[$entry_idx] = $rs; |
$i++; |
// end first loop |
} |
$this->_entries = null; |
if (!is_array($this->_recordset)) |
$this->_recordset = array(); |
if (!empty($this->dbh->sorting)) { |
$sorting_method = (!empty($this->dbh->sorting_method) ? $this->dbh->sorting_method : 'cmp'); |
uksort($this->_recordset, array(&$this, $sorting_method)); |
} |
reset($this->_recordset); |
// end processing result into recordset |
} |
return DB_OK; |
} |
/** |
* Fetch and return a row of data (it uses driver->fetchInto for that) |
* @param int $fetchmode format of fetched row |
* @param int $rownum the row number to fetch |
* |
* @return array a row of data, NULL on no more rows or PEAR_Error on error |
* |
* @access public |
*/ |
function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) |
{ |
$this->getRows(); |
if (count($this->_recordset) == 0) return null; |
if ($this->_record !== null) $this->_record = next($this->_recordset); |
else $this->_record = current($this->_recordset); |
$row = $this->_record; |
return $row; |
} |
/** |
* Fetch a row of data into an existing variable. |
* |
* @param mixed $arr reference to data containing the row |
* @param integer $fetchmode format of fetched row |
* @param integer $rownum the row number to fetch |
* |
* @return mixed DB_OK on success, NULL on no more rows or |
* a DB_Error object on error |
* |
* @access public |
*/ |
function fetchInto(&$ar, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) |
{ |
$this->getRows(); |
if ($this->_record !== null) $this->_record = next($this->_recordset); |
else $this->_record = current($this->_recordset); |
$ar = $this->_record; |
if (!$ar) { |
return null; |
} |
return DB_OK; |
} |
/** |
* return all records |
* |
* returns a hash of all records, basically returning |
* a copy of $this->_recordset |
* @param integer $fetchmode format of fetched row |
* @param integer $rownum the row number to fetch (not used, here for interface compatibility) |
* |
* @return mixed DB_OK on success, NULL on no more rows or |
* a DB_Error object on error |
* |
* @access public |
*/ |
function fetchAll($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) |
{ |
$this->getRows(); |
return($this->_recordset); |
} |
/** |
* Get the the number of columns in a result set. |
* |
* @return int the number of columns, or a DB error |
* |
* @access public |
*/ |
function numCols($result) |
{ |
$this->getRows(); |
return(count(array_keys($this->_record))); |
} |
function cmp($a, $b) |
{ |
return(strcmp(strtolower($this->_recordset[$a][$this->dbh->sorting]), strtolower($this->_recordset[$b][$this->dbh->sorting]))); |
} |
/** |
* Get the number of rows in a result set. |
* |
* @return int the number of rows, or a DB error |
* |
* @access public |
*/ |
function numRows() |
{ |
$this->getRows(); |
return $this->row_counter; |
} |
/** |
* Get the next result if a batch of queries was executed. |
* |
* @return bool true if a new result is available or false if not. |
* |
* @access public |
*/ |
function nextResult() |
{ |
return $this->dbh->nextResult($this->result); |
} |
/** |
* Frees the resources allocated for this result set. |
* @return int error code |
* |
* @access public |
*/ |
function free() |
{ |
$this->_recordset = null; |
$this->_record = null; |
ldap_free_result($this->result); |
$this->result = null; |
return true; |
} |
/** |
* @deprecated |
*/ |
function tableInfo($mode = null) |
{ |
return $this->dbh->tableInfo($this->result, $mode); |
} |
/** |
* returns the actual rows number |
* @return integer |
*/ |
function getRowCounter() |
{ |
$this->getRows(); |
return $this->row_counter; |
} |
} |
/** |
* LDAP DB interface class |
* |
* LDAP extends DB_common to provide DB compliant |
* access to LDAP servers |
* |
* @version 1.0 |
* @author Ludovico Magnocavallo <ludo@sumatrasolutions.com> |
* @package DB |
*/ |
class DB_ldap extends DB_common |
{ |
// {{{ properties |
/** |
* LDAP connection |
* @access private |
*/ |
var $connection; |
/** |
* base dn |
* @access private |
*/ |
var $base = ''; |
/** |
* default base dn |
* @access private |
*/ |
var $d_base = ''; |
/** |
* query base dn |
* @access private |
*/ |
var $q_base = ''; |
/** |
* array of LDAP actions that only manipulate data |
* returning a true/false value |
* @access private |
*/ |
var $manip = array('add', 'compare', 'delete', 'modify', 'mod_add', 'mod_del', 'mod_replace', 'rename'); |
/** |
* store the default real LDAP action to perform |
* @access private |
*/ |
var $action = 'search'; |
/** |
* store the real LDAP action to perform |
* (ie PHP ldap function to call) for a query |
* @access private |
*/ |
var $q_action = ''; |
/** |
* store optional parameters passed |
* to the real LDAP action |
* @access private |
*/ |
var $q_params = array(); |
// }}} |
/** |
* Constructor, calls DB_common constructor |
* |
* @see DB_common::DB_common() |
*/ |
function DB_ldap() |
{ |
$this->DB_common(); |
$this->phptype = 'ldap'; |
$this->dbsyntax = 'ldap'; |
$this->features = array( |
'prepare' => false, |
'pconnect' => false, |
'transactions' => false, |
'limit' => false |
); |
$this->errorcode_map = array( |
0x10 => DB_ERROR_NOSUCHFIELD, // LDAP_NO_SUCH_ATTRIBUTE |
0x11 => DB_ERROR_INVALID, // LDAP_UNDEFINED_TYPE |
0x12 => DB_ERROR_INVALID, // LDAP_INAPPROPRIATE_MATCHING |
0x13 => DB_ERROR_INVALID, // LDAP_CONSTRAINT_VIOLATION |
0x14 => DB_ERROR_ALREADY_EXISTS, // LDAP_TYPE_OR_VALUE_EXISTS |
0x15 => DB_ERROR_INVALID, // LDAP_INVALID_SYNTAX |
0x20 => DB_ERROR_NOT_FOUND, // LDAP_NO_SUCH_OBJECT |
0x21 => DB_ERROR_NOT_FOUND, // LDAP_ALIAS_PROBLEM |
0x22 => DB_ERROR_INVALID, // LDAP_INVALID_DN_SYNTAX |
0x23 => DB_ERROR_INVALID, // LDAP_IS_LEAF |
0x24 => DB_ERROR_INVALID, // LDAP_ALIAS_DEREF_PROBLEM |
0x30 => DB_ERROR_ACCESS_VIOLATION, // LDAP_INAPPROPRIATE_AUTH |
0x31 => DB_ERROR_ACCESS_VIOLATION, // LDAP_INVALID_CREDENTIALS |
0x32 => DB_ERROR_ACCESS_VIOLATION, // LDAP_INSUFFICIENT_ACCESS |
0x40 => DB_ERROR_MISMATCH, // LDAP_NAMING_VIOLATION |
0x41 => DB_ERROR_MISMATCH, // LDAP_OBJECT_CLASS_VIOLATION |
0x44 => DB_ERROR_ALREADY_EXISTS, // LDAP_ALREADY_EXISTS |
0x51 => DB_ERROR_CONNECT_FAILED, // LDAP_SERVER_DOWN |
0x57 => DB_ERROR_SYNTAX // LDAP_FILTER_ERROR |
); |
} |
/** |
* Connect and bind to LDAP server with either anonymous or authenticated bind depending on dsn info |
* |
* @param array $dsninfo dsn info as passed by DB::connect() |
* @param boolean $persistent kept for interface compatibility |
* @return DB_OK if successfully connected. A DB error code is returned on failure. |
*/ |
function connect($dsninfo, $persistent = false) |
{ |
if (!PEAR::loadExtension('ldap')) |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
$this->dsn = $dsninfo; |
$user = $dsninfo['username']; |
$pw = $dsninfo['password']; |
$host = $dsninfo['hostspec']; |
$port = $dsninfo['port']; |
$this->base = $dsninfo['database']; |
$this->d_base = $this->base; |
if (empty($host)) { |
return $this->raiseError("no host specified $host"); |
} // else ... |
if (isset($port)) { |
$conn = ldap_connect($host, $port); |
} else { |
$conn = ldap_connect($host); |
} |
if (!$conn) { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED); |
} |
if ($user && $pw) { |
$bind = @ldap_bind($conn, $user, $pw); |
} else { |
$bind = @ldap_bind($conn); |
} |
if (!$bind) { |
return $this->raiseError(DB_ERROR_BIND_FAILED); |
} |
$this->connection = $conn; |
return DB_OK; |
} |
/** |
* Unbinds from LDAP server |
* |
* @return int ldap_unbind() return value |
*/ |
function disconnect() |
{ |
$ret = @ldap_unbind($this->connection); |
$this->connection = null; |
return $ret; |
} |
/** |
* Performs a request against the LDAP server |
* |
* The type of request (and the corresponding PHP ldap function called) |
* depend on two additional parameters, added in respect to the |
* DB_common interface. |
* |
* @param string $filter text of the request to send to the LDAP server |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @return result from ldap function or DB Error object if no result |
*/ |
function simpleQuery($filter, $action = null, $params = null) |
{ |
if ($action === null) { |
$action = (!empty($this->q_action) ? $this->q_action : $this->action); |
} |
if ($params === null) { |
$params = (count($this->q_params) > 0 ? $this->q_params : array()); |
} |
if (!$this->isManip($action)) { |
$base = $this->q_base ? $this->q_base : $this->base; |
$attributes = array(); |
$attrsonly = 0; |
$sizelimit = 0; |
$timelimit = 0; |
$deref = LDAP_DEREF_NEVER; |
$sorting = ''; |
$sorting_method = ''; |
reset($params); |
while (list($k, $v) = each($params)) { |
if (isset(${$k})) ${$k} = $v; |
} |
$this->sorting = $sorting; |
$this->sorting_method = $sorting_method; |
$this->attributes = $attributes; |
# double escape char for filter: '(o=Przedsi\C4\99biorstwo)' => '(o=Przedsi\\C4\\99biorstwo)' |
$filter = str_replace('\\', '\\\\', $filter); |
$this->last_query = $filter; |
if ($action == 'search') |
$result = @ldap_search($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref); |
else if ($action == 'list') |
$result = @ldap_list($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref); |
else if ($action == 'read') |
$result = @ldap_read($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref); |
else |
return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION); |
if (!$result) { |
return $this->ldapRaiseError(); |
} |
} else { |
# If first argument is an array, it contains the entry with DN. |
if (is_array($filter)) { |
$entry = $filter; |
$filter = $entry["dn"]; |
} else { |
$entry = array(); |
} |
unset($entry["dn"]); |
$attribute = ''; |
$value = ''; |
$newrdn = ''; |
$newparent = ''; |
$deleteoldrdn = false; |
reset($params); |
while (list($k, $v) = each($params)) { |
if (isset(${$k})) ${$k} = $v; |
} |
$this->last_query = $filter; |
if ($action == 'add') |
$result = @ldap_add($this->connection, $filter, $entry); |
else if ($action == 'compare') |
$result = @ldap_add($this->connection, $filter, $attribute, $value); |
else if ($action == 'delete') |
$result = @ldap_delete($this->connection, $filter); |
else if ($action == 'modify') |
$result = @ldap_modify($this->connection, $filter, $entry); |
else if ($action == 'mod_add') |
$result = @ldap_mod_add($this->connection, $filter, $entry); |
else if ($action == 'mod_del') |
$result = @ldap_mod_del($this->connection, $filter, $entry); |
else if ($action == 'mod_replace') |
$result = @ldap_mod_replace($this->connection, $filter, $entry); |
else if ($action == 'rename') |
$result = @ldap_rename($this->connection, $filter, $newrdn, $newparent, $deleteoldrdn); |
else |
return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION); |
if (!$result) { |
return $this->ldapRaiseError(); |
} |
} |
$this->freeQuery(); |
return $result; |
} |
/** |
* Executes a query performing variables substitution in the query text |
* |
* @param string $stmt text of the request to send to the LDAP server |
* @param array $data query variables values to substitute |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @return LDAP_result object or DB Error object if no result |
* @see DB_common::executeEmulateQuery $this->simpleQuery() |
*/ |
function execute($stmt, $data = false, $action = null, $params = array()) |
{ |
$this->q_params = $params; |
$realquery = $this->executeEmulateQuery($stmt, $data); |
if (DB::isError($realquery)) { |
return $realquery; |
} |
$result = $this->simpleQuery($realquery); |
if (DB::isError($result) || $result === DB_OK) { |
return $result; |
} else { |
return new LDAP_result($this, $result); |
} |
} |
/** |
* Executes multiple queries performing variables substitution for each query |
* |
* @param string $stmt text of the request to send to the LDAP server |
* @param array $data query variables values to substitute |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @return LDAP_result object or DB Error object if no result |
* @see DB_common::executeMultiple |
*/ |
function executeMultiple($stmt, &$data, $action = null, $params = array()) |
{ |
$this->q_action = $action ? $action : $this->action; |
$this->q_params = $params; |
return(parent::executeMultiple($stmt, $data)); |
} |
/** |
* Executes a query substituting variables if any are present |
* |
* @param string $query text of the request to send to the LDAP server |
* @param array $data query variables values to substitute |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @return LDAP_result object or DB Error object if no result |
* @see DB_common::prepare() $this->execute()$this->simpleQuery() |
*/ |
function &query($query, $data = array(), $action = null, $params = array()) { |
// $this->q_action = $action ? $action : $this->action; |
// $this->q_params = $params; |
if (sizeof($data) > 0) { |
$sth = $this->prepare($query); |
if (DB::isError($sth)) { |
return $sth; |
} |
return $this->execute($sth, $data); |
} else { |
$result = $this->simpleQuery($query); |
if (DB::isError($result) || $result === DB_OK) { |
return $result; |
} else { |
return new LDAP_result($this, $result); |
} |
} |
} |
/** |
* Modifies a query to return only a set of rows, stores $from and $count for LDAP_result |
* |
* @param string $query text of the request to send to the LDAP server |
* @param int $from record position from which to start returning data |
* @param int $count number of records to return |
* @return modified query text (no modifications are made, see above) |
*/ |
function modifyLimitQuery($query, $from, $count) |
{ |
$this->limit_from = $from; |
$this->limit_count = $count; |
return $query; |
} |
/** |
* Executes a query returning only a specified number of rows |
* |
* This method only saves the $from and $count parameters for LDAP_result |
* where the actual records processing takes place |
* |
* @param string $query text of the request to send to the LDAP server |
* @param int $from record position from which to start returning data |
* @param int $count number of records to return |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @return LDAP_result object or DB Error object if no result |
*/ |
function limitQuery($query, $from, $count, $action = null, $params = array()) |
{ |
$query = $this->modifyLimitQuery($query, $from, $count); |
$this->q_action = $action ? $action : $this->action; |
$this->q_params = $params; |
return $this->query($query, $action, $params); |
} |
/** |
* Fetch the first column of the first row of data returned from |
* a query. Takes care of doing the query and freeing the results |
* when finished. |
* |
* @param $query the SQL query |
* @param $data if supplied, prepare/execute will be used |
* with this array as execute parameters |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @return array |
* @see DB_common::getOne() |
* @access public |
*/ |
function &getOne($query, $data = array(), $action = null, $params = array()) |
{ |
$this->q_action = $action ? $action : $this->action; |
$this->q_params = $params; |
return(parent::getOne($query, $data)); |
} |
/** |
* Fetch the first row of data returned from a query. Takes care |
* of doing the query and freeing the results when finished. |
* |
* @param $query the SQL query |
* @param $fetchmode the fetch mode to use |
* @param $data array if supplied, prepare/execute will be used |
* with this array as execute parameters |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @access public |
* @return array the first row of results as an array indexed from |
* 0, or a DB error code. |
* @see DB_common::getRow() |
* @access public |
*/ |
function &getRow($query, |
$data = null, |
$fetchmode = DB_FETCHMODE_DEFAULT, |
$action = null, $params = array()) |
{ |
$this->q_action = $action ? $action : $this->action; |
$this->q_params = $params; |
return(parent::getRow($query, $data, $fetchmode)); |
} |
/** |
* Fetch the first column of data returned from a query. Takes care |
* of doing the query and freeing the results when finished. |
* |
* @param $query the SQL query |
* @param $col which column to return (integer [column number, |
* starting at 0] or string [column name]) |
* @param $data array if supplied, prepare/execute will be used |
* with this array as execute parameters |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @access public |
* @return array an indexed array with the data from the first |
* row at index 0, or a DB error code. |
* @see DB_common::getCol() |
* @access public |
*/ |
function &getCol($query, $col = 0, $data = array(), $action = null, $params = array()) |
{ |
$this->q_action = $action ? $action : $this->action; |
$this->q_params = $params; |
return(parent::getCol($query, $col, $data)); |
} |
/** |
* Calls DB_common::getAssoc() |
* |
* @param $query the SQL query |
* @param $force_array (optional) 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. |
* starting at 0] or string [column name]) |
* @param array $data if supplied, prepare/execute will be used |
* with this array as execute parameters |
* @param $fetchmode the fetch mode to use |
* @param boolean $group see DB_Common::getAssoc() |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @access public |
* @return array an indexed array with the data from the first |
* row at index 0, or a DB error code. |
* @see DB_common::getAssoc() |
* @access public |
*/ |
function &getAssoc($query, $force_array = false, $data = array(), |
$fetchmode = DB_FETCHMODE_ORDERED, $group = false, |
$action = null, $params = array()) |
{ |
$this->q_action = $action ? $action : $this->action; |
$this->q_params = $params; |
return(parent::getAssoc($query, $force_array, $data, $fetchmode, $group)); |
} |
/** |
* Fetch all the rows returned from a query. |
* |
* @param $query the SQL query |
* @param array $data if supplied, prepare/execute will be used |
* with this array as execute parameters |
* @param $fetchmode the fetch mode to use |
* @param string $action type of request to perform, defaults to search (ldap_search()) |
* @param array $params array of additional parameters to pass to the PHP ldap function requested |
* @access public |
* @return array an nested array, or a DB error |
* @see DB_common::getAll() |
*/ |
function &getAll($query, |
$data = null, |
$fetchmode = DB_FETCHMODE_DEFAULT, |
$action = null, $params = array()) |
{ |
$this->q_action = $action ? $action : $this->action; |
$this->q_params = $params; |
return(parent::getAll($query, $data, $fetchmode)); |
} |
function numRows($result) |
{ |
return $result->numRows(); |
} |
function getTables() |
{ |
return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE); |
} |
function getListOf($type) |
{ |
return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE); |
} |
function isManip($action) |
{ |
return(in_array($action, $this->manip)); |
} |
function freeResult() |
{ |
return true; |
} |
function freeQuery($query = '') |
{ |
$this->q_action = ''; |
$this->q_base = ''; |
$this->q_params = array(); |
$this->attributes = null; |
$this->sorting = ''; |
return true; |
} |
// Deprecated, will be removed in future releases. |
function base($base = null) |
{ |
$this->q_base = ($base !== null) ? $base : null; |
return true; |
} |
function ldapSetBase($base = null) |
{ |
$this->base = ($base !== null) ? $base : $this->d_base; |
$this->q_base = ''; |
return true; |
} |
function ldapSetAction($action = 'search') |
{ |
if ($action != 'search' && $action != 'list' && $action != 'read') { |
return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION); |
} |
$this->action = $action; |
$this->q_action = ''; |
return true; |
} |
/** |
* Get the next value in a sequence. |
* |
* LDAP provides transactions for only one entry and we need to |
* prevent race condition. If unique value before and after modify |
* aren't equal then wait and try again. |
* |
* The name of sequence is LDAP DN of entry. |
* |
* @access public |
* @param string $seq_name the DN of the sequence |
* @param bool $ondemand whether to create the sequence on demand |
* @return a sequence integer, or a DB error |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$repeat = 0; |
do { |
// Get the sequence entry |
$this->base($seq_name); |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$data = $this->getRow("objectClass=*"); |
$this->popErrorHandling(); |
if (DB::isError($data)) { |
// DB_ldap doesn't use DB_ERROR_NOT_FOUND |
if ($ondemand && $repeat == 0 |
&& $data->getCode() == DB_ERROR) { |
// Try to create sequence and repeat |
$repeat = 1; |
$data = $this->createSequence($seq_name); |
if (DB::isError($data)) { |
return $this->ldapRaiseError($data); |
} |
} else { |
// Other error |
return $this->ldapRaiseError($data); |
} |
} else { |
// Increment sequence value |
$data["cn"]++; |
// Unique identificator of transaction |
$seq_unique = mt_rand(); |
$data["uid"] = $seq_unique; |
// Modify the LDAP entry |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$data = $this->simpleQuery($data, 'modify'); |
$this->popErrorHandling(); |
if (DB::isError($data)) { |
return $this->ldapRaiseError($data); |
} |
// Get the entry and check if it contains our unique value |
$this->base($seq_name); |
$data = $this->getRow("objectClass=*"); |
if (DB::isError($data)) { |
return $this->ldapRaiseError($data); |
} |
if ($data["uid"] != $seq_unique) { |
// It is not our entry. Wait a little time and repeat |
sleep(1); |
$repeat = 1; |
} else { |
$repeat = 0; |
} |
} |
} while ($repeat); |
if (DB::isError($data)) { |
return $data; |
} |
return $data["cn"]; |
} |
/** |
* Create the sequence |
* |
* The sequence entry is based on core schema with extensibleObject, |
* so it should work with any LDAP server which doesn't check schema |
* or supports extensibleObject object class. |
* |
* Sequence name have to be DN started with "sn=$seq_id,", i.e.: |
* |
* $seq_name = "sn=uidNumber,ou=sequences,dc=php,dc=net"; |
* |
* dn: $seq_name |
* objectClass: top |
* objectClass: extensibleObject |
* sn: $seq_id |
* cn: $seq_value |
* uid: $seq_uniq |
* |
* @param string $seq_name the DN of the sequence |
* @return mixed DB_OK on success or DB error on error |
* @access public |
*/ |
function createSequence($seq_name) |
{ |
// Extract $seq_id from DN |
ereg("^([^,]*),", $seq_name, $regs); |
$seq_id = $regs[1]; |
// Create the sequence entry |
$data = array( |
dn => $seq_name, |
objectclass => array("top", "extensibleObject"), |
sn => $seq_id, |
cn => 0, |
uid => 0 |
); |
// Add the LDAP entry |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$data = $this->simpleQuery($data, 'add'); |
$this->popErrorHandling(); |
return $data; |
} |
/** |
* Drop a sequence |
* |
* @param string $seq_name the DN of the sequence |
* @return mixed DB_OK on success or DB error on error |
* @access public |
*/ |
function dropSequence($seq_name) |
{ |
// Delete the sequence entry |
$data = array( |
dn => $seq_name, |
); |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$data = $this->simpleQuery($data, 'delete'); |
$this->popErrorHandling(); |
return $data; |
} |
// {{{ ldapRaiseError() |
function ldapRaiseError($errno = null) |
{ |
if ($errno === null) { |
$errno = $this->errorCode(ldap_errno($this->connection)); |
} |
if ($this->q_action !== null) { |
return $this->raiseError($errno, null, null, |
sprintf('%s base="%s" filter="%s"', |
$this->q_action, $this->q_base, $this->last_query |
), |
$errno == DB_ERROR_UNKNOWN_LDAP_ACTION ? null : @ldap_error($this->connection)); |
} else { |
return $this->raiseError($errno, null, null, "???", |
@ldap_error($this->connection)); |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/ibase.php |
---|
New file |
0,0 → 1,1071 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's interbase extension |
* for interacting with Interbase and Firebird databases |
* |
* While this class works with PHP 4, PHP's InterBase extension is |
* unstable in PHP 4. Use PHP 5. |
* |
* 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 Sterling Hughes <sterling@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: ibase.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 interbase extension |
* for interacting with Interbase and Firebird databases |
* |
* These methods overload the ones declared in DB_common. |
* |
* While this class works with PHP 4, PHP's InterBase extension is |
* unstable in PHP 4. Use PHP 5. |
* |
* NOTICE: limitQuery() only works for Firebird. |
* |
* @category Database |
* @package DB |
* @author Sterling Hughes <sterling@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: 1.7.6 |
* @link http://pear.php.net/package/DB |
* @since Class became stable in Release 1.7.0 |
*/ |
class DB_ibase extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'ibase'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'ibase'; |
/** |
* 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 |
* |
* NOTE: only firebird supports limit. |
* |
* @var array |
*/ |
var $features = array( |
'limit' => false, |
'new_link' => false, |
'numrows' => 'emulate', |
'pconnect' => true, |
'prepare' => true, |
'ssl' => false, |
'transactions' => true, |
); |
/** |
* A mapping of native error codes to DB error codes |
* @var array |
*/ |
var $errorcode_map = array( |
-104 => DB_ERROR_SYNTAX, |
-150 => DB_ERROR_ACCESS_VIOLATION, |
-151 => DB_ERROR_ACCESS_VIOLATION, |
-155 => DB_ERROR_NOSUCHTABLE, |
-157 => DB_ERROR_NOSUCHFIELD, |
-158 => DB_ERROR_VALUE_COUNT_ON_ROW, |
-170 => DB_ERROR_MISMATCH, |
-171 => DB_ERROR_MISMATCH, |
-172 => DB_ERROR_INVALID, |
// -204 => // Covers too many errors, need to use regex on msg |
-205 => DB_ERROR_NOSUCHFIELD, |
-206 => DB_ERROR_NOSUCHFIELD, |
-208 => DB_ERROR_INVALID, |
-219 => DB_ERROR_NOSUCHTABLE, |
-297 => DB_ERROR_CONSTRAINT, |
-303 => DB_ERROR_INVALID, |
-413 => DB_ERROR_INVALID_NUMBER, |
-530 => DB_ERROR_CONSTRAINT, |
-551 => DB_ERROR_ACCESS_VIOLATION, |
-552 => DB_ERROR_ACCESS_VIOLATION, |
// -607 => // Covers too many errors, need to use regex on msg |
-625 => DB_ERROR_CONSTRAINT_NOT_NULL, |
-803 => DB_ERROR_CONSTRAINT, |
-804 => DB_ERROR_VALUE_COUNT_ON_ROW, |
-904 => DB_ERROR_CONNECT_FAILED, |
-922 => DB_ERROR_NOSUCHDB, |
-923 => DB_ERROR_CONNECT_FAILED, |
-924 => DB_ERROR_CONNECT_FAILED |
); |
/** |
* The raw database connection created by PHP |
* @var resource |
*/ |
var $connection; |
/** |
* The DSN information for connecting to a database |
* @var array |
*/ |
var $dsn = array(); |
/** |
* The number of rows affected by a data manipulation query |
* @var integer |
* @access private |
*/ |
var $affected = 0; |
/** |
* Should data manipulation queries be committed automatically? |
* @var bool |
* @access private |
*/ |
var $autocommit = true; |
/** |
* The prepared statement handle from the most recently executed statement |
* |
* {@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 |
*/ |
var $last_stmt; |
/** |
* Is the given prepared statement a data manipulation query? |
* @var array |
* @access private |
*/ |
var $manip_query = array(); |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_ibase() |
{ |
$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 ibase driver supports the following extra DSN options: |
* + buffers The number of database buffers to allocate for the |
* server-side cache. |
* + charset The default character set for a database. |
* + dialect The default SQL dialect for any statement |
* executed within a connection. Defaults to the |
* highest one supported by client libraries. |
* Functional only with InterBase 6 and up. |
* + role Functional only with InterBase 5 and up. |
* |
* @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('interbase')) { |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
if ($this->dbsyntax == 'firebird') { |
$this->features['limit'] = 'alter'; |
} |
$params = array( |
$dsn['hostspec'] |
? ($dsn['hostspec'] . ':' . $dsn['database']) |
: $dsn['database'], |
$dsn['username'] ? $dsn['username'] : null, |
$dsn['password'] ? $dsn['password'] : null, |
isset($dsn['charset']) ? $dsn['charset'] : null, |
isset($dsn['buffers']) ? $dsn['buffers'] : null, |
isset($dsn['dialect']) ? $dsn['dialect'] : null, |
isset($dsn['role']) ? $dsn['role'] : null, |
); |
$connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect'; |
$this->connection = @call_user_func_array($connect_function, $params); |
if (!$this->connection) { |
return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED); |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
$ret = @ibase_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); |
$result = @ibase_query($this->connection, $query); |
if (!$result) { |
return $this->ibaseRaiseError(); |
} |
if ($this->autocommit && $ismanip) { |
@ibase_commit($this->connection); |
} |
if ($ismanip) { |
$this->affected = $result; |
return DB_OK; |
} else { |
$this->affected = 0; |
return $result; |
} |
} |
// }}} |
// {{{ modifyLimitQuery() |
/** |
* Adds LIMIT clauses to a query string according to current DBMS standards |
* |
* Only works with Firebird. |
* |
* @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 ($this->dsn['dbsyntax'] == 'firebird') { |
$query = preg_replace('/^([\s(])*SELECT/i', |
"SELECT FIRST $count SKIP $from", $query); |
} |
return $query; |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal ibase 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) |
{ |
if ($rownum !== null) { |
return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
if (function_exists('ibase_fetch_assoc')) { |
$arr = @ibase_fetch_assoc($result); |
} else { |
$arr = get_object_vars(ibase_fetch_object($result)); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @ibase_fetch_row($result); |
} |
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); |
} |
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 @ibase_free_result($result); |
} |
// }}} |
// {{{ freeQuery() |
function freeQuery($query) |
{ |
@ibase_free_query($query); |
return true; |
} |
// }}} |
// {{{ 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 (is_integer($this->affected)) { |
return $this->affected; |
} |
return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); |
} |
// }}} |
// {{{ 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 = @ibase_num_fields($result); |
if (!$cols) { |
return $this->ibaseRaiseError(); |
} |
return $cols; |
} |
// }}} |
// {{{ prepare() |
/** |
* Prepares a query for multiple execution with execute(). |
* |
* prepare() requires a generic query as string like <code> |
* INSERT INTO numbers VALUES (?, ?, ?) |
* </code>. The <kbd>?</kbd> characters are placeholders. |
* |
* Three types of placeholders can be used: |
* + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers |
* + <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) |
* |
* Use backslashes to escape placeholder characters if you don't want |
* them to be interpreted as placeholders. Example: <code> |
* "UPDATE foo SET col=? WHERE col='over \& under'" |
* </code> |
* |
* @param string $query query to be prepared |
* @return mixed DB statement resource on success. DB_Error on failure. |
*/ |
function prepare($query) |
{ |
$tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, |
PREG_SPLIT_DELIM_CAPTURE); |
$token = 0; |
$types = array(); |
$newquery = ''; |
foreach ($tokens as $key => $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: |
$tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); |
$newquery .= $tokens[$key] . '?'; |
} |
} |
$newquery = substr($newquery, 0, -1); |
$this->last_query = $query; |
$newquery = $this->modifyQuery($newquery); |
$stmt = @ibase_prepare($this->connection, $newquery); |
$this->prepare_types[(int)$stmt] = $types; |
$this->manip_query[(int)$stmt] = DB::isManip($query); |
return $stmt; |
} |
// }}} |
// {{{ execute() |
/** |
* Executes a DB statement prepared with prepare(). |
* |
* @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 for non-array items or the |
* quantity of elements in the array. |
* @return object a new DB_Result or a DB_Error when fail |
* @see DB_ibase::prepare() |
* @access public |
*/ |
function &execute($stmt, $data = array()) |
{ |
$data = (array)$data; |
$this->last_parameters = $data; |
$types =& $this->prepare_types[(int)$stmt]; |
if (count($types) != count($data)) { |
$tmp =& $this->raiseError(DB_ERROR_MISMATCH); |
return $tmp; |
} |
$i = 0; |
foreach ($data as $key => $value) { |
if ($types[$i] == DB_PARAM_MISC) { |
/* |
* ibase doesn't seem to have the ability to pass a |
* parameter along unchanged, so strip off quotes from start |
* and end, plus turn two single quotes to one single quote, |
* in order to avoid the quotes getting escaped by |
* ibase and ending up in the database. |
*/ |
$data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); |
$data[$key] = str_replace("''", "'", $data[$key]); |
} elseif ($types[$i] == DB_PARAM_OPAQUE) { |
$fp = @fopen($data[$key], 'rb'); |
if (!$fp) { |
$tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); |
return $tmp; |
} |
$data[$key] = fread($fp, filesize($data[$key])); |
fclose($fp); |
} |
$i++; |
} |
array_unshift($data, $stmt); |
$res = call_user_func_array('ibase_execute', $data); |
if (!$res) { |
$tmp =& $this->ibaseRaiseError(); |
return $tmp; |
} |
/* XXX need this? |
if ($this->autocommit && $this->manip_query[(int)$stmt]) { |
@ibase_commit($this->connection); |
}*/ |
$this->last_stmt = $stmt; |
if ($this->manip_query[(int)$stmt]) { |
$tmp = DB_OK; |
} else { |
$tmp =& new DB_result($this, $res); |
} |
return $tmp; |
} |
/** |
* 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_ibase::prepare() |
*/ |
function freePrepared($stmt, $free_resource = true) |
{ |
if (!is_resource($stmt)) { |
return false; |
} |
if ($free_resource) { |
@ibase_free_query($stmt); |
} |
unset($this->prepare_tokens[(int)$stmt]); |
unset($this->prepare_types[(int)$stmt]); |
unset($this->manip_query[(int)$stmt]); |
return true; |
} |
// }}} |
// {{{ 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) |
{ |
$this->autocommit = $onoff ? 1 : 0; |
return DB_OK; |
} |
// }}} |
// {{{ commit() |
/** |
* Commits the current transaction |
* |
* @return int DB_OK on success. A DB_Error object on failure. |
*/ |
function commit() |
{ |
return @ibase_commit($this->connection); |
} |
// }}} |
// {{{ rollback() |
/** |
* Reverts the current transaction |
* |
* @return int DB_OK on success. A DB_Error object on failure. |
*/ |
function rollback() |
{ |
return @ibase_rollback($this->connection); |
} |
// }}} |
// {{{ transactionInit() |
function transactionInit($trans_args = 0) |
{ |
return $trans_args |
? @ibase_trans($trans_args, $this->connection) |
: @ibase_trans(); |
} |
// }}} |
// {{{ 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_ibase::createSequence(), DB_ibase::dropSequence() |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$sqn = strtoupper($this->getSequenceName($seq_name)); |
$repeat = 0; |
do { |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result =& $this->query("SELECT GEN_ID(${sqn}, 1) " |
. 'FROM RDB$GENERATORS ' |
. "WHERE RDB\$GENERATOR_NAME='${sqn}'"); |
$this->popErrorHandling(); |
if ($ondemand && DB::isError($result)) { |
$repeat = 1; |
$result = $this->createSequence($seq_name); |
if (DB::isError($result)) { |
return $result; |
} |
} else { |
$repeat = 0; |
} |
} 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_ibase::nextID(), DB_ibase::dropSequence() |
*/ |
function createSequence($seq_name) |
{ |
$sqn = strtoupper($this->getSequenceName($seq_name)); |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result = $this->query("CREATE GENERATOR ${sqn}"); |
$this->popErrorHandling(); |
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_ibase::nextID(), DB_ibase::createSequence() |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DELETE FROM RDB$GENERATORS ' |
. "WHERE RDB\$GENERATOR_NAME='" |
. strtoupper($this->getSequenceName($seq_name)) |
. "'"); |
} |
// }}} |
// {{{ _ibaseFieldFlags() |
/** |
* Get the column's flags |
* |
* Supports "primary_key", "unique_key", "not_null", "default", |
* "computed" and "blob". |
* |
* @param string $field_name the name of the field |
* @param string $table_name the name of the table |
* |
* @return string the flags |
* |
* @access private |
*/ |
function _ibaseFieldFlags($field_name, $table_name) |
{ |
$sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE' |
.' FROM RDB$INDEX_SEGMENTS I' |
.' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME' |
.' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\'' |
.' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''; |
$result = @ibase_query($this->connection, $sql); |
if (!$result) { |
return $this->ibaseRaiseError(); |
} |
$flags = ''; |
if ($obj = @ibase_fetch_object($result)) { |
@ibase_free_result($result); |
if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') { |
$flags .= 'primary_key '; |
} |
if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') { |
$flags .= 'unique_key '; |
} |
} |
$sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,' |
.' R.RDB$DEFAULT_SOURCE AS DSOURCE,' |
.' F.RDB$FIELD_TYPE AS FTYPE,' |
.' F.RDB$COMPUTED_SOURCE AS CSOURCE' |
.' FROM RDB$RELATION_FIELDS R ' |
.' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME' |
.' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'' |
.' AND R.RDB$FIELD_NAME=\'' . $field_name . '\''; |
$result = @ibase_query($this->connection, $sql); |
if (!$result) { |
return $this->ibaseRaiseError(); |
} |
if ($obj = @ibase_fetch_object($result)) { |
@ibase_free_result($result); |
if (isset($obj->NFLAG)) { |
$flags .= 'not_null '; |
} |
if (isset($obj->DSOURCE)) { |
$flags .= 'default '; |
} |
if (isset($obj->CSOURCE)) { |
$flags .= 'computed '; |
} |
if (isset($obj->FTYPE) && $obj->FTYPE == 261) { |
$flags .= 'blob '; |
} |
} |
return trim($flags); |
} |
// }}} |
// {{{ ibaseRaiseError() |
/** |
* 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_ibase::errorNative(), DB_ibase::errorCode() |
*/ |
function &ibaseRaiseError($errno = null) |
{ |
if ($errno === null) { |
$errno = $this->errorCode($this->errorNative()); |
} |
$tmp =& $this->raiseError($errno, null, null, null, @ibase_errmsg()); |
return $tmp; |
} |
// }}} |
// {{{ errorNative() |
/** |
* Gets the DBMS' native error code produced by the last query |
* |
* @return int the DBMS' error code. NULL if there is no error code. |
* |
* @since Method available since Release 1.7.0 |
*/ |
function errorNative() |
{ |
if (function_exists('ibase_errcode')) { |
return @ibase_errcode(); |
} |
if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i', |
@ibase_errmsg(), $m)) { |
return (int)$m[1]; |
} |
return null; |
} |
// }}} |
// {{{ errorCode() |
/** |
* Maps native error codes to DB's portable ones |
* |
* @param 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. |
* |
* @since Method available since Release 1.7.0 |
*/ |
function errorCode($nativecode = null) |
{ |
if (isset($this->errorcode_map[$nativecode])) { |
return $this->errorcode_map[$nativecode]; |
} |
static $error_regexps; |
if (!isset($error_regexps)) { |
$error_regexps = array( |
'/generator .* is not defined/' |
=> DB_ERROR_SYNTAX, // for compat. w ibase_errcode() |
'/table.*(not exist|not found|unknown)/i' |
=> DB_ERROR_NOSUCHTABLE, |
'/table .* already exists/i' |
=> DB_ERROR_ALREADY_EXISTS, |
'/unsuccessful metadata update .* failed attempt to store duplicate value/i' |
=> DB_ERROR_ALREADY_EXISTS, |
'/unsuccessful metadata update .* not found/i' |
=> DB_ERROR_NOT_FOUND, |
'/validation error for column .* value "\*\*\* null/i' |
=> DB_ERROR_CONSTRAINT_NOT_NULL, |
'/violation of [\w ]+ constraint/i' |
=> DB_ERROR_CONSTRAINT, |
'/conversion error from string/i' |
=> DB_ERROR_INVALID_NUMBER, |
'/no permission for/i' |
=> DB_ERROR_ACCESS_VIOLATION, |
'/arithmetic exception, numeric overflow, or string truncation/i' |
=> DB_ERROR_INVALID, |
); |
} |
$errormsg = @ibase_errmsg(); |
foreach ($error_regexps as $regexp => $code) { |
if (preg_match($regexp, $errormsg)) { |
return $code; |
} |
} |
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 = @ibase_query($this->connection, |
"SELECT * FROM $result WHERE 1=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->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = @ibase_num_fields($id); |
$res = array(); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
$info = @ibase_field_info($id, $i); |
$res[$i] = array( |
'table' => $got_string ? $case_func($result) : '', |
'name' => $case_func($info['name']), |
'type' => $info['type'], |
'len' => $info['length'], |
'flags' => ($got_string) |
? $this->_ibaseFieldFlags($info['name'], $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) { |
@ibase_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 'SELECT DISTINCT R.RDB$RELATION_NAME FROM ' |
. 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0'; |
case 'views': |
return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS'; |
case 'users': |
return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES'; |
default: |
return null; |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/ifx.php |
---|
New file |
0,0 → 1,681 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's ifx extension |
* for interacting with Informix 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 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: ifx.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 ifx extension |
* for interacting with Informix databases |
* |
* These methods overload the ones declared in DB_common. |
* |
* More info on Informix errors can be found at: |
* http://www.informix.com/answers/english/ierrors.htm |
* |
* TODO: |
* - set needed env Informix vars on connect |
* - implement native prepare/execute |
* |
* @category Database |
* @package DB |
* @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: 1.7.6 |
* @link http://pear.php.net/package/DB |
*/ |
class DB_ifx extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'ifx'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'ifx'; |
/** |
* 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' => 'emulate', |
'new_link' => false, |
'numrows' => 'emulate', |
'pconnect' => true, |
'prepare' => false, |
'ssl' => false, |
'transactions' => true, |
); |
/** |
* A mapping of native error codes to DB error codes |
* @var array |
*/ |
var $errorcode_map = array( |
'-201' => DB_ERROR_SYNTAX, |
'-206' => DB_ERROR_NOSUCHTABLE, |
'-217' => DB_ERROR_NOSUCHFIELD, |
'-236' => DB_ERROR_VALUE_COUNT_ON_ROW, |
'-239' => DB_ERROR_CONSTRAINT, |
'-253' => DB_ERROR_SYNTAX, |
'-292' => DB_ERROR_CONSTRAINT_NOT_NULL, |
'-310' => DB_ERROR_ALREADY_EXISTS, |
'-316' => DB_ERROR_ALREADY_EXISTS, |
'-319' => DB_ERROR_NOT_FOUND, |
'-329' => DB_ERROR_NODBSELECTED, |
'-346' => DB_ERROR_CONSTRAINT, |
'-386' => DB_ERROR_CONSTRAINT_NOT_NULL, |
'-391' => DB_ERROR_CONSTRAINT_NOT_NULL, |
'-554' => DB_ERROR_SYNTAX, |
'-691' => DB_ERROR_CONSTRAINT, |
'-692' => DB_ERROR_CONSTRAINT, |
'-703' => DB_ERROR_CONSTRAINT_NOT_NULL, |
'-1204' => DB_ERROR_INVALID_DATE, |
'-1205' => DB_ERROR_INVALID_DATE, |
'-1206' => DB_ERROR_INVALID_DATE, |
'-1209' => DB_ERROR_INVALID_DATE, |
'-1210' => DB_ERROR_INVALID_DATE, |
'-1212' => DB_ERROR_INVALID_DATE, |
'-1213' => DB_ERROR_INVALID_NUMBER, |
); |
/** |
* 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 |
* @access private |
*/ |
var $affected = 0; |
// }}} |
// {{{ constructor |
/** |
* This constructor calls <kbd>$this->DB_common()</kbd> |
* |
* @return void |
*/ |
function DB_ifx() |
{ |
$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. |
* |
* @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('informix') && |
!PEAR::loadExtension('Informix')) |
{ |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
$dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : ''; |
$dbname = $dsn['database'] ? $dsn['database'] . $dbhost : ''; |
$user = $dsn['username'] ? $dsn['username'] : ''; |
$pw = $dsn['password'] ? $dsn['password'] : ''; |
$connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect'; |
$this->connection = @$connect_function($dbname, $user, $pw); |
if (!is_resource($this->connection)) { |
return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED); |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
$ret = @ifx_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; |
$this->affected = null; |
if (preg_match('/(SELECT)/i', $query)) { //TESTME: Use !DB::isManip()? |
// the scroll is needed for fetching absolute row numbers |
// in a select query result |
$result = @ifx_query($query, $this->connection, IFX_SCROLL); |
} else { |
if (!$this->autocommit && $ismanip) { |
if ($this->transaction_opcount == 0) { |
$result = @ifx_query('BEGIN WORK', $this->connection); |
if (!$result) { |
return $this->ifxRaiseError(); |
} |
} |
$this->transaction_opcount++; |
} |
$result = @ifx_query($query, $this->connection); |
} |
if (!$result) { |
return $this->ifxRaiseError(); |
} |
$this->affected = @ifx_affected_rows($result); |
// Determine which queries should return data, and which |
// should return an error code only. |
if (preg_match('/(SELECT)/i', $query)) { |
return $result; |
} |
// XXX Testme: free results inside a transaction |
// may cause to stop it and commit the work? |
// Result has to be freed even with a insert or update |
@ifx_free_result($result); |
return DB_OK; |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal ifx 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; |
} |
// }}} |
// {{{ 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 $this->affected; |
} else { |
return 0; |
} |
} |
// }}} |
// {{{ 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) && ($rownum < 0)) { |
return null; |
} |
if ($rownum === null) { |
/* |
* Even though fetch_row() should return the next row if |
* $rownum is null, it doesn't in all cases. Bug 598. |
*/ |
$rownum = 'NEXT'; |
} else { |
// Index starts at row 1, unlike most DBMS's starting at 0. |
$rownum++; |
} |
if (!$arr = @ifx_fetch_row($result, $rownum)) { |
return null; |
} |
if ($fetchmode !== DB_FETCHMODE_ASSOC) { |
$i=0; |
$order = array(); |
foreach ($arr as $val) { |
$order[$i++] = $val; |
} |
$arr = $order; |
} elseif ($fetchmode == DB_FETCHMODE_ASSOC && |
$this->options['portability'] & DB_PORTABILITY_LOWERCASE) |
{ |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
$this->_rtrimArrayValues($arr); |
} |
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
$this->_convertNullArrayValuesToEmpty($arr); |
} |
return DB_OK; |
} |
// }}} |
// {{{ 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) |
{ |
if (!$cols = @ifx_num_fields($result)) { |
return $this->ifxRaiseError(); |
} |
return $cols; |
} |
// }}} |
// {{{ 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 @ifx_free_result($result); |
} |
// }}} |
// {{{ 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 = true) |
{ |
// 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) { |
$result = @ifx_query('COMMIT WORK', $this->connection); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->ifxRaiseError(); |
} |
} |
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 = @ifx_query('ROLLBACK WORK', $this->connection); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->ifxRaiseError(); |
} |
} |
return DB_OK; |
} |
// }}} |
// {{{ ifxRaiseError() |
/** |
* 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_ifx::errorNative(), DB_ifx::errorCode() |
*/ |
function ifxRaiseError($errno = null) |
{ |
if ($errno === null) { |
$errno = $this->errorCode(ifx_error()); |
} |
return $this->raiseError($errno, null, null, null, |
$this->errorNative()); |
} |
// }}} |
// {{{ errorNative() |
/** |
* Gets the DBMS' native error code and message produced by the last query |
* |
* @return string the DBMS' error code and message |
*/ |
function errorNative() |
{ |
return @ifx_error() . ' ' . @ifx_errormsg(); |
} |
// }}} |
// {{{ errorCode() |
/** |
* Maps native error codes to DB's portable ones. |
* |
* Requires that the DB implementation's constructor fills |
* in the <var>$errorcode_map</var> property. |
* |
* @param string $nativecode error code returned by the database |
* @return int a portable DB error code, or DB_ERROR if this DB |
* implementation has no mapping for the given error code. |
*/ |
function errorCode($nativecode) |
{ |
if (ereg('SQLCODE=(.*)]', $nativecode, $match)) { |
$code = $match[1]; |
if (isset($this->errorcode_map[$code])) { |
return $this->errorcode_map[$code]; |
} |
} |
return DB_ERROR; |
} |
// }}} |
// {{{ tableInfo() |
/** |
* Returns information about a table or a result set |
* |
* NOTE: only supports 'table' if <var>$result</var> is a table name. |
* |
* If analyzing a query result and the result has duplicate field names, |
* an error will be raised saying |
* <samp>can't distinguish duplicate field names</samp>. |
* |
* @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() |
* @since Method available since Release 1.6.0 |
*/ |
function tableInfo($result, $mode = null) |
{ |
if (is_string($result)) { |
/* |
* Probably received a table name. |
* Create a result resource identifier. |
*/ |
$id = @ifx_query("SELECT * FROM $result WHERE 1=0", |
$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. |
*/ |
$id = $result; |
$got_string = false; |
} |
if (!is_resource($id)) { |
return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
$flds = @ifx_fieldproperties($id); |
$count = @ifx_num_fields($id); |
if (count($flds) != $count) { |
return $this->raiseError("can't distinguish duplicate field names"); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$i = 0; |
$res = array(); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
foreach ($flds as $key => $value) { |
$props = explode(';', $value); |
$res[$i] = array( |
'table' => $got_string ? $case_func($result) : '', |
'name' => $case_func($key), |
'type' => $props[0], |
'len' => $props[1], |
'flags' => $props[4] == 'N' ? 'not_null' : '', |
); |
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; |
} |
$i++; |
} |
// free the result only if we were called on a table |
if ($got_string) { |
@ifx_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 'SELECT tabname FROM systables WHERE tabid >= 100'; |
default: |
return null; |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/pgsql.php |
---|
New file |
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,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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: 1.7.6 |
* @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: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/sybase.php |
---|
New file |
0,0 → 1,907 |
<?php |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
/** |
* The PEAR DB driver for PHP's sybase extension |
* for interacting with Sybase 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 Sterling Hughes <sterling@php.net> |
* @author Antônio Carlos Venâncio Júnior <floripa@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: sybase.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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 sybase extension |
* for interacting with Sybase databases |
* |
* These methods overload the ones declared in DB_common. |
* |
* WARNING: This driver may fail with multiple connections under the |
* same user/pass/host and different databases. |
* |
* @category Database |
* @package DB |
* @author Sterling Hughes <sterling@php.net> |
* @author Antônio Carlos Venâncio Júnior <floripa@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: 1.7.6 |
* @link http://pear.php.net/package/DB |
*/ |
class DB_sybase extends DB_common |
{ |
// {{{ properties |
/** |
* The DB driver type (mysql, oci8, odbc, etc.) |
* @var string |
*/ |
var $phptype = 'sybase'; |
/** |
* The database syntax variant to be used (db2, access, etc.), if any |
* @var string |
*/ |
var $dbsyntax = 'sybase'; |
/** |
* 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' => 'emulate', |
'new_link' => false, |
'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( |
); |
/** |
* 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_sybase() |
{ |
$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 sybase driver supports the following extra DSN options: |
* + appname The application name to use on this connection. |
* Available since PEAR DB 1.7.0. |
* + charset The character set to use on this connection. |
* 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('sybase') && |
!PEAR::loadExtension('sybase_ct')) |
{ |
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
} |
$this->dsn = $dsn; |
if ($dsn['dbsyntax']) { |
$this->dbsyntax = $dsn['dbsyntax']; |
} |
$dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost'; |
$dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false; |
$dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false; |
$dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false; |
$connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect'; |
if ($dsn['username']) { |
$this->connection = @$connect_function($dsn['hostspec'], |
$dsn['username'], |
$dsn['password'], |
$dsn['charset'], |
$dsn['appname']); |
} else { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
null, null, null, |
'The DSN did not contain a username.'); |
} |
if (!$this->connection) { |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
null, null, null, |
@sybase_get_last_message()); |
} |
if ($dsn['database']) { |
if (!@sybase_select_db($dsn['database'], $this->connection)) { |
return $this->raiseError(DB_ERROR_NODBSELECTED, |
null, null, null, |
@sybase_get_last_message()); |
} |
$this->_db = $dsn['database']; |
} |
return DB_OK; |
} |
// }}} |
// {{{ disconnect() |
/** |
* Disconnects from the database server |
* |
* @return bool TRUE on success, FALSE on failure |
*/ |
function disconnect() |
{ |
$ret = @sybase_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; |
if (!@sybase_select_db($this->_db, $this->connection)) { |
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
} |
$query = $this->modifyQuery($query); |
if (!$this->autocommit && $ismanip) { |
if ($this->transaction_opcount == 0) { |
$result = @sybase_query('BEGIN TRANSACTION', $this->connection); |
if (!$result) { |
return $this->sybaseRaiseError(); |
} |
} |
$this->transaction_opcount++; |
} |
$result = @sybase_query($query, $this->connection); |
if (!$result) { |
return $this->sybaseRaiseError(); |
} |
if (is_resource($result)) { |
return $result; |
} |
// Determine which queries that should return data, and which |
// should return an error code only. |
return $ismanip ? DB_OK : $result; |
} |
// }}} |
// {{{ nextResult() |
/** |
* Move the internal sybase result pointer to the next available result |
* |
* @param a valid sybase 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) |
{ |
if ($rownum !== null) { |
if (!@sybase_data_seek($result, $rownum)) { |
return null; |
} |
} |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
if (function_exists('sybase_fetch_assoc')) { |
$arr = @sybase_fetch_assoc($result); |
} else { |
if ($arr = @sybase_fetch_array($result)) { |
foreach ($arr as $key => $value) { |
if (is_int($key)) { |
unset($arr[$key]); |
} |
} |
} |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
$arr = array_change_key_case($arr, CASE_LOWER); |
} |
} else { |
$arr = @sybase_fetch_row($result); |
} |
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); |
} |
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 @sybase_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 = @sybase_num_fields($result); |
if (!$cols) { |
return $this->sybaseRaiseError(); |
} |
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 = @sybase_num_rows($result); |
if ($rows === false) { |
return $this->sybaseRaiseError(); |
} |
return $rows; |
} |
// }}} |
// {{{ 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)) { |
$result = @sybase_affected_rows($this->connection); |
} else { |
$result = 0; |
} |
return $result; |
} |
// }}} |
// {{{ 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_sybase::createSequence(), DB_sybase::dropSequence() |
*/ |
function nextId($seq_name, $ondemand = true) |
{ |
$seqname = $this->getSequenceName($seq_name); |
if (!@sybase_select_db($this->_db, $this->connection)) { |
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
} |
$repeat = 0; |
do { |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
$result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); |
$this->popErrorHandling(); |
if ($ondemand && DB::isError($result) && |
($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) |
{ |
$repeat = 1; |
$result = $this->createSequence($seq_name); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
} elseif (!DB::isError($result)) { |
$result =& $this->query("SELECT @@IDENTITY FROM $seqname"); |
$repeat = 0; |
} else { |
$repeat = false; |
} |
} while ($repeat); |
if (DB::isError($result)) { |
return $this->raiseError($result); |
} |
$result = $result->fetchRow(DB_FETCHMODE_ORDERED); |
return $result[0]; |
} |
/** |
* 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_sybase::nextID(), DB_sybase::dropSequence() |
*/ |
function createSequence($seq_name) |
{ |
return $this->query('CREATE TABLE ' |
. $this->getSequenceName($seq_name) |
. ' (id numeric(10, 0) IDENTITY NOT NULL,' |
. ' vapor int NULL)'); |
} |
// }}} |
// {{{ 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_sybase::nextID(), DB_sybase::createSequence() |
*/ |
function dropSequence($seq_name) |
{ |
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
} |
// }}} |
// {{{ 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 (!@sybase_select_db($this->_db, $this->connection)) { |
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
} |
$result = @sybase_query('COMMIT', $this->connection); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->sybaseRaiseError(); |
} |
} |
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 (!@sybase_select_db($this->_db, $this->connection)) { |
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
} |
$result = @sybase_query('ROLLBACK', $this->connection); |
$this->transaction_opcount = 0; |
if (!$result) { |
return $this->sybaseRaiseError(); |
} |
} |
return DB_OK; |
} |
// }}} |
// {{{ sybaseRaiseError() |
/** |
* 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_sybase::errorNative(), DB_sybase::errorCode() |
*/ |
function sybaseRaiseError($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 |
* |
* @return string the DBMS' error message |
*/ |
function errorNative() |
{ |
return @sybase_get_last_message(); |
} |
// }}} |
// {{{ 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( |
'/Incorrect syntax near/' |
=> DB_ERROR_SYNTAX, |
'/^Unclosed quote before the character string [\"\'].*[\"\']\./' |
=> DB_ERROR_SYNTAX, |
'/Implicit conversion (from datatype|of NUMERIC value)/i' |
=> DB_ERROR_INVALID_NUMBER, |
'/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./' |
=> DB_ERROR_NOSUCHTABLE, |
'/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./' |
=> DB_ERROR_ACCESS_VIOLATION, |
'/^.+ permission denied on object .+, database .+, owner .+/' |
=> DB_ERROR_ACCESS_VIOLATION, |
'/^.* permission denied, database .+, owner .+/' |
=> DB_ERROR_ACCESS_VIOLATION, |
'/[^.*] not found\./' |
=> DB_ERROR_NOSUCHTABLE, |
'/There is already an object named/' |
=> DB_ERROR_ALREADY_EXISTS, |
'/Invalid column name/' |
=> DB_ERROR_NOSUCHFIELD, |
'/does not allow null values/' |
=> DB_ERROR_CONSTRAINT_NOT_NULL, |
'/Command has been aborted/' |
=> DB_ERROR_CONSTRAINT, |
'/^Cannot drop the index .* because it doesn\'t exist/i' |
=> DB_ERROR_NOT_FOUND, |
'/^There is already an index/i' |
=> DB_ERROR_ALREADY_EXISTS, |
'/^There are fewer columns in the INSERT statement than values specified/i' |
=> DB_ERROR_VALUE_COUNT_ON_ROW, |
); |
} |
foreach ($error_regexps as $regexp => $code) { |
if (preg_match($regexp, $errormsg)) { |
return $code; |
} |
} |
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() |
* @since Method available since Release 1.6.0 |
*/ |
function tableInfo($result, $mode = null) |
{ |
if (is_string($result)) { |
/* |
* Probably received a table name. |
* Create a result resource identifier. |
*/ |
if (!@sybase_select_db($this->_db, $this->connection)) { |
return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
} |
$id = @sybase_query("SELECT * FROM $result WHERE 1=0", |
$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->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA); |
} |
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
$case_func = 'strtolower'; |
} else { |
$case_func = 'strval'; |
} |
$count = @sybase_num_fields($id); |
$res = array(); |
if ($mode) { |
$res['num_fields'] = $count; |
} |
for ($i = 0; $i < $count; $i++) { |
$f = @sybase_fetch_field($id, $i); |
// column_source is often blank |
$res[$i] = array( |
'table' => $got_string |
? $case_func($result) |
: $case_func($f->column_source), |
'name' => $case_func($f->name), |
'type' => $f->type, |
'len' => $f->max_length, |
'flags' => '', |
); |
if ($res[$i]['table']) { |
$res[$i]['flags'] = $this->_sybase_field_flags( |
$res[$i]['table'], $res[$i]['name']); |
} |
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) { |
@sybase_free_result($id); |
} |
return $res; |
} |
// }}} |
// {{{ _sybase_field_flags() |
/** |
* Get the flags for a field |
* |
* Currently supports: |
* + <samp>unique_key</samp> (unique index, unique check or primary_key) |
* + <samp>multiple_key</samp> (multi-key index) |
* |
* @param string $table the table name |
* @param string $column the field name |
* |
* @return string space delimited string of flags. Empty string if none. |
* |
* @access private |
*/ |
function _sybase_field_flags($table, $column) |
{ |
static $tableName = null; |
static $flags = array(); |
if ($table != $tableName) { |
$flags = array(); |
$tableName = $table; |
// get unique/primary keys |
$res = $this->getAll("sp_helpindex $table", DB_FETCHMODE_ASSOC); |
if (!isset($res[0]['index_description'])) { |
return ''; |
} |
foreach ($res as $val) { |
$keys = explode(', ', trim($val['index_keys'])); |
if (sizeof($keys) > 1) { |
foreach ($keys as $key) { |
$this->_add_flag($flags[$key], 'multiple_key'); |
} |
} |
if (strpos($val['index_description'], 'unique')) { |
foreach ($keys as $key) { |
$this->_add_flag($flags[$key], 'unique_key'); |
} |
} |
} |
} |
if (array_key_exists($column, $flags)) { |
return(implode(' ', $flags[$column])); |
} |
return ''; |
} |
// }}} |
// {{{ _add_flag() |
/** |
* Adds a string to the flags array if the flag is not yet in there |
* - if there is no flag present the array is created |
* |
* @param array $array reference of flags array to add a value to |
* @param mixed $value value to add to the flag array |
* |
* @return void |
* |
* @access private |
*/ |
function _add_flag(&$array, $value) |
{ |
if (!is_array($array)) { |
$array = array($value); |
} elseif (!in_array($value, $array)) { |
array_push($array, $value); |
} |
} |
// }}} |
// {{{ 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 name FROM sysobjects WHERE type = 'U'" |
. ' ORDER BY name'; |
case 'views': |
return "SELECT name FROM sysobjects WHERE type = 'V'"; |
default: |
return null; |
} |
} |
// }}} |
} |
/* |
* Local variables: |
* tab-width: 4 |
* c-basic-offset: 4 |
* End: |
*/ |
?> |
/tags/Racine_livraison_narmer/api/pear/DB/common.php |
---|
New file |
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,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $ |
* @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: 1.7.6 |
* @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: |
*/ |
?> |