Subversion Repositories Applications.gtt

Compare Revisions

Ignore whitespace Rev 186 → Rev 185

/trunk/bibliotheque/pear/DB/NestedSet/Output.php
New file
0,0 → 1,211
<?php
//
// +----------------------------------------------------------------------+
// | PEAR :: DB_NestedSet_Output |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Daniel Khan <dk@webcluster.at> |
// | Jason Rust <jason@rustyparts.com> |
// +----------------------------------------------------------------------+
// $Id: Output.php,v 1.11 2003/10/07 00:11:27 datenpunk Exp $
//
 
require_once 'PEAR.php';
 
// {{{ constants
 
define('NESEO_ERROR_NO_METHOD', 'E1000');
define('NESEO_DRIVER_NOT_FOUND', 'E1100');
define('NESEO_ERROR_NO_OPTIONS', 'E2100');
 
// }}}
// {{{ DB_NestedSet_Output:: class
 
/**
* DB_NestedSet_Output is a unified API for other output drivers
* Status is beta
*
* At the moment PEAR::HTML_TreeMenu written by Jason Rust is supported
* A driver for treemenu.org will follow soon.
*
* Usage example:
*
* require_once('DB_NestedSet/NestedSet/Output.php');
* $icon = 'folder.gif';
* $expandedIcon = 'folder-expanded.gif';
* // get data (important to fetch it as an array, using the true flag)
* $data = $NeSe->getAllNodes(true);
* // change the events for one of the elements
* $data[35]['events'] = array('onexpand' => 'alert("we expanded!");');
* // add links to each item
* foreach ($data as $a_data) {
* $a_data['link'] = 'http://foo.com/foo.php?' . $a_data['id'];
* }
* $params = array(
* 'structure' => $data,
* 'options' => array(
* 'icon' => $icon,
* 'expandedIcon' => $expandedIcon,
* ),
* 'textField' => 'name',
* 'linkField' => 'link',
* );
* $menu =& DB_NestedSet_Output::factory('TreeMenu', $params);
* $menu->printListbox();
*
* @author Daniel Khan <dk@webcluster.at>
* @package DB_NestedSet
* @version $Revision: 1.11 $
* @access public
*
*/
 
// }}}
class DB_NestedSet_Output {
// {{{ properties
 
/**
* @var object The tree menu structure
* @access private
*/
var $_structTreeMenu = false;
 
/**
* @var array Array of options to be passed to the ouput methods
* @access public
*/
var $options = array();
 
// }}}
// {{{ factory()
 
/**
* Returns a output driver object
*
* @param array $params A DB_NestedSet nodeset
* @param string $driver (optional) The driver, such as TreeMenu (default)
*
* @access public
* @return object The DB_NestedSet_Ouput object
*/
function &factory ($params, $driver = 'TreeMenu') {
 
$path = dirname(__FILE__).'/'.$driver.'.php';
 
if(is_dir($path) || !file_exists($path)) {
PEAR::raiseError("The output driver '$driver' wasn't found", NESEO_DRIVER_NOT_FOUND, PEAR_ERROR_TRIGGER, E_USER_ERROR);
}
 
require_once($path);
$driverClass = 'DB_NestedSet_'.$driver;
return new $driverClass($params);
}
 
// }}}
// {{{ setOptions()
 
/**
* Set's options for a specific output group (printTree, printListbox)
* This enables you to set specific options for each output method
*
* @param string $group Output group ATM 'printTree' or 'printListbox'
* @param array $options Hash with options
*
* @access public
* @return bool
*/
function setOptions($group, $options) {
$this->options[$group] = $options;
return true;
}
 
// }}}
// {{{ _getOptions()
 
/**
* Get's all option for a specific output group (printTree, printListbox)
*
* @param string $group Output group ATM 'printTree' or 'printListbox'
*
* @access private
* @return array Options
*/
function _getOptions($group) {
 
if (!isset($this->options[$group])) {
return array();
}
return $this->options[$group];
}
 
// }}}
// {{{ printTree()
 
/**
* Print's the current tree using the output driver
* Overriden by the driver class
*
* @access public
*/
function printTree() {
PEAR::raiseError("Method not available for this driver", NESEO_ERROR_NO_METHOD, PEAR_ERROR_TRIGGER, E_USER_ERROR);
}
 
// }}}
// {{{ printListbox()
 
/**
* Print's a listbox representing the current tree
* Overriden by the driver class
*
* @access public
*/
function printListbox() {
PEAR::raiseError("Method not available for this driver", NESEO_ERROR_NO_METHOD, PEAR_ERROR_TRIGGER, E_USER_ERROR);
}
 
// }}}
 
// {{{ toHTML()
 
/**
* Returns the HTML for the DHTML-menu. This method can be
* used instead of printMenu() to use the menu system
* with a template system.
*
* @access public
* @return string The HTML for the menu
* @author Emanuel Zueger
*/
function tree_toHTML() {
PEAR::raiseError("Method not available for this driver", NESEO_ERROR_NO_METHOD, PEAR_ERROR_TRIGGER, E_USER_ERROR);
}
 
// }}}
// {{{ listbox_toHTML()
 
/**
* Returns the HTML for the listbox. This method can be
* used instead of printListbox() to use the menu system
* with a template system.
*
* @access public
* @return string The HTML for the listbox
* @author Emanuel Zueger
*/
function listbox_toHTML() {
PEAR::raiseError("Method not available for this driver", NESEO_ERROR_NO_METHOD, PEAR_ERROR_TRIGGER, E_USER_ERROR);
}
 
// }}}
}
?>
/trunk/bibliotheque/pear/DB/NestedSet/Event.php
New file
0,0 → 1,79
<?php
/**
// +----------------------------------------------------------------------+
// | PEAR :: DB_NestedSet_DB |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Daniel Khan <dk@webcluster.at> |
// +----------------------------------------------------------------------+
//
// $Id: Event.php,v 1.3 2003/10/07 00:11:27 datenpunk Exp $
//
//
*/
 
/**
* Poor mans event handler for DB_NestedSet
*
* Mostly for demo purposes or for extending it if
* someone has ideas...
*
* @author Daniel Khan <dk@webcluster.at>
* @package DB_NestedSet
* @version $Revision: 1.3 $
* @access public
*/
Class DB_NestedSetEvent extends PEAR {
 
/**
* Constructor
*
* @return void
*/
function DB_NestedSetEvent() {
 
$this->PEAR();
}
 
/**
* Destructor
*
* @return void
*/
function _DB_NestedSetEvent() {
 
$this->_PEAR();
}
 
 
/**
* Calls the event handler
*
* You may want to do a switch() here and call you methods
* depending on the event
*
* @param string $event The Event that occured
* @param object node $node A Reference to the node object which was subject to changes
* @param array $eparams A associative array of params which may be needed by the handler
* @return void
* @access private
*/
function callEvent($event, &$node, $eparams = array()) {
 
echo "<br>Override callEvent() if you want to have custom event handlers<br>\n";
echo "Event $event was called with the following params:<br><br>\n";
echo "<PRE>";
print_r($eparams);
echo "</PRE><br>\n";
}
}
?>
/trunk/bibliotheque/pear/DB/NestedSet/MDB.php
New file
0,0 → 1,136
<?php
//
// +----------------------------------------------------------------------+
// | PEAR :: DB_NestedSet_MDB |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Daniel Khan <dk@webcluster.at> |
// +----------------------------------------------------------------------+
// Thanks to Hans Lellelid for suggesting support for PEAR::MDB
// and for his help in implementing this.
//
// $Id: MDB.php,v 1.7 2003/10/07 00:11:27 datenpunk Exp $
//
 
require_once 'MDB.php';
 
// {{{ DB_NestedSet_MDB:: class
 
/**
* Wrapper class for PEAR::MDB
*
* @author Daniel Khan <dk@webcluster.at>
* @package DB_NestedSet
* @version $Revision: 1.7 $
* @access public
*/
// }}}
class DB_NestedSet_MDB extends DB_NestedSet {
// {{{ properties
 
/**
* @var object The MDB object
*/
var $db;
 
// }}}
// {{{ constructor
 
/**
* Constructor
*
* @param mixed $dsn DSN as PEAR dsn URI or dsn Array
* @param array $params Database column fields which should be returned
*
*/
function DB_NestedSet_MDB($dsn, $params = array())
{
$this->_debugMessage('DB_NestedSet_MDB($dsn, $params = array())');
$this->DB_NestedSet($params);
$this->db =& $this->_db_Connect($dsn);
$this->db->setFetchMode(MDB_FETCHMODE_ASSOC);
}
 
// }}}
// {{{ destructor
 
/**
* Destructor
*/
function _DB_NestedSet_MDB()
{
$this->_debugMessage('_DB_NestedSet_MDB()');
$this->_DB_NestedSet();
$this->_db_Disconnect();
}
 
// }}}
// {{{ _db_Connect()
 
/**
* Connects to the db
*
* @return object DB The database object
* @access private
*/
function &_db_Connect($dsn)
{
$this->_debugMessage('_db_Connect($dsn)');
if (is_object($this->db)) {
return $this->db;
}
 
$db =& MDB::connect($dsn);
$this->_testFatalAbort($db, __FILE__, __LINE__);
 
return $db;
}
 
// }}}
 
function _isDBError($err) {
if(!MDB::isError($err)) {
return false;
}
return true;
}
 
function _numRows($res) {
return $this->db->numRows($res);
}
 
function _quote($str) {
return $this->db->getTextValue($str);
}
 
// {{{ _db_Disconnect()
 
/**
* Disconnects from db
*
* @return void
* @access private
*/
function _db_Disconnect()
{
$this->_debugMessage('_db_Disconnect()');
if (is_object($this->db)) {
@$this->db->disconnect();
}
 
return true;
}
 
// }}}
}
 
?>
/trunk/bibliotheque/pear/DB/NestedSet/TreeMenu.php
New file
0,0 → 1,205
<?php
//
// +----------------------------------------------------------------------+
// | PEAR :: DB_NestedSet_TreeMenu |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Jason Rust <jrust@rustyparts.com> |
// +----------------------------------------------------------------------+
//
// $Id: TreeMenu.php,v 1.13 2003/10/07 00:11:27 datenpunk Exp $
//
 
require_once 'HTML/TreeMenu.php';
 
// {{{ DB_NestedSet_TreeMenu:: class
 
/**
* A helper class to translate the data from a nested set table into a HTML_TreeMenu object
* so that it can be used to create a dynamic tree menu using the PEAR HTML_TreeMenu class.
*
* @see docs/TreeMenu_example.php
* @author Jason Rust <jrust@rustyparts.com>
* @package DB_NestedSet
* @version $Revision: 1.13 $
* @access public
*/
// }}}
class DB_NestedSet_TreeMenu extends DB_NestedSet_Output {
// {{{ properties
 
/**
* @var array The current menu structure
* @access private
*/
var $_structTreeMenu = false;
 
// }}}
// {{{ DB_NestedSet_TreeMenu
 
function &DB_NestedSet_TreeMenu($params) {
$this->_structTreeMenu =& $this->_createFromStructure($params);
}
 
// }}}
// {{{ _createFromStructure()
 
/**
* <pre>Creates a HTML_TreeMenu structure based off of the results from getAllNodes() method
* of the DB_NestedSet class. The needed parameters are:
* o 'structure' => the result from $nestedSet->getAllNodes(true)
* o 'textField' => the field in the table that has the text for node
* o 'linkField' => the field in the table that has the link for the node
* o 'options' => (optional) an array of any additional options to pass to the node when
* Additionally these parameters may be added to the individual nodes to control their
* behavior:
* o 'ensureVisible' => (optional) whether or not the field should be forced as visible
* creating it such as 'icon' or 'expandedIcon'
* o 'events' => (optional) an array of any events to pass to the node when creating it
* such as 'onclick' or 'onexpand'</pre>
* </pre>
* @access public
* @return object A HTML_TreeMenu object
*/
function &_createFromStructure($params)
{
// Basically we go through the array of nodes checking to see
// if each node has children and if so recursing. The reason this
// works is because the data from getAllNodes() is ordered by level
// so a root node will always be first, and sub children will always
// be after them.
if (!isset($params['treeMenu'])) {
$treeMenu =& new HTML_TreeMenu();
} else {
$treeMenu =& $params['treeMenu'];
}
 
// always start at level 1
if (!isset($params['currentLevel'])) {
$params['currentLevel'] = 1;
}
 
// have to use a while loop here because foreach works on a copy of the array and
// the child nodes are passed by reference during the recursion so that the parent
// will know when they have been hit.
reset($params['structure']);
while(list($key, $node) = each($params['structure'])) {
// see if we've already been here before
if (isset($node['hit'])) {
continue;
}
 
// mark that we've hit this node
$params['structure'][$key]['hit'] = $node['hit'] = true;
$tag = array(
'text' => $node[$params['textField']],
'link' => $node[$params['linkField']],
'ensureVisible' => isset($node['ensureVisible']) ? $node['ensureVisible'] : false,
);
$options = isset($params['options']) ? array_merge($params['options'], $tag) : $tag;
$events = isset($node['events']) ? $node['events'] : array();
$parentNode =& $treeMenu->addItem(new HTML_TreeNode($options, $events));
// see if it has children
if (($node['r'] - 1) != $node['l']) {
$children = array();
// harvest all the children
$tempStructure = $params['structure'];
foreach ($tempStructure as $childKey => $childNode) {
if (!isset($childNode['hit']) &&
$childNode['l'] > $node['l'] &&
$childNode['r'] < $node['r'] &&
$childNode['rootid'] == $node['rootid']) {
// important that we assign it by reference here, so that when the child
// marks itself 'hit' the parent loops will know
$children[] =& $params['structure'][$childKey];
}
}
 
$recurseParams = $params;
$recurseParams['structure'] = $children;
$recurseParams['treeMenu'] =& $parentNode;
$recurseParams['currentLevel']++;
$this->_createFromStructure($recurseParams);
}
}
 
return $treeMenu;
}
 
// }}}
// {{{ printTree()
 
/**
* Print's the current tree using the output driver
*
* @access public
*/
function printTree() {
$options = $this->_getOptions('printTree');
$tree =& new HTML_TreeMenu_DHTML($this->_structTreeMenu, $options);
$tree->printMenu();
}
 
// }}}
// {{{ printListbox()
 
/**
* Print's a listbox representing the current tree
*
* @access public
*/
function printListbox() {
$options = $this->_getOptions('printListbox');
$listBox =& new HTML_TreeMenu_Listbox($this->_structTreeMenu, $options);
$listBox->printMenu();
}
// }}}
 
// }}}
// {{{ tree_toHTML()
 
/**
* Returns the HTML for the DHTML-menu. This method can be
* used instead of printMenu() to use the menu system
* with a template system.
*
* @access public
* @return string The HTML for the menu
* @Author Emanuel Zueger
*/
function tree_toHTML() {
$options = $this->_getOptions('toHTML');
$tree =& new HTML_TreeMenu_DHTML($this->_structTreeMenu, $options);
return $tree->toHTML();
}
 
// }}}
// {{{ listbox_toHTML()
 
/**
* Returns the HTML for the listbox. This method can be
* used instead of printListbox() to use the menu system
* with a template system.
*
* @access public
* @return string The HTML for the listbox
* @author Emanuel Zueger
*/
function listbox_toHTML() {
$options = $this->_getOptions('toHTML');
$listBox =& new HTML_TreeMenu_Listbox($this->_structTreeMenu, $options);
return $listBox->toHTML();
}
 
// }}}
}
?>
/trunk/bibliotheque/pear/DB/NestedSet/DB.php
New file
0,0 → 1,135
<?php
//
// +----------------------------------------------------------------------+
// | PEAR :: DB_NestedSet_DB |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Daniel Khan <dk@webcluster.at> |
// +----------------------------------------------------------------------+
//
// $Id: DB.php,v 1.7 2003/10/07 00:11:27 datenpunk Exp $
//
 
require_once 'DB.php';
 
// {{{ DB_NestedSet_DB:: class
 
/**
* Wrapper class for PEAR::DB
*
* @author Daniel Khan <dk@webcluster.at>
* @package DB_NestedSet
* @version $Revision: 1.7 $
* @access public
*/
 
// }}}
class DB_NestedSet_DB extends DB_NestedSet {
// {{{ properties
 
/**
* @var object Db object
*/
var $db;
 
// }}}
// {{{ constructor
 
/**
* Constructor
*
* @param mixed $dsn DSN as PEAR dsn URI or dsn Array
* @param array $params Database column fields which should be returned
*
*/
function DB_NestedSet_DB($dsn, $params = array())
{
$this->_debugMessage('DB_NestedSet_DB($dsn, $params = array())');
$this->DB_NestedSet($params);
$this->db =& $this->_db_Connect($dsn);
$this->db->setFetchMode(DB_FETCHMODE_ASSOC);
}
 
// }}}
// {{{ destructor
 
/**
* Destructor
*/
function _DB_NestedSet_DB()
{
$this->_debugMessage('_DB_NestedSet_DB()');
$this->_DB_NestedSet();
$this->_db_Disconnect();
}
 
// }}}
// {{{ _db_Connect()
 
/**
* Connects to the db
*
* @return object DB The database object
* @access private
*/
function &_db_Connect($dsn)
{
$this->_debugMessage('_db_Connect($dsn)');
if (is_object($this->db)) {
return $this->db;
}
 
$db =& DB::connect($dsn);
$this->_testFatalAbort($db, __FILE__, __LINE__);
return $db;
}
 
// }}}
 
 
function _numRows($res) {
return $res->numRows();
}
 
function _isDBError($err) {
if(!DB::isError($err)) {
return false;
}
return true;
}
 
function _quote($str) {
return $this->db->quote($str);
}
 
// {{{ _db_Disconnect()
 
/**
* Disconnects from db
*
* @return void
* @access private
*/
function _db_Disconnect()
{
$this->_debugMessage('_db_Disconnect()');
if (is_object($this->db)) {
@$this->db->disconnect();
}
 
return true;
}
 
// }}}
}
 
?>
/trunk/bibliotheque/pear/DB/NestedSet/TigraMenu.php
New file
0,0 → 1,402
<?php
//
// +----------------------------------------------------------------------+
// | PEAR :: DB_NestedSet_TigraMenu |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Daniel Khan <dk@webcluster.at> |
// +----------------------------------------------------------------------+
//
// $Id: TigraMenu.php,v 1.8 2003/10/07 00:11:27 datenpunk Exp $
//
 
// {{{ DB_NestedSet_TigraMenu:: class
 
/**
* This class can be used to generate the data to build javascript popup menu
* from a DB_NestedSet node array.
* The Javascript part is done using the free available TigraMenu
* available at http://www.softcomplex.com/products/tigra_menu/.
* Currently version 1.0 is supported.
* Parts of this class where taken ftom the TreemMenu driver by Jason Rust
*
* @author Daniel Khan <dk@webcluster.at>
* @package DB_NestedSet
* @version $Revision: 1.8 $
* @access public
*/
// }}}
class DB_NestedSet_TigraMenu extends DB_NestedSet_Output {
// {{{{ properties
 
/**
* @var integer The depth of the current menu.
* @access private
*/
var $_levels = 1;
 
/**
* @var integer The level we started at
* @access private
*/
var $_levelOffset = false;
 
 
/**
* @var array The current menu structure
* @access private
*/
var $_structTigraMenu = false;
 
/**
* @var array The longest text for each level
* @access private
*/
var $_strlenByLevel = array();
 
// }}}
// {{{ DB_NestedSet_TigraMenu
 
/**
* Constructor
*
* @param array $params A hash with parameters needed by the class
* @see _createFromStructure()
* @return bool
**/
function &DB_NestedSet_TigraMenu($params) {
$this->_menu_id = $params['menu_id'];
$this->_structTigraMenu = $this->_createFromStructure($params);
return true;
}
 
// }}}
// {{{ _createFromStructure()
 
/**
* Creates the JavaScript array for TigraMenu
* Initially this method was introduced for the TreeMenu driver by Jason Rust
*
* o 'structure' => the result from $nestedSet->getAllNodes(true)
* o 'textField' => the field in the table that has the text for node
* o 'linkField' => the field in the table that has the link for the node
*
* @access private
* @return string The TigraMenu JavaScript array
*/
function &_createFromStructure($params)
{
// Basically we go through the array of nodes checking to see
// if each node has children and if so recursing. The reason this
// works is because the data from getAllNodes() is ordered by level
// so a root node will always be first, and sub children will always
// be after them.
 
static $rootlevel;
 
// always start at level 1
if (!isset($params['currentLevel'])) {
$params['currentLevel'] = 1;
}
 
if (!isset($rootlevel)) {
$rootlevel = $params['currentLevel'];
}
 
if (isset($params['tigraMenu'])) {
$tigraMenu = $tigraMenu.$params['tigraMenu'];
}
 
if(!$this->_levelOffset) {
$this->_levelOffset = $params['currentLevel'];
}
 
if($this->_levels < ($params['currentLevel']- $this->_levelOffset)) {
$this->_levels = $params['currentLevel'] - $this->_levelOffset;
}
 
 
// have to use a while loop here because foreach works on a copy of the array and
// the child nodes are passed by reference during the recursion so that the parent
// will know when they have been hit.
reset($params['structure']);
while(list($key, $node) = each($params['structure'])) {
// see if we've already been here before
if (isset($node['hit']) || $node['level'] < $params['currentLevel']) {
continue;
}
 
// mark that we've hit this node
$params['structure'][$key]['hit'] = $node['hit'] = true;
 
$tag = array(
isset($node[$params['textField']]) ? "'".$node[$params['textField']]."'" : 'null',
isset($node[$params['linkField']]) ? "'".$node[$params['linkField']]."'" : 'null'
);
 
if (!$this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset] ||
strlen($node[$params['textField']]) > $this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset]) {
$this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset] = strlen($node[$params['textField']]);
};
 
$tigraMenu = $tigraMenu.$this->_openSubMenu($tag);
 
// see if it has children
if (($node['r'] - 1) != $node['l']) {
$children = array();
// harvest all the children
$tempStructure = $params['structure'];
foreach ($tempStructure as $childKey => $childNode) {
if (!isset($childNode['hit']) &&
$node['rootid'] == $childNode['rootid'] &&
$node['l'] < $childNode['l'] &&
$node['r'] > $childNode['r'] &&
$childNode['level'] > $params['currentLevel']) {
// important that we assign it by reference here, so that when the child
// marks itself 'hit' the parent loops will know
$children[] =& $params['structure'][$childKey];
}
}
 
$recurseParams = $params;
$recurseParams['structure'] = $children;
$recurseParams['currentLevel']++;
$tigraMenu = $tigraMenu.$this->_createFromStructure($recurseParams);
}
 
$tigraMenu = $tigraMenu.$this->_closeSubMenu();
}
return $tigraMenu;
}
 
// }}}
// {{{ _openMenu()
 
/**
* Returns the string which opens the JavaScript menu
*
* @access private
* @param int $menu_id ID of the menu needed to use more than one menu on a page
* @return string The JavaScript piece
*/
function _openMenu($menu_id=1)
{
$str = false;
$str = $str."var MENU_ITEMS".$menu_id." = new Array();\n";
$str = $str."MENU_ITEMS".$menu_id." = [\n";
return $str;
}
 
// }}}
// {{{ _openSubMenu()
 
/**
* Returns the string which opens a submenu within the JavaScript menu
*
* @access private
* @param array $tag Contains the content of the current item (name, link)
* @return string The JavaScript piece
*/
function _openSubMenu($tag)
{
$rtag = implode(', ', $tag);
return "\n[".$rtag.',';
}
 
// }}}
// {{{ _closeMenu()
 
/**
* Closes the JavaScript array
*
* @access private
* @return string The JavaScript piece
*/
function _closeMenu()
{
 
return '];';
}
 
// }}}
// {{{ _closeSubMenu()
 
/**
* Closes the JavaScript array of a submenu
*
* @access private
* @return string The JavaScript piece
*/
function _closeSubMenu()
{
return "\n],";
}
 
// }}}
// {{{ _addStyles()
 
/**
* Creates the JavaScript code which sets the styles for each level
*
* @access private
* @param int $menu_id ID of the menu needed to use more than one menu on a page
* @param array $rootStyles Array of style attributes for the top items
* @param array $childStyles Array of style attributes for the sub items
* @return string The JavaScript piece
*/
function _addStyles($menu_id, $rootStyles, $childStyles = false)
{
if (!$childStyles) {
$childStyles = $rootStyles;
}
 
$styles = array();
foreach ($rootStyles as $key => $val) {
foreach ($val as $skey => $sval) {
$styles["'$key'"][$skey][] = "'$sval'";
}
}
 
foreach ($childStyles as $key => $val) {
foreach ($val as $skey => $sval) {
for ($i = 1; $i <= $this->_levels; $i++) {
$styles["'$key'"][$skey][] = "'$sval'";
}
}
}
 
$menustyles = false;
$menustyles = $menustyles . 'var MENU_STYLES'.$menu_id." = new Array();\n";
foreach ($styles as $key => $val) {
$menustyles = $menustyles.'MENU_STYLES'.$menu_id."[$key] = [\n";
foreach ($val as $skey => $sval) {
$menustyles = $menustyles . "'$skey', [".implode(', ', $sval)."],\n";
}
$menustyles = $menustyles."];\n";
}
 
return $menustyles;
}
 
// }}}
// {{{ _addGeometry()
 
/**
* Creates the JavaScript code which sets the position and geometry of the menu
*
* @access private
* @param int $menu_id ID of the menu needed to use more than one menu on a page
* @param array $rootGeometry Array of geometry attributes for the top items
* @param array $childGeometry Array of geometry attributes for the sub items
* @return string The JavaScript piece
*/
function _addGeometry($menu_id, $rootGeometry, $childGeometry = false)
{
if (!$childGeometry) {
$childGeometry = $rootGeometry;
}
 
$params = array();
$geometry = array();
foreach ($rootGeometry as $key => $val) {
$geometry["'$key'"][] = $val;
$incr = false;
if (strpos($val, ',') !== false) {
list($start, $interval) = explode(',',$val);
$incr = true;
}
 
$ratio = false;
if ($key == 'width' && strpos($val, '*') !== false) {
$ratio = trim(str_replace('*','', $val));
}
if ($incr) {
$val = trim($interval);
if ($key == 'left' && preg_match('/[+-]/', $interval)) {
$val = $params[0]['width'] + trim($val);
}
} elseif ($incr) {
$val = trim($start);
} elseif ($ratio) {
$val = $ratio * $this->_strlenByLevel[0];
}
$geometry["'$key'"][0] = $val;
$params[0][$key] = $val;
}
 
foreach($childGeometry as $key => $val) {
$incr = false;
if (strpos($val, ',') !== false) {
list($start, $interval) = explode(',', $val);
$incr = true;
}
 
$ratio = false;
if ($key == 'width' && strpos($val, '*') !== false) {
$ratio = trim(str_replace('*', '', $val));
}
 
for ($i = 1; $i <= $this->_levels; $i++) {
if ($incr && isset($lastval[$key])) {
$val = trim($interval);
if($key == 'block_left' && preg_match('/[+-]/', $interval)) {
$val = $params[$i - 1]['width'] + trim($val);
}
} elseif($incr) {
$val = trim($start);
} elseif ($ratio) {
$val = $ratio * $this->_strlenByLevel[$i];
if($val < $params[0]['width']) {
$val = $params[0]['width'];
}
}
 
$lastval[$key] = $val;
$geometry["'$key'"][] = $val;
$params[$i][$key] = $val;
}
 
}
 
$pos = false;
$pos = $pos . 'var MENU_POS'.$menu_id." = new Array();\n";
foreach ($geometry as $key => $val) {
$pos = $pos . 'MENU_POS' . $menu_id . "[$key] = [" . implode(', ', $val) . "];\n";
}
 
return $pos;
}
 
// }}}
// {{{ printTree()
 
/**
* Print's the current tree using the output driver
*
* @access public
*/
function printTree()
{
if (!$options = $this->_getOptions('printTree')) {
return PEAR::raiseError("TigraMenu::printTree() needs options. See TigraMenu::setOptions()", NESEO_ERROR_NO_OPTIONS, PEAR_ERROR_TRIGGER, E_USER_ERROR);
}
 
echo $this->_openMenu($options['menu_id']) . $this->_structTigraMenu .$this->_closeMenu();
echo "\n\n";
echo $this->_addStyles($options['menu_id'], $options['rootStyles'], $options['childStyles']);
echo "\n\n";
echo $this->_addGeometry($options['menu_id'], $options['rootGeometry'], $options['childGeometry']);
}
 
// }}}
}
?>
/trunk/bibliotheque/pear/DB/DataObject/Generator.php
New file
0,0 → 1,929
<?php
/**
* Generation tools for DB_DataObject
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB_DataObject
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Generator.php,v 1.96 2005/06/16 02:03:45 alan_k Exp $
* @link http://pear.php.net/package/DB_DataObject
*/
/**
*
* Config _$ptions
* [DB_DataObject_Generator]
* ; optional default = DB/DataObject.php
* extends_location =
* ; optional default = DB_DataObject
* extends =
* ; alter the extends field when updating a class (defaults to only replacing DB_DataObject)
* generator_class_rewrite = ANY|specific_name // default is DB_DataObject
*
*/
 
/**
* Needed classes
*/
require_once 'DB/DataObject.php';
//require_once('Config.php');
 
/**
* Generator class
*
* @package DB_DataObject
*/
class DB_DataObject_Generator extends DB_DataObject
{
/* =========================================================== */
/* Utility functions - for building db config files */
/* =========================================================== */
 
/**
* Array of table names
*
* @var array
* @access private
*/
var $tables;
 
/**
* associative array table -> array of table row objects
*
* @var array
* @access private
*/
var $_definitions;
 
/**
* active table being output
*
* @var string
* @access private
*/
var $table; // active tablename
 
 
/**
* The 'starter' = call this to start the process
*
* @access public
* @return none
*/
function start()
{
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$databases = array();
foreach($options as $k=>$v) {
if (substr($k,0,9) == 'database_') {
$databases[substr($k,9)] = $v;
}
}
 
if (@$options['database']) {
require_once 'DB.php';
$dsn = DB::parseDSN($options['database']);
if (!isset($database[$dsn['database']])) {
$databases[$dsn['database']] = $options['database'];
}
}
 
foreach($databases as $databasename => $database) {
if (!$database) {
continue;
}
$this->debug("CREATING FOR $databasename\n");
$class = get_class($this);
$t = new $class;
$t->_database_dsn = $database;
$t->_database = $databasename;
$dsn = DB::parseDSN($database);
if (($dsn['phptype'] == 'sqlite') && is_file($databasename)) {
$t->_database = basename($t->_database);
}
$t->_createTableList();
 
foreach(get_class_methods($class) as $method) {
if (substr($method,0,8 ) != 'generate') {
continue;
}
$this->debug("calling $method");
$t->$method();
}
}
$this->debug("DONE\n\n");
}
 
/**
* Output File was config object, now just string
* Used to generate the Tables
*
* @var string outputbuffer for table definitions
* @access private
*/
var $_newConfig;
 
/**
* Build a list of tables;
* Currently this is very Mysql Specific - ideas for more generic stiff welcome
*
* @access private
* @return none
*/
function _createTableList()
{
$this->_connect();
$options = &PEAR::getStaticProperty('DB_DataObject','options');
 
$__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
// try getting a list of schema tables first. (postgres)
$__DB->expectError(DB_ERROR_UNSUPPORTED);
$this->tables = $__DB->getListOf('schema.tables');
$__DB->popExpect();
if (empty($this->tables) || is_a($this->tables , 'PEAR_Error')) {
//if that fails fall back to clasic tables list.
$this->tables = $__DB->getListOf('tables');
}
if (is_a($this->tables , 'PEAR_Error')) {
return PEAR::raiseError($this->tables->toString(), null, PEAR_ERROR_DIE);
}
// build views as well if asked to.
if (!empty($options['build_views'])) {
$views = $__DB->getListOf('views');
if (is_a($views,'PEAR_Error')) {
return PEAR::raiseError(
'Error getting Views (check the PEAR bug database for the fix to DB), ' .
$views->toString(),
null,
PEAR_ERROR_DIE
);
}
$this->tables = array_merge ($this->tables, $views);
}
// declare a temporary table to be filled with matching tables names
$tmp_table = array();
 
 
foreach($this->tables as $table) {
if (isset($options['generator_include_regex']) &&
!preg_match($options['generator_include_regex'],$table)) {
continue;
} else if (isset($options['generator_exclude_regex']) &&
preg_match($options['generator_exclude_regex'],$table)) {
continue;
}
// postgres strip the schema bit from the
if (!empty($options['generator_strip_schema'])) {
$bits = explode('.', $table,2);
$table = $bits[0];
if (count($bits) > 1) {
$table = $bits[1];
}
}
$defs = $__DB->tableInfo($table);
if (is_a($defs,'PEAR_Error')) {
echo $defs->toString();
exit;
}
// cast all definitions to objects - as we deal with that better.
foreach($defs as $def) {
if (!is_array($def)) {
continue;
}
$this->_definitions[$table][] = (object) $def;
}
// we find a matching table, just store it into a temporary array
$tmp_table[] = $table;
}
// the temporary table array is now the right one (tables names matching
// with regex expressions have been removed)
$this->tables = $tmp_table;
//print_r($this->_definitions);
}
 
/**
* Auto generation of table data.
*
* it will output to db_oo_{database} the table definitions
*
* @access private
* @return none
*/
function generateDefinitions()
{
$this->debug("Generating Definitions file: ");
if (!$this->tables) {
$this->debug("-- NO TABLES -- \n");
return;
}
 
$options = &PEAR::getStaticProperty('DB_DataObject','options');
 
 
//$this->_newConfig = new Config('IniFile');
$this->_newConfig = '';
foreach($this->tables as $this->table) {
$this->_generateDefinitionsTable();
}
$this->_connect();
// dont generate a schema if location is not set
// it's created on the fly!
if (!@$options['schema_location'] && @!$options["ini_{$this->_database}"] ) {
return;
}
$base = @$options['schema_location'];
if (isset($options["ini_{$this->_database}"])) {
$file = $options["ini_{$this->_database}"];
} else {
$file = "{$base}/{$this->_database}.ini";
}
if (!file_exists(dirname($file))) {
require_once 'System.php';
System::mkdir(array('-p','-m',0755,dirname($file)));
}
$this->debug("Writing ini as {$file}\n");
touch($file);
//print_r($this->_newConfig);
$fh = fopen($file,'w');
fwrite($fh,$this->_newConfig);
fclose($fh);
//$ret = $this->_newConfig->writeInput($file,false);
 
//if (PEAR::isError($ret) ) {
// return PEAR::raiseError($ret->message,null,PEAR_ERROR_DIE);
// }
}
 
/**
* The table geneation part
*
* @access private
* @return tabledef and keys array.
*/
function _generateDefinitionsTable()
{
global $_DB_DATAOBJECT;
$defs = $this->_definitions[$this->table];
$this->_newConfig .= "\n[{$this->table}]\n";
$keys_out = "\n[{$this->table}__keys]\n";
$keys_out_primary = '';
$keys_out_secondary = '';
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
echo "TABLE STRUCTURE FOR {$this->table}\n";
print_r($defs);
}
$DB = $this->getDatabaseConnection();
$dbtype = $DB->phptype;
$ret = array(
'table' => array(),
'keys' => array(),
);
$ret_keys_primary = array();
$ret_keys_secondary = array();
foreach($defs as $t) {
$n=0;
 
switch (strtoupper($t->type)) {
 
case 'INT':
case 'INT2': // postgres
case 'INT4': // postgres
case 'INT8': // postgres
case 'SERIAL4': // postgres
case 'SERIAL8': // postgres
case 'INTEGER':
case 'TINYINT':
case 'SMALLINT':
case 'MEDIUMINT':
case 'BIGINT':
$type = DB_DATAOBJECT_INT;
if ($t->len == 1) {
$type += DB_DATAOBJECT_BOOL;
}
break;
case 'REAL':
case 'DOUBLE':
case 'FLOAT':
case 'FLOAT8': // double precision (postgres)
case 'DECIMAL':
case 'NUMERIC':
case 'NUMBER': // oci8
$type = DB_DATAOBJECT_INT; // should really by FLOAT!!! / MONEY...
break;
case 'YEAR':
$type = DB_DATAOBJECT_INT;
break;
case 'BIT':
case 'BOOL':
case 'BOOLEAN':
$type = DB_DATAOBJECT_BOOL;
// postgres needs to quote '0'
if ($dbtype == 'pgsql') {
$type += DB_DATAOBJECT_STR;
}
break;
case 'STRING':
case 'CHAR':
case 'VARCHAR':
case 'VARCHAR2':
case 'TINYTEXT':
case 'ENUM':
case 'SET': // not really but oh well
case 'TIMESTAMPTZ': // postgres
case 'BPCHAR': // postgres
case 'INTERVAL': // postgres (eg. '12 days')
case 'CIDR': // postgres IP net spec
case 'INET': // postgres IP
case 'MACADDR': // postgress network Mac address.
$type = DB_DATAOBJECT_STR;
break;
case 'TEXT':
case 'MEDIUMTEXT':
case 'LONGTEXT':
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TXT;
break;
case 'DATE':
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE;
break;
case 'TIME':
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_TIME;
break;
case 'DATETIME':
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
break;
case 'TIMESTAMP': // do other databases use this???
$type = ($dbtype == 'mysql') ?
DB_DATAOBJECT_MYSQLTIMESTAMP :
DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME;
break;
case 'TINYBLOB':
case 'BLOB': /// these should really be ignored!!!???
case 'MEDIUMBLOB':
case 'LONGBLOB':
case 'BYTEA': // postgres blob support..
$type = DB_DATAOBJECT_STR + DB_DATAOBJECT_BLOB;
break;
}
if (!strlen(trim($t->name))) {
continue;
}
if (preg_match('/not_null/i',$t->flags)) {
$type += DB_DATAOBJECT_NOTNULL;
}
$write_ini = true;
if (in_array($t->name,array('null','yes','no','true','false'))) {
echo "*****************************************************************\n".
"** WARNING **\n".
"** Found column '{$t->name}', which is invalid in an .ini file **\n".
"** This line will not be writen to the file - you will have **\n".
"** define the keys()/method manually. **\n".
"*****************************************************************\n";
$write_ini = false;
} else {
$this->_newConfig .= "{$t->name} = $type\n";
}
$ret['table'][$t->name] = $type;
// i've no idea if this will work well on other databases?
// only use primary key or nextval(), cause the setFrom blocks you setting all key items...
// if no keys exist fall back to using unique
//echo "\n{$t->name} => {$t->flags}\n";
if (preg_match("/(auto_increment|nextval\()/i",rawurldecode($t->flags))) {
// native sequences = 2
if ($write_ini) {
$keys_out_primary .= "{$t->name} = N\n";
}
$ret_keys_primary[$t->name] = 'N';
} else if (preg_match("/(primary|unique)/i",$t->flags)) {
// keys.. = 1
if ($write_ini) {
$keys_out_secondary .= "{$t->name} = K\n";
}
$ret_keys_secondary[$t->name] = 'K';
}
}
$this->_newConfig .= $keys_out . (empty($keys_out_primary) ? $keys_out_secondary : $keys_out_primary);
$ret['keys'] = empty($keys_out_primary) ? $ret_keys_secondary : $ret_keys_primary;
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
print_r(array("dump for {$this->table}", $ret));
}
return $ret;
}
 
/*
* building the class files
* for each of the tables output a file!
*/
function generateClasses()
{
//echo "Generating Class files: \n";
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$base = $options['class_location'];
if (strpos($base,'%s') !== false) {
$base = dirname($base);
}
if (!file_exists($base)) {
require_once 'System.php';
System::mkdir(array('-p',$base));
}
$class_prefix = $options['class_prefix'];
if ($extends = @$options['extends']) {
$this->_extends = $extends;
$this->_extendsFile = $options['extends_location'];
}
 
foreach($this->tables as $this->table) {
$this->table = trim($this->table);
$this->classname = $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table));
$i = '';
if (strpos($options['class_location'],'%s') !== false) {
$outfilename = sprintf($options['class_location'], preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)));
} else {
$outfilename = "{$base}/".preg_replace('/[^A-Z0-9]/i','_',ucfirst($this->table)).".php";
}
$oldcontents = '';
if (file_exists($outfilename)) {
// file_get_contents???
$oldcontents = implode('',file($outfilename));
}
$out = $this->_generateClassTable($oldcontents);
$this->debug( "writing $this->classname\n");
$fh = fopen($outfilename, "w");
fputs($fh,$out);
fclose($fh);
}
//echo $out;
}
 
/**
* class being extended (can be overridden by [DB_DataObject_Generator] extends=xxxx
*
* @var string
* @access private
*/
var $_extends = 'DB_DataObject';
 
/**
* line to use for require('DB/DataObject.php');
*
* @var string
* @access private
*/
var $_extendsFile = "DB/DataObject.php";
 
/**
* class being generated
*
* @var string
* @access private
*/
var $_className;
 
/**
* The table class geneation part - single file.
*
* @access private
* @return none
*/
function _generateClassTable($input = '')
{
// title = expand me!
$foot = "";
$head = "<?php\n/**\n * Table Definition for {$this->table}\n */\n";
// requires
$head .= "require_once '{$this->_extendsFile}';\n\n";
// add dummy class header in...
// class
$head .= "class {$this->classname} extends {$this->_extends} \n{";
 
$body = "\n ###START_AUTOCODE\n";
$body .= " /* the code below is auto generated do not remove the above tag */\n\n";
// table
$padding = (30 - strlen($this->table));
if ($padding < 2) $padding =2;
$p = str_repeat(' ',$padding) ;
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var';
$body .= " {$var} \$__table = '{$this->table}'; {$p}// table name\n";
// if we are using the option database_{databasename} = dsn
// then we should add var $_database = here
// as database names may not always match..
if (isset($options["database_{$this->_database}"])) {
$body .= " {$var} \$_database = '{$this->_database}'; {$p}// database name (used with database_{*} config)\n";
}
$var = (substr(phpversion(),0,1) > 4) ? 'public' : 'var';
if (!empty($options['generator_novars'])) {
$var = '//'.$var;
}
$defs = $this->_definitions[$this->table];
 
// show nice information!
$connections = array();
$sets = array();
foreach($defs as $t) {
if (!strlen(trim($t->name))) {
continue;
}
$padding = (30 - strlen($t->name));
if ($padding < 2) $padding =2;
$p = str_repeat(' ',$padding) ;
$body .=" {$var} \${$t->name}; {$p}// {$t->type}({$t->len}) {$t->flags}\n";
// can not do set as PEAR::DB table info doesnt support it.
//if (substr($t->Type,0,3) == "set")
// $sets[$t->Field] = "array".substr($t->Type,3);
$body .= $this->derivedHookVar($t,$padding);
}
 
// THIS IS TOTALLY BORKED old FC creation
// IT WILL BE REMOVED!!!!! in DataObjects 1.6
// grep -r __clone * to find all it's uses
// and replace them with $x = clone($y);
// due to the change in the PHP5 clone design.
if ( substr(phpversion(),0,1) < 5) {
$body .= "\n";
$body .= " /* ZE2 compatibility trick*/\n";
$body .= " function __clone() { return \$this;}\n";
}
 
// simple creation tools ! (static stuff!)
$body .= "\n";
$body .= " /* Static get */\n";
$body .= " function staticGet(\$k,\$v=NULL) { return DB_DataObject::staticGet('{$this->classname}',\$k,\$v); }\n";
// generate getter and setter methods
$body .= $this->_generateGetters($input);
$body .= $this->_generateSetters($input);
/*
theoretically there is scope here to introduce 'list' methods
based up 'xxxx_up' column!!! for heiracitcal trees..
*/
 
// set methods
//foreach ($sets as $k=>$v) {
// $kk = strtoupper($k);
// $body .=" function getSets{$k}() { return {$v}; }\n";
//}
$body .= $this->derivedHookFunctions();
 
$body .= "\n /* the code above is auto generated do not remove the tag below */";
$body .= "\n ###END_AUTOCODE\n";
 
 
// stubs..
if (!empty($options['generator_add_validate_stubs'])) {
foreach($defs as $t) {
if (!strlen(trim($t->name))) {
continue;
}
$validate_fname = 'validate' . ucfirst(strtolower($t->name));
// dont re-add it..
if (preg_match('/\s+function\s+' . $validate_fname . '\s*\(/i', $input)) {
continue;
}
$body .= "\n function {$validate_fname}()\n {\n return false;\n }\n";
}
}
 
 
 
 
$foot .= "}\n";
$full = $head . $body . $foot;
 
if (!$input) {
return $full;
}
if (!preg_match('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n)/s',$input)) {
return $full;
}
if (!preg_match('/(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',$input)) {
return $full;
}
 
 
/* this will only replace extends DB_DataObject by default,
unless use set generator_class_rewrite to ANY or a name*/
 
$class_rewrite = 'DB_DataObject';
$options = &PEAR::getStaticProperty('DB_DataObject','options');
if (!($class_rewrite = @$options['generator_class_rewrite'])) {
$class_rewrite = 'DB_DataObject';
}
if ($class_rewrite == 'ANY') {
$class_rewrite = '[a-z_]+';
}
 
$input = preg_replace(
'/(\n|\r\n)class\s*[a-z0-9_]+\s*extends\s*' .$class_rewrite . '\s*\{(\n|\r\n)/si',
"\nclass {$this->classname} extends {$this->_extends} \n{\n",
$input);
 
return preg_replace(
'/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s',
$body,$input);
}
 
/**
* hook to add extra methods to all classes
*
* called once for each class, use with $this->table and
* $this->_definitions[$this->table], to get data out of the current table,
* use it to add extra methods to the default classes.
*
* @access public
* @return string added to class eg. functions.
*/
function derivedHookFunctions()
{
// This is so derived generator classes can generate functions
// It MUST NOT be changed here!!!
return "";
}
 
/**
* hook for var lines
* called each time a var line is generated, override to add extra var
* lines
*
* @param object t containing type,len,flags etc. from tableInfo call
* @param int padding number of spaces
* @access public
* @return string added to class eg. functions.
*/
function derivedHookVar(&$t,$padding)
{
// This is so derived generator classes can generate variabels
// It MUST NOT be changed here!!!
return "";
}
 
 
/**
* getProxyFull - create a class definition on the fly and instantate it..
*
* similar to generated files - but also evals the class definitoin code.
*
*
* @param string database name
* @param string table name of table to create proxy for.
*
*
* @return object Instance of class. or PEAR Error
* @access public
*/
function getProxyFull($database,$table) {
if ($err = $this->fillTableSchema($database,$table)) {
return $err;
}
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$class_prefix = $options['class_prefix'];
if ($extends = @$options['extends']) {
$this->_extends = $extends;
$this->_extendsFile = $options['extends_location'];
}
 
$classname = $this->classname = $class_prefix.preg_replace('/[^A-Z0-9]/i','_',ucfirst(trim($this->table)));
 
$out = $this->_generateClassTable();
//echo $out;
eval('?>'.$out);
return new $classname;
}
/**
* fillTableSchema - set the database schema on the fly
*
*
*
* @param string database name
* @param string table name of table to create schema info for
*
* @return none | PEAR::error()
* @access public
*/
function fillTableSchema($database,$table) {
global $_DB_DATAOBJECT;
$this->_database = $database;
$this->_connect();
$table = trim($table);
$__DB= &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
$defs = $__DB->tableInfo($table);
if (PEAR::isError($defs)) {
return $defs;
}
if (@$_DB_DATAOBJECT['CONFIG']['debug'] > 2) {
$this->debug("getting def for $database/$table",'fillTable');
$this->debug(print_r($defs,true),'defs');
}
// cast all definitions to objects - as we deal with that better.
foreach($defs as $def) {
if (is_array($def)) {
$this->_definitions[$table][] = (object) $def;
}
}
 
$this->table = trim($table);
$ret = $this->_generateDefinitionsTable();
$_DB_DATAOBJECT['INI'][$database][$table] = $ret['table'];
$_DB_DATAOBJECT['INI'][$database][$table.'__keys'] = $ret['keys'];
return false;
}
/**
* Generate getter methods for class definition
*
* @param string $input Existing class contents
* @return string
* @access public
*/
function _generateGetters($input) {
 
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$getters = '';
 
// only generate if option is set to true
if (empty($options['generate_getters'])) {
return '';
}
 
// remove auto-generated code from input to be able to check if the method exists outside of the auto-code
$input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
 
$getters .= "\n\n";
$defs = $this->_definitions[$this->table];
 
// loop through properties and create getter methods
foreach ($defs = $defs as $t) {
 
// build mehtod name
$methodName = 'get' . ucfirst($t->name);
 
if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
continue;
}
 
$getters .= " /**\n";
$getters .= " * Getter for \${$t->name}\n";
$getters .= " *\n";
$getters .= (stristr($t->flags, 'multiple_key')) ? " * @return object\n"
: " * @return {$t->type}\n";
$getters .= " * @access public\n";
$getters .= " */\n";
$getters .= (substr(phpversion(),0,1) > 4) ? ' public '
: ' ';
$getters .= "function $methodName() {\n";
$getters .= " return \$this->{$t->name};\n";
$getters .= " }\n\n";
}
 
return $getters;
}
 
 
/**
* Generate setter methods for class definition
*
* @param string Existing class contents
* @return string
* @access public
*/
function _generateSetters($input) {
 
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$setters = '';
 
// only generate if option is set to true
if (empty($options['generate_setters'])) {
return '';
}
 
// remove auto-generated code from input to be able to check if the method exists outside of the auto-code
$input = preg_replace('/(\n|\r\n)\s*###START_AUTOCODE(\n|\r\n).*(\n|\r\n)\s*###END_AUTOCODE(\n|\r\n)/s', '', $input);
 
$setters .= "\n";
$defs = $this->_definitions[$this->table];
 
// loop through properties and create setter methods
foreach ($defs = $defs as $t) {
 
// build mehtod name
$methodName = 'set' . ucfirst($t->name);
 
if (!strlen(trim($t->name)) || preg_match("/function[\s]+[&]?$methodName\(/i", $input)) {
continue;
}
 
$setters .= " /**\n";
$setters .= " * Setter for \${$t->name}\n";
$setters .= " *\n";
$setters .= " * @param mixed input value\n";
$setters .= " * @access public\n";
$setters .= " */\n";
$setters .= (substr(phpversion(),0,1) > 4) ? ' public '
: ' ';
$setters .= "function $methodName(\$value) {\n";
$setters .= " \$this->{$t->name} = \$value;\n";
$setters .= " }\n\n";
}
 
return $setters;
}
 
}
/trunk/bibliotheque/pear/DB/DataObject/Error.php
New file
0,0 → 1,53
<?php
/**
* DataObjects error handler, loaded on demand...
*
* DB_DataObject_Error is a quick wrapper around pear error, so you can distinguish the
* error code source.
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB_DataObject
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Error.php,v 1.3 2005/03/23 02:35:35 alan_k Exp $
* @link http://pear.php.net/package/DB_DataObject
*/
class DB_DataObject_Error extends PEAR_Error
{
/**
* DB_DataObject_Error constructor.
*
* @param mixed $code DB error code, or string with error message.
* @param integer $mode what "error mode" to operate in
* @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER
* @param mixed $debuginfo additional debug info, such as the last query
*
* @access public
*
* @see PEAR_Error
*/
function DB_DataObject_Error($message = '', $code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE)
{
$this->PEAR_Error('DB_DataObject Error: ' . $message, $code, $mode, $level);
}
// todo : - support code -> message handling, and translated error messages...
}
/trunk/bibliotheque/pear/DB/DataObject/Cast.php
New file
0,0 → 1,546
<?php
/**
* Prototype Castable Object.. for DataObject queries
*
* Storage for Data that may be cast into a variety of formats.
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB_DataObject
* @author Alan Knowles <alan@akbkhome.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Cast.php,v 1.15 2005/07/07 05:30:53 alan_k Exp $
* @link http://pear.php.net/package/DB_DataObject
*/
/**
*
* Common usages:
* // blobs
* $data = DB_DataObject_Cast::blob($somefile);
* $data = DB_DataObject_Cast::string($somefile);
* $dataObject->someblobfield = $data
*
* // dates?
* $d1 = new DB_DataObject_Cast::date('12/12/2000');
* $d2 = new DB_DataObject_Cast::date(2000,12,30);
* $d3 = new DB_DataObject_Cast::date($d1->year, $d1->month+30, $d1->day+30);
*
* // time, datetime.. ?????????
*
* // raw sql????
* $data = DB_DataObject_Cast::sql('cast("123123",datetime)');
* $data = DB_DataObject_Cast::sql('NULL');
*
* // int's/string etc. are proably pretty pointless..!!!!
*
*
* inside DB_DataObject,
* if (is_a($v,'db_dataobject_class')) {
* $value .= $v->toString(DB_DATAOBJECT_INT,'mysql');
* }
*
*
*
*
 
*/
class DB_DataObject_Cast {
/**
* Type of data Stored in the object..
*
* @var string (date|blob|.....?)
* @access public
*/
var $type;
/**
* Data For date representation
*
* @var int day/month/year
* @access public
*/
var $day;
var $month;
var $year;
 
/**
* Generic Data..
*
* @var string
* @access public
*/
 
var $value;
 
 
 
/**
* Blob consructor
*
* create a Cast object from some raw data.. (binary)
*
*
* @param string (with binary data!)
*
* @return object DB_DataObject_Cast
* @access public
*/
function blob($value) {
$r = new DB_DataObject_Cast;
$r->type = 'blob';
$r->value = $value;
return $r;
}
 
 
/**
* String consructor (actually use if for ints and everything else!!!
*
* create a Cast object from some string (not binary)
*
*
* @param string (with binary data!)
*
* @return object DB_DataObject_Cast
* @access public
*/
function string($value) {
$r = new DB_DataObject_Cast;
$r->type = 'string';
$r->value = $value;
return $r;
}
/**
* SQL constructor (for raw SQL insert)
*
* create a Cast object from some sql
*
* @param string (with binary data!)
*
* @return object DB_DataObject_Cast
* @access public
*/
function sql($value)
{
$r = new DB_DataObject_Cast;
$r->type = 'sql';
$r->value = $value;
return $r;
}
 
 
/**
* Date Constructor
*
* create a Cast object from some string (not binary)
* NO VALIDATION DONE, although some crappy re-calcing done!
*
* @param vargs... accepts
* dd/mm
* dd/mm/yyyy
* yyyy-mm
* yyyy-mm-dd
* array(yyyy,dd)
* array(yyyy,dd,mm)
*
*
*
* @return object DB_DataObject_Cast
* @access public
*/
function date()
{
$args = func_get_args();
switch(count($args)) {
case 0: // no args = today!
$bits = explode('-',date('Y-m-d'));
break;
case 1: // one arg = a string
if (strpos($args[0],'/') !== false) {
$bits = array_reverse(explode('/',$args[0]));
} else {
$bits = explode('-',$args[0]);
}
break;
default: // 2 or more..
$bits = $args;
}
if (count($bits) == 1) { // if YYYY set day = 1st..
$bits[] = 1;
}
if (count($bits) == 2) { // if YYYY-DD set day = 1st..
$bits[] = 1;
}
// if year < 1970 we cant use system tools to check it...
// so we make a few best gueses....
// basically do date calculations for the year 2000!!!
// fix me if anyone has more time...
if (($bits[0] < 1975) || ($bits[0] > 2030)) {
$oldyear = $bits[0];
$bits = explode('-',date('Y-m-d',mktime(1,1,1,$bits[1],$bits[2],2000)));
$bits[0] = ($bits[0] - 2000) + $oldyear;
} else {
// now mktime
$bits = explode('-',date('Y-m-d',mktime(1,1,1,$bits[1],$bits[2],$bits[0])));
}
$r = new DB_DataObject_Cast;
$r->type = 'date';
list($r->year,$r->month,$r->day) = $bits;
return $r;
}
 
/**
* Data For time representation ** does not handle timezones!!
*
* @var int hour/minute/second
* @access public
*/
var $hour;
var $minute;
var $second;
 
/**
* DateTime Constructor
*
* create a Cast object from a Date/Time
* Maybe should accept a Date object.!
* NO VALIDATION DONE, although some crappy re-calcing done!
*
* @param vargs... accepts
* noargs (now)
* yyyy-mm-dd HH:MM:SS (Iso)
* array(yyyy,mm,dd,HH,MM,SS)
*
*
* @return object DB_DataObject_Cast
* @access public
* @author therion 5 at hotmail
*/
function dateTime()
{
$args = func_get_args();
switch(count($args)) {
case 0: // no args = now!
$datetime = date('Y-m-d G:i:s', mktime());
case 1:
// continue on from 0 args.
if (!isset($datetime)) {
$datetime = $args[0];
}
$parts = explode(' ', $datetime);
$bits = explode('-', $parts[0]);
$bits = array_merge($bits, explode(':', $parts[1]));
break;
default: // 2 or more..
$bits = $args;
}
 
if (count($bits) != 6) {
// PEAR ERROR?
return false;
}
$r = DB_DataObject_Cast::date($bits[0], $bits[1], $bits[2]);
if (!$r) {
return $r; // pass thru error (False) - doesnt happen at present!
}
// change the type!
$r->type = 'datetime';
// should we mathematically sort this out..
// (or just assume that no-one's dumb enough to enter 26:90:90 as a time!
$r->hour = $bits[3];
$r->minute = $bits[4];
$r->second = $bits[5];
return $r;
 
}
 
 
 
/**
* time Constructor
*
* create a Cast object from a Date/Time
* Maybe should accept a Date object.!
* NO VALIDATION DONE, and no-recalcing done!
*
* @param vargs... accepts
* noargs (now)
* HH:MM:SS (Iso)
* array(HH,MM,SS)
*
*
* @return object DB_DataObject_Cast
* @access public
* @author therion 5 at hotmail
*/
function time()
{
$args = func_get_args();
switch (count($args)) {
case 0: // no args = now!
$time = date('G:i:s', mktime());
case 1:
// continue on from 0 args.
if (!isset($time)) {
$time = $args[0];
}
$bits = explode(':', $time);
break;
default: // 2 or more..
$bits = $args;
}
if (count($bits) != 3) {
return false;
}
// now take data from bits into object fields
$r = new DB_DataObject_Cast;
$r->type = 'time';
$r->hour = $bits[0];
$r->minute = $bits[1];
$r->second = $bits[2];
return $r;
 
}
 
/**
* get the string to use in the SQL statement for this...
*
*
* @param int $to Type (DB_DATAOBJECT_*
* @param object $db DB Connection Object
*
*
* @return string
* @access public
*/
function toString($to=false,$db)
{
// if $this->type is not set, we are in serious trouble!!!!
// values for to:
$method = 'toStringFrom'.$this->type;
return $this->$method($to,$db);
}
/**
* get the string to use in the SQL statement from a blob of binary data
* ** Suppots only blob->postgres::bytea
*
* @param int $to Type (DB_DATAOBJECT_*
* @param object $db DB Connection Object
*
*
* @return string
* @access public
*/
function toStringFromBlob($to,$db)
{
// first weed out invalid casts..
// in blobs can only be cast to blobs.!
// perhaps we should support TEXT fields???
if (!($to & DB_DATAOBJECT_BLOB)) {
return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::blob to something other than a blob!');
}
switch ($db->dsn["phptype"]) {
case 'pgsql':
return "'".pg_escape_bytea($this->value)."'::bytea";
case 'mysql':
return "'".mysql_real_escape_string($this->value,$db->connection)."'";
case 'mysqli':
// this is funny - the parameter order is reversed ;)
return "'".mysqli_real_escape_string($db->connection, $this->value)."'";
default:
return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
}
}
/**
* get the string to use in the SQL statement for a blob from a string!
* ** Suppots only string->postgres::bytea
*
*
* @param int $to Type (DB_DATAOBJECT_*
* @param object $db DB Connection Object
*
*
* @return string
* @access public
*/
function toStringFromString($to,$db)
{
// first weed out invalid casts..
// in blobs can only be cast to blobs.!
// perhaps we should support TEXT fields???
//
if (!($to & DB_DATAOBJECT_BLOB)) {
return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a blob!'.
' (why not just use native features)');
}
switch ($db->dsn['phptype']) {
case 'pgsql':
return "'".pg_escape_string($this->value)."'::bytea";
case 'mysql':
return "'".mysql_real_escape_string($this->value,$db->connection)."'";
case 'mysqli':
return "'".mysqli_real_escape_string($db->connection, $this->value)."'";
 
default:
return PEAR::raiseError("DB_DataObject_Cast cant handle blobs for Database:{$db->dsn['phptype']} Yet");
}
}
/**
* get the string to use in the SQL statement for a date
*
*
*
* @param int $to Type (DB_DATAOBJECT_*
* @param object $db DB Connection Object
*
*
* @return string
* @access public
*/
function toStringFromDate($to,$db)
{
// first weed out invalid casts..
// in blobs can only be cast to blobs.!
// perhaps we should support TEXT fields???
//
if (($to !== false) && !($to & DB_DATAOBJECT_DATE)) {
return PEAR::raiseError('Invalid Cast from a DB_DataObject_Cast::string to something other than a date!'.
' (why not just use native features)');
}
return "'{$this->year}-{$this->month}-{$this->day}'";
}
/**
* get the string to use in the SQL statement for a datetime
*
*
*
* @param int $to Type (DB_DATAOBJECT_*
* @param object $db DB Connection Object
*
*
* @return string
* @access public
* @author therion 5 at hotmail
*/
function toStringFromDateTime($to,$db)
{
// first weed out invalid casts..
// in blobs can only be cast to blobs.!
// perhaps we should support TEXT fields???
if (($to !== false) &&
!($to & (DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME))) {
return PEAR::raiseError('Invalid Cast from a ' .
' DB_DataObject_Cast::dateTime to something other than a datetime!' .
' (try using native features)');
}
return "'{$this->year}-{$this->month}-{$this->day} {$this->hour}:{$this->minute}:{$this->second}'";
}
 
/**
* get the string to use in the SQL statement for a time
*
*
*
* @param int $to Type (DB_DATAOBJECT_*
* @param object $db DB Connection Object
*
*
* @return string
* @access public
* @author therion 5 at hotmail
*/
 
function toStringFromTime($to,$db)
{
// first weed out invalid casts..
// in blobs can only be cast to blobs.!
// perhaps we should support TEXT fields???
if (($to !== false) && !($to & DB_DATAOBJECT_TIME)) {
return PEAR::raiseError('Invalid Cast from a' .
' DB_DataObject_Cast::time to something other than a time!'.
' (try using native features)');
}
return "'{$this->hour}:{$this->minute}:{$this->second}'";
}
/**
* get the string to use in the SQL statement for a raw sql statement.
*
* @param int $to Type (DB_DATAOBJECT_*
* @param object $db DB Connection Object
*
*
* @return string
* @access public
*/
function toStringFromSql($to,$db)
{
return $this->value;
}
}
 
/trunk/bibliotheque/pear/DB/DataObject/createTables.php
New file
0,0 → 1,55
#!/usr/bin/php -q
<?php
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Alan Knowles <alan@akbkhome.com>
// +----------------------------------------------------------------------+
//
// $Id: createTables.php,v 1.23 2005/05/04 14:13:57 alan_k Exp $
//
 
// since this version doesnt use overload,
// and I assume anyone using custom generators should add this..
 
define('DB_DATAOBJECT_NO_OVERLOAD',1);
 
require_once 'DB/DataObject/Generator.php';
 
if (!ini_get('register_argc_argv')) {
PEAR::raiseError("\nERROR: You must turn register_argc_argv On in you php.ini file for this to work\neg.\n\nregister_argc_argv = On\n\n", null, PEAR_ERROR_DIE);
exit;
}
 
if (!@$_SERVER['argv'][1]) {
PEAR::raiseError("\nERROR: createTable.php usage:\n\nC:\php\pear\DB\DataObjects\createTable.php example.ini\n\n", null, PEAR_ERROR_DIE);
exit;
}
 
$config = parse_ini_file($_SERVER['argv'][1], true);
foreach($config as $class=>$values) {
$options = &PEAR::getStaticProperty($class,'options');
$options = $values;
}
 
 
$options = &PEAR::getStaticProperty('DB_DataObject','options');
if (empty($options)) {
PEAR::raiseError("\nERROR: could not read ini file\n\n", null, PEAR_ERROR_DIE);
exit;
}
set_time_limit(0);
DB_DataObject::debugLevel(1);
$generator = new DB_DataObject_Generator;
$generator->start();
/trunk/bibliotheque/pear/DB/QueryTool/Query.php
New file
0,0 → 1,2395
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* Contains the DB_QueryTool_Query class
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB_QueryTool
* @author Wolfram Kriesing <wk@visionp.de>
* @author Paolo Panto <wk@visionp.de>
* @author Lorenzo Alberton <l dot alberton at quipo dot it>
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Query.php,v 1.57 2005/02/27 19:13:19 quipo Exp $
* @link http://pear.php.net/package/DB_QueryTool
*/
 
/**
* require the PEAR and DB classes
*/
require_once 'PEAR.php';
require_once 'DB.php';
 
/**
* DB_QueryTool_Query class
*
* This class should be extended
*
* @category Database
* @package DB_QueryTool
* @author Wolfram Kriesing <wk@visionp.de>
* @author Paolo Panto <wk@visionp.de>
* @author Lorenzo Alberton <l dot alberton at quipo dot it>
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/DB_QueryTool
*/
class DB_QueryTool_Query
{
// {{{ class vars
 
/**
* @var string the name of the primary column
*/
var $primaryCol = 'id';
 
/**
* @var string the current table the class works on
*/
var $table = '';
 
/**
* @var string the name of the sequence for this table
*/
var $sequenceName = null;
 
/**
* @var object the db-object, a PEAR::DB instance
*/
var $db = null;
 
/**
* @var string the where condition
* @access private
*/
var $_where = '';
 
/**
* @var string the order condition
* @access private
*/
var $_order = '';
 
/**
* @var string the having definition
* @access private
*/
var $_having = '';
 
/**
* @var array contains the join content
* the key is the join type, for now we have 'default' and 'left'
* inside each key 'table' contains the table
* key 'where' contains the where clause for the join
* @access private
*/
var $_join = array();
 
/**
* @var string which column to index the result by
* @access private
*/
var $_index = null;
 
/**
* @var string the group-by clause
* @access private
*/
var $_group = '';
 
/**
* @var array the limit
* @access private
*/
var $_limit = array();
 
/**
* @var string type of result to return
* @access private
*/
var $_resultType = 'none';
 
/**
* @var array the metadata temporary saved
* @access private
*/
var $_metadata = array();
 
/**
* @var string
* @access private
*/
var $_lastQuery = null;
 
/**
* @var string the rows that shall be selected
* @access private
*/
var $_select = '*';
 
/**
* @var string the rows that shall not be selected
* @access private
*/
var $_dontSelect = '';
 
/**
* @var array this array saves different modes in which this class works
* i.e. 'raw' means no quoting before saving/updating data
* @access private
*/
var $options = array(
'raw' => false,
'verbose' => true, // set this to false in a productive environment
// it will produce error-logs if set to true
'useCache' => false,
'logFile' => false,
);
 
/**
* this array contains information about the tables
* those are
* - 'name' => the real table name
* - 'shortName' => the short name used, so that when moving the table i.e.
* onto a provider's db and u have to rename the tables to
* longer names this name will be relevant, i.e. when
* autoJoining, i.e. a table name on your local machine is:
* 'user' but online it has to be 'applName_user' then the
* shortName will be used to determine if a column refers to
* another table, if the colName is 'user_id', it knows the
* shortName 'user' refers to the table 'applName_user'
*/
var $tableSpec = array();
 
/**
* this is the regular expression that shall be used to find a table's shortName
* in a column name, the string found by using this regular expression will be removed
* from the column name and it will be checked if it is a table name
* i.e. the default '/_id$/' would find the table name 'user' from the column name 'user_id'
*/
var $_tableNameToShortNamePreg = '/^.*_/';
 
/**
* @var array this array caches queries that have already been built once
* to reduce the execution time
*/
var $_queryCache = array();
 
/**
* The object that contains the log-instance
*/
var $_logObject = null;
 
/**
* Some internal data the logging needs
*/
var $_logData = array();
 
// }}}
// {{{ __construct()
 
/**
* this is the constructor, as it will be implemented in ZE2 (php5)
*
* @version 2002/04/02
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param object db-object
*/
/*
function __construct($dsn=false, $options=array())
{
if (!isset($options['autoConnect'])) {
$autoConnect = true;
} else {
$autoConnect = $options['autoConnect'];
}
if (isset($options['errorCallback'])) {
$this->setErrorCallback($options['errorCallback']);
}
if (isset($options['errorSetCallback'])) {
$this->setErrorSetCallback($options['errorSetCallback']);
}
if (isset($options['errorLogCallback'])) {
$this->setErrorLogCallback($options['errorLogCallback']);
}
 
if ($autoConnect && $dsn) {
$this->connect($dsn, $options);
}
//we would need to parse the dsn first ... i dont feel like now :-)
// oracle has all column names in upper case
//FIXXXME make the class work only with upper case when we work with oracle
//if ($this->db->phptype=='oci8' && !$this->primaryCol) {
// $this->primaryCol = 'ID';
//}
 
if ($this->sequenceName == null) {
$this->sequenceName = $this->table;
}
}
*/
 
// }}}
// {{{ DB_QueryTool_Query()
 
/**
* @version 2002/04/02
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param mixed $dsn DSN string, DSN array or DB object
* @param array $options
*/
function DB_QueryTool_Query($dsn=false, $options=array())
{
//$this->__construct($dsn, $options);
if (!isset($options['autoConnect'])) {
$autoConnect = true;
} else {
$autoConnect = $options['autoConnect'];
unset($options['autoConnect']);
}
if (isset($options['errorCallback'])) {
$this->setErrorCallback($options['errorCallback']);
unset($options['errorCallback']);
}
if (isset($options['errorSetCallback'])) {
$this->setErrorSetCallback($options['errorSetCallback']);
unset($options['errorSetCallback']);
}
if (isset($options['errorLogCallback'])) {
$this->setErrorLogCallback($options['errorLogCallback']);
unset($options['errorLogCallback']);
}
if ($autoConnect && $dsn) {
$this->connect($dsn, $options);
}
if (is_null($this->sequenceName)) {
$this->sequenceName = $this->table;
}
}
 
// }}}
// {{{ connect()
 
/**
* use this method if you want to connect manually
* @param mixed $dsn DSN string, DSN array or MDB object
* @param array $options
*/
function connect($dsn, $options=array())
{
if (is_object($dsn)) {
$res = $this->db =& $dsn;
} else {
$res = $this->db = DB::connect($dsn, $options);
}
if (DB::isError($res)) {
// FIXXME what shall we do here?
$this->_errorLog($res->getUserInfo());
} else {
$this->db->setFetchMode(DB_FETCHMODE_ASSOC);
}
}
 
// }}}
// {{{ getDbInstance()
 
/**
* @return reference to current DB instance
*/
function &getDbInstance()
{
return $this->db;
}
 
// }}}
// {{{ setDbInstance()
 
/**
* Setup using an existing connection.
* this also sets the DB_FETCHMODE_ASSOC since this class
* needs this to be set!
*
* @param object a reference to an existing DB-object
* @return void
*/
function setDbInstance(&$dbh)
{
$this->db =& $dbh;
$this->db->setFetchMode(DB_FETCHMODE_ASSOC);
}
 
// }}}
// {{{ get()
 
/**
* get the data of a single entry
* if the second parameter is only one column the result will be returned
* directly not as an array!
*
* @version 2002/03/05
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param integer the id of the element to retrieve
* @param string if this is given only one row shall be returned, directly, not an array
* @return mixed (1) an array of the retrieved data
* (2) if the second parameter is given and its only one column,
* only this column's data will be returned
* (3) false in case of failure
*/
function get($id, $column='')
{
$table = $this->table;
$getMethod = 'getRow';
if ($column && !strpos($column, ',')) { // if only one column shall be selected
$getMethod = 'getOne';
}
// we dont use 'setSelect' here, since this changes the setup of the class, we
// build the query directly
// if $column is '' then _buildSelect selects '*' anyway, so that's the same behaviour as before
$query['select'] = $this->_buildSelect($column);
$query['where'] = $this->_buildWhere($this->table.'.'.$this->primaryCol.'='.$id);
$queryString = $this->_buildSelectQuery($query);
 
return $this->returnResult($this->execute($queryString,$getMethod));
}
 
// }}}
// {{{ getMultiple()
 
/**
* gets the data of the given ids
*
* @version 2002/04/23
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param array this is an array of ids to retreive
* @param string the column to search in for
* @return mixed an array of the retreived data, or false in case of failure
* when failing an error is set in $this->_error
*/
function getMultiple($ids, $column='')
{
$col = $this->primaryCol;
if ($column) {
$col = $column;
}
// FIXXME if $ids has no table.col syntax and we are using joins, the table better be put in front!!!
$ids = $this->_quoteArray($ids);
 
$query['where'] = $this->_buildWhere($col.' IN ('.implode(',', $ids).')');
$queryString = $this->_buildSelectQuery($query);
 
return $this->returnResult($this->execute($queryString));
}
 
// }}}
// {{{ getAll()
 
/**
* get all entries from the DB
* for sorting use setOrder!!!, the last 2 parameters are deprecated
*
* @version 2002/03/05
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param int to start from
* @param int the number of rows to show
* @param string the DB-method to use, i dont know if we should leave this param here ...
* @return mixed an array of the retreived data, or false in case of failure
* when failing an error is set in $this->_error
*/
function getAll($from=0,$count=0,$method='getAll')
{
$query = array();
if ($count) {
$query = array('limit' => array($from, $count));
}
return $this->returnResult($this->execute($this->_buildSelectQuery($query), $method));
}
 
// }}}
// {{{ getCol()
 
/**
* this method only returns one column, so the result will be a one dimensional array
* this does also mean that using setSelect() should be set to *one* column, the one you want to
* have returned a most common use case for this could be:
* $table->setSelect('id');
* $ids = $table->getCol();
* OR
* $ids = $table->getCol('id');
* so ids will be an array with all the id's
*
* @version 2003/02/25
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the column that shall be retreived
* @param int to start from
* @param int the number of rows to show
* @return mixed an array of the retreived data, or false in case of failure
* when failing an error is set in $this->_error
*/
function getCol($column=null, $from=0, $count=0)
{
$query = array();
if (!is_null($column)) {
// by using _buildSelect() i can be sure that the table name will not be ambigious
// i.e. in a join, where all the joined tables have a col 'id'
// _buildSelect() will put the proper table name in front in case there is none
$query['select'] = $this->_buildSelect($column);
}
if ($count) {
$query['limit'] = array($from,$count);
}
return $this->returnResult($this->execute($this->_buildSelectQuery($query), 'getCol'));
}
 
// }}}
// {{{ getCount()
 
/**
* get the number of entries
*
* @version 2002/04/02
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param
* @return mixed an array of the retreived data, or false in case of failure
* when failing an error is set in $this->_error
*/
function getCount()
{
/* the following query works on mysql
SELECT count(DISTINCT image.id) FROM image2tree
RIGHT JOIN image ON image.id = image2tree.image_id
the reason why this is needed - i just wanted to get the number of rows that do exist if the result is grouped by image.id
the following query is what i tried first, but that returns the number of rows that have been grouped together
for each image.id
SELECT count(*) FROM image2tree
RIGHT JOIN image ON image.id = image2tree.image_id GROUP BY image.id
 
so that's why we do the following, i am not sure if that is standard SQL and absolutley correct!!!
*/
 
//FIXXME see comment above if this is absolutely correct!!!
if ($group = $this->_buildGroup()) {
$query['select'] = 'COUNT(DISTINCT '.$group.')';
$query['group'] = '';
} else {
$query['select'] = 'COUNT(*)';
}
 
$query['order'] = ''; // order is not of importance and might freak up the special group-handling up there, since the order-col is not be known
/*# FIXXME use the following line, but watch out, then it has to be used in every method, or this
# value will be used always, simply try calling getCount and getAll afterwards, getAll will return the count :-)
# if getAll doesn't use setSelect!!!
*/
//$this->setSelect('count(*)');
$queryString = $this->_buildSelectQuery($query, true);
 
return ($res = $this->execute($queryString, 'getOne')) ? $res : 0;
}
 
// }}}
// {{{ getDefaultValues()
 
/**
* return an empty element where all the array elements do already exist
* corresponding to the columns in the DB
*
* @version 2002/04/05
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @return array an empty, or pre-initialized element
*/
function getDefaultValues()
{
$ret = array();
// here we read all the columns from the DB and initialize them
// with '' to prevent PHP-warnings in case we use error_reporting=E_ALL
foreach ($this->metadata() as $aCol=>$x) {
$ret[$aCol] = '';
}
return $ret;
}
 
// }}}
// {{{ getEmptyElement()
 
/**
* this is just for BC
* @deprecated
*/
function getEmptyElement()
{
$this->getDefaultValues();
}
 
// }}}
// {{{ getQueryString()
 
/**
* Render the current query and return it as a string.
*
* @return string the current query
*/
function getQueryString()
{
$ret = $this->_buildSelectQuery();
if (is_string($ret)) {
$ret = trim($ret);
}
return $ret;
}
 
// }}}
// {{{ save()
 
/**
* save data, calls either update or add
* if the primaryCol is given in the data this method knows that the
* data passed to it are meant to be updated (call 'update'), otherwise it will
* call the method 'add'.
* If you dont like this behaviour simply stick with the methods 'add'
* and 'update' and ignore this one here.
* This method is very useful when you have validation checks that have to
* be done for both adding and updating, then you can simply overwrite this
* method and do the checks in here, and both cases will be validated first.
*
* @version 2002/03/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param array contains the new data that shall be saved in the DB
* @return mixed the data returned by either add or update-method
*/
function save($data)
{
if (!empty($data[$this->primaryCol])) {
return $this->update($data);
}
return $this->add($data);
}
 
// }}}
// {{{ update()
 
/**
* update the member data of a data set
*
* @version 2002/03/06
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param array contains the new data that shall be saved in the DB
* the id has to be given in the field with the key 'ID'
* @return mixed true on success, or false otherwise
*/
function update($newData)
{
$query = array();
// do only set the 'where' part in $query, if a primary column is given
// if not the default 'where' clause is used
if (isset($newData[$this->primaryCol])) {
$query['where'] = $this->primaryCol.'='.$newData[$this->primaryCol];
}
$newData = $this->_checkColumns($newData, 'update');
$values = array();
$raw = $this->getOption('raw');
foreach ($newData as $key => $aData) { // quote the data
//$values[] = "{$this->table}.$key=". ($raw ? $aData : $this->db->quote($aData));
$values[] = "$key=". ($raw ? $aData : $this->db->quote($aData));
}
 
$query['set'] = implode(',', $values);
//FIXXXME _buildUpdateQuery() seems to take joins into account, whcih is bullshit here
$updateString = $this->_buildUpdateQuery($query);
#print '$updateString = '.$updateString;
return $this->execute($updateString, 'query') ? true : false;
}
 
// }}}
// {{{ add()
 
/**
* add a new member in the DB
*
* @version 2002/04/02
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param array contains the new data that shall be saved in the DB
* @return mixed the inserted id on success, or false otherwise
*/
function add($newData)
{
// if no primary col is given, get next sequence value
if (empty($newData[$this->primaryCol])) {
if ($this->primaryCol) { // do only use the sequence if a primary column is given
// otherwise the data are written as given
$id = $this->db->nextId($this->sequenceName);
$newData[$this->primaryCol] = (int)$id;
} else {
// if no primary col is given return true on success
$id = true;
}
} else {
$id = $newData[$this->primaryCol];
}
 
//unset($newData[$this->primaryCol]);
 
$newData = $this->_checkColumns($newData, 'add');
$newData = $this->_quoteArray($newData);
 
$query = sprintf( 'INSERT INTO %s (%s) VALUES (%s)',
$this->table,
implode(', ', array_keys($newData)),
implode(', ', $newData)
);
return $this->execute($query, 'query') ? $id : false;
}
 
// }}}
// {{{ addMultiple()
 
/**
* adds multiple new members in the DB
*
* @version 2002/07/17
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param array contains an array of new data that shall be saved in the DB
* the key-value pairs have to be the same for all the data!!!
* @return mixed the inserted ids on success, or false otherwise
*/
function addMultiple($data)
{
if (!sizeof($data)) {
return false;
}
// the inserted ids which will be returned or if no primaryCol is given
// we return true by default
$retIds = $this->primaryCol ? array() : true;
$allData = array(); // each row that will be inserted
foreach ($data as $key => $aData) {
$aData = $this->_checkColumns($aData, 'add');
$aData = $this->_quoteArray($aData);
 
if (empty($aData[$this->primaryCol])) {
if ($this->primaryCol) { // do only use the sequence if a primary column is given
// otherwise the data are written as given
$retIds[] = $id = (int)$this->db->nextId($this->sequenceName);
$aData[$this->primaryCol] = $id;
}
} else {
$retIds[] = $aData[$this->primaryCol];
}
$allData[] = '('.implode(', ', $aData).')';
}
 
$query = sprintf( 'INSERT INTO %s (%s) VALUES %s',
$this->table,
implode(', ', array_keys($aData)), // use the keys of the last element built
implode(', ', $allData)
);
return $this->execute($query, 'query') ? $retIds : false;
}
 
// }}}
// {{{ remove()
 
/**
* removes a member from the DB
*
* @version 2002/04/08
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param mixed integer/string - the value of the column that shall be removed
* array - multiple columns that shall be matched, the second parameter will be ignored
* @param string the column to match the data against, only if $data is not an array
* @return boolean
*/
function remove($data, $whereCol='')
{
$raw = $this->getOption('raw');
 
if (is_array($data)) {
//FIXXME check $data if it only contains columns that really exist in the table
$wheres = array();
foreach ($data as $key => $val) {
$wheres[] = $key.'='. ($raw ? $val : $this->db->quote($val));
}
$whereClause = implode(' AND ',$wheres);
} else {
if (empty($whereCol)) {
$whereCol = $this->primaryCol;
}
$whereClause = $whereCol.'='. ($raw ? $data : $this->db->quote($data));
}
 
$query = sprintf( 'DELETE FROM %s WHERE %s',
$this->table,
$whereClause
);
return $this->execute($query, 'query') ? true : false;
// i think this method should return the ID's that it removed, this way we could simply use the result
// for further actions that depend on those id ... or? make stuff easier, see ignaz::imail::remove
}
 
// }}}
// {{{ removeAll()
 
/**
* empty a table
*
* @version 2002/06/17
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @return
*/
function removeAll()
{
$query = 'DELETE FROM '.$this->table;
return $this->execute($query, 'query') ? true : false;
}
 
// }}}
// {{{ removeMultiple()
 
/**
* remove the datasets with the given ids
*
* @version 2002/04/24
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param array the ids to remove
* @return
*/
function removeMultiple($ids, $colName='')
{
if (empty($colName)) {
$colName = $this->primaryCol;
}
$ids = $this->_quoteArray($ids);
 
$query = sprintf( 'DELETE FROM %s WHERE %s IN (%s)',
$this->table,
$colName,
implode(',', $ids)
);
return $this->execute($query, 'query') ? true : false;
}
 
// }}}
// {{{ removePrimary()
 
/**
* removes a member from the DB and calls the remove methods of the given objects
* so all rows in another table that refer to this table are erased too
*
* @version 2002/04/08
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param integer the value of the primary key
* @param string the column name of the tables with the foreign keys
* @param object just for convinience, so nobody forgets to call this method
* with at least one object as a parameter
* @return boolean
*/
function removePrimary($id, $colName, $atLeastOneObject)
{
$argCounter = 2; // we have 2 parameters that need to be given at least
// func_get_arg returns false and a warning if there are no more parameters, so
// we suppress the warning and check for false
while ($object = @func_get_arg($argCounter++)) {
//FIXXXME let $object also simply be a table name
if (!$object->remove($id, $colName)) {
//FIXXXME do this better
$this->_errorSet("Error removing '$colName=$id' from table {$object->table}.");
return false;
}
}
 
return ($this->remove($id) ? true : false);
}
 
// }}}
// {{{ setLimit()
 
/**
* @param integer $from
* @param integer $count
*/
function setLimit($from=0, $count=0)
{
if ($from==0 && $count==0) {
$this->_limit = array();
} else {
$this->_limit = array($from, $count);
}
}
 
// }}}
// {{{ getLimit()
 
/**
* @return array
*/
function getLimit()
{
return $this->_limit;
}
 
// }}}
// {{{ setWhere()
 
/**
* sets the where condition which is used for the current instance
*
* @version 2002/04/16
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the where condition, this can be complete like 'X=7 AND Y=8'
*/
function setWhere($whereCondition='')
{
$this->_where = $whereCondition;
//FIXXME parse the where condition and replace ambigious column names, such as "name='Deutschland'" with "country.name='Deutschland'"
// then the users dont have to write that explicitly and can use the same name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix');
}
 
// }}}
// {{{ getWhere()
 
/**
* gets the where condition which is used for the current instance
*
* @version 2002/04/22
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @return string the where condition, this can be complete like 'X=7 AND Y=8'
*/
function getWhere()
{
return $this->_where;
}
 
// }}}
// {{{ addWhere()
 
/**
* only adds a string to the where clause
*
* @version 2002/07/22
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the where clause to add to the existing one
* @param string the condition for how to concatenate the new where clause
* to the existing one
*/
function addWhere($where, $condition='AND')
{
if ($this->getWhere()) {
$where = $this->getWhere().' '.$condition.' '.$where;
}
$this->setWhere($where);
}
 
// }}}
// {{{ addWhereSearch()
 
/**
* add a where-like clause which works like a search for the given string
* i.e. calling it like this:
* $this->addWhereSearch('name', 'otto hans')
* produces a where clause like this one
* LOWER(name) LIKE "%otto%hans%"
* so the search finds the given string
*
* @version 2002/08/14
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the column to search in for
* @param string the string to search for
*/
function addWhereSearch($column, $string, $condition='AND')
{
// if the column doesn't contain a tablename use the current table name
// in case it is a defined column to prevent ambiguous rows
if (strpos($column, '.') === false) {
$meta = $this->metadata();
if (isset($meta[$column])) {
$column = $this->table.".$column";
}
}
 
$string = $this->db->quote('%'.str_replace(' ', '%', strtolower($string)).'%');
$this->addWhere("LOWER($column) LIKE $string", $condition);
}
 
// }}}
// {{{ setOrder()
 
/**
* sets the order condition which is used for the current instance
*
* @version 2002/05/16
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the where condition, this can be complete like 'X=7 AND Y=8'
* @param boolean sorting order (TRUE => ASC, FALSE => DESC)
*/
function setOrder($orderCondition='', $desc=false)
{
$this->_order = $orderCondition .($desc ? ' DESC' : '');
}
 
// }}}
// {{{ addOrder()
 
/**
* Add a order parameter to the query.
*
* @version 2003/05/28
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the where condition, this can be complete like 'X=7 AND Y=8'
* @param boolean sorting order (TRUE => ASC, FALSE => DESC)
*/
function addOrder($orderCondition='', $desc=false)
{
$order = $orderCondition .($desc ? ' DESC' : '');
if ($this->_order) {
$this->_order = $this->_order.','.$order;
} else {
$this->_order = $order;
}
}
 
// }}}
// {{{ getOrder()
 
/**
* gets the order condition which is used for the current instance
*
* @version 2002/05/16
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @return string the order condition, this can be complete like 'ID,TIMESTAMP DESC'
*/
function getOrder()
{
return $this->_order;
}
 
// }}}
// {{{ setHaving()
 
/**
* sets the having definition
*
* @version 2003/06/05
* @access public
* @author Johannes Schaefer <johnschaefer@gmx.de>
* @param string the having definition
*/
function setHaving($having='')
{
$this->_having = $having;
}
 
// }}}
// {{{ getHaving()
 
/**
* gets the having definition which is used for the current instance
*
* @version 2003/06/05
* @access public
* @author Johannes Schaefer <johnschaefer@gmx.de>
* @return string the having definition
*/
function getHaving()
{
return $this->_having;
}
 
// }}}
// {{{ addHaving()
 
/**
* Extend the current having clause. This is very useful, when you are building
* this clause from different places and don't want to overwrite the currently
* set having clause, but extend it.
*
* @param string this is a having clause, i.e. 'column' or 'table.column' or 'MAX(column)'
* @param string the connection string, which usually stays the default, which is ',' (a comma)
*/
function addHaving($what='*', $connectString=' AND ')
{
if ($this->_having) {
$this->_having = $this->_having.$connectString.$what;
} else {
$this->_having = $what;
}
}
 
// }}}
// {{{ setJoin()
 
/**
* sets a join-condition
*
* @version 2002/06/10
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param mixed either a string or an array that contains
* the table(s) to join on the current table
* @param string the where clause for the join
*/
function setJoin($table=null, $where=null, $joinType='default')
{
//FIXXME make it possible to pass a table name as a string like this too 'user u'
// where u is the string that can be used to refer to this table in a where/order
// or whatever condition
// this way it will be possible to join tables with itself, like setJoin(array('user u','user u1'))
// this wouldnt work yet, but for doing so we would need to change the _build methods too!!!
// because they use getJoin('tables') and this simply returns all the tables in use
// but don't take care of the mentioned syntax
 
if (is_null($table) || is_null($where)) { // remove the join if not sufficient parameters are given
$this->_join[$joinType] = array();
return;
}
/* this causes problems if we use the order-by, since it doenst know the name to order it by ... :-)
// replace the table names with the internal name used for the join
// this way we can also join one table multiple times if it will be implemented one day
$this->_join[$table] = preg_replace('/'.$table.'/','j1',$where);
*/
$this->_join[$joinType][$table] = $where;
}
 
// }}}
// {{{ setJoin()
 
/**
* if you do a left join on $this->table you will get all entries
* from $this->table, also if there are no entries for them in the joined table
* if both parameters are not given the left-join will be removed
* NOTE: be sure to only use either a right or a left join
*
* @version 2002/07/22
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the table(s) to be left-joined
* @param string the where clause for the join
*/
function setLeftJoin($table=null, $where=null)
{
$this->setJoin($table, $where, 'left');
}
 
// }}}
// {{{ addLeftJoin()
 
/**
* @param string the table to be left-joined
* @param string the where clause for the join
* @param string the join type
*/
function addLeftJoin($table, $where, $type='left')
{
// init value, to prevent E_ALL-warning
if (!isset($this->_join[$type]) || !$this->_join[$type]) {
$this->_join[$type] = array();
}
$this->_join[$type][$table] = $where;
}
 
// }}}
// {{{ setRightJoin()
 
/**
* see setLeftJoin for further explaination on what a left/right join is
* NOTE: be sure to only use either a right or a left join
//FIXXME check if the above sentence is necessary and if sql doesnt allow the use of both
*
* @see setLeftJoin()
* @version 2002/09/04
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the table(s) to be right-joined
* @param string the where clause for the join
*/
function setRightJoin($table=null, $where=null)
{
$this->setJoin($table, $where, 'right');
}
 
// }}}
// {{{ getJoin()
 
/**
* gets the join-condition
*
* @access public
* @param string [null|''|'table'|'tables'|'right'|'left']
* @return array gets the join parameters
*/
function getJoin($what=null)
{
// if the user requests all the join data or if the join is empty, return it
if (is_null($what) || empty($this->_join)) {
return $this->_join;
}
 
$ret = array();
switch (strtolower($what)) {
case 'table':
case 'tables':
foreach ($this->_join as $aJoin) {
if (count($aJoin)) {
$ret = array_merge($ret, array_keys($aJoin));
}
}
break;
case 'right': // return right-join data only
case 'left': // return left join data only
if (count($this->_join[$what])) {
$ret = array_merge($ret, $this->_join[$what]);
}
break;
}
return $ret;
}
 
// }}}
// {{{ addJoin()
 
/**
* adds a table and a where clause that shall be used for the join
* instead of calling
* setJoin(array(table1,table2),'<where clause1> AND <where clause2>')
* you can also call
* setJoin(table1,'<where clause1>')
* addJoin(table2,'<where clause2>')
* or where it makes more sense is to build a query which is made out of a
* left join and a standard join
* setLeftJoin(table1,'<where clause1>')
* // results in ... FROM $this->table LEFT JOIN table ON <where clause1>
* addJoin(table2,'<where clause2>')
* // results in ... FROM $this->table,table2 LEFT JOIN table ON <where clause1> WHERE <where clause2>
*
* @access public
* @param string the table to be joined
* @param string the where clause for the join
* @param string the join type
*/
function addJoin($table, $where, $type='default')
{
if ($table == $this->table) {
return; //skip. Self joins are not supported.
}
// init value, to prevent E_ALL-warning
if (!isset($this->_join[$type]) || !$this->_join[$type]) {
$this->_join[$type] = array();
}
$this->_join[$type][$table] = $where;
}
 
// }}}
// {{{ setTable()
 
/**
* sets the table this class is currently working on
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the table name
*/
function setTable($table)
{
$this->table = $table;
}
 
// }}}
// {{{ getTable()
 
/**
* gets the table this class is currently working on
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @return string the table name
*/
function getTable()
{
return $this->table;
}
 
// }}}
// {{{ setGroup()
 
/**
* sets the group-by condition
*
* @version 2002/07/22
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the group condition
*/
function setGroup($group='')
{
$this->_group = $group;
//FIXXME parse the condition and replace ambigious column names, such as "name='Deutschland'" with "country.name='Deutschland'"
// then the users dont have to write that explicitly and can use the same name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix');
}
 
// }}}
// {{{ getGroup()
 
/**
* gets the group condition which is used for the current instance
*
* @version 2002/07/22
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @return string the group condition
*/
function getGroup()
{
return $this->_group;
}
 
// }}}
// {{{ setSelect()
 
/**
* limit the result to return only the columns given in $what
* @param string fields that shall be selected
*/
function setSelect($what='*')
{
$this->_select = $what;
}
 
// }}}
// {{{ addSelect()
 
/**
* add a string to the select part of the query
*
* add a string to the select-part of the query and connects it to an existing
* string using the $connectString, which by default is a comma.
* (SELECT xxx FROM - xxx is the select-part of a query)
*
* @version 2003/01/08
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the string that shall be added to the select-part
* @param string the string to connect the new string with the existing one
* @return void
*/
function addSelect($what='*', $connectString=',')
{
// if the select string is not empty add the string, otherwise simply set it
if ($this->_select) {
$this->_select = $this->_select.$connectString.$what;
} else {
$this->_select = $what;
}
}
 
// }}}
// {{{ getSelect()
 
/**
* @return string
*/
function getSelect()
{
return $this->_select;
}
 
// }}}
// {{{ setDontSelect()
 
/**
* @param string
*/
function setDontSelect($what='')
{
$this->_dontSelect = $what;
}
 
// }}}
// {{{ getDontSelect()
 
/**
* @return string
*/
function getDontSelect()
{
return $this->_dontSelect;
}
 
// }}}
// {{{ reset()
 
/**
* reset all the set* settings; with no parameter given, it resets them all
*
* @version 2002/09/16
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @return void
*/
function reset($what=array())
{
if (!sizeof($what)) {
$what = array(
'select',
'dontSelect',
'group',
'having',
'limit',
'where',
'index',
'order',
'join',
'leftJoin',
'rightJoin'
);
}
 
foreach ($what as $aReset) {
$this->{'set'.ucfirst($aReset)}();
}
}
 
// }}}
// {{{ setOption()
 
/**
* set mode the class shall work in
* currently we have the modes:
* 'raw' does not quote the data before building the query
*
* @version 2002/09/17
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the mode to be set
* @param mixed the value of the mode
* @return void
*/
function setOption($option, $value)
{
$this->options[strtolower($option)] = $value;
}
 
// }}}
// {{{ getOption()
 
/**
* @return string
*/
function getOption($option)
{
return $this->options[strtolower($option)];
}
 
// }}}
// {{{ _quoteArray()
 
/**
* quotes all the data in this array if we are not in raw mode!
* @param array
*/
function _quoteArray($data)
{
if (!$this->getOption('raw')) {
foreach ($data as $key => $val) {
$data[$key] = $this->db->quote($val);
}
}
return $data;
}
 
// }}}
// {{{ _checkColumns()
 
/**
* checks if the columns which are given as the array's indexes really exist
* if not it will be unset anyway
*
* @version 2002/04/16
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the actual message, first word should always be the method name,
* to build the message like this: className::methodname
* @param integer the line number
*/
function _checkColumns($newData, $method='unknown')
{
if (!$meta = $this->metadata()) { // if no metadata available, return data as given
return $newData;
}
 
foreach ($newData as $colName => $x) {
if (!isset($meta[$colName])) {
$this->_errorLog("$method, column {$this->table}.$colName doesnt exist, value was removed before '$method'",__LINE__);
unset($newData[$colName]);
} else {
// if the current column exists, check the length too, not to write content that is too long
// prevent DB-errors here
// do only check the data length if this field is given
if (isset($meta[$colName]['len']) && ($meta[$colName]['len'] != -1) &&
($oldLength=strlen($newData[$colName])) > $meta[$colName]['len']
) {
$this->_errorLog("_checkColumns, had to trim column '$colName' from $oldLength to ".
$meta[$colName]['DATA_LENGTH'].' characters.', __LINE__);
$newData[$colName] = substr($newData[$colName], 0, $meta[$colName]['len']);
}
}
}
return $newData;
}
 
// }}}
// {{{ debug()
 
/**
* overwrite this method and i.e. print the query $string
* to see the final query
*
* @param string the query mostly
*/
function debug($string){}
 
//
//
// ONLY ORACLE SPECIFIC, not very nice since it is DB dependent, but we need it!!!
//
//
 
// }}}
// {{{ metadata()
 
/**
* !!!! query COPIED FROM db_oci8.inc - from PHPLIB !!!!
*
* @access public
* @see
* @version 2001/09
* @author PHPLIB
* @param
* @return
*/
function metadata($table='')
{
// is there an alias in the table name, then we have something like this: 'user ua'
// cut of the alias and return the table name
if (strpos($table, ' ') !== false) {
$split = explode(' ', trim($table));
$table = $split[0];
}
 
$full = false;
if (empty($table)) {
$table = $this->table;
}
// to prevent multiple selects for the same metadata
if (isset($this->_metadata[$table])) {
return $this->_metadata[$table];
}
 
// FIXXXME use oci8 implementation of newer PEAR::DB-version
if ($this->db->phptype == 'oci8') {
$count = 0;
$id = 0;
$res = array();
 
//# This is a RIGHT OUTER JOIN: "(+)", if you want to see, what
//# this query results try the following:
//// $table = new Table; $this->db = new my_DB_Sql; // you have to make
//// // your own class
//// $table->show_results($this->db->query(see query vvvvvv))
////
$res = $this->db->getAll("SELECT T.column_name,T.table_name,T.data_type,".
"T.data_length,T.data_precision,T.data_scale,T.nullable,".
"T.char_col_decl_length,I.index_name".
" FROM ALL_TAB_COLUMNS T,ALL_IND_COLUMNS I".
" WHERE T.column_name=I.column_name (+)".
" AND T.table_name=I.table_name (+)".
" AND T.table_name=UPPER('$table') ORDER BY T.column_id");
 
if (DB::isError($res)) {
//$this->_errorSet($res->getMessage());
// i think we only need to log here, since this method is never used
// directly for the user's functionality, which means if it fails it
// is most probably an appl error
$this->_errorLog($res->getUserInfo());
return false;
}
foreach ($res as $key=>$val) {
$res[$key]['name'] = $val['COLUMN_NAME'];
}
} else {
if (!is_object($this->db)) {
return false;
}
$res = $this->db->tableinfo($table);
if (DB::isError($res)) {
$this->_errorSet($res->getUserInfo());
return false;
}
}
 
$ret = array();
foreach ($res as $key => $val) {
$ret[$val['name']] = $val;
}
$this->_metadata[$table] = $ret;
return $ret;
}
 
 
 
//
// methods for building the query
//
 
// }}}
// {{{ _buildFrom()
 
/**
* build the from string
*
* @access private
* @return string the string added after FROM
*/
function _buildFrom()
{
$from = $this->table;
$join = $this->getJoin();
 
if (!$join) { // no join set
return $from;
}
// handle the standard join thingy
if (isset($join['default']) && count($join['default'])) {
$from .= ','.implode(',',array_keys($join['default']));
}
 
// handle left/right joins
foreach (array('left', 'right') as $joinType) {
if (isset($join[$joinType]) && count($join[$joinType])) {
foreach($join[$joinType] as $table => $condition) {
// replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME
// since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange
// FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column'
$regExp = '/_('.$table.')_([^\s]+)/';
$where = preg_replace($regExp, '$1.$2', $condition);
 
// add the table name before any column that has no table prefix
// since this might cause "unambiguous column" errors
if ($meta = $this->metadata()) {
foreach ($meta as $aCol=>$x) {
// this covers the LIKE,IN stuff: 'name LIKE "%you%"' 'id IN (2,3,4,5)'
$condition = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $condition);
// replace also the column names which are behind a '='
// and do this also if the aCol is at the end of the where clause
// that's what the $ is for
$condition = preg_replace('/=\s*'.$aCol.'(\s|$)/', "={$this->table}.$aCol ", $condition);
// replace if colName is first and possibly also if at the beginning of the where-string
$condition = preg_replace('/(^\s*|\s+)'.$aCol.'\s*=/', "$1{$this->table}.$aCol=", $condition);
}
}
$from .= ' '.strtoupper($joinType).' JOIN '.$table.' ON '.$condition;
}
}
}
return $from;
}
 
// }}}
// {{{ getTableShortName()
 
/**
* this method gets the short name for a table
*
* get the short name for a table, this is needed to properly build the
* 'AS' parts in the select query
* @param string the real table name
* @return string the table's short name
*/
function getTableShortName($table)
{
$tableSpec = $this->getTableSpec(false);
if (isset($tableSpec[$table]['shortName']) && $tableSpec[$table]['shortName']) {
//print "$table ... ".$tableSpec[$table]['shortName'].'<br>';
return $tableSpec[$table]['shortName'];
}
 
$possibleTableShortName = preg_replace($this->_tableNameToShortNamePreg, '', $table);
//print "$table ... $possibleTableShortName<br>";
return $possibleTableShortName;
}
 
// }}}
// {{{ getTableSpec()
 
/**
* gets the tableSpec either indexed by the short name or the name
* returns the array for the tables given as parameter or if no
* parameter given for all tables that exist in the tableSpec
*
* @param array table names (not the short names!)
* @param boolean if true the table is returned indexed by the shortName
* otherwise indexed by the name
* @return array the tableSpec indexed
*/
function getTableSpec($shortNameIndexed=true, $tables=array())
{
$newSpec = array();
foreach ($this->tableSpec as $aSpec) {
if (sizeof($tables)==0 || in_array($aSpec['name'],$tables)) {
if ($shortNameIndexed) {
$newSpec[$aSpec['shortName']] = $aSpec;
} else {
$newSpec[$aSpec['name']] = $aSpec;
}
}
}
return $newSpec;
}
 
// }}}
// {{{ _buildSelect()
 
/**
* build the 'SELECT <what> FROM ... 'for a select
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string if given use this string
* @return string the what-clause
*/
function _buildSelect($what=null)
{
// what has preference, that means if what is set it is used
// this is only because the methods like 'get' pass an individually built value, which
// is supposed to be used, but usually it's generically build using the 'getSelect' values
if (empty($what) && $this->getSelect()) {
$what = $this->getSelect();
}
 
//
// replace all the '*' by the real column names, and take care of the dontSelect-columns!
//
$dontSelect = $this->getDontSelect();
$dontSelect = $dontSelect ? explode(',', $dontSelect) : array(); // make sure dontSelect is an array
 
// here we will replace all the '*' and 'table.*' by all the columns that this table
// contains. we do this so we can easily apply the 'dontSelect' values.
// and so we can also handle queries like: 'SELECT *,count() FROM ' and 'SELECT table.*,x FROM ' too
if (strpos($what, '*') !== false) {
// subpattern 1 get all the table names, that are written like this: 'table.*' including '*'
// for '*' the tablename will be ''
preg_match_all('/([^,]*)(\.)?\*\s*(,|$)/U', $what, $res);
//print "$what ... ";print_r($res);print "<br>";
$selectAllFromTables = array_unique($res[1]); // make the table names unique, so we do it all just once for each table
$tables = array();
if (in_array('', $selectAllFromTables)) { // was there a '*' ?
// get all the tables that we need to process, depending on if joined or not
$tables = $this->getJoin() ?
array_merge($this->getJoin('tables'), array($this->table)) : // get the joined tables and this->table
array($this->table); // create an array with only this->table
} else {
$tables = $selectAllFromTables;
}
 
$cols = array();
foreach ($tables as $aTable) { // go thru all the tables and get all columns for each, and handle 'dontSelect'
if ($meta = $this->metadata($aTable)) {
foreach ($meta as $colName => $x) {
// handle the dontSelect's
if (in_array($colName, $dontSelect) || in_array("$aTable.$colName", $dontSelect)) {
continue;
}
 
// build the AS clauses
// put " around them to enable use of reserved words, i.e. SELECT table.option as option FROM...
// and 'option' actually is a reserved word, at least in mysql
// put double quotes around them, since pgsql doesnt work with single quotes
// but don't do this for ibase because it doesn't work!
if ($aTable == $this->table) {
if ($this->db->phptype == 'ibase') {
$cols[$aTable][] = $this->table. '.' .$colName . ' AS '. $colName;
} else {
$cols[$aTable][] = $this->table. '.' .$colName . ' AS "'. $colName .'"';
}
} else {
$cols[$aTable][] = "$aTable.$colName AS \"_".$this->getTableShortName($aTable)."_$colName\"";
}
}
}
}
 
// put the extracted select back in the $what
// that means replace 'table.*' by the i.e. 'table.id AS _table_id'
// or if it is the table of this class replace 'table.id AS id'
if (in_array('', $selectAllFromTables)) {
$allCols = array();
foreach ($cols as $aTable) {
$allCols[] = implode(',', $aTable);
}
$what = preg_replace('/(^|,)\*($|,)/', '$1'.implode(',',$allCols).'$2', $what);
// remove all the 'table.*' since we have selected all anyway (because there was a '*' in the select)
$what = preg_replace('/[^,]*(\.)?\*\s*(,|$)/U', '', $what);
} else {
foreach ($cols as $tableName => $aTable) {
if (is_array($aTable) && sizeof($aTable)) {
// replace all the 'table.*' by their select of each column
$what = preg_replace('/(^|,)\s*'.$tableName.'\.\*\s*($|,)/', '$1'.implode(',',$aTable).'$2', $what);
}
}
}
}
 
if ($this->getJoin()) {
// replace all 'column' by '$this->table.column' to prevent ambigious errors
$metadata = $this->metadata();
if (is_array($metadata)) {
foreach ($metadata as $aCol => $x) {
// handle ',id as xid,MAX(id),id' etc.
// FIXXME do this better!!!
$what = preg_replace( "/(^|,|\()(\s*)$aCol(\)|\s|,|as|$)/i",
// $2 is actually just to keep the spaces, is not really
// necessary, but this way the test works independent of this functionality here
"$1$2{$this->table}.$aCol$3",
$what);
}
}
// replace all 'joinedTable.columnName' by '_joinedTable_columnName'
// this actually only has an effect if there was no 'table.*' for 'table'
// if that was there, then it has already been done before
foreach ($this->getJoin('tables') as $aTable) {
if ($meta = $this->metadata($aTable)) {
foreach ($meta as $aCol=>$x) {
// dont put the 'AS' behind it if there is already one
if (preg_match("/$aTable.$aCol\s*as/i",$what)) {
continue;
}
// this covers a ' table.colName ' surrounded by spaces, and replaces it by ' table.colName AS _table_colName'
$what = preg_replace('/\s'.$aTable.'.'.$aCol.'\s/', " $aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol ", $what);
// replace also the column names which are behind a ','
// and do this also if the aCol is at the end that's what the $ is for
$what = preg_replace('/,\s*'.$aTable.'.'.$aCol.'(,|\s|$)/', ",$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol$1", $what);
// replace if colName is first and possibly also if at the beginning of the where-string
$what = preg_replace('/(^\s*|\s+)'.$aTable.'.'.$aCol.'\s*,/', "$1$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol,", $what);
}
}
}
}
return $what;
}
 
// }}}
// {{{ _buildWhere()
 
/**
* Build WHERE clause
*
* @param string $where WHERE clause
* @return string $where WHERE clause after processing
* @access private
*/
function _buildWhere($where='')
{
$where = trim($where);
$originalWhere = $this->getWhere();
if ($originalWhere) {
if (!empty($where)) {
$where = $originalWhere.' AND '.$where;
} else {
$where = $originalWhere;
}
}
$where = trim($where);
 
if ($join = $this->getJoin()) { // is join set?
// only those where conditions in the default-join have to be added here
// left-join conditions are added behind 'ON', the '_buildJoin()' does that
if (isset($join['default']) && count($join['default'])) {
// we have to add this join-where clause here
// since at least in mysql a query like: select * from tableX JOIN tableY ON ...
// doesnt work, may be that's even SQL-standard...
if (!empty($where)) {
$where = implode(' AND ', $join['default']).' AND '.$where;
} else {
$where = implode(' AND ', $join['default']);
}
}
// replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME
// since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange
// FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column'
$regExp = '/_('.implode('|', $this->getJoin('tables')).')_([^\s]+)/';
$where = preg_replace($regExp, '$1.$2', $where);
// add the table name before any column that has no table prefix
// since this might cause "unambigious column" errors
if ($meta = $this->metadata()) {
foreach ($meta as $aCol => $x) {
// this covers the LIKE,IN stuff: 'name LIKE "%you%"' 'id IN (2,3,4,5)'
$where = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $where);
// replace also the column names which are behind a '='
// and do this also if the aCol is at the end of the where clause
// that's what the $ is for
$where = preg_replace('/([=<>])\s*'.$aCol.'(\s|$)/', "$1{$this->table}.$aCol ", $where);
// replace if colName is first and possibly also if at the beginning of the where-string
$where = preg_replace('/(^\s*|\s+)'.$aCol.'\s*([=<>])/', "$1{$this->table}.$aCol$2", $where);
}
}
}
return $where;
}
 
// }}}
// {{{ _buildOrder()
 
/**
*
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param
* @return
*/
function _buildOrder()
{
$order = $this->getOrder();
// replace 'column' by '$this->table.column' if the column is defined for $this->table
if ($meta = $this->metadata()) {
foreach ($meta as $aCol=>$x) {
$order = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U', "$1{$this->table}.$aCol$2", $order);
}
}
return $order;
}
 
// }}}
// {{{ _buildGroup()
 
/**
* Build the group-clause, replace 'column' by 'table.column'.
*
* @access public
* @param void
* @return string the rendered group clause
*/
function _buildGroup()
{
$group = $this->getGroup();
// replace 'column' by '$this->table.column' if the column is defined for $this->table
if ($meta = $this->metadata()) {
foreach ($meta as $aCol => $x) {
$group = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U', "$1{$this->table}.$aCol$2", $group);
}
}
return $group;
}
 
// }}}
// {{{ _buildHaving()
 
/**
*
* @version 2003/06/05
* @access public
* @author Johannes Schaefer <johnschaefer@gmx.de>
* @param
* @return string the having clause
*/
function _buildHaving()
{
$having = $this->getHaving();
// replace 'column' by '$this->table.column' if the column is defined for $this->table
if ($meta = $this->metadata()) {
foreach ($meta as $aCol => $x) {
$having = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U',"$1{$this->table}.$aCol$2",$having);
}
}
return $having;
}
 
// }}}
// {{{ _buildHaving()
 
/**
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param array this array contains the elements of the query,
* indexed by their key, which are: 'select','from','where', etc.
* @param boolean whether this method is called via getCount() or not.
* @return
*/
function _buildSelectQuery($query=array(), $isCalledViaGetCount = false)
{
/*FIXXXME finish this
$cacheKey = md5(serialize(????));
if (isset($this->_queryCache[$cacheKey])) {
$this->_errorLog('using cached query',__LINE__);
return $this->_queryCache[$cacheKey];
}
*/
$where = isset($query['where']) ? $query['where'] : $this->_buildWhere();
if ($where) {
$where = 'WHERE '.$where;
}
$order = isset($query['order']) ? $query['order'] : $this->_buildOrder();
if ($order) {
$order = 'ORDER BY '.$order;
}
$group = isset($query['group']) ? $query['group'] : $this->_buildGroup();
if ($group) {
$group = 'GROUP BY '.$group;
}
$having = isset($query['having']) ? $query['having'] : $this->_buildHaving();
if ($having) {
$having = 'HAVING '.$having;
}
$queryString = sprintf( 'SELECT %s FROM %s %s %s %s %s',
isset($query['select']) ? $query['select'] : $this->_buildSelect(),
isset($query['from']) ? $query['from'] : $this->_buildFrom(),
$where,
$group,
$having,
$order
);
// $query['limit'] has preference!
$limit = isset($query['limit']) ? $query['limit'] : $this->_limit;
if (!$isCalledViaGetCount && @$limit[1]) { // is there a count set?
$queryString=$this->db->modifyLimitQuery($queryString,$limit[0],$limit[1]);
if (DB::isError($queryString)) {
$this->_errorSet('DB_QueryTool::db::modifyLimitQuery failed '.$queryString->getMessage());
$this->_errorLog($queryString->getUserInfo());
return false;
}
}
// $this->_queryCache[$cacheKey] = $queryString;
return $queryString;
}
 
// }}}
// {{{ _buildUpdateQuery()
 
/**
* this simply builds an update query.
*
* @param array the parameter array might contain the following indexes
* 'where' the where clause to be added, i.e.
* UPDATE table SET x=1 WHERE y=0
* here the 'where' part simply would be 'y=0'
* 'set' the actual data to be updated
* in the example above, that would be 'x=1'
* @return string the resulting query
*/
function _buildUpdateQuery($query=array())
{
$where = isset($query['where']) ? $query['where'] : $this->_buildWhere();
if ($where) {
$where = 'WHERE '.$where;
}
 
$updateString = sprintf('UPDATE %s SET %s %s',
$this->table,
$query['set'],
$where
);
return $updateString;
}
 
// }}}
// {{{ execute()
 
/**
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param
* @param string query method
* @return boolean
*/
function execute($query=null, $method='getAll')
{
$this->writeLog();
if (is_null($query)) {
$query = $this->_buildSelectQuery();
}
$this->writeLog('query built: '.$query);
 
// FIXXME on ORACLE this doesnt work, since we return joined columns as _TABLE_COLNAME and the _ in front
// doesnt work on oracle, add a letter before it!!!
$this->_lastQuery = $query;
 
$this->debug($query);
$this->writeLog('start query');
if (DB::isError($res = $this->db->$method($query))) {
$this->writeLog('end query (failed)');
if ($this->getOption('verbose')) {
$this->_errorSet($res->getMessage());
} else {
$this->_errorLog($res->getMessage());
}
$this->_errorLog($res->getUserInfo(), __LINE__);
return false;
} else {
$this->writeLog('end query');
}
$res = $this->_makeIndexed($res);
return $res;
}
 
// }}}
// {{{ writeLog()
 
/**
* Write events to the logfile.
*
* It does some additional work, like time measuring etc. to
* see some additional info
*
*/
function writeLog($text='START')
{
//its still really a quicky.... 'refactor' (nice word) that
if (!isset($this->options['logfile'])) {
return;
}
 
include_once 'Log.php';
if (!class_exists('Log')) {
return;
}
if (!$this->_logObject) {
$this->_logObject =& Log::factory('file', $this->options['logfile']);
}
 
if ($text==='start query' || $text==='end query') {
$bytesSent = $this->db->getAll("SHOW STATUS like 'Bytes_sent'");
$bytesSent = $bytesSent[0]['Value'];
}
if ($text==='START') {
$startTime = split(' ', microtime());
$this->_logData['startTime'] = $startTime[1] + $startTime[0];
}
if ($text==='start query') {
$this->_logData['startBytesSent'] = $bytesSent;
$startTime = split(' ', microtime());
$this->_logData['startQueryTime'] = $startTime[1] + $startTime[0];
return;
}
if ($text==='end query') {
$text .= ' result size: '.((int)$bytesSent-(int)$this->_logData['startBytesSent']).' bytes';
$endTime = split(' ', microtime());
$endTime = $endTime[1] + $endTime[0];
$text .= ', took: '.(($endTime - $this->_logData['startQueryTime'])).' seconds';
}
if (strpos($text, 'query built')===0) {
$endTime = split(' ', microtime());
$endTime = $endTime[1] + $endTime[0];
$this->writeLog('query building took: '.(($endTime - $this->_logData['startTime'])).' seconds');
}
$this->_logObject->log($text);
 
if (strpos($text, 'end query')===0) {
$endTime = split(' ', microtime());
$endTime = $endTime[1] + $endTime[0];
$text = 'time over all: '.(($endTime - $this->_logData['startTime'])).' seconds';
$this->_logObject->log($text);
}
}
 
// }}}
// {{{ returnResult()
 
/**
* Return the chosen result type
*
* @version 2004/04/28
* @access public
* @param object reference
* @return mixed
*/
function returnResult(&$result)
{
if ($this->_resultType == 'none') {
return $result;
}
if ($result === false) {
return false;
}
//what about allowing other (custom) result types?
switch (strtolower($this->_resultType)) {
case 'object': return new DB_QueryTool_Result_Object($result);
case 'array':
default: return new DB_QueryTool_Result($result);
}
}
 
// }}}
// {{{ _makeIndexed()
 
/**
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param mixed
* @return mixed
*/
function &_makeIndexed(&$data)
{
// we can only return an indexed result if the result has a number of columns
if (is_array($data) && sizeof($data) && $key = $this->getIndex()) {
// build the string to evaluate which might be made up out of multiple indexes of a result-row
$evalString = '$val[\''.implode('\'].\',\'.$val[\'',explode(',',$key)).'\']'; //"
 
$indexedData = array();
//FIXXME actually we also need to check ONCE if $val is an array, so to say if $data is 2-dimensional
foreach ($data as $val) {
eval("\$keyValue = $evalString;"); // get the actual real (string-)key (string if multiple cols are used as index)
$indexedData[$keyValue] = $val;
}
unset($data);
return $indexedData;
}
 
return $data;
}
 
// }}}
// {{{ setIndex()
 
/**
* format the result to be indexed by $key
* NOTE: be careful, when using this you should be aware, that if you
* use an index which's value appears multiple times you may loose data
* since a key cant exist multiple times!!
* the result for a result to be indexed by a key(=columnName)
* (i.e. 'relationtoMe') which's values are 'brother' and 'sister'
* or alike normally returns this:
* $res['brother'] = array('name'=>'xxx')
* $res['sister'] = array('name'=>'xxx')
* but if the column 'relationtoMe' contains multiple entries for 'brother'
* then the returned dataset will only contain one brother, since the
* value from the column 'relationtoMe' is used
* and which 'brother' you get depends on a lot of things, like the sortorder,
* how the db saves the data, and whatever else
*
* you can also set indexes which depend on 2 columns, simply pass the parameters like
* 'table1.id,table2.id' it will be used as a string for indexing the result
* and the index will be built using the 2 values given, so a possible
* index might be '1,2' or '2108,29389' this way you can access data which
* have 2 primary keys. Be sure to remember that the index is a string!
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param
* @return
*/
function setIndex($key=null)
{
if ($this->getJoin()) { // is join set?
// replace TABLENAME.COLUMNNAME by _TABLENAME_COLUMNNAME
// since this is only the result-keys can be used for indexing :-)
$regExp = '/('.implode('|', $this->getJoin('tables')).')\.([^\s]+)/';
$key = preg_replace($regExp, '_$1_$2', $key);
 
// remove the table name if it is in front of '<$this->table>.columnname'
// since the key doesnt contain it neither
if ($meta = $this->metadata()) {
foreach ($meta as $aCol => $x) {
$key = preg_replace('/'.$this->table.'\.'.$aCol.'/', $aCol, $key);
}
}
}
$this->_index = $key;
}
 
// }}}
// {{{ getIndex()
 
/**
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param
* @return
*/
function getIndex()
{
return $this->_index;
}
 
// }}}
// {{{ useResult()
 
/**
* Choose the type of the returned result
*
* @version 2004/04/28
* @access public
* @param string $type ['array' | 'object' | 'none']
* For BC reasons, $type=true is equal to 'array',
* $type=false is equal to 'none'
*/
function useResult($type='array')
{
if ($type === true) {
$type = 'array';
} elseif ($type === false) {
$type = 'none';
}
switch (strtolower($type)) {
case 'array':
$this->_resultType = 'array';
require_once 'DB/QueryTool/Result.php';
break;
case 'object':
$this->_resultType = 'object';
require_once 'DB/QueryTool/Result/Object.php';
break;
default:
$this->_resultType = 'none';
}
}
 
// }}}
// {{{ setErrorCallback()
 
/**
* set both callbacks
* @param string
*/
function setErrorCallback($param='')
{
$this->setErrorLogCallback($param);
$this->setErrorSetCallback($param);
}
 
// }}}
// {{{ setErrorLogCallback()
 
/**
* @param string
*/
function setErrorLogCallback($param='')
{
$errorLogCallback = &PEAR::getStaticProperty('DB_QueryTool','_errorLogCallback');
$errorLogCallback = $param;
}
 
// }}}
// {{{ setErrorSetCallback()
 
/**
* @param string
*/
function setErrorSetCallback($param='')
{
$errorSetCallback = &PEAR::getStaticProperty('DB_QueryTool','_errorSetCallback');
$errorSetCallback = $param;
}
 
// }}}
// {{{ _errorLog()
 
/**
* sets error log and adds additional info
*
* @version 2002/04/16
* @access public
* @author Wolfram Kriesing <wk@visionp.de>
* @param string the actual message, first word should always be the method name,
* to build the message like this: className::methodname
* @param integer the line number
*/
function _errorLog($msg, $line='unknown')
{
$this->_errorHandler('log', $msg, $line);
/*
if ($this->getOption('verbose') == true)
{
$this->_errorLog(get_class($this)."::$msg ($line)");
return;
}
 
if ($this->_errorLogCallback)
call_user_func($this->_errorLogCallback, $msg);
*/
}
 
// }}}
// {{{ _errorSet()
 
/**
* @param string
* @param string
*/
function _errorSet($msg, $line='unknown')
{
$this->_errorHandler('set', $msg, $line);
}
 
// }}}
// {{{ _errorHandler()
 
/**
* @param
* @param string
* @param string
*/
function _errorHandler($logOrSet, $msg, $line='unknown')
{
/* what did i do this for?
if ($this->getOption('verbose') == true)
{
$this->_errorHandler($logOrSet, get_class($this)."::$msg ($line)");
return;
}
*/
 
$msg = get_class($this)."::$msg ($line)";
 
$logOrSet = ucfirst($logOrSet);
$callback = &PEAR::getStaticProperty('DB_QueryTool','_error'.$logOrSet.'Callback');
//var_dump($callback);
//if ($callback)
// call_user_func($callback, $msg);
// else
// ?????
 
}
 
// }}}
}
?>
/trunk/bibliotheque/pear/DB/QueryTool/Result/Object.php
New file
0,0 → 1,89
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* Contains the DB_QueryTool_Result_Row and DB_QueryTool_Result_Object classes
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB_QueryTool
* @author Roman Dostovalov, Com-tec-so S.A.<roman.dostovalov@ctco.lv>
* @copyright 2004-2005 Roman Dostovalov
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Object.php,v 1.3 2005/02/25 16:38:28 quipo Exp $
* @link http://pear.php.net/package/DB_QueryTool
*/
 
/**
* Include parent class
*/
require_once 'DB/QueryTool/Result.php';
 
/**
* DB_QueryTool_Result_Row class
*
* @category Database
* @package DB_QueryTool
* @author Roman Dostovalov, Com-tec-so S.A.<roman.dostovalov@ctco.lv>
* @copyright 2004-2005 Roman Dostovalov
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/DB_QueryTool
*/
class DB_QueryTool_Result_Row
{
/**
* create object properties from the array
* @param array
*/
function DB_QueryTool_Result_Row($arr)
{
foreach ($arr as $key => $value) {
$this->$key = $value;
}
}
}
 
// -----------------------------------------------------------------------------
 
/**
* DB_QueryTool_Result_Object class
*
* @category Database
* @package DB_QueryTool
* @author Roman Dostovalov, Com-tec-so S.A.<roman.dostovalov@ctco.lv>
* @copyright 2004-2005 Roman Dostovalov
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/DB_QueryTool
*/
class DB_QueryTool_Result_Object extends DB_QueryTool_Result
{
// {{{ fetchRow
 
/**
* This function emulates PEAR::DB fetchRow() method
* With this function DB_QueryTool can transparently replace PEAR::DB
*
* @todo implement fetchmode support?
* @access public
* @return void
*/
function fetchRow()
{
$arr = $this->getNext();
if (!PEAR::isError($arr)) {
$row = new DB_QueryTool_Result_Row($arr);
return $row;
}
return false;
}
 
// }}}
}
?>
/trunk/bibliotheque/pear/DB/QueryTool/Result.php
New file
0,0 → 1,261
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* Contains the DB_QueryTool_Result class
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB_QueryTool
* @author Wolfram Kriesing <wk@visionp.de>
* @author Paolo Panto <wk@visionp.de>
* @author Lorenzo Alberton <l dot alberton at quipo dot it>
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Result.php,v 1.8 2005/02/25 16:38:28 quipo Exp $
* @link http://pear.php.net/package/DB_QueryTool
*/
 
/**
* DB_QueryTool_Result class
*
* This result actually contains the 'data' itself, the number of rows
* returned and some additional info
* using ZE2 you can also get retrieve data from the result doing the following:
* <DB_QueryTool_Common-instance>->getAll()->getCount()
* or
* <DB_QueryTool_Common-instance>->getAll()->getData()
*
* @category Database
* @package DB_QueryTool
* @author Wolfram Kriesing <wk@visionp.de>
* @author Paolo Panto <wk@visionp.de>
* @author Lorenzo Alberton <l dot alberton at quipo dot it>
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/DB_QueryTool
*/
class DB_QueryTool_Result
{
// {{{ class vars
 
/**
* @var array
*/
var $_data = array();
 
/**
* @var array
*/
var $_dataKeys = array();
 
/**
* @var integer
*/
var $_count = 0;
 
/**
* the counter for the methods getFirst, getNext
* @var array
*/
var $_counter = null;
 
// }}}
// {{{ DB_QueryTool_Result()
 
/**
* create a new instance of result with the data returned by the query
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wolfram@kriesing.de>
* @param array the data returned by the result
*/
function DB_QueryTool_Result($data)
{
if (!count($data)) {
$this->_count = 0;
} else {
list($firstElement) = $data;
if (is_array($firstElement)) { // is the array a collection of rows?
$this->_count = sizeof($data);
} else {
if (sizeof($data) > 0) {
$this->_count = 1;
} else {
$this->_count = 0;
}
}
}
$this->_data = $data;
}
 
// }}}
// {{{ numRows
 
/**
* return the number of rows returned. This is an alias for getCount().
*
* @access public
* @return integer
*/
function numRows()
{
return $this->_count;
}
 
// }}}
// {{{ getCount()
 
/**
* return the number of rows returned
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wolfram@kriesing.de>
* @return integer the number of rows returned
*/
function getCount()
{
return $this->_count;
}
 
// }}}
// {{{ getData()
 
/**
* get all the data returned
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wolfram@kriesing.de>
* @param string $key
* @return mixed array or PEAR_Error
*/
function getData($key=null)
{
if (is_null($key)) {
return $this->_data;
}
if ($this->_data[$key]) {
return $this->_data[$key];
}
return new PEAR_Error("there is no element with the key '$key'!");
}
 
// }}}
// {{{ getFirst()
 
/**
* get the first result set
* we are not using next, current, and reset, since those ignore keys
* which are empty or 0
*
* @version 2002/07/11
* @access public
* @author Wolfram Kriesing <wolfram@kriesing.de>
* @return mixed
*/
function getFirst()
{
if ($this->getCount() > 0) {
$this->_dataKeys = array_keys($this->_data);
$this->_counter = 0;
return $this->_data[$this->_dataKeys[$this->_counter]];
}
return new PEAR_Error('There are no elements!');
}
 
// }}}
// {{{ getNext()
 
/**
* Get next result set. If getFirst() has never been called before,
* it calls that method.
* @return mixed
* @access public
*/
function getNext()
{
if (!$this->initDone()) {
return $this->getFirst();
}
if ($this->hasMore()) {
$this->_counter++;
return $this->_data[$this->_dataKeys[$this->_counter]];
}
return new PEAR_Error('there are no more elements!');
}
 
// }}}
// {{{ hasMore()
 
/**
* check if there are other rows
*
* @return boolean
* @access public
*/
function hasMore()
{
if ($this->_counter+1 < $this->getCount()) {
return true;
}
return false;
}
 
// }}}
// {{{ fetchRow
 
/**
* This function emulates PEAR::DB fetchRow() method.
* With this method, DB_QueryTool can transparently replace PEAR_DB
*
* @todo implement fetchmode support?
* @access public
* @return void
*/
function fetchRow()
{
if ($this->hasMore()) {
$arr = $this->getNext();
if (!PEAR::isError($arr)) {
return $arr;
}
}
return false;
}
 
// }}}
// {{{ initDone
 
/**
* Helper method. Check if $this->_dataKeys has been initialized
*
* @return boolean
* @access private
*/
function initDone()
{
return (
isset($this->_dataKeys) &&
is_array($this->_dataKeys) &&
count($this->_dataKeys)
);
}
 
// }}}
 
#TODO
#function getPrevious()
#function getLast()
 
}
?>
/trunk/bibliotheque/pear/DB/QueryTool/EasyJoin.php
New file
0,0 → 1,135
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* Contains the DB_QueryTool_EasyJoin class
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Database
* @package DB_QueryTool
* @author Wolfram Kriesing <wk@visionp.de>
* @author Paolo Panto <wk@visionp.de>
* @copyright 2003-2005 Wolfram Kriesing, Paolo Panto
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: EasyJoin.php,v 1.9 2005/02/27 17:15:05 quipo Exp $
* @link http://pear.php.net/package/DB_QueryTool
*/
 
/**
* require the DB_QueryTool_Query class
*/
require_once 'DB/QueryTool/Query.php';
 
/**
* DB_QueryTool_EasyJoin class
*
* @category Database
* @package DB_QueryTool
* @author Wolfram Kriesing <wk@visionp.de>
* @copyright 2003-2005 Wolfram Kriesing
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @link http://pear.php.net/package/DB_QueryTool
*/
class DB_QueryTool_EasyJoin extends DB_QueryTool_Query
{
// {{{ class vars
 
/**
* This is the regular expression that shall be used to find a table's shortName
* in a column name, the string found by using this regular expression will be removed
* from the column name and it will be checked if it is a table name
* i.e. the default '/_id$/' would find the table name 'user' from the column name 'user_id'
*
* @var string regexp
*/
var $_tableNamePreg = '/_id$/';
 
/**
* This is to find the column name that is referred by it, so the default find
* from 'user_id' the column 'id' which will be used to refer to the 'user' table
*
* @var string regexp
*/
var $_columnNamePreg = '/^.*_/';
 
// }}}
// {{{ __construct()
 
/**
* call parent constructor
* @param mixed $dsn DSN string, DSN array or DB object
* @param array $options
*/
function __construct($dsn=false, $options=array())
{
parent::DB_QueryTool_Query($dsn, $options);
}
 
// }}}
// {{{ autoJoin()
 
/**
* Join the given tables, using the column names, to find out how to join the tables;
* i.e., if table1 has a column named &quot;table2_id&quot;, this method will join
* &quot;WHERE table1.table2_id=table2.id&quot;.
* All joins made here are only concatenated via AND.
* @param array $tables
*/
function autoJoin($tables)
{
// FIXXME if $tables is empty autoJoin all available tables that have a relation
// to $this->table, starting to search in $this->table
settype($tables, 'array');
// add this->table to the tables array, so we go thru the current table first
$tables = array_merge(array($this->table), $tables);
 
$shortNameIndexed = $this->getTableSpec(true, $tables);
$nameIndexed = $this->getTableSpec(false, $tables);
 
//print_r($shortNameIndexed);
//print_r($tables); print '<br><br>';
if (sizeof($shortNameIndexed) != sizeof($tables)) {
$this->_errorLog("autoJoin-ERROR: not all the tables are in the tableSpec!<br />");
}
 
$joinTables = array();
$joinConditions = array();
foreach ($tables as $aTable) { // go through $this->table and all the given tables
if ($metadata = $this->metadata($aTable))
foreach ($metadata as $aCol => $x) { // go through each row to check which might be related to $aTable
$possibleTableShortName = preg_replace($this->_tableNamePreg, '' , $aCol);
$possibleColumnName = preg_replace($this->_columnNamePreg, '' , $aCol);
//print "$aTable.$aCol .... possibleTableShortName=$possibleTableShortName .... possibleColumnName=$possibleColumnName<br />";
if (isset($shortNameIndexed[$possibleTableShortName])) {
// are the tables given in the tableSpec?
if (!$shortNameIndexed[$possibleTableShortName]['name'] ||
!$nameIndexed[$aTable]['name']) {
// its an error of the developer, so log the error, dont show it to the end user
$this->_errorLog("autoJoin-ERROR: '$aTable' is not given in the tableSpec!<br />");
} else {
// do only join different table.col combination,
// we should not join stuff like 'question.question=question.question'
// this would be quite stupid, but it used to be :-(
if ($shortNameIndexed[$possibleTableShortName]['name'] != $aTable ||
$possibleColumnName != $aCol
) {
$where = $shortNameIndexed[$possibleTableShortName]['name'].".$possibleColumnName=$aTable.$aCol";
$this->addJoin($nameIndexed[$aTable]['name'], $where);
$this->addJoin($shortNameIndexed[$possibleTableShortName]['name'], $where);
}
}
}
}
}
}
 
// }}}
}
?>
/trunk/bibliotheque/pear/PEAR/tests/pear_info.php
New file
0,0 → 1,38
<?php
/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Davey Shafik <davey@pixelated-dreams.com> |
// +----------------------------------------------------------------------+
//
// $Id: pear_info.php,v 1.5 2003/05/12 11:40:57 davey Exp $
 
/* May be required on slower (dial-up) connections
ini_set('default_socket_timeout',600);
ini_set('max_execution_time',600);
ini_set('max_input_time',600); */
 
// require the PEAR_Info file
require_once 'PEAR/Info.php';
 
// If you need to set a http_proxy uncomment the line below
// PEAR_Info::setProxy('your.proxy.here');
 
// Create PEAR_Info object
$info = new PEAR_Info();
 
// Display PEAR_Info output
$info->show();
 
?>
/trunk/bibliotheque/pear/PEAR/PackageFileManager/File.php
New file
0,0 → 1,421
<?php
//
// +------------------------------------------------------------------------+
// | PEAR :: Package File Manager |
// +------------------------------------------------------------------------+
// | Copyright (c) 2003-2004 Gregory Beaver |
// | Email cellog@phpdoc.org |
// +------------------------------------------------------------------------+
// | This source file is subject to version 3.00 of the PHP License, |
// | that is available at http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +------------------------------------------------------------------------+
// | Portions of this code based on phpDocumentor |
// | Web http://www.phpdoc.org |
// | Mirror http://phpdocu.sourceforge.net/ |
// +------------------------------------------------------------------------+
// $Id: File.php,v 1.21 2005/03/28 06:37:35 cellog Exp $
//
/**
* Retrieve the files from a directory listing
* @package PEAR_PackageFileManager
*/
/**
* Retrieve the files from a directory listing
*
* This class is used to retrieve a raw directory
* listing. Use the {@link PEAR_PackageFileManager_CVS}
* class to only retrieve the contents of a cvs
* repository when generating the package.xml
* @package PEAR_PackageFileManager
*/
class PEAR_PackageFileManager_File {
/**
* @var array
* @access private
*/
var $_options =
array(
);
 
/**
* @access private
* @var PEAR_PackageFileManager
*/
var $_parent;
 
/**
* @access private
* @var array|false
*/
var $_ignore = false;
 
/**
* Set up the File filelist generator
*
* 'ignore' and 'include' are the only options that this class uses. See
* {@link PEAR_PackageFileManager::setOptions()} for
* more information and formatting of this option
* @param PEAR_PackageFileManager
* @param array
*/
function PEAR_PackageFileManager_File(&$parent, $options)
{
$this->_parent = &$parent;
$this->_options = array_merge($this->_options, $options);
}
/**
* Generate the <filelist></filelist> section
* of the package file.
*
* This function performs the backend generation of the array
* containing all files in this package
* @return array
*/
function getFileList()
{
$package_directory = $this->_options['packagedirectory'];
$ignore = $this->_options['ignore'];
// implicitly ignore packagefile
$ignore[] = $this->_options['packagefile'];
if ($this->_options['packagefile'] == 'package.xml') {
// ignore auto-generated package2.xml from PEAR 1.4.0
$ignore[] = 'package2.xml';
}
$include = $this->_options['include'];
$this->ignore = array(false, false);
$this->_setupIgnore($ignore, 1);
$this->_setupIgnore($include, 0);
$allfiles = $this->dirList(substr($package_directory, 0, strlen($package_directory) - 1));
if (PEAR::isError($allfiles)) {
return $allfiles;
}
if (!count($allfiles)) {
return PEAR_PackageFileManager::raiseError(PEAR_PACKAGEFILEMANAGER_NO_FILES,
substr($package_directory, 0, strlen($package_directory) - 1));
}
$struc = array();
foreach($allfiles as $file) {
$path = substr(dirname($file), strlen(str_replace(DIRECTORY_SEPARATOR,
'/',
realpath($package_directory))) + 1);
if (!$path) {
$path = '/';
}
$ext = array_pop(explode('.', $file));
if (strlen($ext) == strlen($file)) {
$ext = '';
}
$struc[$path][] = array('file' => basename($file),
'ext' => $ext,
'path' => (($path == '/') ? basename($file) : $path . '/' . basename($file)),
'fullpath' => $file);
}
if (!count($struc)) {
$newig = implode($this->_options['ignore'], ', ');
return PEAR_PackageFileManager::raiseError(PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING,
substr($package_directory, 0, strlen($package_directory) - 1), $newig);
}
uksort($struc,'strnatcasecmp');
foreach($struc as $key => $ind) {
usort($ind, array($this, 'sortfiles'));
$struc[$key] = $ind;
}
 
$tempstruc = $struc;
if (!isset($tempstruc['/'])) {
$tempstruc['/'] = array();
}
$struc = array('/' => $tempstruc['/']);
$bv = 0;
foreach($tempstruc as $key => $ind) {
$save = $key;
if ($key != '/')
{
$struc['/'] = $this->_setupDirs($struc['/'], explode('/',$key), $tempstruc[$key]);
}
}
uksort($struc['/'], array($this, 'mystrucsort'));
 
return $struc;
}
/**
* Retrieve a listing of every file in $directory and
* all subdirectories.
*
* The return format is an array of full paths to files
* @access protected
* @return array list of files in a directory
* @param string $directory full path to the directory you want the list of
* @throws PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST
*/
function dirList($directory)
{
$ret = false;
if (@is_dir($directory)) {
$ret = array();
$d = @dir($directory); // thanks to Jason E Sweat (jsweat@users.sourceforge.net) for fix
while($d && false !== ($entry=$d->read())) {
if ($this->_testFile($directory, $entry)) {
if (is_file($directory . '/' . $entry)) {
// if include option was set, then only pass included files
if ($this->ignore[0]) {
if ($this->_checkIgnore($entry, $directory . '/' . $entry, 0)) {
continue;
}
}
// if ignore option was set, then only pass included files
if ($this->ignore[1]) {
if ($this->_checkIgnore($entry, $directory . '/' . $entry, 1)) {
continue;
}
}
$ret[] = $directory . '/' . $entry;
}
if (is_dir($directory . '/' . $entry)) {
$tmp = $this->dirList($directory . '/' . $entry);
if (is_array($tmp)) {
foreach($tmp as $ent) {
$ret[] = $ent;
}
}
}
}
}
if ($d) {
$d->close();
}
} else {
return PEAR_PackageFileManager::raiseError(PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST, $directory);
}
return $ret;
}
/**
* Test whether an entry should be processed.
*
* Normally, it ignores all files and directories that begin with "." addhiddenfiles option
* instead only ignores "." and ".." entries
* @access private
* @param string directory name of entry
* @param string name
*/
function _testFile($directory, $entry)
{
if ($this->_options['addhiddenfiles']) {
return is_file($directory . '/' . $entry) || (is_dir($directory . '/' . $entry) && !in_array($entry, array('.', '..')));
} else {
return $entry{0} != '.';
}
}
 
/**
* Tell whether to ignore a file or a directory
* allows * and ? wildcards
*
* @param string $file just the file name of the file or directory,
* in the case of directories this is the last dir
* @param string $path the full path
* @param 1|0 $return value to return if regexp matches. Set this to
* false to include only matches, true to exclude
* all matches
* @return bool true if $path should be ignored, false if it should not
* @access private
*/
function _checkIgnore($file, $path, $return = 1)
{
if (file_exists($path)) {
$path = realpath($path);
}
if (is_array($this->ignore[$return])) {
foreach($this->ignore[$return] as $match) {
// match is an array if the ignore parameter was a /path/to/pattern
if (is_array($match)) {
// check to see if the path matches with a path delimiter appended
preg_match('/^' . strtoupper($match[0]).'$/', strtoupper($path) . '/',$find);
if (!count($find)) {
// check to see if it matches without an appended path delimiter
preg_match('/^' . strtoupper($match[0]).'$/', strtoupper($path), $find);
}
if (count($find)) {
// check to see if the file matches the file portion of the regex string
preg_match('/^' . strtoupper($match[1]).'$/', strtoupper($file), $find);
if (count($find)) {
return $return;
}
}
// check to see if the full path matches the regex
preg_match('/^' . strtoupper($match[0]).'$/',
strtoupper($path . DIRECTORY_SEPARATOR . $file), $find);
if (count($find)) {
return $return;
}
} else {
// ignore parameter was just a pattern with no path delimiters
// check it against the path
preg_match('/^' . strtoupper($match).'$/', strtoupper($path), $find);
if (count($find)) {
return $return;
}
// check it against the file only
preg_match('/^' . strtoupper($match).'$/', strtoupper($file), $find);
if (count($find)) {
return $return;
}
}
}
}
return !$return;
}
/**
* Construct the {@link $ignore} array
* @param array strings of files/paths/wildcards to ignore
* @param 0|1 0 = files to include, 1 = files to ignore
* @access private
*/
function _setupIgnore($ignore, $index)
{
$ig = array();
if (is_array($ignore)) {
for($i=0; $i<count($ignore);$i++) {
$ignore[$i] = strtr($ignore[$i], "\\", "/");
$ignore[$i] = str_replace('//','/',$ignore[$i]);
 
if (!empty($ignore[$i])) {
if (!is_numeric(strpos($ignore[$i], '/'))) {
$ig[] = $this->_getRegExpableSearchString($ignore[$i]);
} else {
if (basename($ignore[$i]) . '/' == $ignore[$i]) {
$ig[] = $this->_getRegExpableSearchString($ignore[$i]);
} else {
$ig[] = array($this->_getRegExpableSearchString($ignore[$i]),
$this->_getRegExpableSearchString(basename($ignore[$i])));
}
}
}
}
if (count($ig)) {
$this->ignore[$index] = $ig;
} else {
$this->ignore[$index] = false;
}
} else $this->ignore[$index] = false;
}
/**
* Converts $s into a string that can be used with preg_match
* @param string $s string with wildcards ? and *
* @return string converts * to .*, ? to ., etc.
* @access private
*/
function _getRegExpableSearchString($s)
{
$y = '\/';
if (DIRECTORY_SEPARATOR == '\\') {
$y = '\\\\';
}
$s = str_replace('/', DIRECTORY_SEPARATOR, $s);
$x = strtr($s, array('?' => '.','*' => '.*','.' => '\\.','\\' => '\\\\','/' => '\\/',
'[' => '\\[',']' => '\\]','-' => '\\-'));
if (strpos($s, DIRECTORY_SEPARATOR) !== false &&
strrpos($s, DIRECTORY_SEPARATOR) === strlen($s) - 1) {
$x = "(?:.*$y$x?.*|$x.*)";
}
return $x;
}
/**
* Recursively move contents of $struc into associative array
*
* The contents of $struc have many indexes like 'dir/subdir/subdir2'.
* This function converts them to
* array('dir' => array('subdir' => array('subdir2')))
* @param array struc is array('dir' => array of files in dir,
* 'dir/subdir' => array of files in dir/subdir,...)
* @param array array form of 'dir/subdir/subdir2' array('dir','subdir','subdir2')
* @return array same as struc but with array('dir' =>
* array(file1,file2,'subdir' => array(file1,...)))
* @access private
*/
function _setupDirs($struc, $dir, $contents)
{
if (!count($dir)) {
foreach($contents as $dir => $files) {
if (is_string($dir)) {
if (strpos($dir, '/')) {
$test = true;
$a = $contents[$dir];
unset($contents[$dir]);
$b = explode('/', $dir);
$c = array_shift($b);
if (isset($contents[$c])) {
$contents[$c] = $this->_setDir($contents[$c], $this->_setupDirs(array(), $b, $a));
} else {
$contents[$c] = $this->_setupDirs(array(), $b, $a);
}
}
}
}
return $contents;
}
$me = array_shift($dir);
if (!isset($struc[$me])) {
$struc[$me] = array();
}
$struc[$me] = $this->_setupDirs($struc[$me], $dir, $contents);
return $struc;
}
 
/**
* Recursively add all the subdirectories of $contents to $dir without erasing anything in
* $dir
* @param array
* @param array
* @return array processed $dir
* @access private
*/
function _setDir($dir, $contents)
{
while(list($one,$two) = each($contents)) {
if (isset($dir[$one])) {
$dir[$one] = $this->_setDir($dir[$one], $contents[$one]);
} else {
$dir[$one] = $two;
}
}
return $dir;
}
 
/**#@+
* Sorting functions for the file list
* @param string
* @param string
* @access private
*/
function sortfiles($a, $b)
{
return strnatcasecmp($a['file'],$b['file']);
}
function mystrucsort($a, $b)
{
if (is_numeric($a) && is_string($b)) return 1;
if (is_numeric($b) && is_string($a)) return -1;
if (is_numeric($a) && is_numeric($b))
{
if ($a > $b) return 1;
if ($a < $b) return -1;
if ($a == $b) return 0;
}
return strnatcasecmp($a,$b);
}
/**#@-*/
}
?>
/trunk/bibliotheque/pear/PEAR/PackageFileManager/SimpleGenerator.php
New file
0,0 → 1,323
<?php
//
// +------------------------------------------------------------------------+
// | PEAR :: Package File Manager |
// +------------------------------------------------------------------------+
// | Copyright (c) 2004 Gregory Beaver |
// | Email cellog@phpdoc.org |
// +------------------------------------------------------------------------+
// | This source file is subject to version 3.00 of the PHP License, |
// | that is available at http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +------------------------------------------------------------------------+
// | Portions of this code based on phpDocumentor |
// | Web http://www.phpdoc.org |
// | Mirror http://phpdocu.sourceforge.net/ |
// +------------------------------------------------------------------------+
// $Id: SimpleGenerator.php,v 1.4 2005/05/20 22:42:59 cellog Exp $
//
 
require_once 'PEAR/PackageFile/Generator/v1.php';
/**
* Class for XML output
*
* @author Greg Beaver <cellog@php.net>
* @since 1.2.0
* @copyright 2003
* @package PEAR_PackageFileManager
*/
class PEAR_PackageFileManager_SimpleGenerator extends PEAR_PackageFile_Generator_v1
{
var $_options;
 
/**
* remove a warning about missing parameters - don't delete this
*/
function PEAR_PackageFileManager_SimpleGenerator()
{
}
 
/**
* @param array
*/
function setPackageFileManagerOptions($opts)
{
$this->_options = $opts;
}
 
/**
* Return an XML document based on the package info (as returned
* by the PEAR_Common::infoFrom* methods).
*
* @param array $pkginfo package info
*
* @return string XML data
*
* @access public
* @deprecated use a PEAR_PackageFile_v* object's generator instead
*/
function xmlFromInfo($pkginfo)
{
include_once 'PEAR/PackageFile.php';
include_once 'PEAR/Config.php';
$config = &PEAR_Config::singleton();
$packagefile = &new PEAR_PackageFile($config);
$pf = &$packagefile->fromArray($pkginfo);
parent::PEAR_PackageFile_Generator_v1($pf);
return $this->toXml();
}
 
function getFileRoles()
{
return PEAR_Common::getFileRoles();
}
 
function getReplacementTypes()
{
return PEAR_Common::getReplacementTypes();
}
 
/**
* Validate XML package definition file.
*
* @param string $info Filename of the package archive or of the
* package definition file
* @param array $errors Array that will contain the errors
* @param array $warnings Array that will contain the warnings
* @param string $dir_prefix (optional) directory where source files
* may be found, or empty if they are not available
* @access public
* @return boolean
* @deprecated use the validation of PEAR_PackageFile objects
*/
function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
{
include_once 'PEAR/PackageFile.php';
include_once 'PEAR/Config.php';
$config = &PEAR_Config::singleton();
$packagefile = &new PEAR_PackageFile($config);
PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
if (is_array($info)) {
$pf = &$packagefile->fromArray($info);
if (!$pf->validate(PEAR_VALIDATE_NORMAL)) {
foreach ($pf->getValidationWarnings() as $err) {
if ($error['level'] == 'error') {
$errors[] = $error['message'];
} else {
$warnings[] = $error['message'];
}
}
return false;
}
} else {
$pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
}
PEAR::staticPopErrorHandling();
if (PEAR::isError($pf)) {
$errs = $pf->getUserinfo();
if (is_array($errs)) {
foreach ($errs as $error) {
if ($error['level'] == 'error') {
$errors[] = $error['message'];
} else {
$warnings[] = $error['message'];
}
}
}
return false;
}
return true;
}
 
/**
* @param array
* @access protected
*/
function recursiveXmlFilelist($list)
{
$this->_dirs = array();
foreach ($list as $file => $attributes) {
$this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes);
}
if (!isset($this->_dirs['dirs'])) {
$this->_dirs['dirs'] = array();
}
if (count($this->_dirs['dirs']) != 1 || isset($this->_dirs['files'])) {
$this->_dirs = array('dirs' => array('/' => $this->_dirs));
}
return $this->_formatDir($this->_dirs, '', '', true);
}
 
/**
* @param array
* @param array
* @param string|null
* @param array|null
* @access private
*/
function _addDir(&$dirs, $dir, $file = null, $attributes = null)
{
if ($dir == array() || $dir == array('.')) {
$dirs['files'][basename($file)] = $attributes;
return;
}
$curdir = array_shift($dir);
if (!isset($dirs['dirs'][$curdir])) {
$dirs['dirs'][$curdir] = array();
}
$this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes);
}
 
/**
* @param array
* @param string
* @param string
* @access private
*/
function _formatDir($dirs, $indent = '', $curdir = '', $toplevel = false)
{
$ret = '';
if (!count($dirs)) {
return '';
}
if (isset($dirs['dirs'])) {
uksort($dirs['dirs'], 'strnatcasecmp');
foreach ($dirs['dirs'] as $dir => $contents) {
if ($dir == '/') {
$usedir = '/';
} else {
if ($curdir == '/') {
$curdir = '';
}
$usedir = "$curdir/$dir";
}
$ret .= "$indent <dir name=\"$dir\"";
if ($toplevel) {
$ret .= ' baseinstalldir="' . $this->_options['baseinstalldir'] . '"';
} else {
if (isset($this->_options['installexceptions'][$dir])) {
$ret .= ' baseinstalldir="' . $this->_options['installexceptions'][$dir] . '"';
}
}
$ret .= ">\n";
$ret .= $this->_formatDir($contents, "$indent ", $usedir);
$ret .= "$indent </dir> <!-- $usedir -->\n";
}
}
if (isset($dirs['files'])) {
uksort($dirs['files'], 'strnatcasecmp');
foreach ($dirs['files'] as $file => $attribs) {
$ret .= $this->_formatFile($file, $attribs, $indent);
}
}
return $ret;
}
 
/**
* @param string
* @param array
* @param string
* @access private
*/
function _formatFile($file, $attributes, $indent)
{
$ret = "$indent <file role=\"$attributes[role]\"";
if (isset($this->_options['installexceptions'][$file])) {
$ret .= ' baseinstalldir="' . $this->_options['installexceptions'][$file] . '"';
}
if (isset($attributes['md5sum'])) {
$ret .= " md5sum=\"$attributes[md5sum]\"";
}
if (isset($attributes['platform'])) {
$ret .= " platform=\"$attributes[platform]\"";
}
if (!empty($attributes['install-as'])) {
$ret .= ' install-as="' .
htmlspecialchars($attributes['install-as']) . '"';
}
$ret .= ' name="' . htmlspecialchars($file) . '"';
if (empty($attributes['replacements'])) {
$ret .= "/>\n";
} else {
$ret .= ">\n";
foreach ($attributes['replacements'] as $r) {
$ret .= "$indent <replace";
foreach ($r as $k => $v) {
$ret .= " $k=\"" . htmlspecialchars($v) .'"';
}
$ret .= "/>\n";
}
$ret .= "$indent </file>\n";
}
return $ret;
}
 
/**
* Generate the <filelist> tag
* @access private
* @return string
*/
function _doFileList($indent, $filelist, $curdir)
{
$ret = '';
foreach ($filelist as $file => $fa) {
if (isset($fa['##files'])) {
$ret .= "$indent <dir";
} else {
$ret .= "$indent <file";
}
 
if (isset($fa['role'])) {
$ret .= " role=\"$fa[role]\"";
}
if (isset($fa['baseinstalldir'])) {
$ret .= ' baseinstalldir="' .
htmlspecialchars($fa['baseinstalldir']) . '"';
}
if (isset($fa['md5sum'])) {
$ret .= " md5sum=\"$fa[md5sum]\"";
}
if (isset($fa['platform'])) {
$ret .= " platform=\"$fa[platform]\"";
}
if (!empty($fa['install-as'])) {
$ret .= ' install-as="' .
htmlspecialchars($fa['install-as']) . '"';
}
$ret .= ' name="' . htmlspecialchars($file) . '"';
if (isset($fa['##files'])) {
$ret .= ">\n";
$recurdir = $curdir;
if ($recurdir == '///') {
$recurdir = '';
}
$ret .= $this->_doFileList("$indent ", $fa['##files'], $recurdir . $file . '/');
$displaydir = $curdir;
if ($displaydir == '///' || $displaydir == '/') {
$displaydir = '';
}
$ret .= "$indent </dir> <!-- $displaydir$file -->\n";
} else {
if (empty($fa['replacements'])) {
$ret .= "/>\n";
} else {
$ret .= ">\n";
foreach ($fa['replacements'] as $r) {
$ret .= "$indent <replace";
foreach ($r as $k => $v) {
$ret .= " $k=\"" . htmlspecialchars($v) .'"';
}
$ret .= "/>\n";
}
$ret .= "$indent </file>\n";
}
}
}
return $ret;
}
}
 
?>
/trunk/bibliotheque/pear/PEAR/PackageFileManager/XMLOutput.php
New file
0,0 → 1,180
<?php
//
// +------------------------------------------------------------------------+
// | PEAR :: Package File Manager |
// +------------------------------------------------------------------------+
// | Copyright (c) 2004 Gregory Beaver |
// | Email cellog@phpdoc.org |
// +------------------------------------------------------------------------+
// | This source file is subject to version 3.00 of the PHP License, |
// | that is available at http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +------------------------------------------------------------------------+
// | Portions of this code based on phpDocumentor |
// | Web http://www.phpdoc.org |
// | Mirror http://phpdocu.sourceforge.net/ |
// +------------------------------------------------------------------------+
// $Id: XMLOutput.php,v 1.4 2004/02/07 18:04:00 cellog Exp $
//
 
/**
* Class for XML output
*
* @author Greg Beaver <cellog@php.net>
* @since 1.2.0
* @copyright 2003
* @package PEAR_PackageFileManager
*/
class PEAR_PackageFileManager_XMLOutput extends PEAR_Common {
 
/**
* Generate part of an XML description with release information.
*
* @param array $pkginfo array with release information
* @param bool $changelog whether the result will be in a changelog element
*
* @return string XML data
*
* @access private
*/
function _makeReleaseXml($pkginfo, $changelog = false)
{
$indent = $changelog ? " " : "";
$ret = "$indent <release>\n";
if (!empty($pkginfo['version'])) {
$ret .= "$indent <version>$pkginfo[version]</version>\n";
}
if (!empty($pkginfo['release_date'])) {
$ret .= "$indent <date>$pkginfo[release_date]</date>\n";
}
if (!empty($pkginfo['release_license'])) {
$ret .= "$indent <license>$pkginfo[release_license]</license>\n";
}
if (!empty($pkginfo['release_state'])) {
$ret .= "$indent <state>$pkginfo[release_state]</state>\n";
}
if (!empty($pkginfo['release_notes'])) {
$ret .= "$indent <notes>".htmlspecialchars($pkginfo['release_notes'])."</notes>\n";
}
if (!empty($pkginfo['release_warnings'])) {
$ret .= "$indent <warnings>".htmlspecialchars($pkginfo['release_warnings'])."</warnings>\n";
}
if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
$ret .= "$indent <deps>\n";
foreach ($pkginfo['release_deps'] as $dep) {
$ret .= "$indent <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
if (isset($dep['version'])) {
$ret .= " version=\"$dep[version]\"";
}
if (isset($dep['optional'])) {
$ret .= " optional=\"$dep[optional]\"";
}
if (isset($dep['name'])) {
$ret .= ">$dep[name]</dep>\n";
} else {
$ret .= "/>\n";
}
}
$ret .= "$indent </deps>\n";
}
if (isset($pkginfo['configure_options'])) {
$ret .= "$indent <configureoptions>\n";
foreach ($pkginfo['configure_options'] as $c) {
$ret .= "$indent <configureoption name=\"".
htmlspecialchars($c['name']) . "\"";
if (isset($c['default'])) {
$ret .= " default=\"" . htmlspecialchars($c['default']) . "\"";
}
$ret .= " prompt=\"" . htmlspecialchars($c['prompt']) . "\"";
$ret .= "/>\n";
}
$ret .= "$indent </configureoptions>\n";
}
if (isset($pkginfo['provides'])) {
foreach ($pkginfo['provides'] as $key => $what) {
$ret .= "$indent <provides type=\"$what[type]\" ";
$ret .= "name=\"$what[name]\" ";
if (isset($what['extends'])) {
$ret .= "extends=\"$what[extends]\" ";
}
$ret .= "/>\n";
}
}
if (isset($pkginfo['filelist'])) {
$ret .= "$indent <filelist>\n";
$ret .= $this->_doFileList($indent, $pkginfo['filelist'], '/');
$ret .= "$indent </filelist>\n";
}
$ret .= "$indent </release>\n";
return $ret;
}
 
/**
* Generate the <filelist> tag
* @access private
* @return string
*/
function _doFileList($indent, $filelist, $curdir)
{
$ret = '';
foreach ($filelist as $file => $fa) {
if (isset($fa['##files'])) {
$ret .= "$indent <dir";
} else {
$ret .= "$indent <file";
}
 
if (isset($fa['role'])) {
$ret .= " role=\"$fa[role]\"";
}
if (isset($fa['baseinstalldir'])) {
$ret .= ' baseinstalldir="' .
htmlspecialchars($fa['baseinstalldir']) . '"';
}
if (isset($fa['md5sum'])) {
$ret .= " md5sum=\"$fa[md5sum]\"";
}
if (isset($fa['platform'])) {
$ret .= " platform=\"$fa[platform]\"";
}
if (!empty($fa['install-as'])) {
$ret .= ' install-as="' .
htmlspecialchars($fa['install-as']) . '"';
}
$ret .= ' name="' . htmlspecialchars($file) . '"';
if (isset($fa['##files'])) {
$ret .= ">\n";
$recurdir = $curdir;
if ($recurdir == '///') {
$recurdir = '';
}
$ret .= $this->_doFileList("$indent ", $fa['##files'], $recurdir . $file . '/');
$displaydir = $curdir;
if ($displaydir == '///' || $displaydir == '/') {
$displaydir = '';
}
$ret .= "$indent </dir> <!-- $displaydir$file -->\n";
} else {
if (empty($fa['replacements'])) {
$ret .= "/>\n";
} else {
$ret .= ">\n";
foreach ($fa['replacements'] as $r) {
$ret .= "$indent <replace";
foreach ($r as $k => $v) {
$ret .= " $k=\"" . htmlspecialchars($v) .'"';
}
$ret .= "/>\n";
}
$ret .= "$indent </file>\n";
}
}
}
return $ret;
}
}
 
?>
/trunk/bibliotheque/pear/PEAR/PackageFileManager/Perforce.php
New file
0,0 → 1,102
<?php
/*
* +------------------------------------------------------------------------+
* | PEAR :: Package File Manager :: Perforce |
* +------------------------------------------------------------------------+
* | Copyright (c) 2004 Jon Parise |
* | Email jon@php.net |
* +------------------------------------------------------------------------+
* | This source file is subject to version 3.00 of the PHP License, |
* | that is available at http://www.php.net/license/3_0.txt. |
* | If you did not receive a copy of the PHP license and are unable to |
* | obtain it through the world-wide-web, please send a note to |
* | license@php.net so we can mail you a copy immediately. |
* +------------------------------------------------------------------------+
* $Id: Perforce.php,v 1.2 2004/08/25 06:02:30 jon Exp $
*/
 
/**
* @package PEAR_PackageFileManager
*/
 
/**
* The PEAR_PackageFileManager_File class
*/
require_once 'PEAR/PackageFileManager/File.php';
 
/**
* Generate a file list from a Perforce checkout. This requires the 'p4'
* command line client, a properly-configured Perforce environment, and a
* connection to the Perforce server. Specifically, the 'p4 have' command
* is used to determine which local files are under Perforce's control.
*
* @author Jon Parise <jon@php.net>
* @package PEAR_PackageFileManager
*/
class PEAR_PackageFileManager_Perforce extends PEAR_PackageFileManager_File
{
/**
* Build a list of files based on the output of the 'p4 have' command.
*
* @param string $directory The directory in which to list the files.
*
* @return mixed An array of full filenames or a PEAR_Error value if
* $directory does not exist.
*/
function dirList($directory)
{
/* Return an error if the directory does not exist. */
if (@is_dir($directory) === false) {
return PEAR_PackageFileManager::raiseError(
PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST,
$directory);
}
 
/* List the files below $directory that are under Perforce control. */
exec("p4 have $directory/...", $output);
 
/* Strip off everything except the filename from each line of output. */
$files = preg_replace('/^.* \- /', '', $output);
 
/* If we have a list of files to include, remove all other entries. */
if ($this->ignore[0]) {
$files = array_filter($files, array($this, '_includeFilter'));
}
 
/* If we have a list of files to ignore, remove them from the array. */
if ($this->ignore[1]) {
$files = array_filter($files, array($this, '_ignoreFilter'));
}
 
return $files;
}
 
/**
* Determine whether a given file should be excluded from the file list.
*
* @param string $file The full pathname of file to check.
*
* @return bool True if the specified file should be included.
*
* @access private
*/
function _includeFilter($file)
{
return ($this->_checkIgnore(basename($file), $file, 0) === 0);
}
 
/**
* Determine whether a given file should be included (i.e., not ignored)
* from the file list.
*
* @param string $file The full pathname of file to check.
*
* @return bool True if the specified file should be included.
*
* @access private
*/
function _ignoreFilter($file)
{
return ($this->_checkIgnore(basename($file), $file, 1) !== 1);
}
}
/trunk/bibliotheque/pear/PEAR/PackageFileManager/Svn.php
New file
0,0 → 1,169
<?php
//
// +------------------------------------------------------------------------+
// | PEAR :: Package File Manager |
// +------------------------------------------------------------------------+
// | Copyright (c) 2003-2004 Gregory Beaver |
// | Email cellog@phpdoc.org |
// +------------------------------------------------------------------------+
// | This source file is subject to version 3.00 of the PHP License, |
// | that is available at http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +------------------------------------------------------------------------+
// | Portions of this code based on phpDocumentor |
// | Web http://www.phpdoc.org |
// | Mirror http://phpdocu.sourceforge.net/ |
// +------------------------------------------------------------------------+
// $Id: Svn.php,v 1.1 2004/05/31 12:02:31 arnaud Exp $
//
/**
* @package PEAR_PackageFileManager
*/
/**
* The PEAR_PackageFileManager_File class
*/
require_once 'PEAR/PackageFileManager/File.php';
 
/**
* Generate a file list from a Subversion checkout
*
* Largely based on the CVS class, modified to suit
* subversion organization.
*
* Note that this will <b>NOT</b> work on a
* repository, only on a checked out Subversion module
* @package PEAR_PackageFileManager
* @author Arnaud Limbour <arnaud@limbourg.com>
*/
class PEAR_PackageFileManager_Svn extends PEAR_PackageFileManager_File
{
/**
* Return a list of all files in the CVS repository
*
* This function is like {@link parent::dirList()} except
* that instead of retrieving a regular filelist, it first
* retrieves a listing of all the .svn/entries files in
* $directory and all of the subdirectories. Then, it
* reads the entries file, and creates a listing of files
* that are a part of the Subversion checkout. No check is
* made to see if they have been modified, but removed files
* are ignored.
*
* @access protected
* @return array list of files in a directory
* @param string $directory full path to the directory you want the list of
* @uses _recurDirList()
* @uses _readSVNEntries()
*/
function dirList($directory)
{
static $in_recursion = false;
if (!$in_recursion) {
// include only .svn/entries files
// since subversion keeps its data in a hidden
// directory we must force PackageFileManager to
// consider hidden directories.
$this->_options['addhiddenfiles'] = true;
$this->_setupIgnore(array('*/.svn/entries'), 0);
$this->_setupIgnore(array(), 1);
$in_recursion = true;
$entries = parent::dirList($directory);
$in_recursion = false;
} else {
return parent::dirList($directory);
}
if (!$entries || !is_array($entries)) {
return PEAR_PackageFileManager::raiseError(PEAR_PACKAGEFILEMANAGER_NOSVNENTRIES, $directory);
}
return $this->_readSVNEntries($entries);
}
/**
* Iterate over the SVN Entries files, and retrieve every
* file in the repository
*
* @uses _getSVNEntries()
* @param array array of full paths to .svn/entries files
* @access private
*/
function _readSVNEntries($entries)
{
$ret = array();
$ignore = $this->_options['ignore'];
// implicitly ignore packagefile
$ignore[] = $this->_options['packagefile'];
$include = $this->_options['include'];
$this->ignore = array(false, false);
$this->_setupIgnore($ignore, 1);
$this->_setupIgnore($include, 0);
foreach($entries as $cvsentry) {
$directory = @dirname(@dirname($cvsentry));
if (!$directory) {
continue;
}
$d = $this->_getSVNEntries($cvsentry);
if (!is_array($d)) {
continue;
}
foreach($d as $entry) {
if ($ignore) {
if ($this->_checkIgnore($entry,
$directory . '/' . $entry, 1)) {
continue;
}
}
if ($include) {
if ($this->_checkIgnore($entry,
$directory . '/' . $entry, 0)) {
continue;
}
}
$ret[] = $directory . '/' . $entry;
}
}
return $ret;
}
/**
* Retrieve the entries in a .svn/entries file
*
* Uses XML_Tree to parse the XML subversion file
*
* It keeps only files, excluding directories. It also
* makes sure no deleted file in included.
*
* @return array an array with full paths to files
* @uses PEAR::XML_Tree
* @param string full path to a .svn/entries file
* @access private
*/
function _getSVNEntries($svnentriesfilename)
{
require_once 'XML/Tree.php';
$parser = &new XML_Tree($svnentriesfilename);
$tree = &$parser->getTreeFromFile();
 
// loop through the xml tree and keep only valid entries being files
$entries = array();
foreach ($tree->children as $entry) {
if ($entry->name == 'entry'
&& $entry->attributes['kind'] == 'file') {
if (isset($entry->attributes['deleted'])) {
continue;
}
array_push($entries, $entry->attributes['name']);
}
}
 
unset($parser, $tree);
 
if (is_array($entries)) {
return $entries;
} else {
return false;
}
}
}
?>
/trunk/bibliotheque/pear/PEAR/PackageFileManager/Cvs.php
New file
0,0 → 1,175
<?php
//
// +------------------------------------------------------------------------+
// | PEAR :: Package File Manager |
// +------------------------------------------------------------------------+
// | Copyright (c) 2003-2004 Gregory Beaver |
// | Email cellog@phpdoc.org |
// +------------------------------------------------------------------------+
// | This source file is subject to version 3.00 of the PHP License, |
// | that is available at http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +------------------------------------------------------------------------+
// | Portions of this code based on phpDocumentor |
// | Web http://www.phpdoc.org |
// | Mirror http://phpdocu.sourceforge.net/ |
// +------------------------------------------------------------------------+
// $Id: Cvs.php,v 1.9 2005/03/25 22:37:15 cellog Exp $
//
/**
* @package PEAR_PackageFileManager
*/
/**
* The PEAR_PackageFileManager_File class
*/
require_once 'PEAR/PackageFileManager/File.php';
 
/**
* Generate a file list from a CVS checkout
*
* Note that this will <b>NOT</b> work on a
* repository, only on a checked out CVS module
* @package PEAR_PackageFileManager
*/
class PEAR_PackageFileManager_CVS extends PEAR_PackageFileManager_File {
/**
* List of CVS-specific files that may exist in CVS but should be
* ignored when building the package's file list.
* @var array
* @access private
*/
var $_cvsIgnore = array('.cvsignore');
 
/**
* Return a list of all files in the CVS repository
*
* This function is like {@link parent::dirList()} except
* that instead of retrieving a regular filelist, it first
* retrieves a listing of all the CVS/Entries files in
* $directory and all of the subdirectories. Then, it
* reads the Entries file, and creates a listing of files
* that are a part of the CVS repository. No check is
* made to see if they have been modified, but newly
* added or removed files are ignored.
* @return array list of files in a directory
* @param string $directory full path to the directory you want the list of
* @uses _recurDirList()
* @uses _readCVSEntries()
*/
function dirList($directory)
{
static $in_recursion = false;
if (!$in_recursion) {
// include only CVS/Entries files
$this->_setupIgnore(array('*/CVS/Entries'), 0);
$this->_setupIgnore(array(), 1);
$in_recursion = true;
$entries = parent::dirList($directory);
$in_recursion = false;
} else {
return parent::dirList($directory);
}
if (!$entries || !is_array($entries)) {
return PEAR_PackageFileManager::raiseError(PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES, $directory);
}
return $this->_readCVSEntries($entries);
}
/**
* Iterate over the CVS Entries files, and retrieve every
* file in the repository
* @uses _getCVSEntries()
* @uses _isCVSFile()
* @param array array of full paths to CVS/Entries files
* @access private
*/
function _readCVSEntries($entries)
{
$ret = array();
$ignore = array_merge((array) $this->_options['ignore'], $this->_cvsIgnore);
// implicitly ignore packagefile
$ignore[] = $this->_options['packagefile'];
$include = $this->_options['include'];
$this->ignore = array(false, false);
$this->_setupIgnore($ignore, 1);
$this->_setupIgnore($include, 0);
foreach($entries as $cvsentry) {
$directory = @dirname(@dirname($cvsentry));
if (!$directory) {
continue;
}
$d = $this->_getCVSEntries($cvsentry);
if (!is_array($d)) {
continue;
}
foreach($d as $entry) {
if ($ignore) {
if ($this->_checkIgnore($this->_getCVSFileName($entry),
$directory . '/' . $this->_getCVSFileName($entry), 1)) {
// print 'Ignoring '.$file."<br>\n";
continue;
}
}
if ($include) {
if ($this->_checkIgnore($this->_getCVSFileName($entry),
$directory . '/' . $this->_getCVSFileName($entry), 0)) {
// print 'Including '.$file."<br\n";
continue;
}
}
if ($this->_isCVSFile($entry)) {
$ret[] = $directory . '/' . $this->_getCVSFileName($entry);
}
}
}
return $ret;
}
/**
* Retrieve the filename from an entry
*
* This method assumes that the entry is a file,
* use _isCVSFile() to verify before calling
* @param string a line in a CVS/Entries file
* @return string the filename (no path information)
* @access private
*/
function _getCVSFileName($cvsentry)
{
$stuff = explode('/', $cvsentry);
array_shift($stuff);
return array_shift($stuff);
}
/**
* Retrieve the entries in a CVS/Entries file
* @return array each line of the entries file, output of file()
* @uses function file()
* @param string full path to a CVS/Entries file
* @access private
*/
function _getCVSEntries($cvsentryfilename)
{
$cvsfile = @file($cvsentryfilename);
if (is_array($cvsfile)) {
return $cvsfile;
} else {
return false;
}
}
/**
* Check whether an entry is a file or a directory
* @return boolean
* @param string a line in a CVS/Entries file
* @access private
*/
function _isCVSFile($cvsentry)
{
// make sure we ignore entries that have either been removed or added, but not committed yet
return $cvsentry{0} == '/' && !strpos($cvsentry, 'dummy timestamp');
}
}
?>