2008-09-30 14:38:47 +01:00
< ? php
/**
* Object Based Database Query Builder and data store
*
2009-07-16 04:35:20 +01:00
* For PHP versions 4 , 5 and 6
2008-09-30 14:38:47 +01:00
*
2009-07-16 04:35:20 +01:00
* LICENSE : This source file is subject to version 3.01 of the PHP license
2008-09-30 14:38:47 +01:00
* that is available through the world - wide - web at the following URI :
2009-07-16 04:35:20 +01:00
* http :// www . php . net / license / 3_01 . txt . If you did not receive a copy of
2008-09-30 14:38:47 +01:00
* 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 - 2006 The PHP Group
2009-07-16 04:35:20 +01:00
* @ license http :// www . php . net / license / 3_01 . txt PHP License 3.01
2017-07-10 12:25:04 +01:00
* @ version CVS : $Id : DataObject . php 336751 2015 - 05 - 12 04 : 39 : 50 Z alan_k $
2008-09-30 14:38:47 +01:00
* @ link http :// pear . php . net / package / DB_DataObject
*/
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
2019-04-16 00:20:20 +01:00
/* ===========================================================================
2008-09-30 14:38:47 +01:00
*
* !!!!!!!!!!!!! W A R N I N G !!!!!!!!!!!
*
2019-04-16 00:20:20 +01:00
* 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
2008-09-30 14:38:47 +01:00
* this file . reducing the optimization level may also solve the segfault .
* ===========================================================================
*/
/**
* Needed classes
* - we use getStaticProperty from PEAR pretty extensively ( cant remove it ATM )
*/
require_once 'PEAR.php' ;
/**
* We are duping fetchmode constants to be compatible with
* both DB and MDB2
*/
2019-04-16 00:20:20 +01:00
define ( 'DB_DATAOBJECT_FETCHMODE_ORDERED' , 1 );
define ( 'DB_DATAOBJECT_FETCHMODE_ASSOC' , 2 );
2008-09-30 14:38:47 +01:00
/**
* these are constants for the get_table array
2019-11-02 00:26:25 +00:00
* used to determine what type of escaping is required around the object vars .
2008-09-30 14:38:47 +01:00
*/
2019-04-16 00:20:20 +01:00
define ( 'DB_DATAOBJECT_INT' , 1 ); // does not require ''
define ( 'DB_DATAOBJECT_STR' , 2 ); // requires ''
2008-09-30 14:38:47 +01:00
define ( 'DB_DATAOBJECT_DATE' , 4 ); // is date #TODO
define ( 'DB_DATAOBJECT_TIME' , 8 ); // is time #TODO
define ( 'DB_DATAOBJECT_BOOL' , 16 ); // is boolean #TODO
2019-04-16 00:20:20 +01:00
define ( 'DB_DATAOBJECT_TXT' , 32 ); // is long text #TODO
2008-09-30 14:38:47 +01:00
define ( 'DB_DATAOBJECT_BLOB' , 64 ); // is blob type
2019-04-16 00:20:20 +01:00
define ( 'DB_DATAOBJECT_NOTNULL' , 128 ); // not null col.
define ( 'DB_DATAOBJECT_MYSQLTIMESTAMP' , 256 ); // mysql timestamps (ignored by update/insert)
2008-09-30 14:38:47 +01:00
/*
* Define this before you include DataObjects . php to disable overload - if it segfaults due to Zend optimizer ..
*/
2019-04-16 00:20:20 +01:00
//define('DB_DATAOBJECT_NO_OVERLOAD',true)
2008-09-30 14:38:47 +01:00
/**
* 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 )
*/
2019-04-16 00:20:20 +01:00
define ( 'DB_DATAOBJECT_ERROR_INVALIDARGS' , - 1 ); // wrong args to function
define ( 'DB_DATAOBJECT_ERROR_NODATA' , - 2 ); // no data available
2008-09-30 14:38:47 +01:00
define ( 'DB_DATAOBJECT_ERROR_INVALIDCONFIG' , - 3 ); // something wrong with the config
2019-04-16 00:20:20 +01:00
define ( 'DB_DATAOBJECT_ERROR_NOCLASS' , - 4 ); // no class exists
define ( 'DB_DATAOBJECT_ERROR_INVALID_CALL' , - 7 ); // overlad getter/setter failure
2008-09-30 14:38:47 +01:00
/**
* 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 !
*/
2019-04-27 18:21:14 +01:00
$GLOBALS [ '_DB_DATAOBJECT' ][ 'RESULTS' ] = array ();
2008-09-30 14:38:47 +01:00
$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 ;
2019-11-02 00:26:25 +00:00
/**
* @ package DB_DataObject
* @ author Alan Knowles < alan @ akbkhome . com >
*/
class DB_DataObject
2008-09-30 14:38:47 +01:00
{
2019-04-16 00:20:20 +01:00
/**
* The Version - use this to check feature changes
*
* @ access private
* @ var string
*/
public $_DB_DataObject_version = " 1.11.3 " ;
2008-09-30 14:38:47 +01:00
/**
* The Database table ( used by table extends )
*
* @ access private
* @ var string
*/
2019-04-16 00:20:20 +01:00
public $__table = '' ; // database table
2008-09-30 14:38:47 +01:00
/**
* The Number of rows returned from a query
*
* @ access public
* @ var int
*/
2019-04-16 00:20:20 +01:00
public $N = 0 ; // Number of rows returned from a query
2008-09-30 14:38:47 +01:00
/* ============================================================= */
/* Major Public Methods */
/* (designed to be optionally then called with parent::method()) */
/* ============================================================= */
/**
2019-04-27 18:21:14 +01:00
* 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 ..
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access private
* @ var string
*/
public $_database_dsn = '' ;
/**
* The Database connection id ( md5 sum of databasedsn )
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access private
* @ var string
*/
public $_database_dsn_md5 = '' ;
/**
* The Database name
* created in __connection
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access private
* @ var string
*/
public $_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
*/
public $_query = array (
'condition' => '' , // the WHERE condition
'group_by' => '' , // the GROUP BY condition
'order_by' => '' , // the ORDER BY condition
'having' => '' , // the HAVING condition
'useindex' => '' , // the USE INDEX condition
'limit_start' => '' , // the LIMIT condition
'limit_count' => '' , // the LIMIT condition
'data_select' => '*' , // the columns to be SELECTed
'unions' => array (), // the added unions,
'derive_table' => '' , // derived table name (BETA)
'derive_select' => '' , // derived table select (BETA)
);
/**
* Database result id ( references global $_DB_DataObject [ results ]
*
* @ access private
* @ var integer
*/
public $_DB_resultid ;
/**
* ResultFields - on the last call to fetch (), resultfields is sent here ,
* so we can clean up the memory .
2008-09-30 14:38:47 +01:00
*
* @ access public
2019-04-27 18:21:14 +01:00
* @ var array
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public $_resultFields = false ;
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* Have the links been loaded ?
* if they have it contains a array of those variables .
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access private
* @ var boolean | array
*/
public $_link_loaded = false ;
/**
* The JOIN condition
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access private
* @ var string
*/
public $_join = '' ;
/**
* Last Error that has occured
* - use $this -> _lastError or
* $last_error = PEAR :: getStaticProperty ( 'DB_DataObject' , 'lastError' );
2013-08-12 11:32:39 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access public
* @ var object PEAR_Error ( or false )
*/
public $_lastError = false ;
/**
* sets and returns debug level
* eg . DB_DataObject :: debugLevel ( 4 );
2013-08-12 11:32:39 +01:00
*
2019-04-27 18:21:14 +01:00
* @ param int $v level
* @ access public
2019-11-02 00:26:25 +00:00
* @ return int | void
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public static function debugLevel ( $v = null )
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
( new DB_DataObject ) -> _loadConfig ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( $v !== null ) {
$r = isset ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ]) ? $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ] : 0 ;
$_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ] = $v ;
return $r ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return isset ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ]) ? $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ] : 0 ;
2008-09-30 14:38:47 +01:00
}
2011-03-26 18:45:15 +00:00
/**
2019-04-27 18:21:14 +01:00
* Define the global $_DB_DATAOBJECT [ 'CONFIG' ] as an alias to PEAR :: getStaticProperty ( 'DB_DataObject' , 'options' );
2019-04-16 00:20:20 +01:00
*
2019-04-27 18:21:14 +01:00
* 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 void an error object
2011-03-26 18:45:15 +00:00
*/
2019-04-27 18:21:14 +01:00
public function _loadConfig ()
2011-03-26 18:45:15 +00:00
{
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
$_DB_DATAOBJECT [ 'CONFIG' ] = & ( new PEAR ) -> getStaticProperty ( 'DB_DataObject' , 'options' );
return null ;
2011-03-26 18:45:15 +00:00
}
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* ( deprecated - use :: get / and your own caching method )
* @ param $class
* @ param $k
* @ param null $v
* @ return bool
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public static function staticGet ( $class , $k , $v = null )
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
$lclass = strtolower ( $class );
2008-09-30 14:38:47 +01:00
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
2019-04-27 18:21:14 +01:00
( new DB_DataObject ) -> _loadConfig ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$key = " $k : $v " ;
if ( $v === null ) {
$key = $k ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
( new DB_DataObject ) -> debug ( " $class $key " , " STATIC GET - TRY CACHE " );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CACHE' ][ $lclass ][ $key ])) {
return $_DB_DATAOBJECT [ 'CACHE' ][ $lclass ][ $key ];
2011-03-26 18:45:15 +00:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
( new DB_DataObject ) -> debug ( " $class $key " , " STATIC GET - NOT IN CACHE " );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$obj = DB_DataObject :: factory ( substr ( $class , strlen ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'class_prefix' ])));
if (( new PEAR ) -> isError ( $obj )) {
$dor = new DB_DataObject ();
$dor -> raiseError ( " could not autoload $class " , DB_DATAOBJECT_ERROR_NOCLASS );
$r = false ;
return $r ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! isset ( $_DB_DATAOBJECT [ 'CACHE' ][ $lclass ])) {
$_DB_DATAOBJECT [ 'CACHE' ][ $lclass ] = array ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! $obj -> get ( $k , $v )) {
$dor = new DB_DataObject ();
$dor -> raiseError ( " No Data return from get $k $v " , DB_DATAOBJECT_ERROR_NODATA );
$r = false ;
return $r ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$_DB_DATAOBJECT [ 'CACHE' ][ $lclass ][ $key ] = $obj ;
return $_DB_DATAOBJECT [ 'CACHE' ][ $lclass ][ $key ];
2008-09-30 14:38:47 +01:00
}
/**
2019-04-27 18:21:14 +01:00
* Debugger . - use this in your extended classes to output debugging information .
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* Uses DB_DataObject :: DebugLevel ( x ) to turn it on
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ param string $message - message to output
* @ param int $logtype - bold at start
* @ param int $level - output level
* @ return void
* @ access public
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function debug ( $message , $logtype = 0 , $level = 1 )
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ]) ||
( is_numeric ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ]) && $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ] < $level )) {
return null ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// 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 );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
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 );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! ini_get ( 'html_errors' )) {
echo " $class : $logtype : $message\n " ;
flush ();
return null ;
2011-03-26 18:45:15 +00:00
}
2019-04-27 18:21:14 +01:00
if ( ! is_string ( $message )) {
$message = print_r ( $message , true );
2011-03-26 18:45:15 +00:00
}
2019-04-27 18:21:14 +01:00
$colorize = ( $logtype == 'ERROR' ) ? '<font color="red">' : '<font>' ;
echo " <code> { $colorize } <B> $class : $logtype :</B> " . nl2br ( htmlspecialchars ( $message )) . " </font></code><BR> \n " ;
2011-03-26 18:45:15 +00:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* 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' )
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* table name can bedatabasename / table
* - and allow modular dataobjects to be written ..
* ( this also helps proxy creation )
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* Experimental Support for Multi - Database factory eg . mydatabase . mytable
*
*
* @ param string $table tablename ( use blank to create a new instance of the same class . )
* @ access private
* @ return DataObject | PEAR | PEAR_Error | true
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public static function factory ( $table = '' )
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
// multi-database support.. - experimental.
$database = '' ;
if ( strpos ( $table , '/' ) !== false ) {
list ( $database , $table ) = explode ( '.' , $table , 2 );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
( new DB_DataObject ) -> _loadConfig ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// no configuration available for database
if ( ! empty ( $database ) && empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'database_' . $database ])) {
$do = new DB_DataObject ();
$do -> raiseError (
" unable to find database_ { $database } in Configuration, It is required for factory with database " ,
0 ,
PEAR_ERROR_DIE
);
2008-09-30 14:38:47 +01:00
}
2011-03-26 18:45:15 +00:00
2019-04-27 18:21:14 +01:00
/*
if ( $table === '' ) {
if ( is_a ( $this , 'DB_DataObject' ) && strlen ( $this -> tableName ())) {
$table = $this -> tableName ();
} else {
return DB_DataObject :: raiseError (
" factory did not recieve a table name " ,
DB_DATAOBJECT_ERROR_INVALIDARGS );
}
2011-03-26 18:45:15 +00:00
}
2019-04-27 18:21:14 +01:00
*/
// does this need multi db support??
$cp = isset ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'class_prefix' ]) ?
explode ( PATH_SEPARATOR , $_DB_DATAOBJECT [ 'CONFIG' ][ 'class_prefix' ]) : '' ;
2011-03-26 18:45:15 +00:00
2019-04-27 18:21:14 +01:00
//print_r($cp);
// multiprefix support.
$tbl = preg_replace ( '/[^A-Z0-9]/i' , '_' , ucfirst ( $table ));
if ( is_array ( $cp )) {
$class = array ();
foreach ( $cp as $cpr ) {
$ce = substr ( phpversion (), 0 , 1 ) > 4 ? class_exists ( $cpr . $tbl , false ) : class_exists ( $cpr . $tbl );
if ( $ce ) {
$class = $cpr . $tbl ;
break ;
}
$class [] = $cpr . $tbl ;
}
} else {
$class = $tbl ;
$ce = substr ( phpversion (), 0 , 1 ) > 4 ? class_exists ( $class , false ) : class_exists ( $class );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$rclass = $ce ? $class : ( new DB_DataObject ) -> _autoloadClass ( $class , $table );
// proxy = full|light
if ( ! $rclass && isset ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'proxy' ])) {
( new DB_DataObject ) -> debug ( " FAILED TO Autoload $database . $table - using proxy. " , " FACTORY " , 1 );
$proxyMethod = 'getProxy' . $_DB_DATAOBJECT [ 'CONFIG' ][ 'proxy' ];
// if you have loaded (some other way) - dont try and load it again..
class_exists ( 'DB_DataObject_Generator' ) ? '' :
//require_once 'DB/DataObject/Generator.php';
require_once 'Generator.php' ;
$d = new DB_DataObject ;
$d -> __table = $table ;
$ret = $d -> _connect ();
if ( is_object ( $ret ) && is_a ( $ret , 'PEAR_Error' )) {
return $ret ;
}
$x = new DB_DataObject_Generator ;
return $x -> $proxyMethod ( $d -> _database , $table );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! $rclass || ! class_exists ( $rclass )) {
$dor = new DB_DataObject ();
return $dor -> raiseError (
" factory could not find class " .
( is_array ( $class ) ? implode ( PATH_SEPARATOR , $class ) : $class ) .
" from $table " ,
DB_DATAOBJECT_ERROR_INVALIDCONFIG
);
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$ret = new $rclass ();
if ( ! empty ( $database )) {
( new DB_DataObject ) -> debug ( " Setting database to $database " , " FACTORY " , 1 );
$ret -> database ( $database );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return $ret ;
2008-09-30 14:38:47 +01:00
}
/**
2019-04-27 18:21:14 +01:00
* 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 !
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ param int $message message
* @ param int $type type
* @ param int $behaviour behaviour ( die or continue ! );
2008-09-30 14:38:47 +01:00
* @ access public
2019-04-27 18:21:14 +01:00
* @ return error | int | object
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function raiseError ( $message , $type = null , $behaviour = null )
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
if ( $behaviour == PEAR_ERROR_DIE && ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'dont_die' ])) {
$behaviour = null ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$error = & ( new PEAR ) -> getStaticProperty ( 'DB_DataObject' , 'lastError' );
// no checks for production here?....... - we log errors before we throw them.
DB_DataObject :: debug ( $message , 'ERROR' , 1 );
if (( new PEAR ) -> isError ( $message )) {
$error = $message ;
} else {
2019-07-01 22:42:37 +01:00
require_once 'DB/DataObject/Error.php' ;
2019-04-27 18:21:14 +01:00
$dor = new PEAR ();
$error = $dor -> raiseError (
$message ,
$type ,
$behaviour ,
$opts = null ,
$userinfo = null ,
'DB_DataObject_Error'
);
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// this will never work totally with PHP's object model.
// as this is passed on static calls (like staticGet in our case)
$_DB_DATAOBJECT [ 'LASTERROR' ] = $error ;
if ( isset ( $this ) && is_object ( $this ) && is_subclass_of ( $this , 'db_dataobject' )) {
$this -> _lastError = $error ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return $error ;
2008-09-30 14:38:47 +01:00
}
/**
2019-04-27 18:21:14 +01:00
* autoload Class
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ param string | array $class Class
* @ param bool $table Table trying to load .
* @ return string classname on Success
* @ access private
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function _autoloadClass ( $class , $table = false )
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
DB_DataObject :: _loadConfig ();
}
$class_prefix = empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'class_prefix' ]) ?
'' : $_DB_DATAOBJECT [ 'CONFIG' ][ 'class_prefix' ];
$table = $table ? $table : substr ( $class , strlen ( $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' ])) {
2008-09-30 14:38:47 +01:00
return false ;
}
2019-04-27 18:21:14 +01:00
// support for:
// class_location = mydir/ => maps to mydir/Tablename.php
// class_location = mydir/myfile_%s.php => maps to mydir/myfile_Tablename
// with directory sepr
// class_location = mydir/:mydir2/: => tries all of thes locations.
$cl = $_DB_DATAOBJECT [ 'CONFIG' ][ 'class_location' ];
switch ( true ) {
case ( strpos ( $cl , '%s' ) !== false ) :
$file = sprintf ( $cl , preg_replace ( '/[^A-Z0-9]/i' , '_' , ucfirst ( $table )));
break ;
case ( strpos ( $cl , PATH_SEPARATOR ) !== false ) :
$file = array ();
foreach ( explode ( PATH_SEPARATOR , $cl ) as $p ) {
$file [] = $p . '/' . preg_replace ( '/[^A-Z0-9]/i' , '_' , ucfirst ( $table )) . " .php " ;
}
break ;
default :
$file = $cl . '/' . preg_replace ( '/[^A-Z0-9]/i' , '_' , ucfirst ( $table )) . " .php " ;
break ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$cls = is_array ( $class ) ? $class : array ( $class );
if ( is_array ( $file ) || ! file_exists ( $file )) {
$found = false ;
$file = is_array ( $file ) ? $file : array ( $file );
$search = implode ( PATH_SEPARATOR , $file );
foreach ( $file as $f ) {
foreach ( explode ( PATH_SEPARATOR , '' . PATH_SEPARATOR . ini_get ( 'include_path' )) as $p ) {
$ff = empty ( $p ) ? $f : " $p / $f " ;
if ( file_exists ( $ff )) {
$file = $ff ;
$found = true ;
break ;
}
}
if ( $found ) {
break ;
}
}
if ( ! $found ) {
$dor = new DB_DataObject ();
$dor -> raiseError (
" autoload:Could not find class " . implode ( ',' , $cls ) .
" using class_location value : " . $search .
" using include_path value : " . ini_get ( 'include_path' ),
DB_DATAOBJECT_ERROR_INVALIDCONFIG
);
return false ;
}
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
include_once $file ;
$ce = false ;
foreach ( $cls as $c ) {
$ce = substr ( phpversion (), 0 , 1 ) > 4 ? class_exists ( $c , false ) : class_exists ( $c );
if ( $ce ) {
$class = $c ;
break ;
}
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! $ce ) {
$dor = new DB_DataObject ();
$dor -> raiseError (
" autoload:Could not autoload " . implode ( ',' , $cls ),
DB_DATAOBJECT_ERROR_INVALIDCONFIG
);
return false ;
}
return $class ;
2008-09-30 14:38:47 +01:00
}
2015-06-06 18:13:57 +01:00
/**
2019-04-27 18:21:14 +01:00
* connects to the database
2015-06-06 18:13:57 +01:00
*
*
2019-04-27 18:21:14 +01:00
* 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 .
2019-04-16 00:20:20 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access private
* @ return error | PEAR | true
2015-06-06 18:13:57 +01:00
*/
2019-04-27 18:21:14 +01:00
public function _connect ()
2015-06-06 18:13:57 +01:00
{
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
$this -> _loadConfig ();
2015-06-06 18:13:57 +01:00
}
2019-04-27 18:21:14 +01:00
// Set database driver for reference
$db_driver = empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'db_driver' ]) ?
'DB' : $_DB_DATAOBJECT [ 'CONFIG' ][ 'db_driver' ];
// is it already connected ?
if ( $this -> _database_dsn_md5 && ! empty ( $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ])) {
// connection is an error...
if (( new 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 ( empty ( $this -> _database )) {
$this -> _database = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ] -> dsn [ 'database' ];
$hasGetDatabase = method_exists ( $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ], 'getDatabase' );
$this -> _database = ( $db_driver != 'DB' && $hasGetDatabase )
? $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ] -> getDatabase ()
: $_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 );
}
if ( $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ] -> dsn [ 'phptype' ] == 'ibase' ) {
$this -> _database = substr ( basename ( $this -> _database ), 0 , - 4 );
}
}
// theoretically we have a md5, it's listed in connections and it's not an error.
// so everything is ok!
return true ;
2015-06-06 18:13:57 +01:00
}
2019-04-27 18:21:14 +01:00
// it's not currently connected!
// try and work out what to use for the dsn !
$options = $_DB_DATAOBJECT [ 'CONFIG' ];
// if the databse dsn dis defined in the object..
$dsn = isset ( $this -> _database_dsn ) ? $this -> _database_dsn : null ;
if ( ! $dsn ) {
if ( ! $this -> _database && ! strlen ( $this -> tableName ())) {
$this -> _database = isset ( $options [ " table_ { $this -> tableName () } " ]) ? $options [ " table_ { $this -> tableName () } " ] : null ;
}
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " Checking for database specific ini (' { $this -> _database } ') : database_ { $this -> _database } in options " , " CONNECT " );
}
if ( $this -> _database && ! empty ( $options [ " database_ { $this -> _database } " ])) {
$dsn = $options [ " database_ { $this -> _database } " ];
} elseif ( ! empty ( $options [ 'database' ])) {
$dsn = $options [ 'database' ];
}
2015-06-06 18:13:57 +01:00
}
2019-04-27 18:21:14 +01:00
// if still no database...
if ( ! $dsn ) {
return $this -> raiseError (
" No database name / dsn found anywhere " ,
DB_DATAOBJECT_ERROR_INVALIDCONFIG ,
PEAR_ERROR_DIE
);
}
if ( is_string ( $dsn )) {
$this -> _database_dsn_md5 = md5 ( $dsn );
} else {
/// support array based dsn's
$this -> _database_dsn_md5 = md5 ( serialize ( $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 ) {
$hasGetDatabase = method_exists ( $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ], 'getDatabase' );
$this -> _database = ( $db_driver != 'DB' && $hasGetDatabase )
? $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ] -> getDatabase ()
: $_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 TP DATABASE : " . $this -> _database , " CONNECT " , 3 );
/* actualy make a connection */
$this -> debug ( print_r ( $dsn , true ) . " { $this -> _database_dsn_md5 } " , " CONNECT " , 3 );
}
2019-11-02 00:26:25 +00:00
// Note this is verbose deliberately!
2019-04-27 18:21:14 +01:00
if ( $db_driver == 'DB' ) {
/* PEAR DB connect */
// this allows the setings of compatibility on DB
$db_options = ( new 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 MDB2 */
require_once 'MDB2.php' ;
// this allows the setings of compatibility on MDB2
$db_options = ( new PEAR ) -> getStaticProperty ( 'MDB2' , 'options' );
$db_options = is_array ( $db_options ) ? $db_options : array ();
$db_options [ 'portability' ] = isset ( $db_options [ 'portability' ])
? $db_options [ 'portability' ] : MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_FIX_CASE ;
$_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ] = MDB2 :: connect ( $dsn , $db_options );
}
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( print_r ( $_DB_DATAOBJECT [ 'CONNECTIONS' ], true ), " CONNECT " , 5 );
2015-06-06 18:13:57 +01:00
}
2019-04-27 18:21:14 +01:00
if (( new 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 ( empty ( $this -> _database )) {
$hasGetDatabase = method_exists ( $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ], 'getDatabase' );
$this -> _database = ( $db_driver != 'DB' && $hasGetDatabase )
? $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ] -> getDatabase ()
: $_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 );
}
}
2019-11-02 00:26:25 +00:00
// Oracle needs to optimise for portability - not sure exactly what this does though :)
2019-04-27 18:21:14 +01:00
return true ;
2015-06-06 18:13:57 +01:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* Return or assign the name of the current table
2008-09-30 14:38:47 +01:00
*
*
2019-04-27 18:21:14 +01:00
* @ param string optinal table name to set
2008-09-30 14:38:47 +01:00
* @ access public
2019-04-27 18:21:14 +01:00
* @ return string The name of the current table
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function tableName ()
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
$args = func_get_args ();
if ( count ( $args )) {
$this -> __table = $args [ 0 ];
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( empty ( $this -> __table )) {
return '' ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'portability' ]) && $_DB_DATAOBJECT [ 'CONFIG' ][ 'portability' ] & 1 ) {
return strtolower ( $this -> __table );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return $this -> __table ;
2008-09-30 14:38:47 +01:00
}
/**
2019-04-27 18:21:14 +01:00
* Get a result using key , value .
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* 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
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* see the fetch example on how to extend this .
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* 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
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function get ( $k = null , $v = null )
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
DB_DataObject :: _loadConfig ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$keys = array ();
if ( $v === null ) {
$v = $k ;
$keys = $this -> keys ();
if ( ! $keys ) {
$this -> raiseError ( " No Keys available for { $this -> tableName () } " , DB_DATAOBJECT_ERROR_INVALIDCONFIG );
return false ;
}
$k = $keys [ 0 ];
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " $k $v " . print_r ( $keys , true ), " GET " );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( $v === null ) {
$this -> raiseError ( " No Value specified for get " , DB_DATAOBJECT_ERROR_INVALIDARGS );
return false ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$this -> $k = $v ;
return $this -> find ( 1 );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* get / set an array of table primary keys
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* set usage : $do -> keys ( 'id' , 'code' );
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* 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
2008-09-30 14:38:47 +01:00
* @ access public
2019-04-27 18:21:14 +01:00
* @ return array
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function keys ()
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
// 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 ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( isset ( $this -> _database_keys )) {
return $this -> _database_keys ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
if ( ! isset ( $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ])) {
2008-09-30 14:38:47 +01:00
$this -> _connect ();
}
2019-04-27 18:21:14 +01:00
if ( isset ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName () . " __keys " ])) {
return array_keys ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName () . " __keys " ]);
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$this -> databaseStructure ();
if ( isset ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName () . " __keys " ])) {
return array_keys ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName () . " __keys " ]);
}
return array ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* Autoload or manually load the table definitions
2008-09-30 14:38:47 +01:00
*
*
2019-04-27 18:21:14 +01:00
* usage :
* DB_DataObject :: databaseStructure ( 'databasename' ,
* parse_ini_file ( 'mydb.ini' , true ),
* parse_ini_file ( 'mydb.link.ini' , true ));
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* obviously you dont have to use ini files .. ( just return array similar to ini files .. )
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* It should append to the table structure array
*
*
2019-11-02 00:26:25 +00:00
* @ param string optional name of database to assign / read
* @ param array optional structure of database , and keys
* @ param array optional table links
2008-09-30 14:38:47 +01:00
*
* @ access public
2019-11-02 00:26:25 +00:00
* @ return true or PEAR : error on wrong parameters .. or false if no file exists ..
2019-04-27 18:21:14 +01:00
* or the array ( tablename => array ( column_name => type )) if called with 1 argument .. ( databasename )
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function databaseStructure ()
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
// Assignment code
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
if ( $args = func_get_args ()) {
if ( count ( $args ) == 1 ) {
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
// this returns all the tables and their structure..
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " Loading Generator as databaseStructure called with args " , 1 );
}
$x = new DB_DataObject ;
$x -> _database = $args [ 0 ];
$this -> _connect ();
$DB = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ];
$tables = $DB -> getListOf ( 'tables' );
class_exists ( 'DB_DataObject_Generator' ) ? '' :
//require_once 'DB/DataObject/Generator.php';
require_once 'Generator.php' ;
foreach ( $tables as $table ) {
$y = new DB_DataObject_Generator ;
$y -> fillTableSchema ( $x -> _database , $table );
}
return $_DB_DATAOBJECT [ 'INI' ][ $x -> _database ];
2008-09-30 14:38:47 +01:00
} else {
2019-04-27 18:21:14 +01:00
$_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 ];
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return true ;
2008-09-30 14:38:47 +01:00
}
}
2019-04-27 18:21:14 +01:00
if ( ! $this -> _database ) {
$this -> _connect ();
}
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
// if this table is already loaded this table..
if ( ! empty ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName ()])) {
2008-09-30 14:38:47 +01:00
return true ;
}
2019-04-27 18:21:14 +01:00
// initialize the ini data.. if empt..
if ( empty ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ])) {
$_DB_DATAOBJECT [ 'INI' ][ $this -> _database ] = array ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
DB_DataObject :: _loadConfig ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// we do not have the data for this table yet...
// if we are configured to use the proxy..
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'proxy' ])) {
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " Loading Generator to fetch Schema " , 1 );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
class_exists ( 'DB_DataObject_Generator' ) ? '' :
//require_once 'DB/DataObject/Generator.php';
require_once 'Generator.php' ;
$x = new DB_DataObject_Generator ;
$x -> fillTableSchema ( $this -> _database , $this -> tableName ());
return true ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// 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 } " ]);
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$_DB_DATAOBJECT [ 'INI' ][ $this -> _database ] = array ();
foreach ( $schemas as $ini ) {
if ( file_exists ( $ini ) && is_file ( $ini )) {
$_DB_DATAOBJECT [ 'INI' ][ $this -> _database ] = array_merge (
$_DB_DATAOBJECT [ 'INI' ][ $this -> _database ],
parse_ini_file ( $ini , true )
);
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
if ( ! is_readable ( $ini )) {
$this -> debug ( " ini file is not readable: $ini " , " databaseStructure " , 1 );
} else {
$this -> debug ( " Loaded ini file: $ini " , " databaseStructure " , 1 );
}
}
} else {
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " Missing ini file: $ini " , " databaseStructure " , 1 );
}
2008-09-30 14:38:47 +01:00
}
}
2019-11-02 00:26:25 +00:00
// are table names lower-cased..
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'portability' ]) && $_DB_DATAOBJECT [ 'CONFIG' ][ 'portability' ] & 1 ) {
foreach ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ] as $k => $v ) {
// results in duplicate cols.. but not a big issue..
$_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ strtolower ( $k )] = $v ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
}
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
// now have we loaded the structure..
if ( ! empty ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName ()])) {
return true ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// - if not try building it..
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'proxy' ])) {
class_exists ( 'DB_DataObject_Generator' ) ? '' :
//require_once 'DB/DataObject/Generator.php';
require_once 'Generator.php' ;
$x = new DB_DataObject_Generator ;
$x -> fillTableSchema ( $this -> _database , $this -> tableName ());
// should this fail!!!???
2008-09-30 14:38:47 +01:00
return true ;
}
2019-04-27 18:21:14 +01:00
$this -> debug ( " Cant find database schema: { $this -> _database } / { $this -> tableName () } \n " .
" in links file data: " . print_r ( $_DB_DATAOBJECT [ 'INI' ], true ), " databaseStructure " , 5 );
// we have to die here!! - it causes chaos if we dont (including looping forever!)
$this -> raiseError ( " Unable to load schema for database and table (turn debugging up to 5 for full error message) " , DB_DATAOBJECT_ERROR_INVALIDARGS , PEAR_ERROR_DIE );
2008-09-30 14:38:47 +01:00
return false ;
}
/**
2019-04-27 18:21:14 +01:00
* find results , either normal or crosstable
2008-09-30 14:38:47 +01:00
*
* for example
*
* $object = new mytable ();
2019-04-27 18:21:14 +01:00
* $object -> ID = 1 ;
* $object -> find ();
2008-09-30 14:38:47 +01:00
*
*
2019-04-27 18:21:14 +01:00
* will set $object -> N to number of rows , and expects next command to fetch rows
* will return $object -> N
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* if an error occurs $object -> N will be set to false and return value will also be false ;
* if numRows is not supported it will
*
*
* @ param boolean $n Fetch first result
* @ access public
* @ return mixed ( number of rows returned , or true if numRows fetching is not supported )
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function find ( $n = false )
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
if ( $this -> _query === false ) {
$this -> raiseError (
" You cannot do two queries on the same object (copy it before finding) " ,
DB_DATAOBJECT_ERROR_INVALIDARGS
);
2008-09-30 14:38:47 +01:00
return false ;
}
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
2019-04-27 18:21:14 +01:00
DB_DataObject :: _loadConfig ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( $n , " find " , 1 );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! strlen ( $this -> tableName ())) {
// xdebug can backtrace this!
trigger_error ( " NO \$ __table SPECIFIED in class definition " , E_USER_ERROR );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$this -> N = 0 ;
$query_before = $this -> _query ;
$this -> _build_condition ( $this -> table ());
2008-09-30 14:38:47 +01:00
$this -> _connect ();
2013-08-12 11:32:39 +01:00
$DB = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ];
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
$sql = $this -> _build_select ();
foreach ( $this -> _query [ 'unions' ] as $union_ar ) {
$sql .= $union_ar [ 1 ] . $union_ar [ 0 ] -> _build_select () . " \n " ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$sql .= $this -> _query [ 'order_by' ] . " \n " ;
/* We are checking for method modifyLimitQuery as it is PEAR DB specific */
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 MDB2! */
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' ]);
}
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$err = $this -> _query ( $sql );
if ( is_a ( $err , 'PEAR_Error' )) {
2008-09-30 14:38:47 +01:00
return false ;
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
2019-04-27 18:21:14 +01:00
$this -> debug ( " CHECK autofetchd $n " , " find " , 1 );
2013-08-12 11:32:39 +01:00
}
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
// find(true)
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
$ret = $this -> N ;
if ( ! $ret && ! empty ( $_DB_DATAOBJECT [ 'RESULTS' ][ $this -> _DB_resultid ])) {
// clear up memory if nothing found!?
unset ( $_DB_DATAOBJECT [ 'RESULTS' ][ $this -> _DB_resultid ]);
}
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
if ( $n && $this -> N > 0 ) {
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " ABOUT TO AUTOFETCH " , " find " , 1 );
}
$fs = $this -> fetch ();
// if fetch returns false (eg. failed), then the backend doesnt support numRows (eg. ret=true)
// - hence find() also returns false..
$ret = ( $ret === true ) ? $fs : $ret ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " DONE " , " find " , 1 );
}
$this -> _query = $query_before ;
2008-09-30 14:38:47 +01:00
return $ret ;
}
/* ==================================================== */
/* Major Private Vars */
/* ==================================================== */
/**
2019-04-27 18:21:14 +01:00
* Builds the WHERE based on the values of of this object
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ 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 .. )
2008-09-30 14:38:47 +01:00
* @ access private
2019-04-27 18:21:14 +01:00
* @ return string
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function _build_condition ( $keys , $filter = array (), $negative_filter = array ())
{
global $_DB_DATAOBJECT ;
$this -> _connect ();
$DB = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ];
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
$quoteIdentifiers = ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'quote_identifiers' ]);
$options = $_DB_DATAOBJECT [ 'CONFIG' ];
// if we dont have query vars.. - reset them.
if ( $this -> _query === false ) {
$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 -> tableName ()) . '.' . $DB -> quoteIdentifier ( $k ))
: " { $this -> tableName () } . { $k } " ;
if ( is_object ( $this -> $k ) && is_a ( $this -> $k , 'DB_DataObject_Cast' )) {
$dbtype = $DB -> dsn [ " phptype " ];
$value = $this -> $k -> toString ( $v , $DB );
if (( new 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 ( ! ( $v & DB_DATAOBJECT_NOTNULL ) && DB_DataObject :: _is_null ( $this , $k )) {
$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 ));
}
return " " ;
}
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* Adds a condition to the WHERE statement , defaults to AND
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* $object -> whereAdd (); //reset or cleaer ewhwer
* $object -> whereAdd ( " ID > 20 " );
* $object -> whereAdd ( " age > 20 " , " OR " );
*
* @ param bool $cond condition
* @ param string $logic optional logic " OR " ( defaults to " AND " )
* @ return string | PEAR :: Error - previous condition or Error when invalid args found
* @ access public
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function whereAdd ( $cond = false , $logic = 'AND' )
{
// for PHP5.2.3 - there is a bug with setting array properties of an object.
$_query = $this -> _query ;
if ( ! isset ( $this -> _query ) || ( $_query === false )) {
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' ];
$_query [ 'condition' ] = '' ;
$this -> _query = $_query ;
return preg_replace ( '/^\s+WHERE\s+/' , '' , $r );
}
// check input...= 0 or ' ' == error!
if ( ! trim ( $cond )) {
return $this -> raiseError ( " WhereAdd: No Valid Arguments " , DB_DATAOBJECT_ERROR_INVALIDARGS );
}
$r = $_query [ 'condition' ];
if ( $_query [ 'condition' ]) {
$_query [ 'condition' ] .= " { $logic } ( { $cond } ) " ;
$this -> _query = $_query ;
return $r ;
}
$_query [ 'condition' ] = " WHERE ( { $cond } ) " ;
$this -> _query = $_query ;
return $r ;
}
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* Evaluate whether or not a value is set to null , taking the 'disable_null_strings' option into account .
* If the value is a string set to " null " and the " disable_null_strings " option is not set to
* true , then the value is considered to be null .
* If the value is actually a PHP NULL value , and " disable_null_strings " has been set to
* the value " full " , then it will also be considered null . - this can not differenticate between not set
2019-04-16 00:20:20 +01:00
*
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ param object | array $obj_or_ar
* @ param string | false $prop prperty
* @ access private
* @ return bool object
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function _is_null ( $obj_or_ar , $prop )
{
global $_DB_DATAOBJECT ;
$isset = $prop === false ? isset ( $obj_or_ar ) :
( is_array ( $obj_or_ar ) ? isset ( $obj_or_ar [ $prop ]) : isset ( $obj_or_ar -> $prop ));
$value = $isset ?
( $prop === false ? $obj_or_ar :
( is_array ( $obj_or_ar ) ? $obj_or_ar [ $prop ] : $obj_or_ar -> $prop ))
: null ;
$options = $_DB_DATAOBJECT [ 'CONFIG' ];
$null_strings = ! isset ( $options [ 'disable_null_strings' ])
|| $options [ 'disable_null_strings' ] === false ;
$crazy_null = isset ( $options [ 'disable_null_strings' ])
&& is_string ( $options [ 'disable_null_strings' ])
&& strtolower ( $options [ 'disable_null_strings' ] === 'full' );
if ( $null_strings && $isset && is_string ( $value ) && ( strtolower ( $value ) === 'null' )) {
return true ;
}
if ( $crazy_null && ! $isset ) {
return true ;
}
return false ;
}
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* backend wrapper for quoting , as MDB2 and DB do it differently ...
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access private
* @ param $str
* @ return string quoted
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public 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 );
}
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* get / set an associative array of table columns
2008-09-30 14:38:47 +01:00
*
* @ access public
2019-04-27 18:21:14 +01:00
* @ param array key => type array
* @ return array ( associative )
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function table ()
2008-09-30 14:38:47 +01:00
{
2019-04-27 18:21:14 +01:00
// 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 ];
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( isset ( $this -> _database_fields )) {
return $this -> _database_fields ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
global $_DB_DATAOBJECT ;
if ( ! isset ( $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ])) {
$this -> _connect ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( isset ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName ()])) {
return $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName ()];
2013-08-12 11:32:39 +01:00
}
2019-04-27 18:21:14 +01:00
$this -> databaseStructure ();
$ret = array ();
if ( isset ( $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName ()])) {
$ret = $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName ()];
2008-09-30 14:38:47 +01:00
}
return $ret ;
}
/**
2019-04-27 18:21:14 +01:00
* build the basic select query .
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ access private
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function _build_select ()
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
$quoteIdentifiers = ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'quote_identifiers' ]);
if ( $quoteIdentifiers ) {
2008-09-30 14:38:47 +01:00
$this -> _connect ();
2019-04-27 18:21:14 +01:00
$DB = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ];
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$tn = ( $quoteIdentifiers ? $DB -> quoteIdentifier ( $this -> tableName ()) : $this -> tableName ());
if ( ! empty ( $this -> _query [ 'derive_table' ]) && ! empty ( $this -> _query [ 'derive_select' ])) {
// this is a derived select..
// not much support in the api yet..
$sql = 'SELECT ' .
$this -> _query [ 'derive_select' ]
. ' FROM ( SELECT' .
$this -> _query [ 'data_select' ] . " \n " .
" FROM $tn " . $this -> _query [ 'useindex' ] . " \n " .
$this -> _join . " \n " .
$this -> _query [ 'condition' ] . " \n " .
$this -> _query [ 'group_by' ] . " \n " .
$this -> _query [ 'having' ] . " \n " .
') ' . $this -> _query [ 'derive_table' ];
return $sql ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$sql = 'SELECT ' .
$this -> _query [ 'data_select' ] . " \n " .
" FROM $tn " . $this -> _query [ 'useindex' ] . " \n " .
$this -> _join . " \n " .
$this -> _query [ 'condition' ] . " \n " .
$this -> _query [ 'group_by' ] . " \n " .
$this -> _query [ 'having' ] . " \n " ;
return $sql ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
/* ============================================================== */
/* Table definition layer (started of very private but 'came out'*/
/* ============================================================== */
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* sends query to database - this is the private one that must work
* - internal functions use this rather than $this -> query ()
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ param string $string
* @ access private
* @ return mixed none or PEAR_Error
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function _query ( $string )
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
$this -> _connect ();
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
$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 " );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if (
strtoupper ( $string ) == 'BEGIN' ||
strtoupper ( $string ) == 'START TRANSACTION'
) {
if ( $_DB_driver == 'DB' ) {
$DB -> autoCommit ( false );
$DB -> simpleQuery ( 'BEGIN' );
} else {
$DB -> beginTransaction ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return true ;
2019-04-16 00:20:20 +01:00
}
2019-04-27 18:21:14 +01:00
if ( strtoupper ( $string ) == 'COMMIT' ) {
$res = $DB -> commit ();
if ( $_DB_driver == 'DB' ) {
$DB -> autoCommit ( true );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return $res ;
2008-09-30 14:38:47 +01:00
}
if ( strtoupper ( $string ) == 'ROLLBACK' ) {
$DB -> rollback ();
if ( $_DB_driver == 'DB' ) {
$DB -> autoCommit ( true );
}
return true ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
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' );
2019-04-27 18:21:14 +01:00
return $this -> raiseError ( " Disabling Update as you are in debug mode " , null );
2008-09-30 14:38:47 +01:00
}
//if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 1) {
2019-04-16 00:20:20 +01:00
// this will only work when PEAR:DB supports it.
//$this->debug($DB->getAll('explain ' .$string,DB_DATAOBJECT_FETCHMODE_ASSOC), $log="sql",2);
2008-09-30 14:38:47 +01:00
//}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// some sim
2019-04-27 18:21:14 +01:00
$t = explode ( ' ' , microtime ());
$_DB_DATAOBJECT [ 'QUERYENDTIME' ] = $time = $t [ 0 ] + $t [ 1 ];
for ( $tries = 0 ; $tries < 3 ; $tries ++ ) {
2009-07-16 04:35:20 +01:00
if ( $_DB_driver == 'DB' ) {
$result = $DB -> query ( $string );
} else {
2019-04-16 00:20:20 +01:00
switch ( strtolower ( substr ( trim ( $string ), 0 , 6 ))) {
2019-04-27 18:21:14 +01:00
2009-07-16 04:35:20 +01:00
case 'insert' :
case 'update' :
case 'delete' :
$result = $DB -> exec ( $string );
break ;
2019-04-27 18:21:14 +01:00
2009-07-16 04:35:20 +01:00
default :
$result = $DB -> query ( $string );
break ;
}
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2009-07-16 04:35:20 +01:00
// see if we got a failure.. - try again a few times..
2019-04-16 00:20:20 +01:00
if ( ! is_object ( $result ) || ! is_a ( $result , 'PEAR_Error' )) {
2009-07-16 04:35:20 +01:00
break ;
}
if ( $result -> getCode () != - 14 ) { // *DB_ERROR_NODBSELECTED
break ; // not a connection error..
}
sleep ( 1 ); // wait before retyring..
$DB -> connect ( $DB -> dsn );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
2019-04-16 00:20:20 +01:00
if ( is_object ( $result ) && is_a ( $result , 'PEAR_Error' )) {
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( $result -> toString (), " Query Error " , 1 );
2008-09-30 14:38:47 +01:00
}
2013-08-12 11:32:39 +01:00
$this -> N = false ;
2008-09-30 14:38:47 +01:00
return $this -> raiseError ( $result );
}
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
2019-04-27 18:21:14 +01:00
$t = explode ( ' ' , microtime ());
$_DB_DATAOBJECT [ 'QUERYENDTIME' ] = $t [ 0 ] + $t [ 1 ];
$this -> debug ( 'QUERY DONE IN ' . ( $t [ 0 ] + $t [ 1 ] - $time ) . " seconds " , 'query' , 1 );
2008-09-30 14:38:47 +01:00
}
2019-04-16 00:20:20 +01:00
switch ( strtolower ( substr ( trim ( $string ), 0 , 6 ))) {
2008-09-30 14:38:47 +01:00
case 'insert' :
case 'update' :
case 'delete' :
if ( $_DB_driver == 'DB' ) {
// pear DB specific
2019-04-16 00:20:20 +01:00
return $DB -> affectedRows ();
2008-09-30 14:38:47 +01:00
}
return $result ;
}
if ( is_object ( $result )) {
// lets hope that copying the result object is OK!
2019-04-27 18:21:14 +01:00
$_DB_resultid = $GLOBALS [ '_DB_DATAOBJECT' ][ 'RESULTSEQ' ] ++ ;
2019-04-16 00:20:20 +01:00
$_DB_DATAOBJECT [ 'RESULTS' ][ $_DB_resultid ] = $result ;
2008-09-30 14:38:47 +01:00
$this -> _DB_resultid = $_DB_resultid ;
}
$this -> N = 0 ;
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
2019-04-16 00:20:20 +01:00
$this -> debug ( serialize ( $result ), 'RESULT' , 5 );
2008-09-30 14:38:47 +01:00
}
2013-08-12 11:32:39 +01:00
if ( method_exists ( $result , 'numRows' )) {
2008-09-30 14:38:47 +01:00
if ( $_DB_driver == 'DB' ) {
$DB -> expectError ( DB_ERROR_UNSUPPORTED );
} else {
$DB -> expectError ( MDB2_ERROR_UNSUPPORTED );
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
$this -> N = $result -> numRows ();
//var_dump($this->N);
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
if ( is_object ( $this -> N ) && is_a ( $this -> N , 'PEAR_Error' )) {
2008-09-30 14:38:47 +01:00
$this -> N = true ;
}
$DB -> popExpect ();
}
2019-04-27 18:21:14 +01:00
return null ;
2008-09-30 14:38:47 +01:00
}
/**
2019-04-27 18:21:14 +01:00
* fetches next row into this objects var ' s
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* 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
*/
public function fetch ()
{
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
DB_DataObject :: _loadConfig ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( empty ( $this -> N )) {
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " No data returned from FIND (eg. N is 0) " , " FETCH " , 3 );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
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)' );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return false ;
}
$array = $result -> fetchRow ( DB_DATAOBJECT_FETCHMODE_ASSOC );
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( serialize ( $array ), " FETCH " );
}
// fetched after last row..
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
);
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// reduce the memory usage a bit... (but leave the id in, so count() works ok on it)
unset ( $_DB_DATAOBJECT [ 'RESULTS' ][ $this -> _DB_resultid ]);
// we need to keep a copy of resultfields locally so toArray() still works
// however we dont want to keep it in the global cache..
if ( ! empty ( $_DB_DATAOBJECT [ 'RESULTFIELDS' ][ $this -> _DB_resultid ])) {
$this -> _resultFields = $_DB_DATAOBJECT [ 'RESULTFIELDS' ][ $this -> _DB_resultid ];
unset ( $_DB_DATAOBJECT [ 'RESULTFIELDS' ][ $this -> _DB_resultid ]);
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// this is probably end of data!!
//DB_DataObject::raiseError("fetch: no data returned", DB_DATAOBJECT_ERROR_NODATA);
return false ;
}
// make sure resultFields is always empty..
$this -> _resultFields = 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 ));
}
2019-09-11 09:25:39 +01:00
$dbtype = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ] -> dsn [ 'phptype' ];
if ( $dbtype === 'pgsql' ) {
if (( $_DB_DATAOBJECT [ 'CONFIG' ][ 'db_driver' ] ? ? 'DB' ) === 'DB' ) {
$tableInfo = $result -> tableInfo ();
} elseif ( $result -> db -> supports ( 'result_introspection' )) { // MDB2
$result -> db -> loadModule ( 'Reverse' , null , true );
$tableInfo = $result -> db -> reverse -> tableInfo ( $result );
}
}
2019-04-27 18:21:14 +01:00
$replace = array ( '.' , ' ' );
2019-09-11 09:25:39 +01:00
foreach ( array_keys ( $array ) as $i => $k ) {
2019-04-27 18:21:14 +01:00
// use strpos as str_replace is slow.
$kk = ( strpos ( $k , '.' ) === false && strpos ( $k , ' ' ) === false ) ?
$k : str_replace ( $replace , '_' , $k );
2019-09-11 12:14:40 +01:00
if ( $dbtype === 'pgsql' ) {
switch ( $tableInfo [ $i ][ 'type' ]) {
case 'bool' :
$array [ $k ] = str_replace ([ 't' , 'f' ], [ '1' , '0' ], $array [ $k ]);
break ;
case 'bytea' :
$array [ $k ] = pg_unescape_bytea ( $array [ $k ]);
break ;
}
2019-09-11 09:25:39 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " $kk = " . $array [ $k ], " fetchrow LINE " , 3 );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$this -> $kk = $array [ $k ];
}
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
// set link flag
$this -> _link_loaded = false ;
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " { $this -> tableName () } DONE " , " fetchrow " , 2 );
}
if (( $this -> _query !== false ) && empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'keep_query_after_fetch' ])) {
$this -> _query = false ;
}
return true ;
}
/**
* Get the value of the primary id
*
* While I normally use 'id' as the PRIMARY KEY value , some database use
* { table } _id as the column name .
*
* To save a bit of typing ,
*
* $id = $do -> pid ();
*
* @ return bool | the
*/
public function pid ()
{
$keys = $this -> keys ();
if ( ! $keys ) {
$this -> raiseError (
" No Keys available for { $this -> tableName () } " ,
DB_DATAOBJECT_ERROR_INVALIDCONFIG
);
return false ;
}
$k = $keys [ 0 ];
if ( empty ( $this -> $k )) { // we do not
$this -> raiseError (
" pid() called on Object where primary key value not available " ,
DB_DATAOBJECT_ERROR_NODATA
);
return false ;
}
return $this -> $k ;
}
/**
* fetches all results as an array ,
*
* return format is dependant on args .
* if selectAdd () has not been called on the object , then it will add the correct columns to the query .
*
* A ) Array of values ( eg . a list of 'id' )
*
* $x = DB_DataObject :: factory ( 'mytable' );
* $x -> whereAdd ( 'something = 1' )
* $ar = $x -> fetchAll ( 'id' );
* -- returns array ( 1 , 2 , 3 , 4 , 5 )
*
* B ) Array of values ( not from table )
*
* $x = DB_DataObject :: factory ( 'mytable' );
* $x -> whereAdd ( 'something = 1' );
* $x -> selectAdd ();
* $x -> selectAdd ( 'distinct(group_id) as group_id' );
* $ar = $x -> fetchAll ( 'group_id' );
* -- returns array ( 1 , 2 , 3 , 4 , 5 )
* *
* C ) A key => value associative array
*
* $x = DB_DataObject :: factory ( 'mytable' );
* $x -> whereAdd ( 'something = 1' )
* $ar = $x -> fetchAll ( 'id' , 'name' );
* -- returns array ( 1 => 'fred' , 2 => 'blogs' , 3 => .......
*
* D ) array of objects
* $x = DB_DataObject :: factory ( 'mytable' );
* $x -> whereAdd ( 'something = 1' );
* $ar = $x -> fetchAll ();
*
* E ) array of arrays ( for example )
* $x = DB_DataObject :: factory ( 'mytable' );
* $x -> whereAdd ( 'something = 1' );
* $ar = $x -> fetchAll ( false , false , 'toArray' );
*
*
* @ param string | false $k key
* @ param string | false $v value
* @ param string | false $method method to call on each result to get array value ( eg . 'toArray' )
* @ access public
* @ return array format dependant on arguments , may be empty
*/
public function fetchAll ( $k = false , $v = false , $method = false )
{
// should it even do this!!!?!?
if ( $k !== false &&
( // only do this is we have not been explicit..
empty ( $this -> _query [ 'data_select' ]) ||
( $this -> _query [ 'data_select' ] == '*' )
)
) {
$this -> selectAdd ();
$this -> selectAdd ( $k );
if ( $v !== false ) {
$this -> selectAdd ( $v );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
}
$this -> find ();
$ret = array ();
while ( $this -> fetch ()) {
if ( $v !== false ) {
$ret [ $this -> $k ] = $this -> $v ;
2008-09-30 14:38:47 +01:00
continue ;
}
2019-04-27 18:21:14 +01:00
$ret [] = $k === false ?
( $method == false ? clone ( $this ) : $this -> $method ())
: $this -> $k ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
return $ret ;
2008-09-30 14:38:47 +01:00
}
2019-04-16 00:20:20 +01:00
/**
2019-04-27 18:21:14 +01:00
* 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 .
*/
public function selectAdd ( $k = null )
2013-08-12 11:32:39 +01:00
{
2019-04-27 18:21:14 +01:00
if ( $this -> _query === false ) {
$this -> raiseError (
" You cannot do two queries on the same object (copy it before finding) " ,
DB_DATAOBJECT_ERROR_INVALIDARGS
);
return false ;
2009-07-16 04:35:20 +01:00
}
2019-04-27 18:21:14 +01:00
if ( $k === null ) {
$old = $this -> _query [ 'data_select' ];
$this -> _query [ 'data_select' ] = '' ;
return $old ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// check input...= 0 or ' ' == error!
if ( ! trim ( $k )) {
return $this -> raiseError ( " selectAdd: No Valid Arguments " , DB_DATAOBJECT_ERROR_INVALIDARGS );
2019-04-16 00:20:20 +01:00
}
2019-04-27 18:21:14 +01:00
if ( $this -> _query [ 'data_select' ]) {
$this -> _query [ 'data_select' ] .= ', ' ;
}
$this -> _query [ 'data_select' ] .= " $k " ;
return null ;
}
/**
* Adds a 'IN' condition to the WHERE statement
*
* $object -> whereAddIn ( 'id' , $array , 'int' ); //minimal usage
* $object -> whereAddIn ( 'price' , $array , 'float' , 'OR' ); // cast to float, and call whereAdd with 'OR'
* $object -> whereAddIn ( 'name' , $array , 'string' ); // quote strings
*
* @ param string $key key column to match
* @ param array $list list of values to match
* @ param string $type string | int | integer | float | bool cast to type .
* @ param string $logic optional logic to call whereAdd with eg . " OR " ( defaults to " AND " )
* @ access public
* @ return string | PEAR :: Error - previous condition or Error when invalid args found
*/
public function whereAddIn ( $key , $list , $type , $logic = 'AND' )
{
$not = '' ;
if ( $key [ 0 ] == '!' ) {
$not = 'NOT ' ;
$key = substr ( $key , 1 );
}
// fix type for short entry.
$type = $type == 'int' ? 'integer' : $type ;
if ( $type == 'string' ) {
$this -> _connect ();
}
$ar = array ();
foreach ( $list as $k ) {
settype ( $k , $type );
$ar [] = $type == 'string' ? $this -> _quote ( $k ) : $k ;
}
if ( ! $ar ) {
return $not ? $this -> _query [ 'condition' ] : $this -> whereAdd ( " 1=0 " );
}
return $this -> whereAdd ( " $key $not IN ( " . implode ( ',' , $ar ) . ')' , $logic );
}
/* =========================================================== */
/* Major Private Methods - the core part! */
/* =========================================================== */
/**
* Adds a order by condition
*
* $object -> orderBy (); //clears order by
* $object -> orderBy ( " ID " );
* $object -> orderBy ( " ID,age " );
*
* @ param bool $order Order
* @ return bool | error | none | PEAR
* @ access public
*/
public function orderBy ( $order = false )
{
if ( $this -> _query === false ) {
$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 null ;
}
// 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 null ;
}
$this -> _query [ 'order_by' ] .= " , { $order } " ;
return null ;
}
/**
* Adds a group by condition
*
* $object -> groupBy (); //reset the grouping
* $object -> groupBy ( " ID DESC " );
* $object -> groupBy ( " ID,age " );
*
* @ param bool $group Grouping
* @ return bool | none | PEAR
* @ access public
*/
public function groupBy ( $group = false )
{
if ( $this -> _query === false ) {
$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 null ;
}
// 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 null ;
}
$this -> _query [ 'group_by' ] .= " , { $group } " ;
return null ;
}
/**
* Adds a having clause
*
* $object -> having (); //reset the grouping
* $object -> having ( " sum(value) > 0 " );
*
* @ param bool $having condition
* @ return bool | none | PEAR
* @ access public
*/
public function having ( $having = false )
{
if ( $this -> _query === false ) {
$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 null ;
}
// 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 null ;
}
$this -> _query [ 'having' ] .= " AND { $having } " ;
return null ;
}
/**
* Adds a using Index
*
* $object -> useIndex (); //reset the use Index
* $object -> useIndex ( " some_index " );
*
* Note do not put unfiltered user input into theis method .
* This is mysql specific at present ? - might need altering to support other databases .
*
* @ param bool $index index or indexes to use .
* @ return bool | none | PEAR
* @ access public
*/
public function useIndex ( $index = false )
{
if ( $this -> _query === false ) {
$this -> raiseError (
" You cannot do two queries on the same object (copy it before finding) " ,
DB_DATAOBJECT_ERROR_INVALIDARGS
);
return false ;
}
if ( $index === false ) {
$this -> _query [ 'useindex' ] = '' ;
return null ;
}
// check input...= 0 or ' ' == error!
if (( is_string ( $index ) && ! trim ( $index )) || ( is_array ( $index ) && ! count ( $index ))) {
return $this -> raiseError ( " Having: No Valid Arguments " , DB_DATAOBJECT_ERROR_INVALIDARGS );
}
$index = is_array ( $index ) ? implode ( ', ' , $index ) : $index ;
if ( ! $this -> _query [ 'useindex' ]) {
$this -> _query [ 'useindex' ] = " USE INDEX ( { $index } ) " ;
return null ;
}
$this -> _query [ 'useindex' ] = substr ( $this -> _query [ 'useindex' ], 0 , - 2 ) . " , { $index } ) " ;
return null ;
}
/**
* 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
* @ return bool | none | PEAR
* @ access public
*/
public function limit ( $a = null , $b = null )
{
if ( $this -> _query === false ) {
$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 null ;
}
// 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 ;
return null ;
}
/**
* 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
*/
public 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 = $this -> table ();
if ( ! $items ) {
$this -> raiseError (
" insert:No table definition for { $this -> tableName () } " ,
DB_DATAOBJECT_ERROR_INVALIDCONFIG
);
return false ;
}
$options = $_DB_DATAOBJECT [ 'CONFIG' ];
$datasaved = 1 ;
$leftq = '' ;
$rightq = '' ;
$seqKeys = isset ( $_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()]) ?
$_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()] :
$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 ) {
$keyvalue = $DB -> nextId ( $this -> tableName ());
} else {
$f = $DB -> getOption ( 'seqname_format' );
$DB -> setOption ( 'seqname_format' , '%s' );
$keyvalue = $DB -> nextId ( $seq );
$DB -> setOption ( 'seqname_format' , $f );
}
if (( new PEAR ) -> isError ( $keyvalue )) {
$this -> raiseError ( $keyvalue -> toString (), DB_DATAOBJECT_ERROR_INVALIDCONFIG );
return false ;
}
$this -> $key = $keyvalue ;
}
// if we haven't set disable_null_strings to "full"
$ignore_null = ! isset ( $options [ 'disable_null_strings' ])
|| ! is_string ( $options [ 'disable_null_strings' ])
|| strtolower ( $options [ 'disable_null_strings' ]) !== 'full' ;
foreach ( $items as $k => $v ) {
// if we are using autoincrement - skip the column...
if ( $key && ( $k == $key ) && $useNative ) {
continue ;
}
// Ignore INTEGERS which aren't set to a value - or empty string..
if (( ! isset ( $this -> $k ) || ( $v == 1 && $this -> $k === '' ))
&& $ignore_null
) {
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_object ( $this -> $k ) && is_a ( $this -> $k , 'DB_DataObject_Cast' )) {
$value = $this -> $k -> toString ( $v , $DB );
if (( new PEAR ) -> isError ( $value )) {
$this -> raiseError ( $value -> toString (), DB_DATAOBJECT_ERROR_INVALIDARGS );
return false ;
}
$rightq .= $value ;
continue ;
}
if ( ! ( $v & DB_DATAOBJECT_NOTNULL ) && DB_DataObject :: _is_null ( $this , $k )) {
$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 ;
}
/* flag up string values - only at debug level... !!!??? */
if ( is_object ( $this -> $k ) || is_array ( $this -> $k )) {
$this -> debug ( 'ODD DATA: ' . $k . ' ' . print_r ( $this -> $k , true ), 'ERROR' );
}
// 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 -> tableName ()) : $this -> tableName ());
if (( $dbtype == 'pgsql' ) && empty ( $leftq )) {
$r = $this -> _query ( " INSERT INTO { $table } DEFAULT VALUES " );
} else {
$r = $this -> _query ( " INSERT INTO { $table } ( $leftq ) VALUES ( $rightq ) " );
}
if (( new 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');
$db_driver = empty ( $options [ 'db_driver' ]) ? 'DB' : $options [ 'db_driver' ];
$method = ( $db_driver == 'DB' ) ? 'getOne' : 'queryOne' ;
$mssql_key = $DB -> $method ( " SELECT @@IDENTITY " );
if (( new PEAR ) -> isError ( $mssql_key )) {
$this -> raiseError ( $mssql_key );
return false ;
}
$this -> $key = $mssql_key ;
break ;
case 'pgsql' :
if ( ! $seq ) {
2019-09-11 12:14:40 +01:00
$seq = $DB -> getSequenceName ( strtolower ( $this -> tableName () . '_' . $key ));
2019-04-27 18:21:14 +01:00
}
$db_driver = empty ( $options [ 'db_driver' ]) ? 'DB' : $options [ 'db_driver' ];
$method = ( $db_driver == 'DB' ) ? 'getOne' : 'queryOne' ;
$pgsql_key = $DB -> $method ( " SELECT currval(' " . $seq . " ') " );
if (( new PEAR ) -> isError ( $pgsql_key )) {
$this -> raiseError ( $pgsql_key );
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 ;
}
/**
* 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 public
* @ return array ( column , use_native , sequence_name )
*/
public 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 -> tableName ()] = $args ;
}
if ( isset ( $_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()])) {
return $_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()];
}
// end call setting (eg. $do->sequenceKeys(a,b,c); )
$keys = $this -> keys ();
if ( ! $keys ) {
return $_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()]
= array ( false , false , false );
}
$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 -> tableName ()])) {
$seqname = $_DB_DATAOBJECT [ 'CONFIG' ][ 'sequence_' . $this -> tableName ()];
if ( strpos ( $seqname , ':' ) !== false ) {
list ( $usekey , $seqname ) = explode ( ':' , $seqname );
}
}
// if the key is not an integer - then it's not a sequence or native
if ( empty ( $table [ $usekey ]) || ! ( $table [ $usekey ] & DB_DATAOBJECT_INT )) {
return $_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()] = 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 -> tableName ()] = array ( false , false , $seqname );
}
if ( is_string ( $ignore )) {
$ignore = $_DB_DATAOBJECT [ 'CONFIG' ][ 'ignore_sequence_keys' ] = explode ( ',' , $ignore );
}
if ( in_array ( $this -> tableName (), $ignore )) {
return $_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()] = array ( false , false , $seqname );
}
}
$realkeys = $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ][ $this -> tableName () . " __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 -> tableName ()] = array ( false , false , $seqname );
}
// use native sequence keys...
// technically postgres native here...
// we need to get the new improved tabledata sorted out first.
// support named sequence keys.. - currently postgres only..
if ( in_array ( $dbtype , array ( 'pgsql' )) &&
( $table [ $usekey ] & DB_DATAOBJECT_INT ) &&
isset ( $realkeys [ $usekey ]) && strlen ( $realkeys [ $usekey ]) > 1 ) {
return $_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()] = array ( $usekey , true , $realkeys [ $usekey ]);
}
if ( in_array ( $dbtype , array ( 'pgsql' , 'mysql' , 'mysqli' , 'mssql' , 'ifx' )) &&
( $table [ $usekey ] & DB_DATAOBJECT_INT ) &&
isset ( $realkeys [ $usekey ]) && ( $realkeys [ $usekey ] == 'N' )
) {
return $_DB_DATAOBJECT [ 'SEQUENCE' ][ $this -> _database ][ $this -> tableName ()] = 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 -> tableName ()] = array ( $usekey , false , $seqname );
}
/**
* clear the cache values for this class - normally done on insert / update etc .
*
* @ access private
* @ return void
*/
public function _clear_cache ()
{
global $_DB_DATAOBJECT ;
$class = strtolower ( 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 ]);
}
}
/**
* Updates current objects variables into the database
2019-11-02 00:26:25 +00:00
*
2019-04-27 18:21:14 +01:00
* uses the keys () to decide how to update
*
2019-11-02 00:26:25 +00:00
* WARNING : Be aware that this function doesn ' t update keys .
2019-04-27 18:21:14 +01:00
*
2019-11-02 00:26:25 +00:00
* Usage :
* `` `
2019-04-27 18:21:14 +01:00
* $object = DB_DataObject :: factory ( 'mytable' );
* $object -> get ( " ID " , 234 );
* $object -> email = " testing@test.com " ;
2019-11-02 00:26:25 +00:00
* if ( ! $object -> update ())
2019-04-27 18:21:14 +01:00
* echo " UPDATE FAILED " ;
2019-11-02 00:26:25 +00:00
* `` `
2019-04-27 18:21:14 +01:00
*
2019-11-02 00:26:25 +00:00
* to only update changed items :
* `` `
2019-04-27 18:21:14 +01:00
* $dataobject -> get ( 132 );
* $original = $dataobject ; // clone/copy it..
* $dataobject -> setFrom ( $_POST );
* if ( $dataobject -> validate ()) {
* $dataobject -> update ( $original );
* } // otherwise an error...
2019-11-02 00:26:25 +00:00
* `` `
2019-04-27 18:21:14 +01:00
*
* performing global updates :
2019-11-02 00:26:25 +00:00
* `` `
2019-04-27 18:21:14 +01:00
* $object = DB_DataObject :: factory ( 'mytable' );
* $object -> status = " dead " ;
* $object -> whereAdd ( 'age > 150' );
* $object -> update ( DB_DATAOBJECT_WHEREADD_ONLY );
2019-11-02 00:26:25 +00:00
* `` `
2019-04-27 18:21:14 +01:00
*
2019-11-02 00:26:25 +00:00
* @ param object | bool ( optional ) $dataObject | DB_DataObject :: WHERE_ONLY - used to only update changed items .
* @ return int | bool Number rows affected ( may be 0 ), true ( if no difference between old / new ), false on failure
2019-04-27 18:21:14 +01:00
* @ access public
*/
public function update ( $dataObject = false )
{
global $_DB_DATAOBJECT ;
// connect will load the config!
$this -> _connect ();
$original_query = $this -> _query ;
$items = $this -> table ();
// only apply update against sequence key if it is set?????
$seq = $this -> sequenceKey ();
if ( $seq [ 0 ] !== false ) {
$keys = array ( $seq [ 0 ]);
if ( ! isset ( $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
" . print_r(array('seq' => $seq , 'keys' => $keys ), true), DB_DATAOBJECT_ERROR_INVALIDARGS);
return false ;
}
} else {
$keys = $this -> keys ();
}
if ( ! $items ) {
$this -> raiseError ( " update:No table definition for { $this -> tableName () } " , 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' ]);
$options = $_DB_DATAOBJECT [ 'CONFIG' ];
$ignore_null = ! isset ( $options [ 'disable_null_strings' ])
|| ! is_string ( $options [ 'disable_null_strings' ])
|| strtolower ( $options [ 'disable_null_strings' ]) !== 'full' ;
foreach ( $items as $k => $v ) {
2019-11-02 00:26:25 +00:00
// I think this is ignoring empty values
2019-04-27 18:21:14 +01:00
if (( ! isset ( $this -> $k ) || ( $v == 1 && $this -> $k === '' ))
&& $ignore_null
) {
continue ;
}
// ignore stuff thats
2019-11-02 00:26:25 +00:00
// don't write things that haven't changed..
2019-04-27 18:21:14 +01:00
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_object ( $this -> $k ) && is_a ( $this -> $k , 'DB_DataObject_Cast' )) {
$value = $this -> $k -> toString ( $v , $DB );
if (( new 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 ( ! ( $v & DB_DATAOBJECT_NOTNULL ) && DB_DataObject :: _is_null ( $this , $k )) {
$settings .= " $kSql = NULL " ;
continue ;
}
// DATE is empty... on a col. that can be null..
2019-11-02 00:26:25 +00:00
// note: this may be useful for time as well..
2019-04-27 18:21:14 +01:00
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 -> tableName ()) : $this -> tableName ());
$r = $this -> _query ( " UPDATE { $table } SET { $settings } { $this -> _query [ 'condition' ] } " );
// restore original query conditions.
$this -> _query = $original_query ;
if (( new 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 Int ( No . of rows affected ) on success , false on failure , 0 on no data affected
*/
public 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 (( $this -> _query !== false ) && $this -> _query [ 'condition' ]) {
$table = ( $quoteIdentifiers ? $DB -> quoteIdentifier ( $this -> tableName ()) : $this -> tableName ());
$sql = " DELETE " ;
// using a joined delete. - with useWhere..
$sql .= ( ! empty ( $this -> _join ) && $useWhere ) ?
" { $table } FROM { $table } { $this -> _join } " :
" FROM { $table } " ;
$sql .= $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 {
// MDB2
$DB -> setLimit ( $this -> _query [ 'limit_count' ], $this -> _query [ 'limit_start' ]);
}
}
$r = $this -> _query ( $sql );
if (( new 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
*/
public function fetchRow ( $row = null )
{
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
$this -> _loadConfig ();
}
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( " { $this -> tableName () } $row of { $this -> N } " , " fetchrow " , 3 );
}
if ( ! $this -> tableName ()) {
$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 -> tableName () } $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 ;
}
2019-09-11 09:25:39 +01:00
$dbtype = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ] -> dsn [ 'phptype' ];
if ( $dbtype === 'pgsql' ) {
if (( $_DB_DATAOBJECT [ 'CONFIG' ][ 'db_driver' ] ? ? 'DB' ) === 'DB' ) {
$tableInfo = $result -> tableInfo ();
} elseif ( $result -> db -> supports ( 'result_introspection' )) { // MDB2
$result -> db -> loadModule ( 'Reverse' , null , true );
$tableInfo = $result -> db -> reverse -> tableInfo ( $result );
}
}
2019-04-27 18:21:14 +01:00
$replace = array ( '.' , ' ' );
2019-09-11 09:25:39 +01:00
foreach ( array_keys ( $array ) as $i => $k ) {
2019-04-27 18:21:14 +01:00
// use strpos as str_replace is slow.
$kk = ( strpos ( $k , '.' ) === false && strpos ( $k , ' ' ) === false ) ?
$k : str_replace ( $replace , '_' , $k );
2019-09-11 12:14:40 +01:00
if ( $dbtype === 'pgsql' ) {
switch ( $tableInfo [ $i ][ 'type' ]) {
case 'bool' :
$array [ $k ] = str_replace ([ 't' , 'f' ], [ '1' , '0' ], $array [ $k ]);
break ;
case 'bytea' :
$array [ $k ] = pg_unescape_bytea ( $array [ $k ]);
break ;
}
2019-09-11 09:25:39 +01:00
}
2019-04-27 18:21:14 +01:00
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 -> tableName () } 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
*/
public function count ( $countWhat = false , $whereAddOnly = false )
{
global $_DB_DATAOBJECT ;
if ( is_bool ( $countWhat )) {
$whereAddOnly = $countWhat ;
}
$t = clone ( $this );
$items = $t -> table ();
$quoteIdentifiers = ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'quote_identifiers' ]);
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 ( empty ( $keys [ 0 ]) && ( ! is_string ( $countWhat ) || ( strtoupper ( $countWhat ) == 'DISTINCT' ))) {
$this -> raiseError (
" You cannot do run count without keys - use \$ do->count('id'), or use \$ do->count('distinct id')'; " ,
DB_DATAOBJECT_ERROR_INVALIDARGS ,
PEAR_ERROR_DIE
);
return false ;
}
$table = ( $quoteIdentifiers ? $DB -> quoteIdentifier ( $this -> tableName ()) : $this -> tableName ());
$key_col = empty ( $keys [ 0 ]) ? '' : (( $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 (( new PEAR ) -> isError ( $r )) {
return false ;
}
$result = $_DB_DATAOBJECT [ 'RESULTS' ][ $t -> _DB_resultid ];
$l = $result -> fetchRow ( DB_DATAOBJECT_FETCHMODE_ORDERED );
// free the results - essential on oracle.
$t -> free ();
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'debug' ])) {
$this -> debug ( 'Count returned ' . $l [ 0 ], 1 );
}
return ( int ) $l [ 0 ];
}
/**
* Free global arrays associated with this object .
*
*
* @ access public
* @ return none
*/
public 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 ]);
}
// clear the staticGet cache as well.
$this -> _clear_cache ();
// 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 ();
}
if ( is_array ( $this -> _link_loaded )) {
foreach ( $this -> _link_loaded as $do ) {
if (
! empty ( $this -> { $do }) &&
is_object ( $this -> { $do }) &&
method_exists ( $this -> { $do }, 'free' )
) {
$this -> { $do } -> free ();
}
}
}
return null ;
}
/**
* 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
*/
public 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
* @ param bool $likeEscape escapes % and _ as well . - so like queries can be protected .
* @ access public
* @ return string
*/
public function escape ( $string , $likeEscape = false )
{
global $_DB_DATAOBJECT ;
$this -> _connect ();
$DB = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ];
// mdb2 uses escape...
$dd = empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'db_driver' ]) ? 'DB' : $_DB_DATAOBJECT [ 'CONFIG' ][ 'db_driver' ];
$ret = ( $dd == 'DB' ) ? $DB -> escapeSimple ( $string ) : $DB -> escape ( $string );
if ( $likeEscape ) {
$ret = str_replace ( array ( '_' , '%' ), array ( '\_' , '\%' ), $ret );
}
return $ret ;
}
/**
* 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
*/
public function database ()
{
$args = func_get_args ();
if ( count ( $args )) {
$this -> _database = $args [ 0 ];
} else {
$this -> _connect ();
}
return $this -> _database ;
}
/**
* generic getter / setter for links
*
* This is the new 'recommended' way to get get / set linked objects .
* must be used with links . ini
*
* usage :
* get :
* $obj = $do -> link ( 'company_id' );
* $obj = $do -> link ( array ( 'local_col' , 'linktable:linked_col' ));
*
* set :
* $do -> link ( 'company_id' , 0 );
* $do -> link ( 'company_id' , $obj );
* $do -> link ( 'company_id' , array ( $obj ));
*
* example function
*
* function company () {
* $this -> link ( array ( 'company_id' , 'company:id' ), func_get_args ());
* }
*
*
*
* @ param $field
* @ param array $set_args
* @ return mixed true or false on setting , object on getting
* @ author Alan Knowles
* @ access public
*/
public function link ( $field , $set_args = array ())
{
//require_once 'DB/DataObject/Links.php';
require_once 'Links.php' ;
$l = new DB_DataObject_Links ( $this );
return $l -> link ( $field , $set_args );
}
/**
* load related objects
*
* Generally not recommended to use this .
* The generator should support creating getter_setter methods which are better suited .
*
* Relies on < dbname >. links . ini
*
* Sets properties on the calling dataobject 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 .
* @ return boolean , true on success
* @ author Tim White < tim @ cyface . com >
* @ access public
*/
public function getLinks ( $format = '_%s' )
{
//require_once 'DB/DataObject/Links.php';
require_once 'Links.php' ;
$l = new DB_DataObject_Links ( $this );
return $l -> applyLinks ( $format );
}
/**
* deprecited : @ use link ()
* @ param $row
* @ param null $table
* @ param bool $link
* @ return mixed
*/
public function getLink ( $row , $table = null , $link = false )
{
//require_once 'DB/DataObject/Links.php';
require_once 'Links.php' ;
$l = new DB_DataObject_Links ( $this );
return $l -> getLink ( $row , $table === null ? false : $table , $link );
}
/**
* getLinkArray
* Fetch an array of related objects . This should be used in conjunction with a < dbname >. links . ini file configuration ( see the introduction on linking for details on this ) .
* You may also use this with all parameters to specify , the column and related 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
* @ param $row
* @ param string $table - name of table to look up value in
* @ return array - array of results ( empty array on failure )
*
* Example - Getting the related objects
*
* $person = new DataObjects_Person ;
* $person -> get ( 12 );
* $children = $person -> getLinkArray ( 'children' );
*
* echo 'There are ' , count ( $children ), ' descendant(s):<br />' ;
* foreach ( $children as $child ) {
* echo $child -> name , '<br />' ;
* }
*/
public function getLinkArray ( $row , $table = null )
{
//require_once 'DB/DataObject/Links.php';
require_once 'Links.php' ;
$l = new DB_DataObject_Links ( $this );
return $l -> getLinkArray ( $row , $table === null ? false : $table );
}
/**
* unionAdd - adds another dataobject to this , building a unioned query .
*
* usage :
* $doTable1 = DB_DataObject :: factory ( " table1 " );
* $doTable2 = DB_DataObject :: factory ( " table2 " );
*
* $doTable1 -> selectAdd ();
* $doTable1 -> selectAdd ( " col1,col2 " );
* $doTable1 -> whereAdd ( " col1 > 100 " );
* $doTable1 -> orderBy ( " col1 " );
*
* $doTable2 -> selectAdd ();
* $doTable2 -> selectAdd ( " col1, col2 " );
* $doTable2 -> whereAdd ( " col2 = 'v' " );
*
* $doTable1 -> unionAdd ( $doTable2 );
* $doTable1 -> find ();
*
* Note : this model may be a better way to implement joinAdd ? , eg . do the building in find ?
*
*
* @ param $obj object | false the union object or false to reset
* @ param string $is_all string 'ALL' to do all .
* @ return false | mixed | object
*/
public function unionAdd ( $obj , $is_all = '' )
{
if ( $obj === false ) {
$ret = $this -> _query [ 'unions' ];
$this -> _query [ 'unions' ] = array ();
return $ret ;
}
$this -> _query [ 'unions' ][] = array ( $obj , 'UNION ' . $is_all . ' ' );
return $obj ;
}
/**
* autoJoin - using the links . ini file , it builds a query with all the joins
* usage :
* $x = DB_DataObject :: factory ( 'mytable' );
* $x -> autoJoin ();
* $x -> get ( 123 );
* will result in all of the joined data being added to the fetched object ..
*
* $x = DB_DataObject :: factory ( 'mytable' );
* $x -> autoJoin ();
* $ar = $x -> fetchAll ();
* will result in an array containing all the data from the table , and any joined tables ..
*
* $x = DB_DataObject :: factory ( 'mytable' );
* $jdata = $x -> autoJoin ();
* $x -> selectAdd (); //reset..
* foreach ( $_REQUEST [ 'requested_cols' ] as $c ) {
* if ( ! isset ( $jdata [ $c ])) continue ; // ignore columns not available..
* $x -> selectAdd ( $jdata [ $c ] . ' as ' . $c );
* }
* $ar = $x -> fetchAll ();
* will result in only the columns requested being fetched ...
*
*
*
* @ param array Configuration
* exclude Array of columns to exclude from results ( eg . modified_by_id )
* links The equivilant links . ini data for this table eg .
* array ( 'person_id' => 'person:id' , .... )
* include Array of columns to include
* distinct Array of distinct columns .
*
* @ return array info about joins
* cols => map of resulting { joined_tablename } . { joined_table_column_name }
* join_names => map of resulting { join_name_as } . { joined_table_column_name }
* count => the column to count on .
* @ access public
*/
public function autoJoin ( $cfg = array ())
{
global $_DB_DATAOBJECT ;
//var_Dump($cfg);exit;
$pre_links = $this -> links ();
if ( ! empty ( $cfg [ 'links' ])) {
$this -> links ( array_merge ( $pre_links , $cfg [ 'links' ]));
}
$map = $this -> links ();
$this -> databaseStructure ();
$dbstructure = $_DB_DATAOBJECT [ 'INI' ][ $this -> _database ];
//print_r($map);
$tabdef = $this -> table ();
// we need this as normally it's only cleared by an empty selectAs call.
$keys = array_keys ( $tabdef );
if ( ! empty ( $cfg [ 'exclude' ])) {
$keys = array_intersect ( $keys , array_diff ( $keys , $cfg [ 'exclude' ]));
}
if ( ! empty ( $cfg [ 'include' ])) {
$keys = array_intersect ( $keys , $cfg [ 'include' ]);
}
$selectAs = array ();
if ( ! empty ( $keys )) {
$selectAs = array ( array ( $keys , '%s' , false ));
}
$ret = array (
'cols' => array (),
'join_names' => array (),
'count' => false ,
);
$has_distinct = false ;
if ( ! empty ( $cfg [ 'distinct' ]) && $keys ) {
// reset the columsn?
$cols = array ();
//echo '<PRE>' ;print_r($xx);exit;
foreach ( $keys as $c ) {
//var_dump($c);
if ( $cfg [ 'distinct' ] == $c ) {
$has_distinct = 'DISTINCT( ' . $this -> tableName () . '.' . $c . ') as ' . $c ;
$ret [ 'count' ] = 'DISTINCT ' . $this -> tableName () . '.' . $c . '' ;
continue ;
}
// cols is in our filtered keys...
$cols = $c ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// apply our filtered version, which excludes the distinct column.
$selectAs = empty ( $cols ) ? array () : array ( array ( array ( $cols ), '%s' , false ));
}
foreach ( $keys as $k ) {
$ret [ 'cols' ][ $k ] = $this -> tableName () . '.' . $k ;
2008-09-30 14:38:47 +01:00
}
2019-04-16 00:20:20 +01:00
2019-04-27 18:21:14 +01:00
foreach ( $map as $ocl => $info ) {
list ( $tab , $col ) = explode ( ':' , $info );
// what about multiple joins on the same table!!!
// if links point to a table that does not exist - ignore.
if ( ! isset ( $dbstructure [ $tab ])) {
continue ;
}
$xx = DB_DataObject :: factory ( $tab );
if ( ! is_object ( $xx ) || ! is_a ( $xx , 'DB_DataObject' )) {
continue ;
}
// skip columns that are excluded.
// we ignore include here... - as
// this is borked ... for multiple jions..
$this -> joinAdd ( $xx , 'LEFT' , 'join_' . $ocl . '_' . $col , $ocl );
if ( ! empty ( $cfg [ 'exclude' ]) && in_array ( $ocl , $cfg [ 'exclude' ])) {
continue ;
}
$tabdef = $xx -> table ();
$table = $xx -> tableName ();
$keys = array_keys ( $tabdef );
if ( ! empty ( $cfg [ 'exclude' ])) {
$keys = array_intersect ( $keys , array_diff ( $keys , $cfg [ 'exclude' ]));
foreach ( $keys as $k ) {
if ( in_array ( $ocl . '_' . $k , $cfg [ 'exclude' ])) {
$keys = array_diff ( $keys , $k ); // removes the k..
}
2009-12-08 20:32:50 +00:00
}
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $cfg [ 'include' ])) {
// include will basically be BASECOLNAME_joinedcolname
$nkeys = array ();
foreach ( $keys as $k ) {
if ( in_array ( sprintf ( $ocl . '_%s' , $k ), $cfg [ 'include' ])) {
$nkeys [] = $k ;
}
2009-12-08 20:32:50 +00:00
}
2019-04-27 18:21:14 +01:00
$keys = $nkeys ;
}
2009-12-08 20:32:50 +00:00
2019-04-27 18:21:14 +01:00
if ( empty ( $keys )) {
continue ;
}
// got distinct, and not yet found it..
if ( ! $has_distinct && ! empty ( $cfg [ 'distinct' ])) {
$cols = array ();
foreach ( $keys as $c ) {
$tn = sprintf ( $ocl . '_%s' , $c );
if ( $tn == $cfg [ 'distinct' ]) {
$has_distinct = 'DISTINCT( ' . 'join_' . $ocl . '_' . $col . '.' . $c . ') as ' . $tn ;
$ret [ 'count' ] = 'DISTINCT join_' . $ocl . '_' . $col . '.' . $c ;
// var_dump($this->countWhat );
continue ;
2009-12-08 20:32:50 +00:00
}
2019-04-27 18:21:14 +01:00
$cols [] = $c ;
2009-12-08 20:32:50 +00:00
}
2019-04-27 18:21:14 +01:00
if ( ! empty ( $cols )) {
$selectAs [] = array ( $cols , $ocl . '_%s' , 'join_' . $ocl . '_' . $col );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
} else {
$selectAs [] = array ( $keys , $ocl . '_%s' , 'join_' . $ocl . '_' . $col );
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
foreach ( $keys as $k ) {
$ret [ 'cols' ][ sprintf ( '%s_%s' , $ocl , $k )] = $tab . '.' . $k ;
$ret [ 'join_names' ][ sprintf ( '%s_%s' , $ocl , $k )] = sprintf ( 'join_%s_%s.%s' , $ocl , $col , $k );
2008-09-30 14:38:47 +01:00
}
}
2019-04-27 18:21:14 +01:00
// fill in the select details..
$this -> selectAdd ();
if ( $has_distinct ) {
$this -> selectAdd ( $has_distinct );
2009-12-08 20:32:50 +00:00
}
2019-04-27 18:21:14 +01:00
foreach ( $selectAs as $ar ) {
$this -> selectAs ( $ar [ 0 ], $ar [ 1 ], $ar [ 2 ]);
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
// restore links..
$this -> links ( $pre_links );
return $ret ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
/**
2019-04-27 18:21:14 +01:00
* Get the links associate array as defined by the links . ini file .
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
*
* 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
* false - if no links . ini exists for this database ( hence try auto_links ) .
* @ access public
* @ see DB_DataObject :: getLinks (), DB_DataObject :: getLink ()
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
public function links ()
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
$this -> _loadConfig ();
}
// have to connect.. -> otherwise things break later.
$this -> _connect ();
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
// alias for shorter code..
2019-04-27 18:21:14 +01:00
$lcfg = & $_DB_DATAOBJECT [ 'LINKS' ];
$cfg = $_DB_DATAOBJECT [ 'CONFIG' ];
2013-08-12 11:32:39 +01:00
if ( $args = func_get_args ()) {
// an associative array was specified, that updates the current
// schema... - be careful doing this
2019-04-16 00:20:20 +01:00
if ( empty ( $lcfg [ $this -> _database ])) {
2013-08-12 11:32:39 +01:00
$lcfg [ $this -> _database ] = array ();
}
$lcfg [ $this -> _database ][ $this -> tableName ()] = $args [ 0 ];
}
// loaded and available.
if ( isset ( $lcfg [ $this -> _database ][ $this -> tableName ()])) {
return $lcfg [ $this -> _database ][ $this -> tableName ()];
}
2019-04-16 00:20:20 +01:00
// loaded
2013-08-12 11:32:39 +01:00
if ( isset ( $lcfg [ $this -> _database ])) {
// either no file, or empty..
return $lcfg [ $this -> _database ] === false ? null : array ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
// links are same place as schema by default.
$schemas = isset ( $cfg [ 'schema_location' ]) ?
array ( " { $cfg [ 'schema_location' ] } / { $this -> _database } .ini " ) :
2019-04-27 18:21:14 +01:00
array ();
2013-08-12 11:32:39 +01:00
// if ini_* is set look there instead.
2019-04-16 00:20:20 +01:00
// and support multiple locations.
2013-08-12 11:32:39 +01:00
if ( isset ( $cfg [ " ini_ { $this -> _database } " ])) {
$schemas = is_array ( $cfg [ " ini_ { $this -> _database } " ]) ?
$cfg [ " ini_ { $this -> _database } " ] :
2019-04-16 00:20:20 +01:00
explode ( PATH_SEPARATOR , $cfg [ " ini_ { $this -> _database } " ]);
2013-08-12 11:32:39 +01:00
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
// default to not available.
$lcfg [ $this -> _database ] = false ;
foreach ( $schemas as $ini ) {
$links = isset ( $cfg [ " links_ { $this -> _database } " ]) ?
2019-04-27 18:21:14 +01:00
$cfg [ " links_ { $this -> _database } " ] :
str_replace ( '.ini' , '.links.ini' , $ini );
2013-08-12 11:32:39 +01:00
// file really exists..
if ( ! file_exists ( $links ) || ! is_file ( $links )) {
if ( ! empty ( $cfg [ 'debug' ])) {
2019-04-16 00:20:20 +01:00
$this -> debug ( " Missing links.ini file: $links " , " links " , 1 );
2013-08-12 11:32:39 +01:00
}
continue ;
2008-09-30 14:38:47 +01:00
}
2013-08-12 11:32:39 +01:00
// set to empty array - as we have at least one file now..
$lcfg [ $this -> _database ] = empty ( $lcfg [ $this -> _database ]) ? array () : $lcfg [ $this -> _database ];
// merge schema file into lcfg..
$lcfg [ $this -> _database ] = array_merge (
$lcfg [ $this -> _database ],
parse_ini_file ( $links , true )
);
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
if ( ! empty ( $cfg [ 'debug' ])) {
2019-04-16 00:20:20 +01:00
$this -> debug ( " Loaded links.ini file: $links " , " links " , 1 );
2013-08-12 11:32:39 +01:00
}
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
if ( ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'portability' ]) && $_DB_DATAOBJECT [ 'CONFIG' ][ 'portability' ] & 1 ) {
2019-04-27 18:21:14 +01:00
foreach ( $lcfg [ $this -> _database ] as $k => $v ) {
2013-08-12 11:32:39 +01:00
$nk = strtolower ( $k );
// results in duplicate cols.. but not a big issue..
$lcfg [ $this -> _database ][ $nk ] = isset ( $lcfg [ $this -> _database ][ $nk ])
2019-04-27 18:21:14 +01:00
? $lcfg [ $this -> _database ][ $nk ] : array ();
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
foreach ( $v as $kk => $vv ) {
//var_Dump($vv);exit;
$vv = explode ( ':' , $vv );
$vv [ 0 ] = strtolower ( $vv [ 0 ]);
$lcfg [ $this -> _database ][ $nk ][ $kk ] = implode ( ':' , $vv );
}
}
}
//echo '<PRE>';print_r($lcfg);exit;
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
// if there is no link data at all on the file!
// we return null.
if ( $lcfg [ $this -> _database ] === false ) {
return null ;
}
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
if ( isset ( $lcfg [ $this -> _database ][ $this -> tableName ()])) {
return $lcfg [ $this -> _database ][ $this -> tableName ()];
2011-03-26 18:45:15 +00:00
}
2019-04-27 18:21:14 +01:00
return array ();
}
2008-09-30 14:38:47 +01:00
/**
* 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
* }
*
*
2019-04-27 18:21:14 +01:00
* @ param bool $obj object | array the joining object ( no value resets the join )
2008-09-30 14:38:47 +01:00
* If you use an array here it should be in the format :
* array ( 'local_column' , 'remotetable:remote_column' );
2013-08-12 11:32:39 +01:00
* if remotetable does not have a definition , you should
* use @ to hide the include error message ..
* array ( 'local_column' , $dataobject , 'remote_column' );
* if array has 3 args , then second is assumed to be the linked dataobject .
2008-09-30 14:38:47 +01:00
*
2019-04-27 18:21:14 +01:00
* @ param string $joinType string | array
2019-04-16 00:20:20 +01:00
* 'LEFT' | 'INNER' | 'RIGHT' | '' Inner is default , '' indicates
* just select ... from a , b , c with no join and
2008-09-30 14:38:47 +01:00
* links are added as where items .
2019-04-16 00:20:20 +01:00
*
2008-09-30 14:38:47 +01:00
* If second Argument is array , it is assumed to be an associative
* array with arguments matching below = eg .
* 'joinType' => 'INNER' ,
* 'joinAs' => '...'
* 'joinCol' => ....
* 'useWhereAsOn' => false ,
*
2019-04-27 18:21:14 +01:00
* @ param bool $joinAs string if you want to select the table as anther name
2008-09-30 14:38:47 +01:00
* useful when you want to select multiple columsn
* from a secondary table .
2019-04-27 18:21:14 +01:00
* @ param bool $joinCol string The column on This objects table to match ( needed
2019-04-16 00:20:20 +01:00
* if this table links to the child object in
2008-09-30 14:38:47 +01:00
* multiple places eg .
* user -> friend ( is a link to another user )
* user -> mother ( is a link to another user .. )
*
* optional 'useWhereAsOn' bool default false ;
* convert the where argments from the object being added
* into ON arguments .
2019-04-16 00:20:20 +01:00
*
*
2019-04-27 18:21:14 +01:00
* @ return error | none
2008-09-30 14:38:47 +01:00
* @ access public
* @ author Stijn de Reede < sjr @ gmx . co . uk >
*/
2019-04-27 18:21:14 +01:00
public function joinAdd ( $obj = false , $joinType = 'INNER' , $joinAs = false , $joinCol = false )
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
if ( $obj === false ) {
$this -> _join = '' ;
2019-04-27 18:21:14 +01:00
return null ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2009-07-16 04:35:20 +01:00
//echo '<PRE>'; print_r(func_get_args());
2008-09-30 14:38:47 +01:00
$useWhereAsOn = false ;
// support for 2nd argument as an array of options
if ( is_array ( $joinType )) {
// new options can now go in here... (dont forget to document them)
$useWhereAsOn = ! empty ( $joinType [ 'useWhereAsOn' ]);
2019-04-27 18:21:14 +01:00
$joinCol = isset ( $joinType [ 'joinCol' ]) ? $joinType [ 'joinCol' ] : $joinCol ;
$joinAs = isset ( $joinType [ 'joinAs' ]) ? $joinType [ 'joinAs' ] : $joinAs ;
$joinType = isset ( $joinType [ 'joinType' ]) ? $joinType [ 'joinType' ] : 'INNER' ;
2008-09-30 14:38:47 +01:00
}
2019-04-16 00:20:20 +01:00
// support for array as first argument
2008-09-30 14:38:47 +01:00
// this assumes that you dont have a links.ini for the specified table.
// and it doesnt exist as am extended dataobject!! - experimental.
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$ofield = false ; // object field
$tfield = false ; // this field
$toTable = false ;
if ( is_array ( $obj )) {
$tfield = $obj [ 0 ];
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
if ( count ( $obj ) == 3 ) {
$ofield = $obj [ 2 ];
$obj = $obj [ 1 ];
} else {
2019-04-16 00:20:20 +01:00
list ( $toTable , $ofield ) = explode ( ':' , $obj [ 1 ]);
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
$obj = is_string ( $toTable ) ? DB_DataObject :: factory ( $toTable ) : $toTable ;
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
if ( ! $obj || ! is_object ( $obj ) || is_a ( $obj , 'PEAR_Error' )) {
2013-08-12 11:32:39 +01:00
$obj = new DB_DataObject ;
$obj -> __table = $toTable ;
}
$obj -> _connect ();
2008-09-30 14:38:47 +01:00
}
// set the table items to nothing.. - eg. do not try and match
// things in the child table...???
$items = array ();
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
if ( ! is_object ( $obj ) || ! is_a ( $obj , 'DB_DataObject' )) {
return $this -> raiseError ( " joinAdd: called without an object " , DB_DATAOBJECT_ERROR_NODATA , PEAR_ERROR_DIE );
2008-09-30 14:38:47 +01:00
}
/* make sure $this->_database is set. */
$this -> _connect ();
2013-08-12 11:32:39 +01:00
$DB = $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ];
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
2009-07-16 04:35:20 +01:00
/// CHANGED 26 JUN 2009 - we prefer links from our local table over the remote one.
2019-04-27 18:21:14 +01:00
2009-07-16 04:35:20 +01:00
/* otherwise see if there are any links from this table to the obj. */
//print_r($this->links());
if (( $ofield === false ) && ( $links = $this -> links ())) {
2011-03-26 18:45:15 +00:00
// this enables for support for arrays of links in ini file.
// link contains this_column[] = linked_table:linked_column
// or standard way.
// link contains this_column = linked_table:linked_column
foreach ( $links as $k => $linkVar ) {
if ( ! is_array ( $linkVar )) {
2019-04-27 18:21:14 +01:00
$linkVar = array ( $linkVar );
2009-07-16 04:35:20 +01:00
}
2019-04-16 00:20:20 +01:00
foreach ( $linkVar as $v ) {
2011-03-26 18:45:15 +00:00
2019-04-27 18:21:14 +01:00
2011-03-26 18:45:15 +00:00
/* link contains {this column} = {linked table}:{linked column} */
$ar = explode ( ':' , $v );
// Feature Request #4266 - Allow joins with multiple keys
if ( strpos ( $k , ',' ) !== false ) {
$k = explode ( ',' , $k );
}
if ( strpos ( $ar [ 1 ], ',' ) !== false ) {
$ar [ 1 ] = explode ( ',' , $ar [ 1 ]);
}
2009-07-16 04:35:20 +01:00
2013-08-12 11:32:39 +01:00
if ( $ar [ 0 ] != $obj -> tableName ()) {
2011-03-26 18:45:15 +00:00
continue ;
}
2009-07-16 04:35:20 +01:00
if ( $joinCol !== false ) {
if ( $k == $joinCol ) {
2011-03-26 18:45:15 +00:00
// got it!?
2009-07-16 04:35:20 +01:00
$tfield = $k ;
$ofield = $ar [ 1 ];
break ;
2019-04-16 00:20:20 +01:00
}
2011-03-26 18:45:15 +00:00
continue ;
2019-04-16 00:20:20 +01:00
}
2011-03-26 18:45:15 +00:00
$tfield = $k ;
$ofield = $ar [ 1 ];
break ;
2009-07-16 04:35:20 +01:00
}
}
}
2019-04-16 00:20:20 +01:00
/* look up the links for obj table */
2008-09-30 14:38:47 +01:00
//print_r($obj->links());
if ( ! $ofield && ( $olinks = $obj -> links ())) {
2011-03-26 18:45:15 +00:00
foreach ( $olinks as $k => $linkVar ) {
/* link contains {this column} = array ( {linked table}:{linked column} )*/
if ( ! is_array ( $linkVar )) {
2019-04-27 18:21:14 +01:00
$linkVar = array ( $linkVar );
2008-09-30 14:38:47 +01:00
}
2019-04-16 00:20:20 +01:00
foreach ( $linkVar as $v ) {
2019-04-27 18:21:14 +01:00
2011-03-26 18:45:15 +00:00
/* link contains {this column} = {linked table}:{linked column} */
$ar = explode ( ':' , $v );
2019-04-27 18:21:14 +01:00
2011-03-26 18:45:15 +00:00
// Feature Request #4266 - Allow joins with multiple keys
2019-04-16 00:20:20 +01:00
$links_key_array = strpos ( $k , ',' );
2011-03-26 18:45:15 +00:00
if ( $links_key_array !== false ) {
$k = explode ( ',' , $k );
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
$ar_array = strpos ( $ar [ 1 ], ',' );
2011-03-26 18:45:15 +00:00
if ( $ar_array !== false ) {
$ar [ 1 ] = explode ( ',' , $ar [ 1 ]);
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
if ( $ar [ 0 ] != $this -> tableName ()) {
2011-03-26 18:45:15 +00:00
continue ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// you have explictly specified the column
// and the col is listed here..
// not sure if 1:1 table could cause probs here..
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( $joinCol !== false ) {
2019-04-16 00:20:20 +01:00
$this -> raiseError (
2008-09-30 14:38:47 +01:00
" joinAdd: You cannot target a join column in the " .
2019-04-16 00:20:20 +01:00
" 'link from' table ( { $obj -> tableName () } ). " .
2019-04-27 18:21:14 +01:00
" Either remove the fourth argument to joinAdd() " .
2008-09-30 14:38:47 +01:00
" ( { $joinCol } ), or alter your links.ini file. " ,
2019-04-16 00:20:20 +01:00
DB_DATAOBJECT_ERROR_NODATA
);
2008-09-30 14:38:47 +01:00
return false ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$ofield = $k ;
$tfield = $ar [ 1 ];
break ;
}
}
}
// finally if these two table have column names that match do a join by default on them
if (( $ofield === false ) && $joinCol ) {
$ofield = $joinCol ;
$tfield = $joinCol ;
}
/* did I find a conneciton between them? */
if ( $ofield === false ) {
$this -> raiseError (
2013-08-12 11:32:39 +01:00
" joinAdd: { $obj -> tableName () } has no link with { $this -> tableName () } " ,
2019-04-16 00:20:20 +01:00
DB_DATAOBJECT_ERROR_NODATA
);
2008-09-30 14:38:47 +01:00
return false ;
}
$joinType = strtoupper ( $joinType );
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// we default to joining as the same name (this is remvoed later..)
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( $joinAs === false ) {
2013-08-12 11:32:39 +01:00
$joinAs = $obj -> tableName ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$quoteIdentifiers = ! empty ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'quote_identifiers' ]);
$options = $_DB_DATAOBJECT [ 'CONFIG' ];
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// not sure how portable adding database prefixes is..
2019-04-16 00:20:20 +01:00
$objTable = $quoteIdentifiers ?
2019-04-27 18:21:14 +01:00
$DB -> quoteIdentifier ( $obj -> tableName ()) :
$obj -> tableName ();
$dbPrefix = '' ;
if ( strlen ( $obj -> _database ) && in_array ( $DB -> dsn [ 'phptype' ], array ( 'mysql' , 'mysqli' ))) {
2008-09-30 14:38:47 +01:00
$dbPrefix = ( $quoteIdentifiers
2019-04-27 18:21:14 +01:00
? $DB -> quoteIdentifier ( $obj -> _database )
: $obj -> _database ) . '.' ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
// if they are the same, then dont add a prefix...
2008-09-30 14:38:47 +01:00
if ( $obj -> _database == $this -> _database ) {
2019-04-16 00:20:20 +01:00
$dbPrefix = '' ;
2008-09-30 14:38:47 +01:00
}
// as far as we know only mysql supports database prefixes..
// prefixing the database name is now the default behaviour,
// as it enables joining mutiple columns from multiple databases...
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
// prefix database (quoted if neccessary..)
2008-09-30 14:38:47 +01:00
$objTable = $dbPrefix . $objTable ;
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$cond = '' ;
// if obj only a dataobject - eg. no extended class has been defined..
// it obvioulsy cant work out what child elements might exist...
// until we get on the fly querying of tables..
// note: we have already checked that it is_a(db_dataobject earlier)
2019-04-16 00:20:20 +01:00
if ( strtolower ( get_class ( $obj )) != 'db_dataobject' ) {
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
// now add where conditions for anything that is set in the object
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$items = $obj -> table ();
// will return an array if no items..
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// only fail if we where expecting it to work (eg. not joined on a array)
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( ! $items ) {
$this -> raiseError (
2019-04-16 00:20:20 +01:00
" joinAdd: No table definition for { $obj -> tableName () } " ,
DB_DATAOBJECT_ERROR_INVALIDCONFIG
);
2008-09-30 14:38:47 +01:00
return false ;
}
2019-04-27 18:21:14 +01:00
2009-12-08 20:32:50 +00:00
$ignore_null = ! isset ( $options [ 'disable_null_strings' ])
2019-04-27 18:21:14 +01:00
|| ! is_string ( $options [ 'disable_null_strings' ])
|| strtolower ( $options [ 'disable_null_strings' ]) !== 'full' ;
2008-09-30 14:38:47 +01:00
2019-04-16 00:20:20 +01:00
foreach ( $items as $k => $v ) {
2009-12-08 20:32:50 +00:00
if ( ! isset ( $obj -> $k ) && $ignore_null ) {
2008-09-30 14:38:47 +01:00
continue ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$kSql = ( $quoteIdentifiers ? $DB -> quoteIdentifier ( $k ) : $k );
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
if ( DB_DataObject :: _is_null ( $obj , $k )) {
$obj -> whereAdd ( " { $joinAs } . { $kSql } IS NULL " );
continue ;
2009-12-08 20:32:50 +00:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( $v & DB_DATAOBJECT_STR ) {
2019-04-27 18:21:14 +01:00
$obj -> whereAdd ( " { $joinAs } . { $kSql } = " . $this -> _quote (( string )(
2019-04-16 00:20:20 +01:00
( $v & DB_DATAOBJECT_BOOL ) ?
2019-04-27 18:21:14 +01:00
// 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
2008-09-30 14:38:47 +01:00
)));
continue ;
}
if ( is_numeric ( $obj -> $k )) {
$obj -> whereAdd ( " { $joinAs } . { $kSql } = { $obj -> $k } " );
continue ;
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
if ( is_object ( $obj -> $k ) && is_a ( $obj -> $k , 'DB_DataObject_Cast' )) {
$value = $obj -> $k -> toString ( $v , $DB );
2019-04-27 18:21:14 +01:00
if (( new PEAR ) -> isError ( $value )) {
2019-04-16 00:20:20 +01:00
$this -> raiseError ( $value -> getMessage (), DB_DATAOBJECT_ERROR_INVALIDARG );
2008-09-30 14:38:47 +01:00
return false ;
2019-04-16 00:20:20 +01:00
}
2009-12-08 20:32:50 +00:00
$obj -> whereAdd ( " { $joinAs } . { $kSql } = $value " );
continue ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
/* this is probably an error condition! */
$obj -> whereAdd ( " { $joinAs } . { $kSql } = 0 " );
}
if ( $this -> _query === false ) {
$this -> raiseError (
" joinAdd can not be run from a object that has had a query run on it,
2019-04-16 00:20:20 +01:00
clone the object or create a new one and use setFrom () " ,
DB_DATAOBJECT_ERROR_INVALIDARGS
);
2008-09-30 14:38:47 +01:00
return false ;
}
}
// and finally merge the whereAdd from the child..
if ( $obj -> _query [ 'condition' ]) {
2019-04-16 00:20:20 +01:00
$cond = preg_replace ( '/^\sWHERE/i' , '' , $obj -> _query [ 'condition' ]);
2008-09-30 14:38:47 +01:00
if ( ! $useWhereAsOn ) {
$this -> whereAdd ( $cond );
}
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// 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..
2019-04-16 00:20:20 +01:00
if ( in_array ( $DB -> dsn [ " phptype " ], array ( 'pgsql' ))) {
2008-09-30 14:38:47 +01:00
$objTable = " ( $objTable { $obj -> _join } ) " ;
} else {
$appendJoin = $obj -> _join ;
}
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// fix for #2216
// add the joinee object's conditions to the ON clause instead of the WHERE clause
if ( $useWhereAsOn && strlen ( $cond )) {
$appendJoin = ' AND ' . $cond . ' ' . $appendJoin ;
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
$table = $this -> tableName ();
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( $quoteIdentifiers ) {
2019-04-27 18:21:14 +01:00
$joinAs = $DB -> quoteIdentifier ( $joinAs );
$table = $DB -> quoteIdentifier ( $table );
$ofield = ( is_array ( $ofield )) ? array_map ( array ( $DB , 'quoteIdentifier' ), $ofield ) : $DB -> quoteIdentifier ( $ofield );
$tfield = ( is_array ( $tfield )) ? array_map ( array ( $DB , 'quoteIdentifier' ), $tfield ) : $DB -> quoteIdentifier ( $tfield );
2008-09-30 14:38:47 +01:00
}
// add database prefix if they are different databases
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$fullJoinAs = '' ;
2019-04-27 18:21:14 +01:00
$addJoinAs = ( $quoteIdentifiers ? $DB -> quoteIdentifier ( $obj -> tableName ()) : $obj -> tableName ()) != $joinAs ;
2008-09-30 14:38:47 +01:00
if ( $addJoinAs ) {
// join table a AS b - is only supported by a few databases and is probably not needed
// , however since it makes the whole Statement alot clearer we are leaving it in
// for those databases.
2019-04-27 18:21:14 +01:00
$fullJoinAs = in_array ( $DB -> dsn [ " phptype " ], array ( 'mysql' , 'mysqli' , 'pgsql' )) ? " AS { $joinAs } " : $joinAs ;
2008-09-30 14:38:47 +01:00
} else {
2019-04-16 00:20:20 +01:00
// if
2008-09-30 14:38:47 +01:00
$joinAs = $dbPrefix . $joinAs ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
switch ( $joinType ) {
case 'INNER' :
2019-04-16 00:20:20 +01:00
case 'LEFT' :
2008-09-30 14:38:47 +01:00
case 'RIGHT' : // others??? .. cross, left outer, right outer, natural..?
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// Feature Request #4266 - Allow joins with multiple keys
2009-07-16 04:35:20 +01:00
$jadd = " \n { $joinType } JOIN { $objTable } { $fullJoinAs } " ;
//$this->_join .= "\n {$joinType} JOIN {$objTable} {$fullJoinAs}";
2008-09-30 14:38:47 +01:00
if ( is_array ( $ofield )) {
2019-04-16 00:20:20 +01:00
$key_count = count ( $ofield );
for ( $i = 0 ; $i < $key_count ; $i ++ ) {
if ( $i == 0 ) {
$jadd .= " ON ( { $joinAs } . { $ofield [ $i ] } = { $table } . { $tfield [ $i ] } ) " ;
} else {
$jadd .= " AND { $joinAs } . { $ofield [ $i ] } = { $table } . { $tfield [ $i ] } " ;
}
2008-09-30 14:38:47 +01:00
}
2009-07-16 04:35:20 +01:00
$jadd .= ' ' . $appendJoin . ' ' ;
2008-09-30 14:38:47 +01:00
} else {
2019-04-16 00:20:20 +01:00
$jadd .= " ON ( { $joinAs } . { $ofield } = { $table } . { $tfield } ) { $appendJoin } " ;
2008-09-30 14:38:47 +01:00
}
2009-07-16 04:35:20 +01:00
// jadd avaliable for debugging join build.
//echo $jadd ."\n";
$this -> _join .= $jadd ;
2008-09-30 14:38:47 +01:00
break ;
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
case '' : // this is just a standard multitable select..
$this -> _join .= " \n , { $objTable } { $fullJoinAs } { $appendJoin } " ;
$this -> whereAdd ( " { $joinAs } . { $ofield } = { $table } . { $tfield } " );
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
return true ;
}
2013-08-12 11:32:39 +01:00
/**
2019-04-27 18:21:14 +01:00
* Adds multiple Columns or objects to select with formating .
2013-08-12 11:32:39 +01:00
*
2019-04-27 18:21:14 +01:00
* $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
2019-04-16 00:20:20 +01:00
*
2019-04-27 18:21:14 +01:00
* @ param array | object | null the array or object to take column names from .
* @ param string $format
* @ param bool $tableName
* @ return bool | void
* @ access public
2013-08-12 11:32:39 +01:00
*/
2019-04-27 18:21:14 +01:00
public function selectAs ( $from = null , $format = '%s' , $tableName = false )
2013-08-12 11:32:39 +01:00
{
2017-07-10 12:25:04 +01:00
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
if ( $this -> _query === false ) {
$this -> raiseError (
" You cannot do two queries on the same object (copy it before finding) " ,
DB_DATAOBJECT_ERROR_INVALIDARGS
);
return false ;
2013-08-12 11:32:39 +01:00
}
2019-04-27 18:21:14 +01:00
if ( $from === null ) {
// blank the '*'
$this -> selectAdd ();
$from = $this ;
2013-08-12 11:32:39 +01:00
}
2019-04-27 18:21:14 +01:00
$table = $this -> tableName ();
if ( is_object ( $from )) {
$table = $from -> tableName ();
$from = array_keys ( $from -> table ());
2013-08-12 11:32:39 +01:00
}
2019-04-27 18:21:14 +01:00
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 );
$format = $DB -> quoteIdentifier ( $format );
}
foreach ( $from as $k ) {
$this -> selectAdd ( sprintf ( " { $s } . { $s } as { $format } " , $table , $k , $k ));
}
$this -> _query [ 'data_select' ] .= " \n " ;
2013-08-12 11:32:39 +01:00
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
/**
* Factory method for calling DB_DataObject_Cast
*
* if used with 1 argument DB_DataObject_Cast :: sql ( $value ) is called
2019-04-16 00:20:20 +01:00
*
2013-08-12 11:32:39 +01:00
* if used with 2 arguments DB_DataObject_Cast :: $value ( $callvalue ) is called
* valid first arguments are : blob , string , date , sql
2019-04-16 00:20:20 +01:00
*
2013-08-12 11:32:39 +01:00
* eg . $member -> updated = $member -> sqlValue ( 'NOW()' );
2019-04-16 00:20:20 +01:00
*
*
2013-08-12 11:32:39 +01:00
* might handle more arguments for escaping later ...
2019-04-16 00:20:20 +01:00
*
2013-08-12 11:32:39 +01:00
*
* @ param string $value ( or type if used with 2 arguments )
2019-04-27 18:21:14 +01:00
* @ return mixed
2013-08-12 11:32:39 +01:00
*/
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
public function sqlValue ( $value )
2013-08-12 11:32:39 +01:00
{
$method = 'sql' ;
if ( func_num_args () == 2 ) {
$method = $value ;
$value = func_get_arg ( 1 );
}
2019-11-02 09:21:43 +00:00
require_once 'DB/DataObject/Cast.php' ;
2013-08-12 11:32:39 +01:00
return call_user_func ( array ( 'DB_DataObject_Cast' , $method ), $value );
}
2019-04-27 18:21:14 +01:00
/* ----------------------- Debugger ------------------ */
2008-09-30 14:38:47 +01:00
/**
* Copies items that are in the table definitions from an
* array or object into the current object
* will not override key values .
*
*
2019-04-27 18:21:14 +01:00
* @ param array | object $from
* @ param string $format eg . map xxxx_name to $object -> name using 'xxxx_%s' ( defaults to % s - eg . name -> $object -> name
* @ param boolean $skipEmpty ( dont assign empty values if a column is empty ( eg . '' / 0 etc ... )
2008-09-30 14:38:47 +01:00
* @ access public
2019-04-27 18:21:14 +01:00
* @ return array | true
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
public function setFrom ( $from , $format = '%s' , $skipEmpty = false )
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
$keys = $this -> keys ();
2008-09-30 14:38:47 +01:00
$items = $this -> table ();
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( ! $items ) {
$this -> raiseError (
2019-04-16 00:20:20 +01:00
" setFrom:Could not find table definition for { $this -> tableName () } " ,
DB_DATAOBJECT_ERROR_INVALIDCONFIG
);
2019-04-27 18:21:14 +01:00
return null ;
2008-09-30 14:38:47 +01:00
}
$overload_return = array ();
foreach ( array_keys ( $items ) as $k ) {
2019-04-16 00:20:20 +01:00
if ( in_array ( $k , $keys )) {
2008-09-30 14:38:47 +01:00
continue ; // dont overwrite keys
}
if ( ! $k ) {
continue ; // ignore empty keys!!! what
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
$chk = is_object ( $from ) &&
(
2019-04-27 18:21:14 +01:00
version_compare ( phpversion (), " 5.1.0 " , " >= " ) ?
2019-04-16 00:20:20 +01:00
property_exists ( $from , sprintf ( $format , $k )) : // php5.1
array_key_exists ( sprintf ( $format , $k ), get_class_vars ( $from )) //older
2011-03-26 18:45:15 +00:00
);
2019-04-16 00:20:20 +01:00
// if from has property ($format($k)
2011-03-26 18:45:15 +00:00
if ( $chk ) {
2008-09-30 14:38:47 +01:00
$kk = ( strtolower ( $k ) == 'from' ) ? '_from' : $k ;
2019-04-27 18:21:14 +01:00
if ( method_exists ( $this , 'set' . $kk )) {
$ret = $this -> { 'set' . $kk }( $from -> { sprintf ( $format , $k )});
2008-09-30 14:38:47 +01:00
if ( is_string ( $ret )) {
$overload_return [ $k ] = $ret ;
}
continue ;
}
2019-04-16 00:20:20 +01:00
$this -> $k = $from -> { sprintf ( $format , $k )};
2008-09-30 14:38:47 +01:00
continue ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( is_object ( $from )) {
continue ;
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
if ( empty ( $from [ sprintf ( $format , $k )]) && $skipEmpty ) {
2008-09-30 14:38:47 +01:00
continue ;
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
if ( ! isset ( $from [ sprintf ( $format , $k )]) && ! DB_DataObject :: _is_null ( $from , sprintf ( $format , $k ))) {
2008-09-30 14:38:47 +01:00
continue ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$kk = ( strtolower ( $k ) == 'from' ) ? '_from' : $k ;
2019-04-27 18:21:14 +01:00
if ( method_exists ( $this , 'set' . $kk )) {
$ret = $this -> { 'set' . $kk }( $from [ sprintf ( $format , $k )]);
2008-09-30 14:38:47 +01:00
if ( is_string ( $ret )) {
$overload_return [ $k ] = $ret ;
}
continue ;
}
2019-04-16 00:20:20 +01:00
$val = $from [ sprintf ( $format , $k )];
2013-08-12 11:32:39 +01:00
if ( is_a ( $val , 'DB_DataObject_Cast' )) {
$this -> $k = $val ;
2008-09-30 14:38:47 +01:00
continue ;
}
2013-08-12 11:32:39 +01:00
if ( is_object ( $val ) || is_array ( $val )) {
2008-09-30 14:38:47 +01:00
continue ;
}
2019-04-16 00:20:20 +01:00
$ret = $this -> fromValue ( $k , $val );
if ( $ret !== true ) {
2008-09-30 14:38:47 +01:00
$overload_return [ $k ] = 'Not A Valid Value' ;
}
//$this->$k = $from[sprintf($format,$k)];
}
if ( $overload_return ) {
return $overload_return ;
}
return true ;
}
2019-04-27 18:21:14 +01:00
/**
* 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
*/
public function fromValue ( $col , $value )
{
global $_DB_DATAOBJECT ;
$options = $_DB_DATAOBJECT [ 'CONFIG' ];
$cols = $this -> table ();
// dont know anything about this col..
if ( ! isset ( $cols [ $col ]) || is_a ( $value , 'DB_DataObject_Cast' )) {
$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 (( ! ( $cols [ $col ] & DB_DATAOBJECT_NOTNULL )) && DB_DataObject :: _is_null ( $value , false )) :
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 (( $cols [ $col ] & DB_DATAOBJECT_NOTNULL ) && DB_DataObject :: _is_null ( $value , false )) :
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 ;
}
}
2008-09-30 14:38:47 +01:00
/**
* 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 .
*
2019-04-27 18:21:14 +01:00
* @ param string sprintf format for array
* @ param bool || number [ true = elemnts that have a value set ],
2013-08-12 11:32:39 +01:00
* [ false = table + returned colums ] ,
* [ 0 = returned columsn only ]
2008-09-30 14:38:47 +01:00
*
* @ access public
* @ return array of key => value for row
*/
2019-04-16 00:20:20 +01:00
public function toArray ( $format = '%s' , $hideEmpty = false )
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
// we use false to ignore sprintf.. (speed up..)
$format = $format == '%s' ? false : $format ;
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
$ret = array ();
2019-04-16 00:20:20 +01:00
$rf = ( $this -> _resultFields !== false ) ? $this -> _resultFields :
2019-04-27 18:21:14 +01:00
( isset ( $_DB_DATAOBJECT [ 'RESULTFIELDS' ][ $this -> _DB_resultid ]) ?
$_DB_DATAOBJECT [ 'RESULTFIELDS' ][ $this -> _DB_resultid ] : false );
2008-09-30 14:38:47 +01:00
$ar = ( $rf !== false ) ?
2013-08-12 11:32:39 +01:00
(( $hideEmpty === 0 ) ? $rf : array_merge ( $rf , $this -> table ())) :
2008-09-30 14:38:47 +01:00
$this -> table ();
2019-04-27 18:21:14 +01:00
foreach ( $ar as $k => $v ) {
2008-09-30 14:38:47 +01:00
if ( ! isset ( $this -> $k )) {
if ( ! $hideEmpty ) {
2019-04-16 00:20:20 +01:00
$ret [ $format === false ? $k : sprintf ( $format , $k )] = '' ;
2008-09-30 14:38:47 +01:00
}
continue ;
}
// call the overloaded getXXXX() method. - except getLink and getLinks
2019-04-27 18:21:14 +01:00
if ( method_exists ( $this , 'get' . $k ) && ! in_array ( strtolower ( $k ), array ( 'links' , 'link' ))) {
$ret [ $format === false ? $k : sprintf ( $format , $k )] = $this -> { 'get' . $k }();
2008-09-30 14:38:47 +01:00
continue ;
}
// should this call toValue() ???
2019-04-16 00:20:20 +01:00
$ret [ $format === false ? $k : sprintf ( $format , $k )] = $this -> $k ;
2008-09-30 14:38:47 +01:00
}
if ( ! $this -> _link_loaded ) {
return $ret ;
}
2019-04-16 00:20:20 +01:00
foreach ( $this -> _link_loaded as $k ) {
$ret [ $format === false ? $k : sprintf ( $format , $k )] = $this -> $k -> toArray ();
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
return $ret ;
}
/**
* validate the values of the object ( usually prior to inserting / updating .. )
*
* Note : This was always intended as a simple validation routine .
* It lacks understanding of field length , whether you are inserting or updating ( and hence null key values )
*
2019-04-16 00:20:20 +01:00
* This should be moved to another class : DB_DataObject_Validate
2008-09-30 14:38:47 +01:00
* FEEL FREE TO SEND ME YOUR VERSION FOR CONSIDERATION !!!
*
* Usage :
* if ( is_array ( $ret = $obj -> validate ())) { ... there are problems with the data ... }
*
* Logic :
* - defaults to only testing strings / numbers if numbers or strings are the correct type and null values are correct
* - validate Column methods : " validate { ROWNAME}() " are called if they are defined .
2019-04-16 00:20:20 +01:00
* These methods should return
2008-09-30 14:38:47 +01:00
* true = everything ok
* false | object = something is wrong !
2019-04-16 00:20:20 +01:00
*
2008-09-30 14:38:47 +01:00
* - This method loads and uses the PEAR Validate Class .
*
*
* @ access public
2019-04-27 18:21:14 +01:00
* @ return array | bool
2008-09-30 14:38:47 +01:00
*/
2019-04-16 00:20:20 +01:00
public function validate ()
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
require_once 'Validate.php' ;
$table = $this -> table ();
2019-04-27 18:21:14 +01:00
$ret = array ();
$seq = $this -> sequenceKey ();
2008-09-30 14:38:47 +01:00
$options = $_DB_DATAOBJECT [ 'CONFIG' ];
2019-04-16 00:20:20 +01:00
foreach ( $table as $key => $val ) {
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// call user defined validation always...
$method = " Validate " . ucfirst ( $key );
if ( method_exists ( $this , $method )) {
$ret [ $key ] = $this -> $method ();
continue ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// if not null - and it's not set.......
2019-04-27 18:21:14 +01:00
2009-12-08 20:32:50 +00:00
if ( $val & DB_DATAOBJECT_NOTNULL && DB_DataObject :: _is_null ( $this , $key )) {
2008-09-30 14:38:47 +01:00
// dont check empty sequence key values..
if (( $key == $seq [ 0 ]) && ( $seq [ 1 ] == true )) {
continue ;
}
$ret [ $key ] = false ;
continue ;
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
if ( DB_DataObject :: _is_null ( $this , $key )) {
2008-09-30 14:38:47 +01:00
if ( $val & DB_DATAOBJECT_NOTNULL ) {
$this -> debug ( " 'null' field used for ' $key ', but it is defined as NOT NULL " , 'VALIDATION' , 4 );
$ret [ $key ] = false ;
continue ;
}
continue ;
}
// ignore things that are not set. ?
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( ! isset ( $this -> $key )) {
continue ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// if the string is empty.. assume it is ok..
2019-04-27 18:21:14 +01:00
if ( ! is_object ( $this -> $key ) && ! is_array ( $this -> $key ) && ! strlen (( string ) $this -> $key )) {
2008-09-30 14:38:47 +01:00
continue ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// dont try and validate cast objects - assume they are problably ok..
2019-04-16 00:20:20 +01:00
if ( is_object ( $this -> $key ) && is_a ( $this -> $key , 'DB_DataObject_Cast' )) {
2008-09-30 14:38:47 +01:00
continue ;
}
// at this point if you have set something to an object, and it's not expected
2019-04-16 00:20:20 +01:00
// the Validate will probably break!!... - rightly so! (your design is broken,
2008-09-30 14:38:47 +01:00
// so issuing a runtime error like PEAR_Error is probably not appropriate..
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
switch ( true ) {
// todo: date time.....
case ( $val & DB_DATAOBJECT_STR ) :
2019-04-27 18:21:14 +01:00
$ret [ $key ] = ( new Validate ) -> string ( $this -> $key , VALIDATE_PUNCTUATION . VALIDATE_NAME );
2019-04-16 00:20:20 +01:00
break ;
2008-09-30 14:38:47 +01:00
case ( $val & DB_DATAOBJECT_INT ) :
2019-04-27 18:21:14 +01:00
$ret [ $key ] = ( new Validate ) -> number ( $this -> $key , array ( 'decimal' => '.' ));
2019-04-16 00:20:20 +01:00
break ;
2008-09-30 14:38:47 +01:00
}
}
// if any of the results are false or an object (eg. PEAR_Error).. then return the array..
foreach ( $ret as $key => $val ) {
if ( $val !== true ) {
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
2019-04-27 18:21:14 +01:00
* @ return bool | object
2008-09-30 14:38:47 +01:00
*/
2019-04-16 00:20:20 +01:00
public function getDatabaseConnection ()
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
if (( $e = $this -> _connect ()) !== true ) {
return $e ;
}
if ( ! isset ( $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ])) {
$r = false ;
return $r ;
}
return $_DB_DATAOBJECT [ 'CONNECTIONS' ][ $this -> _database_dsn_md5 ];
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
/**
* 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
2019-04-27 18:21:14 +01:00
* @ return bool | object
2008-09-30 14:38:47 +01:00
*/
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
public function getDatabaseResult ()
2008-09-30 14:38:47 +01:00
{
global $_DB_DATAOBJECT ;
$this -> _connect ();
if ( ! isset ( $_DB_DATAOBJECT [ 'RESULTS' ][ $this -> _DB_resultid ])) {
$r = false ;
return $r ;
}
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 .
2019-04-16 00:20:20 +01:00
* NOTE this currently means that a few Names are NO - NO ' s
2008-09-30 14:38:47 +01:00
* eg . links , link , linksarray , from , Databaseconnection , databaseresult
*
2019-04-16 00:20:20 +01:00
* note
2008-09-30 14:38:47 +01:00
* - set is automatically called by setFrom .
* - get is automatically called by toArray ()
2019-04-16 00:20:20 +01:00
*
2008-09-30 14:38:47 +01:00
* setters return true on success . = strings on failure
* getters return the value !
*
2019-04-16 00:20:20 +01:00
* this fires off trigger_error - if any problems .. pear_error ,
2008-09-30 14:38:47 +01:00
* has problems with 4.3 . 2 RC2 here
*
* @ access public
2019-04-27 18:21:14 +01:00
* @ param $method
* @ param $params
* @ param $return
2008-09-30 14:38:47 +01:00
* @ return true ?
2019-04-27 18:21:14 +01:00
* @ throws ReflectionException
2008-09-30 14:38:47 +01:00
* @ see overload
*/
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
public function _call ( $method , $params , & $return )
{
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
//$this->debug("ATTEMPTING OVERLOAD? $method");
// ignore constructors : - mm
if ( strtolower ( $method ) == strtolower ( get_class ( $this ))) {
return true ;
}
2019-04-16 00:20:20 +01:00
$type = strtolower ( substr ( $method , 0 , 3 ));
2008-09-30 14:38:47 +01:00
$class = get_class ( $this );
if (( $type != 'set' ) && ( $type != 'get' )) {
return false ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// deal with naming conflick of setFrom = this is messy ATM!
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
if ( strtolower ( $method ) == 'set_from' ) {
2019-04-16 00:20:20 +01:00
$return = $this -> toValue ( 'from' , isset ( $params [ 0 ]) ? $params [ 0 ] : null );
2019-04-27 18:21:14 +01:00
return true ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
$element = substr ( $method , 3 );
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
// dont you just love php's case insensitivity!!!!
2019-04-27 18:21:14 +01:00
$array = array_keys ( get_class_vars ( $class ));
2008-09-30 14:38:47 +01:00
/* php5 version which segfaults on 5.0.3 */
if ( class_exists ( 'ReflectionClass' )) {
2019-04-27 18:21:14 +01:00
$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 ])) {
2008-09-30 14:38:47 +01:00
return false ;
2019-04-27 18:21:14 +01:00
}
// use the mundged case
$element = $case [ $element ]; // real case !
}
if ( $type == 'get' ) {
$return = $this -> toValue ( $element , isset ( $params [ 0 ]) ? $params [ 0 ] : null );
return true ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
$return = $this -> fromValue ( $element , $params [ 0 ]);
return true ;
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
/**
2019-04-27 18:21:14 +01:00
* standard get * implementation .
*
* with formaters ..
* supported formaters :
* date / time : % d /% m /% Y ( eg . php strftime ) or pear :: Date
* numbers : % 02 d ( eg . sprintf )
* NOTE you will get unexpected results with times like 0000 - 00 - 00 !!!
*
*
*
* @ param string column of database
* @ param format foramt
*
* @ return string | true
* @ access public
* @ see DB_DataObject :: _call (), strftime (), Date :: format ()
*/
2019-04-16 00:20:20 +01:00
public function toValue ( $col , $format = null )
2008-09-30 14:38:47 +01:00
{
if ( is_null ( $format )) {
return $this -> $col ;
}
$cols = $this -> table ();
switch ( true ) {
2019-04-27 18:21:14 +01:00
case (( $cols [ $col ] & DB_DATAOBJECT_DATE ) && ( $cols [ $col ] & DB_DATAOBJECT_TIME )) :
2008-09-30 14:38:47 +01:00
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 '' ;
2019-04-16 00:20:20 +01:00
}
2008-09-30 14:38:47 +01:00
$guess = strtotime ( $this -> $col );
if ( $guess != - 1 ) {
2019-04-16 00:20:20 +01:00
return strftime ( $format , $guess );
2008-09-30 14:38:47 +01:00
}
// try date!!!!
require_once 'Date.php' ;
$x = new Date ( $this -> $col );
return $x -> format ( $format );
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
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 ;
2019-04-27 18:21:14 +01:00
case ( $cols [ $col ] & DB_DATAOBJECT_MYSQLTIMESTAMP ) :
2008-09-30 14:38:47 +01:00
if ( ! $this -> $col ) {
return '' ;
}
require_once 'Date.php' ;
2019-04-27 18:21:14 +01:00
$x = new Date ( $this -> $col );
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
return $x -> format ( $format );
2008-09-30 14:38:47 +01:00
2019-04-27 18:21:14 +01:00
case ( $cols [ $col ] & DB_DATAOBJECT_BOOL ) :
2009-12-08 20:32:50 +00:00
2019-04-27 18:21:14 +01:00
if ( $cols [ $col ] & DB_DATAOBJECT_STR ) {
// it's a 't'/'f' !
return ( $this -> $col === 't' );
2015-02-12 21:17:02 +00:00
}
2019-04-27 18:21:14 +01:00
return ( bool ) $this -> $col ;
2013-08-12 11:32:39 +01:00
2019-04-27 18:21:14 +01:00
default :
return sprintf ( $format , $this -> col );
2013-08-12 11:32:39 +01:00
}
}
2019-04-27 18:21:14 +01:00
2013-08-12 11:32:39 +01:00
/**
* autoload Class relating to a table
* ( deprecited - use :: factory )
*
2019-04-27 18:21:14 +01:00
* @ param string $table table
2013-08-12 11:32:39 +01:00
* @ access private
* @ return string classname on Success
*/
2019-04-16 00:20:20 +01:00
public function staticAutoloadTable ( $table )
2013-08-12 11:32:39 +01:00
{
global $_DB_DATAOBJECT ;
if ( empty ( $_DB_DATAOBJECT [ 'CONFIG' ])) {
DB_DataObject :: _loadConfig ();
}
$p = isset ( $_DB_DATAOBJECT [ 'CONFIG' ][ 'class_prefix' ]) ?
$_DB_DATAOBJECT [ 'CONFIG' ][ 'class_prefix' ] : '' ;
2019-04-16 00:20:20 +01:00
$class = $p . preg_replace ( '/[^A-Z0-9]/i' , '_' , ucfirst ( $table ));
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
$ce = substr ( phpversion (), 0 , 1 ) > 4 ? class_exists ( $class , false ) : class_exists ( $class );
2019-04-27 18:21:14 +01:00
$class = $ce ? $class : DB_DataObject :: _autoloadClass ( $class );
2013-08-12 11:32:39 +01:00
return $class ;
}
2019-04-27 18:21:14 +01:00
2008-09-30 14:38:47 +01:00
/* ---- LEGACY BC METHODS - NOT DOCUMENTED - See Documentation on New Methods. ---*/
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
public function _get_table ()
{
return $this -> table ();
}
2019-04-27 18:21:14 +01:00
2019-04-16 00:20:20 +01:00
public function _get_keys ()
{
return $this -> keys ();
}
2008-09-30 14:38:47 +01:00
}
2019-04-27 18:21:14 +01:00
2019-11-02 00:26:25 +00:00
// technically 4.3.2RC1 was broken!!
2008-09-30 14:38:47 +01:00
// looks like 4.3.3 may have problems too....
if ( ! defined ( 'DB_DATAOBJECT_NO_OVERLOAD' )) {
2019-04-16 00:20:20 +01:00
if (( phpversion () != '4.3.2-RC1' ) && ( version_compare ( phpversion (), " 4.3.1 " ) > 0 )) {
if ( version_compare ( phpversion (), " 5 " ) < 0 ) {
overload ( 'DB_DataObject' );
}
2008-09-30 14:38:47 +01:00
$GLOBALS [ '_DB_DATAOBJECT' ][ 'OVERLOADED' ] = true ;
}
}