mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-24 02:58:43 +02:00
Initial import on SourceForge
SVN:code[2]
This commit is contained in:
383
core/MyHelpers.class.inc.php
Normal file
383
core/MyHelpers.class.inc.php
Normal file
@@ -0,0 +1,383 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MyHelpers
|
||||
* various dev/debug helpers, to cleanup or at least re-organize
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
class MyHelpers
|
||||
{
|
||||
public static function CheckValueInArray($sDescription, $value, $aData)
|
||||
{
|
||||
if (!in_array($value, $aData))
|
||||
{
|
||||
self::HandleWrongValue($sDescription, $value, $aData);
|
||||
}
|
||||
}
|
||||
|
||||
public static function CheckKeyInArray($sDescription, $key, $aData)
|
||||
{
|
||||
if (!array_key_exists($key, $aData))
|
||||
{
|
||||
self::HandleWrongValue($sDescription, $key, array_keys($aData));
|
||||
}
|
||||
}
|
||||
|
||||
public static function HandleWrongValue($sDescription, $value, $aData)
|
||||
{
|
||||
if (count($aData) == 0)
|
||||
{
|
||||
$sArrayDesc = "{}";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sArrayDesc = "{".implode(", ", $aData)."}";
|
||||
}
|
||||
// exit!
|
||||
trigger_error("Wrong value for $sDescription, found '$value' while expecting a value in $sArrayDesc", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// getmicrotime()
|
||||
// format sss.mmmuuupppnnn
|
||||
public static function getmicrotime()
|
||||
{
|
||||
list($usec, $sec) = explode(" ",microtime());
|
||||
return ((float)$usec + (float)$sec);
|
||||
}
|
||||
|
||||
/*
|
||||
* MakeSQLComment
|
||||
* converts hash into text comment which we can use in a (mySQL) query
|
||||
*/
|
||||
public static function MakeSQLComment ($aHash)
|
||||
{
|
||||
if (empty($aHash)) return "";
|
||||
$sComment = "";
|
||||
{
|
||||
foreach($aHash as $sKey=>$sValue)
|
||||
{
|
||||
$sComment .= "\n-- ". $sKey ."=>" . $sValue;
|
||||
}
|
||||
}
|
||||
return $sComment;
|
||||
}
|
||||
|
||||
public static function var_dump_html($aWords, $bFullDisplay = false)
|
||||
{
|
||||
echo "<pre>\n";
|
||||
if ($bFullDisplay)
|
||||
{
|
||||
print_r($aWords); // full dump!
|
||||
}
|
||||
else
|
||||
{
|
||||
var_dump($aWords); // truncate things when they are too big
|
||||
}
|
||||
echo "\n</pre>\n";
|
||||
}
|
||||
|
||||
public static function arg_dump_html()
|
||||
{
|
||||
echo "<pre>\n";
|
||||
echo "GET:\n";
|
||||
var_dump($_GET);
|
||||
echo "POST:\n";
|
||||
var_dump($_POST);
|
||||
echo "\n</pre>\n";
|
||||
}
|
||||
|
||||
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
{
|
||||
if ($aCallStack == null) $aCallStack = debug_backtrace();
|
||||
|
||||
$aCallStack = array_slice($aCallStack, $iLevelsToIgnore);
|
||||
|
||||
$aDigestCallStack = array();
|
||||
$bFirstLine = true;
|
||||
foreach ($aCallStack as $aCallInfo)
|
||||
{
|
||||
$sLine = empty($aCallInfo['line']) ? "" : $aCallInfo['line'];
|
||||
$sFile = empty($aCallInfo['file']) ? "" : $aCallInfo['file'];
|
||||
$sClass = empty($aCallInfo['class']) ? "" : $aCallInfo['class'];
|
||||
$sType = empty($aCallInfo['type']) ? "" : $aCallInfo['type'];
|
||||
$sFunction = empty($aCallInfo['function']) ? "" : $aCallInfo['function'];
|
||||
|
||||
if ($bFirstLine)
|
||||
{
|
||||
$bFirstLine = false;
|
||||
// For this line do not display the "function name" because
|
||||
// that will be the name of our error handler for sure !
|
||||
$sFunctionInfo = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
$args = '';
|
||||
if (empty($aCallInfo['args'])) $aCallInfo['args'] = array();
|
||||
foreach ($aCallInfo['args'] as $a)
|
||||
{
|
||||
if (!empty($args))
|
||||
{
|
||||
$args .= ', ';
|
||||
}
|
||||
switch (gettype($a))
|
||||
{
|
||||
case 'integer':
|
||||
case 'double':
|
||||
$args .= $a;
|
||||
break;
|
||||
case 'string':
|
||||
$a = Str::pure2html(self::beautifulstr($a, 1024, true, true));
|
||||
$args .= "\"$a\"";
|
||||
break;
|
||||
case 'array':
|
||||
$args .= 'Array('.count($a).')';
|
||||
break;
|
||||
case 'object':
|
||||
$args .= 'Object('.get_class($a).')';
|
||||
break;
|
||||
case 'resource':
|
||||
$args .= 'Resource('.strstr($a, '#').')';
|
||||
break;
|
||||
case 'boolean':
|
||||
$args .= $a ? 'True' : 'False';
|
||||
break;
|
||||
case 'NULL':
|
||||
$args .= 'Null';
|
||||
break;
|
||||
default:
|
||||
$args .= 'Unknown';
|
||||
}
|
||||
}
|
||||
$sFunctionInfo = "$sClass $sType $sFunction($args)";
|
||||
}
|
||||
$aDigestCallStack[] = array('File'=>$sFile, 'Line'=>$sLine, 'Function'=>$sFunctionInfo);
|
||||
}
|
||||
return self::make_table_from_assoc_array($aDigestCallStack);
|
||||
}
|
||||
|
||||
public static function dump_callstack($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
{
|
||||
return self::get_callstack_html($iLevelsToIgnore, $aCallStack);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Source: New
|
||||
// Last modif: 2004/12/20 RQU
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
public static function make_table_from_assoc_array(&$aData)
|
||||
{
|
||||
if (!is_array($aData)) trigger_error("make_table_from_assoc_array: Error - the passed argument is not an array", E_USER_ERROR);
|
||||
$aFirstRow = reset($aData);
|
||||
if (!is_array($aFirstRow)) trigger_error("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array", E_USER_ERROR);
|
||||
$sOutput = "";
|
||||
$sOutput .= "<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"1\" CELLPADDING=\"1\">\n";
|
||||
|
||||
// Table header
|
||||
//
|
||||
$sOutput .= " <TR CLASS=celltitle>\n";
|
||||
foreach ($aFirstRow as $fieldname=>$trash) {
|
||||
$sOutput .= " <TD><B>".$fieldname."</B></TD>\n";
|
||||
}
|
||||
$sOutput .= " </TR>\n";
|
||||
|
||||
// Table contents
|
||||
//
|
||||
$iCount = 0;
|
||||
foreach ($aData as $aRow) {
|
||||
$sStyle = ($iCount++ % 2 ? "STYLE=\"background-color : #eeeeee\"" : "");
|
||||
$sOutput .= " <TR $sStyle CLASS=cell>\n";
|
||||
foreach ($aRow as $data) {
|
||||
if (strlen($data) == 0) {
|
||||
$data = " ";
|
||||
}
|
||||
$sOutput .= " <TD>".$data."</TD>\n";
|
||||
}
|
||||
$sOutput .= " </TR>\n";
|
||||
}
|
||||
|
||||
$sOutput .= "</TABLE>\n";
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
public static function debug_breakpoint($arg)
|
||||
{
|
||||
echo "<H1> Debug breakpoint </H1>\n";
|
||||
MyHelpers::var_dump_html($arg);
|
||||
MyHelpers::dump_callstack();
|
||||
exit;
|
||||
}
|
||||
public static function debug_breakpoint_notempty($arg)
|
||||
{
|
||||
if (empty($arg)) return;
|
||||
echo "<H1> Debug breakpoint (triggered on non-empty value) </H1>\n";
|
||||
MyHelpers::var_dump_html($arg);
|
||||
MyHelpers::dump_callstack();
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8... converts non ASCII chars into '?'
|
||||
* Decided after some complex investigations, to have the tools work fine (Oracle+Perl vs mySQL+PHP...)
|
||||
*/
|
||||
public static function utf8($strText)
|
||||
{
|
||||
return iconv("WINDOWS-1252", "ASCII//TRANSLIT", $strText);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlentities()
|
||||
* ... same as htmlentities, but designed for xml !
|
||||
*/
|
||||
public static function xmlentities($string)
|
||||
{
|
||||
return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&' , '"', ''' , '<' , '>' ), $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlencode()
|
||||
* Encodes a string so that for sure it can be output as an xml data string
|
||||
*/
|
||||
public static function xmlencode($string)
|
||||
{
|
||||
return xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Source: New - format strings for output
|
||||
// Last modif: 2005/01/18 RQU
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
public static function beautifulstr($sLongString, $iMaxLen, $bShowLen=false, $bShowTooltip=true)
|
||||
{
|
||||
if (!is_string($sLongString)) trigger_error("beautifulstr: expect a string as 1st argument", E_USER_ERROR);
|
||||
|
||||
// Nothing to do if the string is short
|
||||
if (strlen($sLongString) <= $iMaxLen) return $sLongString;
|
||||
|
||||
// Truncate the string
|
||||
$sSuffix = "...";
|
||||
if ($bShowLen) {
|
||||
$sSuffix .= "(".strlen($sLongString)." chars)...";
|
||||
}
|
||||
$sOutput = substr($sLongString, 0, $iMaxLen - strlen($sSuffix)).$sSuffix;
|
||||
$sOutput = htmlspecialchars($sOutput);
|
||||
|
||||
// Add tooltip if required
|
||||
//if ($bShowTooltip) {
|
||||
// $oTooltip = new gui_tooltip($sLongString);
|
||||
// $sOutput = "<SPAN ".$oTooltip->get_mouseOver_code().">".$sOutput."</SPAN>";
|
||||
//}
|
||||
return $sOutput;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Utility class: static methods for cleaning & escaping untrusted (i.e.
|
||||
user-supplied) strings.
|
||||
Any string can (usually) be thought of as being in one of these 'modes':
|
||||
pure = what the user actually typed / what you want to see on the page /
|
||||
what is actually stored in the DB
|
||||
gpc = incoming GET, POST or COOKIE data
|
||||
sql = escaped for passing safely to RDBMS via SQL (also, data from DB
|
||||
queries and file reads if you have magic_quotes_runtime on--which
|
||||
is rare)
|
||||
html = safe for html display (htmlentities applied)
|
||||
Always knowing what mode your string is in--using these methods to
|
||||
convert between modes--will prevent SQL injection and cross-site scripting.
|
||||
This class refers to its own namespace (so it can work in PHP 4--there is no
|
||||
self keyword until PHP 5). Do not change the name of the class w/o changing
|
||||
all the internal references.
|
||||
Example usage: a POST value that you want to query with:
|
||||
$username = Str::gpc2sql($_POST['username']);
|
||||
*/
|
||||
//This sets SQL escaping to use slashes; for Sybase(/MSSQL)-style escaping
|
||||
// ( ' --> '' ), set to true.
|
||||
define('STR_SYBASE', false);
|
||||
class Str
|
||||
{
|
||||
public static function gpc2sql($gpc, $maxLength = false)
|
||||
{
|
||||
return self::pure2sql(self::gpc2pure($gpc), $maxLength);
|
||||
}
|
||||
public static function gpc2html($gpc, $maxLength = false)
|
||||
{
|
||||
return self::pure2html(self::gpc2pure($gpc), $maxLength);
|
||||
}
|
||||
public static function gpc2pure($gpc)
|
||||
{
|
||||
if (ini_get('magic_quotes_sybase')) $pure = str_replace("''", "'", $gpc);
|
||||
else $pure = get_magic_quotes_gpc() ? stripslashes($gpc) : $gpc;
|
||||
return $pure;
|
||||
}
|
||||
public static function html2pure($html)
|
||||
{
|
||||
return html_entity_decode($html);
|
||||
}
|
||||
public static function html2sql($html, $maxLength = false)
|
||||
{
|
||||
return self::pure2sql(self::html2pure($html), $maxLength);
|
||||
}
|
||||
public static function pure2html($pure, $maxLength = false)
|
||||
{
|
||||
return $maxLength
|
||||
? htmlentities(substr($pure, 0, $maxLength))
|
||||
: htmlentities($pure);
|
||||
}
|
||||
public static function pure2sql($pure, $maxLength = false)
|
||||
{
|
||||
if ($maxLength) $pure = substr($pure, 0, $maxLength);
|
||||
return (STR_SYBASE)
|
||||
? str_replace("'", "''", $pure)
|
||||
: addslashes($pure);
|
||||
}
|
||||
public static function sql2html($sql, $maxLength = false)
|
||||
{
|
||||
$pure = self::sql2pure($sql);
|
||||
if ($maxLength) $pure = substr($pure, 0, $maxLength);
|
||||
return self::pure2html($pure);
|
||||
}
|
||||
public static function sql2pure($sql)
|
||||
{
|
||||
return (STR_SYBASE)
|
||||
? str_replace("''", "'", $sql)
|
||||
: stripslashes($sql);
|
||||
}
|
||||
|
||||
public static function xml2pure($xml)
|
||||
{
|
||||
// #@# - not implemented
|
||||
return $xml;
|
||||
}
|
||||
public static function pure2xml($pure)
|
||||
{
|
||||
return self::xmlencode($pure);
|
||||
}
|
||||
|
||||
protected static function xmlentities($string)
|
||||
{
|
||||
return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&' , '"', ''' , '<' , '>' ), $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlencode()
|
||||
* Encodes a string so that for sure it can be output as an xml data string
|
||||
*/
|
||||
protected static function xmlencode($string)
|
||||
{
|
||||
return self::xmlentities(iconv("ISO-8859-1", "UTF-8//IGNORE",$string));
|
||||
}
|
||||
|
||||
public static function islowcase($sString)
|
||||
{
|
||||
return (strtolower($sString) == $sString);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
323
core/archive.class.inc.php
Normal file
323
core/archive.class.inc.php
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* archive.class.inc.php
|
||||
* Utility to import/export the DB from/to a ZIP file
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
/**
|
||||
* iTopArchive a class to manipulate (read/write) iTop archives with their catalog
|
||||
* Each iTop archive is a zip file that contains (at the root of the archive)
|
||||
* a file called catalog.xml holding the description of the archive
|
||||
*/
|
||||
class iTopArchive
|
||||
{
|
||||
const read = 0;
|
||||
const create = ZipArchive::CREATE;
|
||||
|
||||
protected $m_sZipPath;
|
||||
protected $m_oZip;
|
||||
protected $m_sVersion;
|
||||
protected $m_sTitle;
|
||||
protected $m_sDescription;
|
||||
protected $m_aPackages;
|
||||
protected $m_aErrorMessages;
|
||||
|
||||
/**
|
||||
* Construct an iTopArchive object
|
||||
* @param $sArchivePath string The full path the archive file
|
||||
* @param $iMode integrer Either iTopArchive::read for reading an existing archive or iTopArchive::create for creating a new one. Updating is not supported (yet)
|
||||
*/
|
||||
public function __construct($sArchivePath, $iMode = iTopArchive::read)
|
||||
{
|
||||
$this->m_sZipPath = $sArchivePath;
|
||||
$this->m_oZip = new ZipArchive();
|
||||
$this->m_oZip->open($this->m_sZipPath, $iMode);
|
||||
$this->m_aErrorMessages = array();
|
||||
$this->m_sVersion = '1.0';
|
||||
$this->m_sTitle = '';
|
||||
$this->m_sDescription = '';
|
||||
$this->m_aPackages = array();
|
||||
}
|
||||
|
||||
public function SetTitle($sTitle)
|
||||
{
|
||||
$this->m_sTitle = $sTitle;
|
||||
}
|
||||
|
||||
public function SetDescription($sDescription)
|
||||
{
|
||||
$this->m_sDescription = $sDescription;
|
||||
}
|
||||
|
||||
public function GetTitle()
|
||||
{
|
||||
return $this->m_sTitle;
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return $this->m_sDescription;
|
||||
}
|
||||
|
||||
public function GetPackages()
|
||||
{
|
||||
return $this->m_aPackages;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->m_oZip->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error message explaining the latest error encountered
|
||||
* @return array All the error messages encountered during the validation
|
||||
*/
|
||||
public function GetErrors()
|
||||
{
|
||||
return $this->m_aErrorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the catalog from the archive (zip) file
|
||||
* @param sPath string Path the the zip file
|
||||
* @return boolean True in case of success, false otherwise
|
||||
*/
|
||||
public function ReadCatalog()
|
||||
{
|
||||
if ($this->IsValid())
|
||||
{
|
||||
$sXmlCatalog = $this->m_oZip->getFromName('catalog.xml');
|
||||
$oParser = xml_parser_create();
|
||||
xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes);
|
||||
xml_parser_free($oParser);
|
||||
|
||||
$iIndex = $aIndexes['ARCHIVE'][0];
|
||||
$this->m_sVersion = $aValues[$iIndex]['attributes']['VERSION'];
|
||||
$iIndex = $aIndexes['TITLE'][0];
|
||||
$this->m_sTitle = $aValues[$iIndex]['value'];
|
||||
$iIndex = $aIndexes['DESCRIPTION'][0];
|
||||
if (array_key_exists('value', $aValues[$iIndex]))
|
||||
{
|
||||
// #@# implement a get_array_value(array, key, default) ?
|
||||
$this->m_sDescription = $aValues[$iIndex]['value'];
|
||||
}
|
||||
|
||||
foreach($aIndexes['PACKAGE'] as $iIndex)
|
||||
{
|
||||
$this->m_aPackages[$aValues[$iIndex]['attributes']['HREF']] = array( 'type' => $aValues[$iIndex]['attributes']['TYPE'], 'title'=> $aValues[$iIndex]['attributes']['TITLE'], 'description' => $aValues[$iIndex]['value']);
|
||||
}
|
||||
|
||||
//echo "Archive path: {$this->m_sZipPath}<br/>\n";
|
||||
//echo "Archive format version: {$this->m_sVersion}<br/>\n";
|
||||
//echo "Title: {$this->m_sTitle}<br/>\n";
|
||||
//echo "Description: {$this->m_sDescription}<br/>\n";
|
||||
//foreach($this->m_aPackages as $aFile)
|
||||
//{
|
||||
// echo "{$aFile['title']} ({$aFile['type']}): {$aFile['description']}<br/>\n";
|
||||
//}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function WriteCatalog()
|
||||
{
|
||||
$sXml = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?".">\n"; // split the XML closing tag that disturbs PSPad's syntax coloring
|
||||
$sXml .= "<archive version=\"1.0\">\n";
|
||||
$sXml .= "<title>{$this->m_sTitle}</title>\n";
|
||||
$sXml .= "<description>{$this->m_sDescription}</description>\n";
|
||||
foreach($this->m_aPackages as $sFileName => $aFile)
|
||||
{
|
||||
$sXml .= "<package title=\"{$aFile['title']}\" type=\"{$aFile['type']}\" href=\"$sFileName\">{$aFile['description']}</package>\n";
|
||||
}
|
||||
$sXml .= "</archive>";
|
||||
$this->m_oZip->addFromString('catalog.xml', $sXml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a package to the archive
|
||||
* @param string $sExternalFilePath The path to the file to be added to the archive as a package (directories are not yet implemented)
|
||||
* @param string $sFilePath The name of the file inside the archive
|
||||
* @param string $sTitle A short title for this package
|
||||
* @param string $sType Type of the package. SQL scripts must be of type 'text/sql'
|
||||
* @param string $sDescription A longer description of the purpose of this package
|
||||
* @return none
|
||||
*/
|
||||
public function AddPackage($sExternalFilePath, $sFilePath, $sTitle, $sType, $sDescription)
|
||||
{
|
||||
$this->m_aPackages[$sFilePath] = array('title' => $sTitle, 'type' => $sType, 'description' => $sDescription);
|
||||
$this->m_oZip->addFile($sExternalFilePath, $sFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of the given file from the archive
|
||||
* @param string $sFileName The path to the file inside the archive
|
||||
* @return string The content of the file read from the archive
|
||||
*/
|
||||
public function GetFileContents($sFileName)
|
||||
{
|
||||
return $this->m_oZip->getFromName($sFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the contents of the given file from the archive
|
||||
* @param string $sFileName The path to the file inside the archive
|
||||
* @param string $sDestinationFileName The path of the file to write
|
||||
* @return none
|
||||
*/
|
||||
public function ExtractToFile($sFileName, $sDestinationFileName)
|
||||
{
|
||||
$iBufferSize = 64 * 1024; // Read 64K at a time
|
||||
$oZipStream = $this->m_oZip->getStream($sFileName);
|
||||
$oDestinationStream = fopen($sDestinationFileName, 'wb');
|
||||
while (!feof($oZipStream)) {
|
||||
$sContents = fread($oZipStream, $iBufferSize);
|
||||
fwrite($oDestinationStream, $sContents);
|
||||
}
|
||||
fclose($oZipStream);
|
||||
fclose($oDestinationStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a SQL script taken from the archive. The package must be listed in the catalog and of type text/sql
|
||||
* @param string $sFileName The path to the SQL package inside the archive
|
||||
* @return boolean false in case of error, true otherwise
|
||||
*/
|
||||
public function ImportSql($sFileName, $sDatabase = 'itop')
|
||||
{
|
||||
if ( ($this->m_oZip->locateName($sFileName) == false) || (!isset($this->m_aPackages[$sFileName])) || ($this->m_aPackages[$sFileName]['type'] != 'text/sql'))
|
||||
{
|
||||
// invalid type or not listed in the catalog
|
||||
return false;
|
||||
}
|
||||
$sTempName = tempnam("../tmp/", "sql");
|
||||
//echo "Extracting to: '$sTempName'<br/>\n";
|
||||
$this->ExtractToFile($sFileName, $sTempName);
|
||||
// Note: the command line below works on Windows with the right path to mysql !!!
|
||||
$sCommandLine = 'type "'.$sTempName.'" | "/iTop/MySQL Server 5.0/bin/mysql.exe" -u root '.$sDatabase;
|
||||
//echo "Executing: '$sCommandLine'<br/>\n";
|
||||
exec($sCommandLine, $aOutput, $iRet);
|
||||
//echo "Return code: $iRet<br/>\n";
|
||||
//echo "Output:<br/><pre>\n";
|
||||
//print_r($aOutput);
|
||||
//echo "</pre><br/>\n";
|
||||
unlink($sTempName);
|
||||
return ($iRet == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps some part of the specified MySQL database into the archive as a text/sql package
|
||||
* @param $sTitle string A short title for this SQL script
|
||||
* @param $sDescription string A longer description of the purpose of this SQL script
|
||||
* @param $sFileName string The name of the package inside the archive
|
||||
* @param $sDatabase string name of the database
|
||||
* @param $aTables array array or table names. If empty, all tables are dumped
|
||||
* @param $bStructureOnly boolean Whether or not to dump the data or just the schema
|
||||
* @return boolean False in case of error, true otherwise
|
||||
*/
|
||||
public function AddDatabaseDump($sTitle, $sDescription, $sFileName, $sDatabase = 'itop', $aTables = array(), $bStructureOnly = true)
|
||||
{
|
||||
$sTempName = tempnam("../tmp/", "sql");
|
||||
$sNoData = $bStructureOnly ? "--no-data" : "";
|
||||
$sCommandLine = "\"/iTop/MySQL Server 5.0/bin/mysqldump.exe\" --user=root --opt $sNoData --result-file=$sTempName $sDatabase ".implode(" ", $aTables);
|
||||
//echo "Executing command: '$sCommandLine'<br/>\n";
|
||||
exec($sCommandLine, $aOutput, $iRet);
|
||||
//echo "Return code: $iRet<br/>\n";
|
||||
//echo "Output:<br/><pre>\n";
|
||||
//print_r($aOutput);
|
||||
//echo "</pre><br/>\n";
|
||||
if ($iRet == 0)
|
||||
{
|
||||
$this->AddPackage($sTempName, $sFileName, $sTitle, 'text/sql', $sDescription);
|
||||
}
|
||||
//unlink($sTempName);
|
||||
return ($iRet == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the consistency of the archive
|
||||
* @return boolean True if the archive file is consistent
|
||||
*/
|
||||
public function IsValid()
|
||||
{
|
||||
// TO DO: use a DTD to validate the XML instead of this hand-made validation
|
||||
$bResult = true;
|
||||
$aMandatoryTags = array('ARCHIVE' => array('VERSION'),
|
||||
'TITLE' => array(),
|
||||
'DESCRIPTION' => array(),
|
||||
'PACKAGE' => array('TYPE', 'HREF', 'TITLE'));
|
||||
|
||||
$sXmlCatalog = $this->m_oZip->getFromName('catalog.xml');
|
||||
$oParser = xml_parser_create();
|
||||
xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes);
|
||||
xml_parser_free($oParser);
|
||||
|
||||
foreach($aMandatoryTags as $sTag => $aAttributes)
|
||||
{
|
||||
// Check that all the required tags are present
|
||||
if (!isset($aIndexes[$sTag]))
|
||||
{
|
||||
$this->m_aErrorMessages[] = "The XML catalog does not contain the mandatory tag $sTag.";
|
||||
$bResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($aIndexes[$sTag] as $iIndex)
|
||||
{
|
||||
switch($aValues[$iIndex]['type'])
|
||||
{
|
||||
case 'complete':
|
||||
case 'open':
|
||||
// Check that all the required attributes are present
|
||||
foreach($aAttributes as $sAttribute)
|
||||
{
|
||||
if (!isset($aValues[$iIndex]['attributes'][$sAttribute]))
|
||||
{
|
||||
$this->m_aErrorMessages[] = "The tag $sTag ($iIndex) does not contain the required attribute $sAttribute.";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// ignore other type of tags: close or cdata
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
}
|
||||
/*
|
||||
// Unit test - reading an archive
|
||||
$sArchivePath = '../tmp/archive.zip';
|
||||
$oArchive = new iTopArchive($sArchivePath, iTopArchive::read);
|
||||
$oArchive->ReadCatalog();
|
||||
$oArchive->ImportSql('full_backup.sql');
|
||||
|
||||
// Writing an archive --
|
||||
|
||||
$sArchivePath = '../tmp/archive2.zip';
|
||||
$oArchive = new iTopArchive($sArchivePath, iTopArchive::create);
|
||||
$oArchive->SetTitle('First Archive !');
|
||||
$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.');
|
||||
$oArchive->AddPackage('../tmp/schema.sql', 'test.sql', 'this is just a test', 'text/sql', 'My first attempt at creating an archive from PHP...');
|
||||
$oArchive->WriteCatalog();
|
||||
|
||||
|
||||
$sArchivePath = '../tmp/archive2.zip';
|
||||
$oArchive = new iTopArchive($sArchivePath, iTopArchive::create);
|
||||
$oArchive->SetTitle('First Archive !');
|
||||
$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.');
|
||||
$oArchive->AddDatabaseDump('Test', 'This is my first automatic dump', 'schema.sql', 'itop', array('objects'));
|
||||
$oArchive->WriteCatalog();
|
||||
*/
|
||||
?>
|
||||
989
core/attributedef.class.inc.php
Normal file
989
core/attributedef.class.inc.php
Normal file
@@ -0,0 +1,989 @@
|
||||
<?php
|
||||
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* add some description here...
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
define('EXTKEY_RELATIVE', 1);
|
||||
|
||||
/**
|
||||
* add some description here...
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
define('EXTKEY_ABSOLUTE', 2);
|
||||
|
||||
|
||||
/**
|
||||
* Attribute definition API, implemented in and many flavours (Int, String, Enum, etc.)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
abstract class AttributeDefinition
|
||||
{
|
||||
abstract public function GetType();
|
||||
abstract public function GetTypeDesc();
|
||||
abstract public function GetEditClass();
|
||||
abstract public function GetDBFieldType();
|
||||
|
||||
protected $m_sCode;
|
||||
private $m_aParams = array();
|
||||
private $m_sHostClass = array();
|
||||
protected function Get($sParamName) {return $this->m_aParams[$sParamName];}
|
||||
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
$this->m_sCode = $sCode;
|
||||
$this->m_aParams = $aParams;
|
||||
$this->ConsistencyCheck();
|
||||
}
|
||||
public function OverloadParams($aParams)
|
||||
{
|
||||
foreach ($aParams as $sParam => $value)
|
||||
{
|
||||
if (!array_key_exists($sParam, $this->m_aParams))
|
||||
{
|
||||
trigger_error("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aParams[$sParam] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function SetHostClass($sHostClass)
|
||||
{
|
||||
$this->m_sHostClass = $sHostClass;
|
||||
}
|
||||
public function GetHostClass()
|
||||
{
|
||||
return $this->m_sHostClass;
|
||||
}
|
||||
|
||||
// Note: I could factorize this code with the parameter management made for the AttributeDef class
|
||||
// to be overloaded
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array("label", "description", "allowed_values");
|
||||
}
|
||||
|
||||
private function ConsistencyCheck()
|
||||
{
|
||||
|
||||
// Check that any mandatory param has been specified
|
||||
//
|
||||
$aExpectedParams = $this->ListExpectedParams();
|
||||
foreach($aExpectedParams as $sParamName)
|
||||
{
|
||||
if (!array_key_exists($sParamName, $this->m_aParams))
|
||||
{
|
||||
$aBacktrace = debug_backtrace();
|
||||
$sTargetClass = $aBacktrace[2]["class"];
|
||||
$sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"];
|
||||
trigger_error("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)</br>\n", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// table, key field, name field
|
||||
public function ListDBJoins()
|
||||
{
|
||||
return "";
|
||||
// e.g: return array("Site", "infrid", "name");
|
||||
}
|
||||
public function IsDirectField() {return false;}
|
||||
public function IsScalar() {return false;}
|
||||
public function IsLinkSet() {return false;}
|
||||
public function IsExternalKey($iType = EXTKEY_RELATIVE) {return false;}
|
||||
public function IsExternalField() {return false;}
|
||||
public function IsWritable() {return false;}
|
||||
public function GetCode() {return $this->m_sCode;}
|
||||
public function GetLabel() {return $this->Get("label");}
|
||||
public function GetDescription() {return $this->Get("description");}
|
||||
public function GetValuesDef() {return $this->Get("allowed_values");}
|
||||
public function GetPrerequisiteAttributes() {return $this->Get("depends_on");}
|
||||
//public function IsSearchableStd() {return $this->Get("search_std");}
|
||||
//public function IsSearchableGlobal() {return $this->Get("search_global");}
|
||||
//public function IsMandatory() {return $this->Get("is_mandatory");}
|
||||
//public function GetMinVal() {return $this->Get("min");}
|
||||
//public function GetMaxVal() {return $this->Get("max");}
|
||||
//public function GetSize() {return $this->Get("size");}
|
||||
//public function GetCheckRegExp() {return $this->Get("regexp");}
|
||||
//public function GetCheckFunc() {return $this->Get("checkfunc");}
|
||||
|
||||
// Definition: real value is what will be stored in memory and maintained by MetaModel
|
||||
// DBObject::Set() relies on MakeRealValue()
|
||||
// MetaModel::MakeQuery() relies on RealValueToSQLValue()
|
||||
// DBObject::FromRow() relies on SQLToRealValue()
|
||||
public function MakeRealValue($proposedValue) {return $proposedValue;} // force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!)
|
||||
public function RealValueToSQLValue($value) {return $value;} // format value as a valuable SQL literal (quoted outside)
|
||||
public function SQLValueToRealValue($value) {return $value;} // take the result of a fetch... and make it a PHP variable
|
||||
|
||||
public function GetJSCheckFunc()
|
||||
{
|
||||
$sRegExp = $this->Get("regexp");
|
||||
if (empty($sRegExp)) return 'return true;';
|
||||
|
||||
return "return regexp('$sRegExp', myvalue);";
|
||||
}
|
||||
public function CheckValue($value)
|
||||
{
|
||||
$sRegExp = $this->Get("regexp");
|
||||
if (empty($sRegExp)) return true;
|
||||
|
||||
return preg_match(preg_escape($this->Get("regexp")), $value);
|
||||
}
|
||||
|
||||
public function MakeValue()
|
||||
{
|
||||
$sComputeFunc = $this->Get("compute_func");
|
||||
if (empty($sComputeFunc)) return null;
|
||||
|
||||
return call_user_func($sComputeFunc);
|
||||
}
|
||||
|
||||
abstract public function DBGetUsedFields();
|
||||
abstract public function GetDefaultValue();
|
||||
|
||||
//
|
||||
// To be overloaded in subclasses
|
||||
//
|
||||
|
||||
abstract public function GetBasicFilterOperators(); // returns an array of "opCode"=>"description"
|
||||
abstract public function GetBasicFilterLooseOperator(); // returns an "opCode"
|
||||
//abstract protected GetBasicFilterHTMLInput();
|
||||
abstract public function GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
|
||||
public function GetAsHTML($sValue)
|
||||
{
|
||||
return Str::pure2html($sValue);
|
||||
}
|
||||
|
||||
public function GetAsXML($sValue)
|
||||
{
|
||||
return Str::pure2xml($sValue);
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',')
|
||||
{
|
||||
return str_replace($sSeparator, $sSepEscape, $sValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of objects directly linked to an object, and being part of its definition
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeLinkedSet extends AttributeDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("depends_on", "linked_class", "ext_key_to_me", "count_min", "count_max"));
|
||||
}
|
||||
|
||||
public function GetType() {return "Array of objects";}
|
||||
public function GetTypeDesc() {return "Any kind of objects [subclass] of the same class";}
|
||||
public function GetEditClass() {return "List";}
|
||||
public function GetDBFieldType() {return "N/A";} // should be moved out of the AttributeDef root class
|
||||
|
||||
public function IsWritable() {return true;}
|
||||
public function IsLinkSet() {return true;}
|
||||
|
||||
public function GetDefaultValue() {return DBObjectSet::FromScratch($this->Get('linked_class'));}
|
||||
|
||||
public function GetLinkedClass() {return $this->Get('linked_class');}
|
||||
public function GetExtKeyToMe() {return $this->Get('ext_key_to_me');}
|
||||
|
||||
public function DBGetUsedFields() {return array();}
|
||||
public function GetBasicFilterOperators() {return array();}
|
||||
public function GetBasicFilterLooseOperator() {return '';}
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value) {return '';}
|
||||
|
||||
public function GetAsHTML($sValue)
|
||||
{
|
||||
return "ERROR: LIST OF OBJECTS";
|
||||
}
|
||||
|
||||
public function GetAsXML($sValue)
|
||||
{
|
||||
return "ERROR: LIST OF OBJECTS";
|
||||
}
|
||||
|
||||
public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',')
|
||||
{
|
||||
return "ERROR: LIST OF OBJECTS";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of objects linked to an object (n-n), and being part of its definition
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeLinkedSetIndirect extends AttributeLinkedSet
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("ext_key_to_remote"));
|
||||
}
|
||||
public function GetExtKeyToRemote() { return $this->Get('ext_key_to_remote'); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class implementing default filters for a DB column
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeDBFieldVoid extends AttributeDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("depends_on", "sql"));
|
||||
}
|
||||
|
||||
public function GetType() {return "Void";}
|
||||
public function GetTypeDesc() {return "Any kind of value, from the DB";}
|
||||
public function GetEditClass() {return "String";}
|
||||
public function GetDBFieldType() {return "VARCHAR(255)";}
|
||||
|
||||
public function IsDirectField() {return true;}
|
||||
public function IsScalar() {return true;}
|
||||
public function IsWritable() {return true;}
|
||||
public function GetSQLExpr() {return $this->Get("sql");}
|
||||
public function GetDefaultValue() {return "";}
|
||||
public function IsNullAllowed() {return false;}
|
||||
public function DBGetUsedFields()
|
||||
{
|
||||
// #@# bugge a mort... a suivre...
|
||||
return array($this->Get("sql"));
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array("="=>"equals", "!="=>"differs from");
|
||||
}
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return "=";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode)
|
||||
{
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." != $sQValue";
|
||||
break;
|
||||
case '=':
|
||||
default:
|
||||
return $this->GetSQLExpr()." = $sQValue";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for all kind of DB attributes, with the exception of external keys
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeDBField extends AttributeDBFieldVoid
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("default_value", "is_null_allowed"));
|
||||
}
|
||||
public function GetDefaultValue() {return $this->Get("default_value");}
|
||||
public function IsNullAllowed() {return strtolower($this->Get("is_null_allowed"));}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an integer column to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeInteger extends AttributeDBField
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetType() {return "Integer";}
|
||||
public function GetTypeDesc() {return "Numeric value (could be negative)";}
|
||||
public function GetEditClass() {return "String";}
|
||||
public function GetDBFieldType() {return "INT";}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array(
|
||||
"!="=>"differs from",
|
||||
"="=>"equals",
|
||||
">"=>"greater (strict) than",
|
||||
">="=>"greater than",
|
||||
"<"=>"less (strict) than",
|
||||
"<="=>"less than",
|
||||
"in"=>"in"
|
||||
);
|
||||
}
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
// Unless we implement an "equals approximately..." or "same order of magnitude"
|
||||
return "=";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode)
|
||||
{
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." != $sQValue";
|
||||
break;
|
||||
case '>':
|
||||
return $this->GetSQLExpr()." > $sQValue";
|
||||
break;
|
||||
case '>=':
|
||||
return $this->GetSQLExpr()." >= $sQValue";
|
||||
break;
|
||||
case '<':
|
||||
return $this->GetSQLExpr()." < $sQValue";
|
||||
break;
|
||||
case '<=':
|
||||
return $this->GetSQLExpr()." <= $sQValue";
|
||||
break;
|
||||
case 'in':
|
||||
if (!is_array($value)) trigger_error("Expected an array for argument value (sOpCode='$sOpCode')");
|
||||
return $this->GetSQLExpr()." IN ('".implode("', '", $value)."')";
|
||||
break;
|
||||
|
||||
case '=':
|
||||
default:
|
||||
return $this->GetSQLExpr()." = \"$value\"";
|
||||
}
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue)
|
||||
{
|
||||
//return intval($proposedValue); could work as well
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
public function RealValueToSQLValue($value)
|
||||
{
|
||||
assert(is_numeric($value));
|
||||
return $value; // supposed to be an int
|
||||
}
|
||||
public function SQLValueToRealValue($value)
|
||||
{
|
||||
// Use cast (int) or intval() ?
|
||||
return (int)$value;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a varchar column (size < ?) to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeString extends AttributeDBField
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetType() {return "String";}
|
||||
public function GetTypeDesc() {return "Alphanumeric string";}
|
||||
public function GetEditClass() {return "String";}
|
||||
public function GetDBFieldType() {return "VARCHAR(255)";}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array(
|
||||
"="=>"equals",
|
||||
"!="=>"differs from",
|
||||
"Like"=>"equals (no case)",
|
||||
"NotLike"=>"differs from (no case)",
|
||||
"Contains"=>"contains",
|
||||
"Begins with"=>"begins with",
|
||||
"Finishes with"=>"finishes with"
|
||||
);
|
||||
}
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return "Contains";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch ($sOpCode)
|
||||
{
|
||||
case '=':
|
||||
case '!=':
|
||||
return $this->GetSQLExpr()." $sOpCode $sQValue";
|
||||
case 'Begins with':
|
||||
return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("$value%");
|
||||
case 'Finishes with':
|
||||
return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("%$value");
|
||||
case 'Contains':
|
||||
return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("%$value%");
|
||||
case 'NotLike':
|
||||
return $this->GetSQLExpr()." NOT LIKE $sQValue";
|
||||
case 'Like':
|
||||
default:
|
||||
return $this->GetSQLExpr()." LIKE $sQValue";
|
||||
}
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue)
|
||||
{
|
||||
return (string)$proposedValue;
|
||||
// if (!settype($proposedValue, "string"))
|
||||
// {
|
||||
// trigger_error("Failed to change the type of '$proposedValue' to a string", E_USER_WARNING);
|
||||
// }
|
||||
}
|
||||
public function RealValueToSQLValue($value)
|
||||
{
|
||||
assert(is_string($value));
|
||||
return $value;
|
||||
}
|
||||
public function SQLValueToRealValue($value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a text column (size > ?) to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeText extends AttributeString
|
||||
{
|
||||
public function GetType() {return "Text";}
|
||||
public function GetTypeDesc() {return "Multiline character string";}
|
||||
public function GetEditClass() {return "Text";}
|
||||
public function GetDBFieldType() {return "TEXT";}
|
||||
|
||||
public function GetAsHTML($sValue)
|
||||
{
|
||||
return str_replace("\n", "<br>\n", parent::GetAsHTML($sValue));
|
||||
}
|
||||
|
||||
public function GetAsXML($value)
|
||||
{
|
||||
return Str::pure2xml($value);
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',')
|
||||
{
|
||||
return str_replace("\n", "[newline]", parent::GetAsCSV($sValue, $sSeparator, $sSepEscape));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a enum column to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeEnum extends AttributeString
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetType() {return "Enum";}
|
||||
public function GetTypeDesc() {return "List of predefined alphanumeric strings";}
|
||||
public function GetEditClass() {return "String";}
|
||||
public function GetDBFieldType()
|
||||
{
|
||||
$oValDef = $this->GetValuesDef();
|
||||
if ($oValDef)
|
||||
{
|
||||
$aValues = CMDBSource::Quote($oValDef->GetValues(array(), ""), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues = array();
|
||||
}
|
||||
if (count($aValues) > 0)
|
||||
{
|
||||
return "ENUM(".implode(", ", $aValues).")";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "VARCHAR(255)"; // ENUM() is not an allowed syntax!
|
||||
}
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return parent::GetBasicFilterOperators();
|
||||
}
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return parent::GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return parent::GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a date+time column to an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeDate extends AttributeDBField
|
||||
{
|
||||
const MYDATEFORMAT = "Y-m-d H:i:s";
|
||||
//const MYDATETIMEZONE = "UTC";
|
||||
const MYDATETIMEZONE = "Europe/Paris";
|
||||
static protected $const_TIMEZONE = null; // set once for all upon object construct
|
||||
|
||||
static public function InitStatics()
|
||||
{
|
||||
// Init static constant once for all (remove when PHP allows real static const)
|
||||
self::$const_TIMEZONE = new DateTimeZone(self::MYDATETIMEZONE);
|
||||
|
||||
// #@# Init default timezone -> do not get a notice... to be improved !!!
|
||||
date_default_timezone_set(self::MYDATETIMEZONE);
|
||||
}
|
||||
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return parent::ListExpectedParams();
|
||||
//return array_merge(parent::ListExpectedParams(), array());
|
||||
}
|
||||
|
||||
public function GetType() {return "Date";}
|
||||
public function GetTypeDesc() {return "Date and time";}
|
||||
public function GetEditClass() {return "Date";}
|
||||
public function GetDBFieldType() {return "TIMESTAMP";}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array(
|
||||
"="=>"equals",
|
||||
"!="=>"differs from",
|
||||
"<"=>"before",
|
||||
"<="=>"before",
|
||||
">"=>"after (strictly)",
|
||||
">="=>"after",
|
||||
"SameDay"=>"same day (strip time)",
|
||||
"SameMonth"=>"same year/month",
|
||||
"SameYear"=>"same year",
|
||||
"Today"=>"today",
|
||||
">|"=>"after today + N days",
|
||||
"<|"=>"before today + N days",
|
||||
"=|"=>"equals today + N days",
|
||||
);
|
||||
}
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
// Unless we implement a "same xxx, depending on given precision" !
|
||||
return "=";
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
|
||||
switch ($sOpCode)
|
||||
{
|
||||
case '=':
|
||||
case '!=':
|
||||
case '<':
|
||||
case '<=':
|
||||
case '>':
|
||||
case '>=':
|
||||
return $this->GetSQLExpr()." $sOpCode $sQValue";
|
||||
case 'SameDay':
|
||||
return "DATE(".$this->GetSQLExpr().") = DATE($sQValue)";
|
||||
case 'SameMonth':
|
||||
return "DATE_FORMAT(".$this->GetSQLExpr().", '%Y-%m') = DATE_FORMAT($sQValue, '%Y-%m')";
|
||||
case 'SameYear':
|
||||
return "MONTH(".$this->GetSQLExpr().") = MONTH($sQValue)";
|
||||
case 'Today':
|
||||
return "DATE(".$this->GetSQLExpr().") = CURRENT_DATE()";
|
||||
case '>|':
|
||||
return "DATE(".$this->GetSQLExpr().") > DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
|
||||
case '<|':
|
||||
return "DATE(".$this->GetSQLExpr().") < DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
|
||||
case '=|':
|
||||
return "DATE(".$this->GetSQLExpr().") = DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)";
|
||||
default:
|
||||
return $this->GetSQLExpr()." = $sQValue";
|
||||
}
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue)
|
||||
{
|
||||
if (!is_numeric($proposedValue))
|
||||
{
|
||||
return $proposedValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return date("Y-m-d H:i:s", $proposedValue);
|
||||
}
|
||||
trigger_error("Invalid type for a date (found ".gettype($proposedValue)." and accepting string/int/DateTime)", E_USER_ERROR);
|
||||
return null;
|
||||
}
|
||||
public function RealValueToSQLValue($value)
|
||||
{
|
||||
if (empty($value))
|
||||
{
|
||||
// Make a valid date for MySQL. TO DO: support NULL as a literal value for fields that can be null.
|
||||
return '0000-00-00 00:00:00';
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
public function SQLValueToRealValue($value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function GetAsHTML($value)
|
||||
{
|
||||
return Str::pure2html($value);
|
||||
}
|
||||
|
||||
public function GetAsXML($value)
|
||||
{
|
||||
return Str::pure2xml($value);
|
||||
}
|
||||
|
||||
public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',')
|
||||
{
|
||||
return str_replace($sSeparator, $sSepEscape, $value);
|
||||
}
|
||||
}
|
||||
|
||||
// Init static constant once for all (remove when PHP allows real static const)
|
||||
AttributeDate::InitStatics();
|
||||
|
||||
|
||||
/**
|
||||
* Map a foreign key to an attribute
|
||||
* AttributeExternalKey and AttributeExternalField may be an external key
|
||||
* the difference is that AttributeExternalKey corresponds to a column into the defined table
|
||||
* where an AttributeExternalField corresponds to a column into another table (class)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("targetclass", "is_null_allowed"));
|
||||
}
|
||||
|
||||
public function GetType() {return "Extkey";}
|
||||
public function GetTypeDesc() {return "Link to another object";}
|
||||
public function GetEditClass() {return "ExtKey";}
|
||||
public function GetDBFieldType() {return "INT";}
|
||||
|
||||
public function IsExternalKey($iType = EXTKEY_RELATIVE) {return true;}
|
||||
public function GetTargetClass($iType = EXTKEY_RELATIVE) {return $this->Get("targetclass");}
|
||||
public function GetKeyAttDef($iType = EXTKEY_RELATIVE){return $this;}
|
||||
public function GetKeyAttCode() {return $this->GetCode();}
|
||||
|
||||
|
||||
public function GetDefaultValue() {return 0;}
|
||||
public function IsNullAllowed() {return $this->Get("is_null_allowed");}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return parent::GetBasicFilterOperators();
|
||||
}
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
return parent::GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
return parent::GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
}
|
||||
|
||||
// overloaded here so that an ext key always have the answer to
|
||||
// "what are you possible values?"
|
||||
public function GetValuesDef()
|
||||
{
|
||||
$oValSetDef = $this->Get("allowed_values");
|
||||
if (!$oValSetDef)
|
||||
{
|
||||
// Let's propose every existing value
|
||||
$oValSetDef = new ValueSetObjects($this->GetTargetClass());
|
||||
}
|
||||
return $oValSetDef;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute which corresponds to an external key (direct or indirect)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeExternalField extends AttributeDefinition
|
||||
{
|
||||
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode"));
|
||||
}
|
||||
|
||||
public function GetType() {return "ExtkeyField";}
|
||||
public function GetTypeDesc() {return "Field of an object pointed to by the current object";}
|
||||
public function GetEditClass() {return "ExtField";}
|
||||
public function GetDBFieldType()
|
||||
{
|
||||
trigger_error("external attribute: does it make any sense to request its type ?", E_USER_WARNING);
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetDBFieldType();
|
||||
}
|
||||
|
||||
public function IsExternalKey($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
switch($iType)
|
||||
{
|
||||
case EXTKEY_ABSOLUTE:
|
||||
// see further
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
return $oRemoteAtt->IsExternalKey($iType);
|
||||
|
||||
case EXTKEY_RELATIVE:
|
||||
return false;
|
||||
|
||||
default:
|
||||
trigger_error("Unexpected value for argument iType: '$iType'", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetTargetClass($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
return $this->GetKeyAttDef($iType)->GetTargetClass();
|
||||
}
|
||||
|
||||
public function IsExternalField() {return true;}
|
||||
public function GetKeyAttCode() {return $this->Get("extkey_attcode");}
|
||||
public function GetExtAttCode() {return $this->Get("target_attcode");}
|
||||
|
||||
public function GetKeyAttDef($iType = EXTKEY_RELATIVE)
|
||||
{
|
||||
switch($iType)
|
||||
{
|
||||
case EXTKEY_ABSOLUTE:
|
||||
// see further
|
||||
$oRemoteAtt = $this->GetExtAttDef();
|
||||
if ($oRemoteAtt->IsExternalField())
|
||||
{
|
||||
return $oRemoteAtt->GetKeyAttDef(EXTKEY_ABSOLUTE);
|
||||
}
|
||||
else if ($oRemoteAtt->IsExternalKey())
|
||||
{
|
||||
return $oRemoteAtt;
|
||||
}
|
||||
return $this->GetKeyAttDef(EXTKEY_RELATIVE); // which corresponds to the code hereafter !
|
||||
|
||||
case EXTKEY_RELATIVE:
|
||||
return MetaModel::GetAttributeDef($this->GetHostClass(), $this->Get("extkey_attcode"));
|
||||
|
||||
default:
|
||||
trigger_error("Unexpected value for argument iType: '$iType'", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetExtAttDef()
|
||||
{
|
||||
$oKeyAttDef = $this->GetKeyAttDef();
|
||||
$oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->Get("targetclass"), $this->Get("target_attcode"));
|
||||
return $oExtAttDef;
|
||||
}
|
||||
|
||||
public function GetSQLExpr()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetSQLExpr();
|
||||
}
|
||||
public function DBGetUsedFields()
|
||||
{
|
||||
// No field is used but the one defined in the field of the external class
|
||||
// #@# so what ?
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetDefaultValue()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetDefaultValue();
|
||||
}
|
||||
public function IsNullAllowed()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->IsNullAllowed();
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetBasicFilterOperators();
|
||||
}
|
||||
public function GetBasicFilterLooseOperator()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetBasicFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->MakeRealValue($proposedValue);
|
||||
}
|
||||
public function RealValueToSQLValue($value)
|
||||
{
|
||||
// This one could be used in case of filtering only
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->RealValueToSQLValue($value);
|
||||
}
|
||||
public function SQLValueToRealValue($value)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->SQLValueToRealValue($value);
|
||||
}
|
||||
public function GetAsHTML($value)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetAsHTML($value);
|
||||
}
|
||||
public function GetAsXML($value)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetAsXML($value);
|
||||
}
|
||||
public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',')
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
return $oExtAttDef->GetAsCSV($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a varchar column to an URL (formats the ouput in HMTL)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class AttributeURL extends AttributeString
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
//return parent::ListExpectedParams();
|
||||
return array_merge(parent::ListExpectedParams(), array("target", "label"));
|
||||
}
|
||||
|
||||
public function GetType() {return "Url";}
|
||||
public function GetTypeDesc() {return "Absolute or relative URL as a text string";}
|
||||
public function GetEditClass() {return "String";}
|
||||
|
||||
public function GetAsHTML($sValue)
|
||||
{
|
||||
$sTarget = $this->Get("target");
|
||||
if (empty($sTarget)) $sTarget = "_blank";
|
||||
$sLabel = Str::pure2html($sValue);
|
||||
if (strlen($sLabel) > 40)
|
||||
{
|
||||
// Truncate the length to about 40 characters, by removing the middle
|
||||
$sLabel = substr($sLabel, 0, 25).'...'.substr($sLabel, -15);
|
||||
}
|
||||
return "<a target=\"$sTarget\" href=\"$sValue\">$sLabel</a>";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
44
core/cmdbchange.class.inc.php
Normal file
44
core/cmdbchange.class.inc.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A change as requested/validated at once by user, may groups many atomic changes
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
class CMDBChange extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"name" => "change",
|
||||
"description" => "Changes tracking",
|
||||
"key_type" => "autoincrement",
|
||||
"key_label" => "",
|
||||
"name_attcode" => "date",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_change",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeDate("date", array("label"=>"date", "description"=>"date and time at which the changes have been recorded", "allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("label"=>"misc. info", "description"=>"caller's defined information", "allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
//MetaModel::Init_InheritFilters();
|
||||
MetaModel::Init_AddFilterFromAttribute("date");
|
||||
MetaModel::Init_AddFilterFromAttribute("userinfo");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
165
core/cmdbchangeop.class.inc.php
Normal file
165
core/cmdbchangeop.class.inc.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Various atomic change operations, to be tracked
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
class CMDBChangeOp extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"name" => "change operation",
|
||||
"description" => "Change operations tracking",
|
||||
"key_type" => "autoincrement",
|
||||
"key_label" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "optype",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("change", array("label"=>"change", "description"=>"change", "allowed_values"=>null, "sql"=>"changeid", "targetclass"=>"CMDBChange", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("label"=>"date", "description"=>"date and time of the change", "allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("label"=>"user", "description"=>"who made this change", "allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("objclass", array("label"=>"object class", "description"=>"object class", "allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("objkey", array("label"=>"object id", "description"=>"object id", "allowed_values"=>null, "sql"=>"objkey", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddFilterFromAttribute("objclass");
|
||||
MetaModel::Init_AddFilterFromAttribute("objkey");
|
||||
MetaModel::Init_AddFilterFromAttribute("date");
|
||||
MetaModel::Init_AddFilterFromAttribute("userinfo");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Record the creation of an object
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class CMDBChangeOpCreate extends CMDBChangeOp
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"name" => "object creation",
|
||||
"description" => "Object creation tracking",
|
||||
"key_type" => "",
|
||||
"key_label" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_create",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_InheritFilters();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record the deletion of an object
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class CMDBChangeOpDelete extends CMDBChangeOp
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"name" => "object deletion",
|
||||
"description" => "Object deletion tracking",
|
||||
"key_type" => "",
|
||||
"key_label" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_delete",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_InheritFilters();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record the modification of an attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class CMDBChangeOpSetAttribute extends CMDBChangeOp
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"name" => "object change",
|
||||
"description" => "Object properties change tracking",
|
||||
"key_type" => "",
|
||||
"key_label" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("label"=>"Attribute", "description"=>"code of the modified property", "allowed_values"=>null, "sql"=>"attcode", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("newvalue", array("label"=>"New value", "description"=>"new value of the attribute", "allowed_values"=>null, "sql"=>"newvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_InheritFilters();
|
||||
MetaModel::Init_AddFilterFromAttribute("attcode");
|
||||
MetaModel::Init_AddFilterFromAttribute("newvalue");
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'newvalue')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'newvalue')); // Attributes to be displayed for a list
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
424
core/cmdbobject.class.inc.php
Normal file
424
core/cmdbobject.class.inc.php
Normal file
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* cmdbObjectClass
|
||||
* the file to include, then the core is yours
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
require_once('coreexception.class.inc.php');
|
||||
|
||||
require_once('config.class.inc.php');
|
||||
|
||||
require_once('attributedef.class.inc.php');
|
||||
require_once('filterdef.class.inc.php');
|
||||
require_once('stimulus.class.inc.php');
|
||||
require_once('valuesetdef.class.inc.php');
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
|
||||
require_once('expression.class.inc.php');
|
||||
|
||||
require_once('cmdbsource.class.inc.php');
|
||||
require_once('sqlquery.class.inc.php');
|
||||
require_once('oql/oqlquery.class.inc.php');
|
||||
require_once('oql/oqlexception.class.inc.php');
|
||||
require_once('oql/oql-parser.php');
|
||||
require_once('oql/oql-lexer.php');
|
||||
require_once('oql/oqlinterpreter.class.inc.php');
|
||||
|
||||
require_once('dbobject.class.php');
|
||||
require_once('dbobjectsearch.class.php');
|
||||
require_once('dbobjectset.class.php');
|
||||
|
||||
require_once('cmdbchange.class.inc.php');
|
||||
require_once('cmdbchangeop.class.inc.php');
|
||||
|
||||
require_once('userrights.class.inc.php');
|
||||
|
||||
//
|
||||
// Error handling
|
||||
// To be finalized... or removed ?
|
||||
//
|
||||
function cmdbErrorHandler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
// font-family: Courier-New, Courier, Arial, Helevtica;
|
||||
$sErrorStyle = "
|
||||
background-color: #ffaaaa;
|
||||
color: #000000;
|
||||
border: 1px dashed #000000;
|
||||
padding: 0.25em;
|
||||
margin-top: 1em;
|
||||
";
|
||||
$sCallStackStyle = "
|
||||
font-size: smaller;
|
||||
background-color: #ffcccc;
|
||||
color: #000000;
|
||||
border: 1px dashed #000000;
|
||||
padding: 0.25em;
|
||||
margin-top: 1em;
|
||||
";
|
||||
|
||||
switch ($errno)
|
||||
{
|
||||
case E_USER_ERROR:
|
||||
case E_ERROR:
|
||||
echo "<div style=\"$sErrorStyle\">\n";
|
||||
echo "<b>Error</b> [$errno] $errstr<br />\n";
|
||||
echo "<div style=\"$sCallStackStyle\">\n";
|
||||
MyHelpers::dump_callstack(1);
|
||||
echo "</div>\n";
|
||||
echo "Hereafter the biz model internals:<br />\n";
|
||||
echo "<pre>\n";
|
||||
MetaModel::static_var_dump();
|
||||
echo "</pre>\n";
|
||||
echo "Aborting...<br />\n";
|
||||
echo "</div>\n";
|
||||
exit(1);
|
||||
break;
|
||||
case E_USER_WARNING:
|
||||
case E_WARNING:
|
||||
echo "<div style=\"background-color:#FAA;\">\n";
|
||||
echo "<b>Warning</b> [$errno] $errstr<br />\n";
|
||||
echo "<div style=\"background-color:#FCC;\">\n";
|
||||
MyHelpers::dump_callstack(1);
|
||||
echo "</div>\n";
|
||||
echo "</div>\n";
|
||||
break;
|
||||
case E_USER_NOTICE:
|
||||
case E_NOTICE:
|
||||
echo "<div style=\"background-color:#FAA;\">\n";
|
||||
echo "<b>Notice</b> [$errno] $errstr<br />\n";
|
||||
echo "<div style=\"background-color:#FCC;\">\n";
|
||||
MyHelpers::dump_callstack(1);
|
||||
echo "</div>\n";
|
||||
echo "</div>\n";
|
||||
break;
|
||||
default:
|
||||
echo "Unknown error type: [$errno] $errstr<br />\n";
|
||||
MyHelpers::dump_callstack(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
error_reporting(E_ALL | E_STRICT);
|
||||
//set_error_handler("cmdbErrorHandler");
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* A persistent object, which changes are accurately recorded
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
abstract class CMDBObject extends DBObject
|
||||
{
|
||||
protected $m_datCreated;
|
||||
protected $m_datUpdated;
|
||||
protected static $m_oCurrChange = null;
|
||||
|
||||
|
||||
private function RecordObjCreation(CMDBChange $oChange)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$iId = $oMyChangeOp->DBInsert();
|
||||
}
|
||||
private function RecordObjDeletion(CMDBChange $oChange, $objkey)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpDelete");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $objkey);
|
||||
$iId = $oMyChangeOp->DBInsert();
|
||||
}
|
||||
private function RecordAttChanges(CMDBChange $oChange, array $aValues = array())
|
||||
{
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
// ... some values...
|
||||
//
|
||||
if (empty($aValues))
|
||||
{
|
||||
// ... or every object values
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
if ($oAttDef->IsLinkSet()) continue; // #@# temporary
|
||||
$aValues[$sAttCode] = $this->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
foreach ($aValues as $sAttCode=> $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsLinkSet()) continue; // #@# temporary
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttribute");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsert();
|
||||
}
|
||||
}
|
||||
|
||||
public function DBInsert()
|
||||
{
|
||||
if(!is_object(self::$m_oCurrChange))
|
||||
{
|
||||
trigger_error("DBInsert() could not be used here, please use DBInsertTracked() instead", E_USER_ERROR);
|
||||
}
|
||||
return $this->DBInsertTracked_Internal();
|
||||
}
|
||||
|
||||
public function DBInsertTracked(CMDBChange $oChange)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBInsertTracked_Internal();
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected function DBInsertTracked_Internal()
|
||||
{
|
||||
$ret = parent::DBInsert();
|
||||
$this->RecordObjCreation(self::$m_oCurrChange);
|
||||
$this->RecordAttChanges(self::$m_oCurrChange);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBClone($newKey = null)
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
trigger_error("DBClone() could not be used here, please use DBCloneTracked() instead", E_USER_ERROR);
|
||||
}
|
||||
return $this->DBCloneTracked_Internal();
|
||||
}
|
||||
|
||||
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBCloneTracked_Internal($newKey);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected function DBCloneTracked_Internal($newKey = null)
|
||||
{
|
||||
$newKey = parent::DBClone($newKey);
|
||||
$oClone = MetaModel::GetObject(get_class($this), $newKey);
|
||||
|
||||
$oClone->RecordObjCreation(self::$m_oCurrChange);
|
||||
$oClone->RecordAttChanges(self::$m_oCurrChange);
|
||||
return $newKey;
|
||||
}
|
||||
|
||||
public function DBUpdate()
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
trigger_error("DBUpdate() could not be used here, please use DBUpdateTracked() instead", E_USER_ERROR);
|
||||
}
|
||||
return $this->DBUpdateTracked_internal();
|
||||
}
|
||||
|
||||
public function DBUpdateTracked(CMDBChange $oChange)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBUpdateTracked_Internal();
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected function DBUpdateTracked_Internal()
|
||||
{
|
||||
// Copy the changes list before the update (the list should be reset afterwards)
|
||||
$aChanges = $this->ListChanges();
|
||||
if (count($aChanges) == 0)
|
||||
{
|
||||
trigger_error("Attempting to update an unchanged object", E_USER_NOTICE);
|
||||
return;
|
||||
}
|
||||
|
||||
$ret = parent::DBUpdate();
|
||||
$this->RecordAttChanges(self::$m_oCurrChange, $aChanges);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBDelete()
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
trigger_error("DBDelete() could not be used here, please use DBDeleteTracked() instead", E_USER_ERROR);
|
||||
}
|
||||
return $this->DBDeleteTracked_Internal();
|
||||
}
|
||||
|
||||
public function DBDeleteTracked(CMDBChange $oChange)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBDeleteTracked_Internal();
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected function DBDeleteTracked_Internal()
|
||||
{
|
||||
$prevkey = $this->GetKey();
|
||||
$ret = parent::DBDelete();
|
||||
$this->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function BulkDelete(DBObjectSearch $oFilter)
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
trigger_error("BulkDelete() could not be used here, please use BulkDeleteTracked() instead", E_USER_ERROR);
|
||||
}
|
||||
return $this->BulkDeleteTracked_Internal($oFilter);
|
||||
}
|
||||
|
||||
public static function BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->BulkDeleteTracked_Internal($oFilter);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter)
|
||||
{
|
||||
trigger_error("Change tracking not tested for bulk operations", E_USER_WARNING);
|
||||
|
||||
// Get the list of objects to delete (and record data before deleting the DB records)
|
||||
$oObjSet = new CMDBObjectSet($oFilter);
|
||||
$aObjAndKeys = array(); // array of pkey=>object
|
||||
while ($oItem = $oObjSet->Fetch())
|
||||
{
|
||||
$aObjAndKeys[$oItem->GetKey()] = $oItem;
|
||||
}
|
||||
$oObjSet->FreeResult();
|
||||
|
||||
// Delete in one single efficient query
|
||||
$ret = parent::BulkDelete($oFilter);
|
||||
// Record... in many queries !!!
|
||||
foreach($aObjAndKeys as $prevkey=>$oItem)
|
||||
{
|
||||
$oItem->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
trigger_error("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead", E_USER_ERROR);
|
||||
}
|
||||
return $this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
}
|
||||
|
||||
public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
|
||||
{
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
|
||||
// Get the list of objects to update (and load it before doing the change)
|
||||
$oObjSet = new CMDBObjectSet($oFilter);
|
||||
$oObjSet->Load();
|
||||
|
||||
// Update in one single efficient query
|
||||
$ret = parent::BulkUpdate($oFilter, $aValues);
|
||||
|
||||
// Record... in many queries !!!
|
||||
while ($oItem = $oObjSet->Fetch())
|
||||
{
|
||||
$aChangedValues = $oItem->ListChangedValues($aValues);
|
||||
$oItem->RecordAttChanges(self::$m_oCurrChange, $aChangedValues);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design!
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
class CMDBObjectSet extends DBObjectSet
|
||||
{
|
||||
// this is the public interface (?)
|
||||
|
||||
// I have to define those constructors here... :-(
|
||||
// just to get the right object class in return.
|
||||
// I have to think again to those things: maybe it will work fine if a have a constructor define here (?)
|
||||
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE
|
||||
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private...
|
||||
// BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!!
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
static public function FromArray($sClass, $aObjects)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE
|
||||
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private...
|
||||
// BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!!
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
$oRetSet->AddObjectArray($aObjects);
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design!
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
class CMDBSearchFilter extends DBObjectSearch
|
||||
{
|
||||
// this is the public interface (?)
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
386
core/cmdbsource.class.inc.php
Normal file
386
core/cmdbsource.class.inc.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CMDBSource
|
||||
* database access wrapper
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
|
||||
class MySQLException extends CoreException
|
||||
{
|
||||
public function __construct($sIssue, $aContext)
|
||||
{
|
||||
$aContext['mysql_error'] = mysql_error();
|
||||
parent::__construct($sIssue, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CMDBSource
|
||||
{
|
||||
protected static $m_sDBHost;
|
||||
protected static $m_sDBUser;
|
||||
protected static $m_sDBPwd;
|
||||
protected static $m_sDBName;
|
||||
protected static $m_resDBLink;
|
||||
|
||||
public static function Init($sServer, $sUser, $sPwd, $sSource = '')
|
||||
{
|
||||
self::$m_sDBHost = $sServer;
|
||||
self::$m_sDBUser = $sUser;
|
||||
self::$m_sDBPwd = $sPwd;
|
||||
self::$m_sDBName = $sSource;
|
||||
if (!self::$m_resDBLink = mysql_pconnect($sServer, $sUser, $sPwd))
|
||||
{
|
||||
throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer, 'user'=>$sUser));
|
||||
}
|
||||
if (!empty($sSource))
|
||||
{
|
||||
if (!mysql_select_db($sSource, self::$m_resDBLink))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('db_name'=>$sSource));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function IsDB($sSource)
|
||||
{
|
||||
$aDBs = self::QueryToCol('SHOW DATABASES', 'Database');
|
||||
|
||||
// Show Database does return the DB names in lower case
|
||||
$sSourceRef = strtolower($sSource);
|
||||
return (in_array($sSourceRef, $aDBs));
|
||||
}
|
||||
|
||||
public static function SelectDB($sSource)
|
||||
{
|
||||
if (!mysql_select_db($sSource, self::$m_resDBLink))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('db_name'=>$sSource));
|
||||
}
|
||||
self::$m_sDBName = $sSource;
|
||||
}
|
||||
|
||||
public static function CreateDB($sSource)
|
||||
{
|
||||
self::Query("CREATE DATABASE `$sSource`");
|
||||
self::SelectDB($sSource);
|
||||
}
|
||||
|
||||
public static function DropDB($sDBToDrop = '')
|
||||
{
|
||||
if (empty($sDBToDrop))
|
||||
{
|
||||
$sDBToDrop = self::$m_sDBName;
|
||||
}
|
||||
self::Query("DROP DATABASE `$sDBToDrop`");
|
||||
if ($sDBToDrop == self::$m_sDBName)
|
||||
{
|
||||
self::$m_sDBName = '';
|
||||
}
|
||||
}
|
||||
|
||||
public static function CreateTable($sQuery)
|
||||
{
|
||||
$res = self::Query($sQuery);
|
||||
self::_TablesInfoCacheReset(); // reset the table info cache!
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function DropTable($sTable)
|
||||
{
|
||||
$res = self::Query("DROP TABLE `$sTable`");
|
||||
self::_TablesInfoCacheReset(true); // reset the table info cache!
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function DBHost() {return self::$m_sDBHost;}
|
||||
public static function DBUser() {return self::$m_sDBUser;}
|
||||
public static function DBPwd() {return self::$m_sDBPwd;}
|
||||
public static function DBName() {return self::$m_sDBName;}
|
||||
|
||||
public static function Quote($value, $bAlways = false, $cQuoteStyle = "'")
|
||||
{
|
||||
// Quote variable and protect against SQL injection attacks
|
||||
// Code found in the PHP documentation: quote_smart($value)
|
||||
|
||||
// bAlways should be set to true when the purpose is to create a IN clause,
|
||||
// otherwise and if there is a mix of strings and numbers, the clause
|
||||
// would always be false
|
||||
|
||||
if (is_array($value))
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($value as $key => $itemvalue)
|
||||
{
|
||||
$aRes[$key] = self::Quote($itemvalue, $bAlways, $cQuoteStyle);
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
// Stripslashes
|
||||
if (get_magic_quotes_gpc())
|
||||
{
|
||||
$value = stripslashes($value);
|
||||
}
|
||||
// Quote if not a number or a numeric string
|
||||
if ($bAlways || !is_numeric($value))
|
||||
{
|
||||
$value = $cQuoteStyle . mysql_real_escape_string($value, self::$m_resDBLink) . $cQuoteStyle;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
public static function Query($sSQLQuery)
|
||||
{
|
||||
// Add info into the query as a comment, for easier error tracking
|
||||
//
|
||||
//if ($user_contact) $aTraceInf['userID'] = $user_contact->get_key();
|
||||
//$aTraceInf['file'] = __FILE__;
|
||||
if ($_SERVER['REQUEST_URI']) $aTraceInf['requestURI'] = $_SERVER['REQUEST_URI'];
|
||||
$i = 0;
|
||||
foreach(debug_backtrace() as $aCallData)
|
||||
{
|
||||
$sClass = key_exists("class", $aCallData) ? $aCallData["class"]."::" : "";
|
||||
//if ($aCallData['function'] !== 'mysql_simple_query' AND $sClass !== 'r2_set::')
|
||||
//{
|
||||
if ($i == 3) break;
|
||||
$aTraceInf['function'.$i] = $sClass.$aCallData["function"]." on line ".$aCallData['line'];
|
||||
$i++;
|
||||
//}
|
||||
}
|
||||
// disabled until we need it really!
|
||||
// $sSQLQuery = $sSQLQuery.MyHelpers::MakeSQLComment($aTraceInf);
|
||||
|
||||
$mu_t1 = MyHelpers::getmicrotime();
|
||||
$result = mysql_query($sSQLQuery, self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery));
|
||||
}
|
||||
$mu_t2 = MyHelpers::getmicrotime();
|
||||
// #@# todo - query_trace($sSQLQuery, $mu_t2 - $mu_t1);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function GetInsertId()
|
||||
{
|
||||
return mysql_insert_id(self::$m_resDBLink);
|
||||
}
|
||||
public static function InsertInto($sSQLQuery)
|
||||
{
|
||||
if (self::Query($sSQLQuery))
|
||||
{
|
||||
return self::GetInsertId();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function QueryToArray($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
$result = mysql_query($sSql, self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
while ($aRow = mysql_fetch_array($result, MYSQL_BOTH))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
mysql_free_result($result);
|
||||
return $aData;
|
||||
}
|
||||
|
||||
public static function QueryToCol($sSql, $col)
|
||||
{
|
||||
$aColumn = array();
|
||||
$aData = self::QueryToArray($sSql);
|
||||
foreach($aData as $aRow)
|
||||
{
|
||||
@$aColumn[] = $aRow[$col];
|
||||
}
|
||||
return $aColumn;
|
||||
}
|
||||
|
||||
public static function ExplainQuery($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
$result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
$aNames = array();
|
||||
for ($i = 0; $i < mysql_num_fields($result) ; $i++)
|
||||
{
|
||||
$meta = mysql_fetch_field($result, $i);
|
||||
if (!$meta)
|
||||
{
|
||||
throw new MySQLException('mysql_fetch_field: No information available', array('query'=>$sSql, 'i'=>$i));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aNames[] = $meta->name;
|
||||
}
|
||||
}
|
||||
|
||||
$aData[] = $aNames;
|
||||
while ($aRow = mysql_fetch_array($result, MYSQL_ASSOC))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
mysql_free_result($result);
|
||||
return $aData;
|
||||
}
|
||||
|
||||
public static function TestQuery($sSql)
|
||||
{
|
||||
$result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
return mysql_error();
|
||||
}
|
||||
|
||||
mysql_free_result($result);
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function NbRows($result)
|
||||
{
|
||||
return mysql_num_rows($result);
|
||||
}
|
||||
|
||||
public static function FetchArray($result)
|
||||
{
|
||||
return mysql_fetch_array($result, MYSQL_ASSOC);
|
||||
}
|
||||
|
||||
public static function Seek($result, $iRow)
|
||||
{
|
||||
return mysql_data_seek($result, $iRow);
|
||||
}
|
||||
|
||||
public static function FreeResult($result)
|
||||
{
|
||||
return mysql_free_result($result);
|
||||
}
|
||||
|
||||
public static function IsTable($sTable)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
return (!empty($aTableInfo));
|
||||
}
|
||||
|
||||
public static function IsKey($sTable, $iKey)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($iKey, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$iKey];
|
||||
if (!array_key_exists("Key", $aFieldData)) return false;
|
||||
return ($aFieldData["Key"] == "PRI");
|
||||
}
|
||||
|
||||
public static function IsAutoIncrement($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
if (!array_key_exists("Extra", $aFieldData)) return false;
|
||||
//MyHelpers::debug_breakpoint($aFieldData);
|
||||
return (strstr($aFieldData["Extra"], "auto_increment"));
|
||||
}
|
||||
|
||||
public static function IsField($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsNullAllowed($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
return (strtolower($aFieldData["Null"]) == "yes");
|
||||
}
|
||||
|
||||
// Returns an array of (fieldname => array of field info)
|
||||
public static function GetTableFieldsList($sTable)
|
||||
{
|
||||
assert(!empty($sTable));
|
||||
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return array(); // #@# or an error ?
|
||||
|
||||
return array_keys($aTableInfo["Fields"]);
|
||||
}
|
||||
|
||||
// Cache the information about existing tables, and their fields
|
||||
private static $m_aTablesInfo = array();
|
||||
private static function _TablesInfoCacheReset()
|
||||
{
|
||||
self::$m_aTablesInfo = array();
|
||||
}
|
||||
private static function _TablesInfoCacheInit()
|
||||
{
|
||||
if (!empty(self::$m_aTablesInfo)) return;
|
||||
|
||||
$aTables = self::QueryToArray("SHOW TABLES FROM `".self::$m_sDBName."`", 0);
|
||||
foreach ($aTables as $aTableData)
|
||||
{
|
||||
$sTableName = $aTableData[0];
|
||||
$aFields = self::QueryToArray("SHOW COLUMNS FROM `$sTableName`");
|
||||
// Note: without backticks, you get an error with some table names (e.g. "group")
|
||||
foreach ($aFields as $aFieldData)
|
||||
{
|
||||
$sFieldName = $aFieldData["Field"];
|
||||
self::$m_aTablesInfo[$sTableName]["Fields"][$sFieldName] =
|
||||
array
|
||||
(
|
||||
"Name"=>$aFieldData["Field"],
|
||||
"Type"=>$aFieldData["Type"],
|
||||
"Null"=>$aFieldData["Null"],
|
||||
"Key"=>$aFieldData["Key"],
|
||||
"Default"=>$aFieldData["Default"],
|
||||
"Extra"=>$aFieldData["Extra"]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static function EnumTables()
|
||||
{
|
||||
self::_TablesInfoCacheInit();
|
||||
return array_keys(self::$m_aTablesInfo);
|
||||
}
|
||||
public static function GetTableInfo($sTable)
|
||||
{
|
||||
self::_TablesInfoCacheInit();
|
||||
|
||||
// table names are transformed into lower case
|
||||
// is that 100% specific to innodb and myISAM?
|
||||
$sTableLC = strtolower($sTable);
|
||||
|
||||
if (array_key_exists($sTableLC, self::$m_aTablesInfo)) return self::$m_aTablesInfo[$sTableLC];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
170
core/config.class.inc.php
Normal file
170
core/config.class.inc.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Config
|
||||
* configuration data
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
class ConfigException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
class Config
|
||||
{
|
||||
//protected $m_bIsLoaded = false;
|
||||
protected $m_sFile = '';
|
||||
|
||||
protected $m_aAppModules;
|
||||
protected $m_aDataModels;
|
||||
protected $m_aAddons;
|
||||
|
||||
protected $m_sDBHost;
|
||||
protected $m_sDBUser;
|
||||
protected $m_sDBPwd;
|
||||
protected $m_sDBName;
|
||||
protected $m_sDBSubname;
|
||||
|
||||
public function __construct($sConfigFile)
|
||||
{
|
||||
$this->m_sFile = $sConfigFile;
|
||||
$this->Load($sConfigFile);
|
||||
$this->Verify();
|
||||
}
|
||||
|
||||
protected function CheckFile($sPurpose, $sFileName)
|
||||
{
|
||||
if (!file_exists($sFileName))
|
||||
{
|
||||
throw new ConfigException("Could not find $sPurpose file", array('file' => $sFileName));
|
||||
}
|
||||
}
|
||||
|
||||
protected function Load($sConfigFile)
|
||||
{
|
||||
$this->CheckFile('configuration', $sConfigFile);
|
||||
|
||||
$sConfigCode = trim(file_get_contents($sConfigFile));
|
||||
|
||||
// This does not work on several lines
|
||||
// preg_match('/^<\\?php(.*)\\?'.'>$/', $sConfigCode, $aMatches)...
|
||||
// So, I've implemented a solution suggested in the PHP doc (search for phpWrapper)
|
||||
try
|
||||
{
|
||||
ob_start();
|
||||
$sCode = str_replace('<'.'?php','<'.'?', $sConfigCode);
|
||||
eval('?'.'>'.trim($sCode).'<'.'?');
|
||||
$sNoise = trim(ob_get_contents());
|
||||
ob_end_clean();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// well, never reach in case of parsing error :-(
|
||||
// will be improved in PHP 6 ?
|
||||
throw new ConfigException('Error in configuration file', array('file' => $sConfigFile, 'error' => $e->getMessage()));
|
||||
}
|
||||
if (strlen($sNoise) > 0)
|
||||
{
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
throw new ConfigException('Syntax error in configuration file', array('file' => $sConfigFile, 'error' => $sNoise));
|
||||
}
|
||||
|
||||
if (!isset($MySettings) || !is_array($MySettings))
|
||||
{
|
||||
throw new ConfigException('Missing array in configuration file', array('file' => $sConfigFile, 'expected' => '$MySettings'));
|
||||
}
|
||||
if (!isset($MyModules) || !is_array($MyModules))
|
||||
{
|
||||
throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules'));
|
||||
}
|
||||
if (!array_key_exists('application', $MyModules))
|
||||
{
|
||||
throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'application\']'));
|
||||
}
|
||||
if (!array_key_exists('business', $MyModules))
|
||||
{
|
||||
throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'business\']'));
|
||||
}
|
||||
if (!array_key_exists('addons', $MyModules))
|
||||
{
|
||||
throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'addons\']'));
|
||||
}
|
||||
if (!array_key_exists('user rights', $MyModules['addons']))
|
||||
{
|
||||
$MyModules['addons']['user rights'] = '../addons/userrights/userrightsnull.class.inc.php';
|
||||
}
|
||||
$this->m_aAppModules = $MyModules['application'];
|
||||
$this->m_aDataModels = $MyModules['business'];
|
||||
$this->m_aAddons = $MyModules['addons'];
|
||||
|
||||
$this->m_sDBHost = trim($MySettings['db_host']);
|
||||
$this->m_sDBUser = trim($MySettings['db_user']);
|
||||
$this->m_sDBPwd = trim($MySettings['db_pwd']);
|
||||
$this->m_sDBName = trim($MySettings['db_name']);
|
||||
$this->m_sDBSubname = trim($MySettings['db_subname']);
|
||||
}
|
||||
|
||||
protected function Verify()
|
||||
{
|
||||
foreach ($this->m_aAppModules as $sModule => $sToInclude)
|
||||
{
|
||||
$this->CheckFile('application module', $sToInclude);
|
||||
}
|
||||
foreach ($this->m_aDataModels as $sModule => $sToInclude)
|
||||
{
|
||||
$this->CheckFile('business model', $sToInclude);
|
||||
}
|
||||
foreach ($this->m_aAddons as $sModule => $sToInclude)
|
||||
{
|
||||
$this->CheckFile('addon module', $sToInclude);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAppModules()
|
||||
{
|
||||
return $this->m_aAppModules;
|
||||
}
|
||||
|
||||
public function GetDataModels()
|
||||
{
|
||||
return $this->m_aDataModels;
|
||||
}
|
||||
|
||||
public function GetAddons()
|
||||
{
|
||||
return $this->m_aAddons;
|
||||
}
|
||||
|
||||
public function GetDBHost()
|
||||
{
|
||||
return $this->m_sDBHost;
|
||||
}
|
||||
|
||||
|
||||
public function GetDBName()
|
||||
{
|
||||
return $this->m_sDBName;
|
||||
}
|
||||
|
||||
public function GetDBSubname()
|
||||
{
|
||||
return $this->m_sDBSubname;
|
||||
}
|
||||
|
||||
public function GetDBUser()
|
||||
{
|
||||
return $this->m_sDBUser;
|
||||
}
|
||||
|
||||
public function GetDBPwd()
|
||||
{
|
||||
return $this->m_sDBPwd;
|
||||
}
|
||||
}
|
||||
?>
|
||||
52
core/coreexception.class.inc.php
Normal file
52
core/coreexception.class.inc.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
class CoreException extends Exception
|
||||
{
|
||||
public function __construct($sIssue, $aContextData = null, $sImpact = '')
|
||||
{
|
||||
$this->m_sIssue = $sIssue;
|
||||
$this->m_sImpact = $sImpact;
|
||||
$this->m_aContextData = $aContextData ? $aContextData : array();
|
||||
|
||||
$sMessage = $sIssue;
|
||||
if (!empty($sImpact)) $sMessage .= "($sImpact)";
|
||||
if (count($this->m_aContextData) > 0)
|
||||
{
|
||||
$sMessage .= ": ";
|
||||
$aContextItems = array();
|
||||
foreach($this->m_aContextData as $sKey => $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
$aPairs = array();
|
||||
foreach($value as $key => $val)
|
||||
{
|
||||
if (is_array($val))
|
||||
{
|
||||
$aPairs[$key] = '('.implode(', ', $val).')';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPairs[$key] = $val;
|
||||
}
|
||||
}
|
||||
$sValue = '{'.implode('; ', $aPairs).'}';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sValue = $value;
|
||||
}
|
||||
$aContextItems[] = "$sKey = $sValue";
|
||||
}
|
||||
$sMessage .= implode(', ', $aContextItems);
|
||||
}
|
||||
parent::__construct($sMessage, 0);
|
||||
}
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
{
|
||||
return $this->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
362
core/data.generator.class.inc.php
Normal file
362
core/data.generator.class.inc.php
Normal file
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* data generator
|
||||
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
|
||||
*
|
||||
* @package tbd
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data Generator helper class
|
||||
*
|
||||
* This class is useful to generate a lot of sample data that look consistent
|
||||
* for a given organization in order to simulate a real CMDB
|
||||
*/
|
||||
class cmdbDataGenerator
|
||||
{
|
||||
protected $m_sOrganizationKey;
|
||||
protected $m_sOrganizationCode;
|
||||
protected $m_sOrganizationName;
|
||||
protected $m_OrganizationDomains;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($sOrganizationId = "")
|
||||
{
|
||||
global $aCompanies, $aCompaniesCode;
|
||||
if ($sOrganizationId == '')
|
||||
{
|
||||
// No organization provided, pick a random and unused one from our predefined list
|
||||
$retries = 5*count($aCompanies);
|
||||
while ( ($retries > 0) && !isset($this->m_sOrganizationCode)) // Stupid algorithm, but I'm too lazy to do something bulletproof tonight
|
||||
{
|
||||
$index = rand(0, count($aCompanies) - 1);
|
||||
if (!$this->OrganizationExists($aCompanies[$index]['code']))
|
||||
{
|
||||
$this->m_sOrganizationCode = $aCompanies[$index]['code'];
|
||||
$this->m_sOrganizationName = $aCompanies[$index]['name'];
|
||||
$this->m_OrganizationDomains = $aCompanies[$index]['domain'];
|
||||
}
|
||||
$retries--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// A code has been provided, let's take the information we need from the organization itself
|
||||
$this->m_sOrganizationId = $sOrganizationId;
|
||||
$oOrg = $this->GetOrganization($sOrganizationId);
|
||||
if ($oOrg == null)
|
||||
{
|
||||
echo "Unable to find the organization '$sOrganisationCode' in the database... can not add objects into this organization.<br/>\n";
|
||||
exit();
|
||||
}
|
||||
$this->m_sOrganizationCode = $oOrg->Get('code');
|
||||
$this->m_sOrganizationName = $oOrg->Get('name');
|
||||
if (!isset($aCompaniesCode[$this->m_sOrganizationCode]['domain']))
|
||||
{
|
||||
// Generate some probable domain names for this organization
|
||||
$this->m_OrganizationDomains = array(strtolower($this->m_sOrganizationCode).".com", strtolower($this->m_sOrganizationCode).".org", strtolower($this->m_sOrganizationCode)."corp.net",);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pick the domain names for this organization from the predefined list
|
||||
$this->m_OrganizationDomains = $aCompaniesCode[$this->m_sOrganizationCode]['domain'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($this->m_sOrganizationCode))
|
||||
{
|
||||
echo "Unable to find an organization code which is not already used... can not create a new organization. Enhance the list of fake organizations (\$aCompanies in data_sample.inc.php).<br/>\n";
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current organization id used by the generator
|
||||
*
|
||||
* @return string The organization id
|
||||
*/
|
||||
public function GetOrganizationId()
|
||||
{
|
||||
return $this->m_sOrganizationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current organization id used by the generator
|
||||
*
|
||||
* @param string The organization id
|
||||
* @return none
|
||||
*/
|
||||
public function SetOrganizationId($sId)
|
||||
{
|
||||
$this->m_sOrganizationId = $sId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current organization code used by the generator
|
||||
*
|
||||
* @return string The organization code
|
||||
*/
|
||||
public function GetOrganizationCode()
|
||||
{
|
||||
return $this->m_sOrganizationCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current organization name used by the generator
|
||||
*
|
||||
* @return string The organization name
|
||||
*/
|
||||
function GetOrganizationName()
|
||||
{
|
||||
return $this->m_sOrganizationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random first name taken from a (big) prefedined list
|
||||
*
|
||||
* @return string A random first name
|
||||
*/
|
||||
function GenerateFirstName()
|
||||
{
|
||||
global $aFirstNames;
|
||||
return $aFirstNames[rand(0, count($aFirstNames) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random last name taken from a (big) prefedined list
|
||||
*
|
||||
* @return string A random last name
|
||||
*/
|
||||
function GenerateLastName()
|
||||
{
|
||||
global $aNames;
|
||||
return $aNames[rand(0, count($aNames) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random country name taken from a prefedined list
|
||||
*
|
||||
* @return string A random city name
|
||||
*/
|
||||
function GenerateCountryName()
|
||||
{
|
||||
global $aCountries;
|
||||
return $aCountries[rand(0, count($aCountries) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random city name taken from a (big) prefedined list
|
||||
*
|
||||
* @return string A random city name
|
||||
*/
|
||||
function GenerateCityName()
|
||||
{
|
||||
global $aCities;
|
||||
return $aCities[rand(0, count($aCities) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random email address made of the first name, last name and organization's domain
|
||||
*
|
||||
* @return string A random email address
|
||||
*/
|
||||
function GenerateEmail($sFirstName, $sLastName)
|
||||
{
|
||||
if (rand(1, 20) > 18)
|
||||
{
|
||||
// some people (let's say 5~10%) have an irregular email address
|
||||
$sEmail = strtolower($this->CleanForEmail($sLastName))."@".strtolower($this->GenerateDomain());
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEmail = strtolower($this->CleanForEmail($sFirstName)).".".strtolower($this->CleanForEmail($sLastName))."@".strtolower($this->GenerateDomain());
|
||||
}
|
||||
return $sEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate (pseudo) random strings that follow a given pattern
|
||||
*
|
||||
* The template is made of any number of 'parts' separated by pipes '|'
|
||||
* Each part is either:
|
||||
* - domain() => returns a domain name for the current organization
|
||||
* - enum(aaa,bb,c,dddd) => returns randomly one of aaa,bb,c or dddd with the same
|
||||
* probability of occurence. If you want to change the probability you can repeat some values
|
||||
* i.e enum(most probable,most probable,most probable,most probable,most probable,rare)
|
||||
* - number(xxx-yyy) => a random number between xxx and yyy (bounds included)
|
||||
* note that if the first number (xxx) begins with a zero, then the result will zero padded
|
||||
* to the same number of digits as xxx.
|
||||
* All other 'part' that does not follow one of the above mentioned pattern is returned as is
|
||||
*
|
||||
* Example: GenerateString("enum(sw,rtr,gw)|number(00-99)|.|domain()")
|
||||
* will produce strings like "sw01.netcmdb.com" or "rtr45.itop.org"
|
||||
*
|
||||
* @param string $sTemplate The template used for generating the string
|
||||
* @return string The generated pseudo random the string
|
||||
*/
|
||||
function GenerateString($sTemplate)
|
||||
{
|
||||
$sResult = "";
|
||||
$aParts = split("\|", $sTemplate);
|
||||
foreach($aParts as $sPart)
|
||||
{
|
||||
if (preg_match("/domain\(\)/", $sPart, $aMatches))
|
||||
{
|
||||
$sResult .= strtolower($this->GenerateDomain());
|
||||
}
|
||||
elseif (preg_match("/enum\((.+)\)/", $sPart, $aMatches))
|
||||
{
|
||||
$sEnumValues = $aMatches[1];
|
||||
$aEnumValues = split(",", $sEnumValues);
|
||||
$sResult .= $aEnumValues[rand(0, count($aEnumValues) - 1)];
|
||||
}
|
||||
elseif (preg_match("/number\((\d+)-(\d+)\)/", $sPart, $aMatches))
|
||||
{
|
||||
$sStartNumber = $aMatches[1];
|
||||
if ($sStartNumber[0] == '0')
|
||||
{
|
||||
// number must be zero padded
|
||||
$sFormat = "%0".strlen($sStartNumber)."d";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFormat = "%d";
|
||||
}
|
||||
$sEndNumber = $aMatches[2];
|
||||
$sResult .= sprintf($sFormat, rand($sStartNumber, $sEndNumber));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sResult .= $sPart;
|
||||
}
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a foreign key by picking a random element of the given class in a set limited by the given search criteria
|
||||
*
|
||||
* Example: GenerateKey("bizLocation", array('org_id', $oGenerator->GetOrganizationId());
|
||||
* will produce the foreign key of a Location object picked at random in the same organization
|
||||
*
|
||||
* @param string $sClass The name of the class to search for
|
||||
* @param string $aFilterCriteria A hash array of filterCOde => FilterValue (the strict operator '=' is used )
|
||||
* @return mixed The key to an object of the given class, or null if none are found
|
||||
*/
|
||||
function GenerateKey($sClass, $aFilterCriteria)
|
||||
{
|
||||
$retKey = null;
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
foreach($aFilterCriteria as $sFilterCode => $filterValue)
|
||||
{
|
||||
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
if ($oSet->Count() > 0)
|
||||
{
|
||||
$max_count = $index = rand(1, $oSet->Count());
|
||||
do
|
||||
{
|
||||
$oObj = $oSet->Fetch();
|
||||
$index--;
|
||||
}
|
||||
while($index > 0);
|
||||
|
||||
if (!is_object($oObj))
|
||||
{
|
||||
echo "<pre>";
|
||||
echo "ERROR: non empty set, but invalid object picked! class='$sClass'\n";
|
||||
echo "Index chosen: $max_count\n";
|
||||
echo "The set is supposed to contain ".$oSet->Count()." object(s)\n";
|
||||
echo "Filter criteria:\n";
|
||||
print_r($aFilterCriteria);
|
||||
echo "</pre>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$retKey = $oObj->GetKey();
|
||||
}
|
||||
}
|
||||
return $retKey;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Protected methods
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Generate a (random) domain name consistent with the organization name & code
|
||||
*
|
||||
* The values are pulled from a (limited) predefined list. Note that a given
|
||||
* organization may have several domain names, so the result may be random
|
||||
*
|
||||
* @return string A domain name (like netcnmdb.com)
|
||||
*/
|
||||
protected function GenerateDomain()
|
||||
{
|
||||
if (is_array($this->m_OrganizationDomains))
|
||||
{
|
||||
$sDomain = $this->m_OrganizationDomains[rand(0, count($this->m_OrganizationDomains)-1)];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDomain = $this->m_OrganizationDomains;
|
||||
}
|
||||
return $sDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips accented characters from a string in order to produce a suitable email address
|
||||
*
|
||||
* @param string The text string to clean
|
||||
* @return string The cleanified text string
|
||||
*/
|
||||
protected function CleanForEmail($sText)
|
||||
{
|
||||
return str_replace(array("'", "é", "è", "ê", "ç", "à", "â", "ñ", "ö", "ä"), array("", "e", "e", "e", "c", "a", "a", "n", "oe", "ae"), $sText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an organization with the given code already exists in the database
|
||||
*
|
||||
* @param string $sCode The code to look for
|
||||
* @return boolean true if the given organization exists, false otherwise
|
||||
*/
|
||||
protected function OrganizationExists($sCode)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter('bizOrganization');
|
||||
$oFilter->AddCondition('code', $sCode, '=');
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
return ($oSet->Count() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for an organization with the given code in the database
|
||||
*
|
||||
* @param string $Id The organization Id to look for
|
||||
* @return cmdbOrganization the organization if it exists, null otherwise
|
||||
*/
|
||||
protected function GetOrganization($sId)
|
||||
{
|
||||
$oOrg = null;
|
||||
$oFilter = new CMDBSearchFilter('bizOrganization');
|
||||
$oFilter->AddCondition('pkey', $sId, '=');
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
if ($oSet->Count() > 0)
|
||||
{
|
||||
$oOrg = $oSet->Fetch(); // Let's take the first one found
|
||||
}
|
||||
return $oOrg;
|
||||
}
|
||||
}
|
||||
?>
|
||||
791
core/dbobject.class.php
Normal file
791
core/dbobject.class.php
Normal file
@@ -0,0 +1,791 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ???
|
||||
* the class a persistent object must be derived from
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
require_once('metamodel.class.php');
|
||||
|
||||
|
||||
/**
|
||||
* A persistent object, as defined by the metamodel
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class DBObject
|
||||
{
|
||||
private static $m_aMemoryObjectsByClass = array();
|
||||
|
||||
private $m_bIsInDB = false; // true IIF the object is mapped to a DB record
|
||||
private $m_iKey = null;
|
||||
private $m_aCurrValues = array();
|
||||
private $m_aOrigValues = array();
|
||||
|
||||
private $m_bFullyLoaded = false; // Compound objects can be partially loaded
|
||||
private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode
|
||||
|
||||
// Use the MetaModel::NewObject to build an object (do we have to force it?)
|
||||
public function __construct($aRow = null)
|
||||
{
|
||||
if (!empty($aRow))
|
||||
{
|
||||
$this->FromRow($aRow);
|
||||
$this->m_bFullyLoaded = $this->IsFullyLoaded();
|
||||
return;
|
||||
}
|
||||
// Creation of brand new object
|
||||
//
|
||||
|
||||
$this->m_iKey = self::GetNextTempId(get_class($this));
|
||||
|
||||
// set default values
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue();
|
||||
$this->m_aOrigValues[$sAttCode] = null;
|
||||
// ??? $this->m_aLoadedAtt[$sAttCode] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an Id for memory objects
|
||||
static protected function GetNextTempId($sClass)
|
||||
{
|
||||
if (!array_key_exists($sClass, self::$m_aMemoryObjectsByClass))
|
||||
{
|
||||
self::$m_aMemoryObjectsByClass[$sClass] = 0;
|
||||
}
|
||||
self::$m_aMemoryObjectsByClass[$sClass]++;
|
||||
return (- self::$m_aMemoryObjectsByClass[$sClass]);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$sRet = '';
|
||||
$sClass = get_class($this);
|
||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||
$iPKey = $this->GetKey();
|
||||
$sRet .= "<b title=\"$sRootClass\">$sClass</b>::$iPKey<br/>\n";
|
||||
$sRet .= "<ul class=\"treeview\">\n";
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
$sRet .= "<li>".$oAttDef->GetLabel()." = ".$this->GetAsHtml($sAttCode)."</li>\n";
|
||||
}
|
||||
$sRet .= "</ul>";
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
// Restore initial values... mmmm, to be discussed
|
||||
public function DBRevert()
|
||||
{
|
||||
$this->Reload();
|
||||
}
|
||||
|
||||
protected function IsFullyLoaded()
|
||||
{
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
@$bIsLoaded = $this->m_aLoadedAtt[$sAttCode];
|
||||
if ($bIsLoaded !== true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function Reload()
|
||||
{
|
||||
assert($this->m_bIsInDB);
|
||||
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey);
|
||||
if (empty($aRow))
|
||||
{
|
||||
trigger_error("Failed to reload object of class '".get_class($this)."', pkey = ".$this->m_iKey, E_USER_ERROR);
|
||||
}
|
||||
$this->FromRow($aRow);
|
||||
|
||||
// Process linked set attributes
|
||||
//
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
if (!$oAttDef->IsLinkSet()) continue;
|
||||
|
||||
// Load the link information
|
||||
$sLinkClass = $oAttDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
|
||||
// The class to target is not the current class, because if this is a derived class,
|
||||
// it may differ from the target class, then things start to become confusing
|
||||
$oRemoteExtKeyAtt = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToMe);
|
||||
$sMyClass = $oRemoteExtKeyAtt->GetTargetClass();
|
||||
|
||||
$oMyselfSearch = new DBObjectSearch($sMyClass);
|
||||
$oMyselfSearch->AddCondition('pkey', $this->m_iKey, '=');
|
||||
|
||||
$oLinkSearch = new DBObjectSearch($sLinkClass);
|
||||
$oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe);
|
||||
$oLinks = new DBObjectSet($oLinkSearch);
|
||||
|
||||
$this->m_aCurrValues[$sAttCode] = $oLinks;
|
||||
$this->m_aOrigValues[$sAttCode] = clone $this->m_aCurrValues[$sAttCode];
|
||||
$this->m_aLoadedAtt[$sAttCode] = true;
|
||||
}
|
||||
|
||||
$this->m_bFullyLoaded = true;
|
||||
}
|
||||
|
||||
protected function FromRow($aRow)
|
||||
{
|
||||
$this->m_iKey = null;
|
||||
$this->m_bIsInDB = true;
|
||||
$this->m_aCurrValues = array();
|
||||
$this->m_aOrigValues = array();
|
||||
$this->m_aLoadedAtt = array();
|
||||
|
||||
// Get the key
|
||||
//
|
||||
$sKeyField = "pkey";
|
||||
if (!array_key_exists($sKeyField, $aRow))
|
||||
{
|
||||
// #@# Bug ?
|
||||
trigger_error("Missing key for class '".get_class($this)."'", E_USER_ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iPKey = $aRow[$sKeyField];
|
||||
if (!self::IsValidPKey($iPKey))
|
||||
{
|
||||
trigger_error("An object PKey must be an integer value ($iPKey)", E_USER_NOTICE);
|
||||
}
|
||||
$this->m_iKey = $iPKey;
|
||||
}
|
||||
|
||||
// Build the object from an array of "attCode"=>"value")
|
||||
//
|
||||
$bFullyLoaded = true; // ... set to false if any attribute is not found
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
// Say something, whatever the type of attribute
|
||||
$this->m_aLoadedAtt[$sAttCode] = false;
|
||||
|
||||
// Skip links (could not be loaded by the mean of this query)
|
||||
if ($oAttDef->IsLinkSet()) continue;
|
||||
|
||||
if (array_key_exists($sAttCode, $aRow))
|
||||
{
|
||||
$sValue = $oAttDef->SQLValueToRealValue($aRow[$sAttCode]);
|
||||
$this->m_aCurrValues[$sAttCode] = $sValue;
|
||||
$this->m_aOrigValues[$sAttCode] = $sValue;
|
||||
$this->m_aLoadedAtt[$sAttCode] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This attribute was expected and not found in the query columns
|
||||
$bFullyLoaded = false;
|
||||
}
|
||||
}
|
||||
return $bFullyLoaded;
|
||||
}
|
||||
|
||||
public function Set($sAttCode, $value)
|
||||
{
|
||||
if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this))))
|
||||
{
|
||||
trigger_error("Unknown attribute code '$sAttCode' for the class ".get_class($this), E_USER_ERROR);
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($this->m_bIsInDB && !$this->m_bFullyLoaded)
|
||||
{
|
||||
// First time Set is called... ensure that the object gets fully loaded
|
||||
// Otherwise we would lose the values on a further Reload
|
||||
// + consistency does not make sense !
|
||||
$this->Reload();
|
||||
}
|
||||
if($oAttDef->IsScalar() && !$oAttDef->IsNullAllowed() && is_null($value))
|
||||
{
|
||||
trigger_error("null not allowed for attribute '$sAttCode', setting default value", E_USER_NOTICE);
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue();
|
||||
return;
|
||||
}
|
||||
if ($oAttDef->IsExternalKey() && is_object($value))
|
||||
{
|
||||
// Setting an external key with a whole object (instead of just an ID)
|
||||
// let's initialize also the external fields that depend on it
|
||||
// (useful when building objects in memory and not from a query)
|
||||
if ( (get_class($value) != $oAttDef->GetTargetClass()) && (!is_subclass_of($value, $oAttDef->GetTargetClass())))
|
||||
{
|
||||
trigger_error("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored", E_USER_NOTICE);
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aCurrValues[$sAttCode] = $value->GetKey();
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
|
||||
{
|
||||
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
{
|
||||
$this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(!$oAttDef->IsScalar() && !is_object($value))
|
||||
{
|
||||
trigger_error("scalar not allowed for attribute '$sAttCode', setting default value (empty list)", E_USER_NOTICE);
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue();
|
||||
return;
|
||||
}
|
||||
if($oAttDef->IsLinkSet())
|
||||
{
|
||||
if((get_class($value) != 'DBObjectSet') && !is_subclass_of($value, 'DBObjectSet'))
|
||||
{
|
||||
trigger_error("expecting a set of persistent objects (found a '".get_class($value)."'), setting default value (empty list)", E_USER_NOTICE);
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue();
|
||||
return;
|
||||
}
|
||||
|
||||
$oObjectSet = $value;
|
||||
$sSetClass = $oObjectSet->GetClass();
|
||||
$sLinkClass = $oAttDef->GetLinkedClass();
|
||||
// not working fine :-( if (!is_subclass_of($sSetClass, $sLinkClass))
|
||||
if ($sSetClass != $sLinkClass)
|
||||
{
|
||||
trigger_error("expecting a set of '$sLinkClass' objects (found a set of '$sSetClass'), setting default value (empty list)", E_USER_NOTICE);
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue();
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->MakeRealValue($value);
|
||||
}
|
||||
|
||||
public function Get($sAttCode)
|
||||
{
|
||||
if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this))))
|
||||
{
|
||||
trigger_error("Unknown attribute code '$sAttCode' for the class ".get_class($this), E_USER_ERROR);
|
||||
}
|
||||
if ($this->m_bIsInDB && !$this->m_aLoadedAtt[$sAttCode])
|
||||
{
|
||||
// #@# non-scalar attributes.... handle that differentely
|
||||
$this->Reload();
|
||||
}
|
||||
$this->ComputeFields();
|
||||
return $this->m_aCurrValues[$sAttCode];
|
||||
}
|
||||
|
||||
public function ComputeFields()
|
||||
{
|
||||
if (is_callable(array($this, 'ComputeValues')))
|
||||
{
|
||||
// First check that we are not currently computing the fields
|
||||
// (yes, we need to do some things like Set/Get to compute the fields which will in turn trigger the update...)
|
||||
foreach (debug_backtrace() as $aCallInfo)
|
||||
{
|
||||
if (!array_key_exists("class", $aCallInfo)) continue;
|
||||
if ($aCallInfo["class"] != get_class($this)) continue;
|
||||
if ($aCallInfo["function"] != "ComputeValues") continue;
|
||||
return; //skip!
|
||||
}
|
||||
|
||||
$this->ComputeValues();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetHyperLink($sLabel = "")
|
||||
{
|
||||
if (empty($sLabel))
|
||||
{
|
||||
$sLabel = $this->GetName();
|
||||
}
|
||||
$aAvailableFields = array($sLabel);
|
||||
return call_user_func(array(get_class($this), 'MakeHyperLink'), get_class($this), $this->GetKey(), $aAvailableFields);
|
||||
}
|
||||
|
||||
public function GetAsHTML($sAttCode)
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
$oAtt = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
|
||||
$aExtKeyFriends = MetaModel::GetExtKeyFriends($sClass, $sAttCode);
|
||||
if (count($aExtKeyFriends) > 0)
|
||||
{
|
||||
// This attribute is an ext key (in this class or in another class)
|
||||
// The corresponding value is an id of the remote object
|
||||
// Let's try to use the corresponding external fields for a sexy display
|
||||
|
||||
$aAvailableFields = array();
|
||||
foreach ($aExtKeyFriends as $sDispAttCode => $oExtField)
|
||||
{
|
||||
$aAvailableFields[$oExtField->GetExtAttCode()] = $oExtField->GetAsHTML($this->Get($oExtField->GetCode()));
|
||||
}
|
||||
|
||||
$sTargetClass = $oAtt->GetTargetClass(EXTKEY_ABSOLUTE);
|
||||
return call_user_func(array(get_class($this), 'MakeHyperLink'), $sTargetClass, $this->Get($sAttCode), $aAvailableFields);
|
||||
}
|
||||
|
||||
// That's a standard attribute (might be an ext field or a direct field, etc.)
|
||||
return $oAtt->GetAsHTML($this->Get($sAttCode));
|
||||
}
|
||||
|
||||
public function GetAsXML($sAttCode)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsXML($this->Get($sAttCode));
|
||||
}
|
||||
|
||||
public function GetAsCSV($sAttCode, $sSeparator = ';', $sSepEscape = ',')
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sSepEscape);
|
||||
}
|
||||
|
||||
// could be in the metamodel ?
|
||||
public static function IsValidPKey($value)
|
||||
{
|
||||
return ((string)$value === (string)(int)$value);
|
||||
}
|
||||
|
||||
public function GetKey()
|
||||
{
|
||||
return $this->m_iKey;
|
||||
}
|
||||
public function SetKey($iNewKey)
|
||||
{
|
||||
if (!self::IsValidPKey($iNewKey))
|
||||
{
|
||||
trigger_error("An object PKey must be an integer value ($iNewKey)", E_USER_ERROR);
|
||||
}
|
||||
|
||||
if ($this->m_bIsInDB && !empty($this->m_iKey) && ($this->m_iKey != $iNewKey))
|
||||
{
|
||||
trigger_error("Changing the key ({$this->m_iKey} to $iNewKey) on an object (class {".get_class($this).") wich already exists in the Database", E_USER_NOTICE);
|
||||
}
|
||||
$this->m_iKey = $iNewKey;
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
$sNameAttCode = MetaModel::GetNameAttributeCode(get_class($this));
|
||||
if (empty($sNameAttCode))
|
||||
{
|
||||
return $this->m_iKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->Get($sNameAttCode);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetState()
|
||||
{
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||
if (empty($sStateAttCode))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aStates = MetaModel::EnumStates(get_class($this));
|
||||
return $aStates[$this->Get($sStateAttCode)]['label'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
|
||||
* for the given attribute in the current state of the object
|
||||
* @param string $sAttCode The code of the attribute
|
||||
* @return integer Flags: the binary combination of the flags applicable to this attribute
|
||||
*/
|
||||
public function GetAttributeFlags($sAttCode)
|
||||
{
|
||||
$iFlags = 0; // By default (if no life cycle) no flag at all
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||
if (!empty($sStateAttCode))
|
||||
{
|
||||
$iFlags = MetaModel::GetAttributeFlags(get_class($this), $this->Get($sStateAttCode), $sAttCode);
|
||||
}
|
||||
return $iFlags;
|
||||
}
|
||||
|
||||
// check if the given (or current) value is suitable for the attribute
|
||||
public function CheckValue($sAttCode, $value = null)
|
||||
{
|
||||
$oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAtt->IsExternalKey())
|
||||
{
|
||||
if (!$oAtt->IsNullAllowed() || ($this->Get($sAttCode) != 0) )
|
||||
{
|
||||
$oTargetObj = MetaModel::GetObject($oAtt->GetTargetClass(), $this->Get($sAttCode));
|
||||
if (!$oTargetObj)
|
||||
{
|
||||
echo "Invalid value (".$this->Get($sAttCode).") for ExtKey $sAttCode.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// check attributes together
|
||||
public function CheckConsistency()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if it is allowed to record the new object into the database
|
||||
// a displayable error is returned
|
||||
// Note: checks the values and consistency
|
||||
public function CheckToInsert()
|
||||
{
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
if (!$this->CheckValue($sAttCode)) return false;
|
||||
}
|
||||
if (!$this->CheckConsistency()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if it is allowed to update the existing object into the database
|
||||
// a displayable error is returned
|
||||
// Note: checks the values and consistency
|
||||
public function CheckToUpdate()
|
||||
{
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
if (!$this->CheckValue($sAttCode)) return false;
|
||||
}
|
||||
if (!$this->CheckConsistency()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if it is allowed to delete the existing object from the database
|
||||
// a displayable error is returned
|
||||
public function CheckToDelete()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function ListChangedValues(array $aProposal)
|
||||
{
|
||||
$aDelta = array();
|
||||
foreach ($aProposal as $sAtt => $proposedValue)
|
||||
{
|
||||
if (!array_key_exists($sAtt, $this->m_aOrigValues) || ($this->m_aOrigValues[$sAtt] != $proposedValue))
|
||||
{
|
||||
$aDelta[$sAtt] = $proposedValue;
|
||||
}
|
||||
}
|
||||
return $aDelta;
|
||||
}
|
||||
|
||||
// List the attributes that have been changed
|
||||
// Returns an array of attname => currentvalue
|
||||
public function ListChanges()
|
||||
{
|
||||
return $this->ListChangedValues($this->m_aCurrValues);
|
||||
}
|
||||
|
||||
// used both by insert/update
|
||||
private function DBWriteLinks()
|
||||
{
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
if (!$oAttDef->IsLinkSet()) continue;
|
||||
|
||||
$oLinks = $this->Get($sAttCode);
|
||||
$oLinks->Rewind();
|
||||
while ($oLinkedObject = $oLinks->Fetch())
|
||||
{
|
||||
$oLinkedObject->Set($oAttDef->GetExtKeyToMe(), $this->m_iKey);
|
||||
$oLinkedObject->DBWrite();
|
||||
}
|
||||
|
||||
// Delete the objects that were initialy present and disappeared from the list
|
||||
// (if any)
|
||||
$oOriginalSet = $this->m_aOrigValues[$sAttCode];
|
||||
if ($oOriginalSet != null)
|
||||
{
|
||||
$aOriginalList = $oOriginalSet->ToArray();
|
||||
$aNewSet = $oLinks->ToArray();
|
||||
$aToDelete = array_diff($aOriginalList, $aNewSet);
|
||||
foreach ($aToDelete as $iKey => $oObject)
|
||||
{
|
||||
$oObject->DBDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function DBInsertSingleTable($sTableClass)
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
|
||||
// fields in first array, values in the second
|
||||
$aFieldsToWrite = array();
|
||||
$aValuesToWrite = array();
|
||||
|
||||
if (!empty($this->m_iKey) && ($this->m_iKey >= 0))
|
||||
{
|
||||
// Add it to the list of fields to write
|
||||
$aFieldsToWrite[] = MetaModel::DBGetKey($sTableClass);
|
||||
$aValuesToWrite[] = CMDBSource::Quote($this->m_iKey);
|
||||
}
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
// Skip this attribute if not defined in this table
|
||||
if (!MetaModel::IsAttributeOrigin($sTableClass, $sAttCode)) continue;
|
||||
if ($oAttDef->IsDirectField())
|
||||
{
|
||||
$aFieldsToWrite[] = $oAttDef->GetSQLExpr();
|
||||
$aValuesToWrite[] = CMDBSource::Quote($oAttDef->RealValueToSQLValue($this->m_aCurrValues[$sAttCode]));
|
||||
}
|
||||
}
|
||||
|
||||
if (count($aValuesToWrite) == 0) return false;
|
||||
|
||||
$sTable = MetaModel::DBGetTable($sTableClass);
|
||||
$sInsertSQL = "INSERT INTO $sTable (".join(",", $aFieldsToWrite).") VALUES (".join(", ", $aValuesToWrite).")";
|
||||
|
||||
$iNewKey = CMDBSource::InsertInto($sInsertSQL);
|
||||
// Note that it is possible to have a key defined here, and the autoincrement expected, this is acceptable in a non root class
|
||||
if (empty($this->m_iKey))
|
||||
{
|
||||
// Take the autonumber
|
||||
$this->m_iKey = $iNewKey;
|
||||
}
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
// Insert of record for the new object into the database
|
||||
// Returns the key of the newly created object
|
||||
public function DBInsert()
|
||||
{
|
||||
if ($this->m_bIsInDB)
|
||||
{
|
||||
trigger_error("The object already exists into the Database, you may want to use the clone function", E_USER_ERROR);
|
||||
}
|
||||
|
||||
$sClass = get_class($this);
|
||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||
|
||||
// Ensure the update of the values (we are accessing the data directly)
|
||||
$this->ComputeFields();
|
||||
|
||||
if ($this->m_iKey < 0)
|
||||
{
|
||||
// This was a temporary "memory" key: discard it so that DBInsertSingleTable will not try to use it!
|
||||
$this->m_iKey = null;
|
||||
}
|
||||
|
||||
// If not automatically computed, then check that the key is given by the caller
|
||||
if (!MetaModel::IsAutoIncrementKey($sRootClass))
|
||||
{
|
||||
if (empty($this->m_iKey))
|
||||
{
|
||||
trigger_error("Missing key for the object to write - This class is supposed to have a user defined key, not an autonumber", E_USER_NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
// First query built upon on the root class, because the ID must be created first
|
||||
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||
|
||||
// Then do the leaf class, if different from the root class
|
||||
if ($sClass != $sRootClass)
|
||||
{
|
||||
$this->DBInsertSingleTable($sClass);
|
||||
}
|
||||
|
||||
// Then do the other classes
|
||||
foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
|
||||
{
|
||||
if ($sParentClass == $sRootClass) continue;
|
||||
if (MetaModel::DBGetTable($sParentClass) == "") continue;
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->DBWriteLinks();
|
||||
|
||||
// Reload to update the external attributes
|
||||
$this->m_bIsInDB = true;
|
||||
$this->Reload();
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
// Creates a copy of the current object into the database
|
||||
// Returns the id of the newly created object
|
||||
public function DBClone($iNewKey = null)
|
||||
{
|
||||
$this->m_bIsInDB = false;
|
||||
$this->m_iKey = $iNewKey;
|
||||
return $this->DBInsert();
|
||||
}
|
||||
|
||||
// Update a record
|
||||
public function DBUpdate()
|
||||
{
|
||||
if (!$this->m_bIsInDB)
|
||||
{
|
||||
trigger_error("DBUpdate: could not update a newly created object, please call DBInsert instead", E_USER_ERROR);
|
||||
}
|
||||
$aChanges = $this->ListChanges();
|
||||
if (count($aChanges) == 0)
|
||||
{
|
||||
trigger_error("Attempting to update an unchanged object", E_USER_NOTICE);
|
||||
return;
|
||||
}
|
||||
$bHasANewExternalKeyValue = false;
|
||||
foreach($aChanges as $sAttCode => $valuecurr)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsExternalKey()) $bHasANewExternalKeyValue = true;
|
||||
if (!$oAttDef->IsDirectField()) unset($aChanges[$sAttCode]);
|
||||
}
|
||||
|
||||
// Update scalar attributes
|
||||
if (count($aChanges) != 0)
|
||||
{
|
||||
$oFilter = new DBObjectSearch(get_class($this));
|
||||
$oFilter->AddCondition('pkey', $this->m_iKey, '=');
|
||||
|
||||
$sSQL = MetaModel::MakeUpdateQuery($oFilter, $aChanges);
|
||||
CMDBSource::Query($sSQL);
|
||||
}
|
||||
|
||||
$this->DBWriteLinks();
|
||||
|
||||
// Reload to get the external attributes
|
||||
if ($bHasANewExternalKeyValue) $this->Reload();
|
||||
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
// Make the current changes persistent - clever wrapper for Insert or Update
|
||||
public function DBWrite()
|
||||
{
|
||||
if ($this->m_bIsInDB)
|
||||
{
|
||||
return $this->DBUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->DBInsert();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete a record
|
||||
public function DBDelete()
|
||||
{
|
||||
$oFilter = new DBObjectSearch(get_class($this));
|
||||
$oFilter->AddCondition('pkey', $this->m_iKey, '=');
|
||||
|
||||
$sSQL = MetaModel::MakeDeleteQuery($oFilter);
|
||||
CMDBSource::Query($sSQL);
|
||||
|
||||
$this->m_bIsInDB = false;
|
||||
$this->m_iKey = null;
|
||||
}
|
||||
|
||||
public function EnumTransitions()
|
||||
{
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||
if (empty($sStateAttCode)) return array();
|
||||
|
||||
$sState = $this->Get(MetaModel::GetStateAttributeCode(get_class($this)));
|
||||
return MetaModel::EnumTransitions(get_class($this), $sState);
|
||||
}
|
||||
|
||||
public function ApplyStimulus($sStimulusCode)
|
||||
{
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||
if (empty($sStateAttCode)) return false;
|
||||
|
||||
MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli(get_class($this)));
|
||||
|
||||
$aStateTransitions = $this->EnumTransitions();
|
||||
$aTransitionDef = $aStateTransitions[$sStimulusCode];
|
||||
|
||||
// Change the state before proceeding to the actions, this is necessary because an action might
|
||||
// trigger another stimuli (alternative: push the stimuli into a queue)
|
||||
$this->Set($sStateAttCode, $aTransitionDef['target_state']);
|
||||
|
||||
// $aTransitionDef is an
|
||||
// array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD
|
||||
|
||||
$bSuccess = true;
|
||||
foreach ($aTransitionDef['actions'] as $sActionHandler)
|
||||
{
|
||||
// std PHP spec
|
||||
$aActionCallSpec = array($this, $sActionHandler);
|
||||
|
||||
if (!is_callable($aActionCallSpec))
|
||||
{
|
||||
trigger_error("Unable to call action: ".get_class($this)."::$sActionHandler", E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
|
||||
// if one call fails, the whole is considered as failed
|
||||
if (!$bRet) $bSuccess = false;
|
||||
}
|
||||
|
||||
return $bSuccess;
|
||||
}
|
||||
|
||||
// Return an empty set for the parent of all
|
||||
public static function GetRelationQueries($sRelCode)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array())
|
||||
{
|
||||
foreach (MetaModel::EnumRelationQueries(get_class($this), $sRelCode) as $sDummy => $aQueryInfo)
|
||||
{
|
||||
MetaModel::DbgTrace("object=".$this->GetKey().", depth=$iMaxDepth, rel=".$aQueryInfo["sQuery"]);
|
||||
$sQuery = $aQueryInfo["sQuery"];
|
||||
$bPropagate = $aQueryInfo["bPropagate"];
|
||||
$iDistance = $aQueryInfo["iDistance"];
|
||||
|
||||
$iDepth = $bPropagate ? $iMaxDepth - 1 : 0;
|
||||
|
||||
$oFlt = DBObjectSearch::FromSibusQL($sQuery, array(), $this);
|
||||
$oObjSet = new DBObjectSet($oFlt);
|
||||
while ($oObj = $oObjSet->Fetch())
|
||||
{
|
||||
$sRootClass = MetaModel::GetRootClass(get_class($oObj));
|
||||
$sObjKey = $oObj->GetKey();
|
||||
if (array_key_exists($sRootClass, $aResults))
|
||||
{
|
||||
if (array_key_exists($sObjKey, $aResults[$sRootClass]))
|
||||
{
|
||||
continue; // already visited, skip
|
||||
}
|
||||
}
|
||||
|
||||
$aResults[$sRootClass][$sObjKey] = $oObj;
|
||||
if ($iDepth > 0)
|
||||
{
|
||||
$oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aResults;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
967
core/dbobjectsearch.class.php
Normal file
967
core/dbobjectsearch.class.php
Normal file
@@ -0,0 +1,967 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Define filters for a given class of objects (formerly named "filter")
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Sibusql - value set start
|
||||
* @package iTopORM
|
||||
* @info zis is private
|
||||
*/
|
||||
define('VS_START', '{');
|
||||
/**
|
||||
* Sibusql - value set end
|
||||
* @package iTopORM
|
||||
*/
|
||||
define('VS_END', '}');
|
||||
|
||||
|
||||
define('SIBUSQLPARAMREGEXP', "/\\$\\[(.*)\\:(.*)\\:(.*)\\]/U");
|
||||
define('SIBUSQLTHISREGEXP', "/this\\.(.*)/U");
|
||||
|
||||
|
||||
/**
|
||||
* Define filters for a given class of objects (formerly named "filter")
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @mytagrom youpi
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
class DBObjectSearch
|
||||
{
|
||||
private $m_sClass;
|
||||
private $m_sClassAlias;
|
||||
private $m_aClasses; // queried classes (alias => class name)
|
||||
private $m_oSearchCondition;
|
||||
private $m_aFullText;
|
||||
private $m_aPointingTo;
|
||||
private $m_aReferencedBy;
|
||||
private $m_aRelatedTo;
|
||||
|
||||
public function __construct($sClass, $sClassAlias = '')
|
||||
{
|
||||
if (empty($sClassAlias)) $sClassAlias = $sClass;
|
||||
assert('is_string($sClass)');
|
||||
assert('MetaModel::IsValidClass($sClass)'); // #@# could do better than an assert, or at least give the caller's reference
|
||||
// => idee d'un assert avec call stack (autre utilisation = echec sur query SQL)
|
||||
if (empty($sClassAlias)) $sClassAlias = $sClass;
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sClassAlias = $sClassAlias;
|
||||
$this->m_aClasses = array($sClassAlias => $sClass);
|
||||
$this->m_oSearchCondition = new TrueExpression;
|
||||
$this->m_aFullText = array();
|
||||
$this->m_aPointingTo = array();
|
||||
$this->m_aReferencedBy = array();
|
||||
$this->m_aRelatedTo = array();
|
||||
}
|
||||
|
||||
public function IsAny()
|
||||
{
|
||||
// #@# todo - if (!$this->m_oSearchCondition->IsTrue()) return false;
|
||||
if (count($this->m_aFullText) > 0) return false;
|
||||
if (count($this->m_aPointingTo) > 0) return false;
|
||||
if (count($this->m_aReferencedBy) > 0) return false;
|
||||
if (count($this->m_aRelatedTo) > 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function Describe()
|
||||
{
|
||||
// To replace __Describe
|
||||
}
|
||||
|
||||
public function DescribeConditionPointTo($sExtKeyAttCode)
|
||||
{
|
||||
if (!isset($this->m_aPointingTo[$sExtKeyAttCode])) return "";
|
||||
$oFilter = $this->m_aPointingTo[$sExtKeyAttCode];
|
||||
if ($oFilter->IsAny()) return "";
|
||||
$oAtt = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode);
|
||||
return $oAtt->GetLabel()." having ({$oFilter->DescribeConditions()})";
|
||||
}
|
||||
|
||||
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode)
|
||||
{
|
||||
if (!isset($this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode])) return "";
|
||||
$oFilter = $this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode];
|
||||
if ($oFilter->IsAny()) return "";
|
||||
$oAtt = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||
return "being ".$oAtt->GetLabel()." for ".$sForeignClass."s in ({$oFilter->DescribeConditions()})";
|
||||
}
|
||||
|
||||
public function DescribeConditionRelTo($aRelInfo)
|
||||
{
|
||||
$oFilter = $aRelInfo['flt'];
|
||||
$sRelCode = $aRelInfo['relcode'];
|
||||
$iMaxDepth = $aRelInfo['maxdepth'];
|
||||
return "related ($sRelCode... peut mieux faire !, $iMaxDepth dig depth) to a {$oFilter->GetClass()} ({$oFilter->DescribeConditions()})";
|
||||
}
|
||||
|
||||
public function DescribeConditions()
|
||||
{
|
||||
$aConditions = array();
|
||||
|
||||
$aCondFT = array();
|
||||
foreach($this->m_aFullText as $sFullText)
|
||||
{
|
||||
$aCondFT[] = " contain word(s) '$sFullText'";
|
||||
}
|
||||
if (count($aCondFT) > 0)
|
||||
{
|
||||
$aConditions[] = "which ".implode(" and ", $aCondFT);
|
||||
}
|
||||
|
||||
// #@# todo - review textual description of the JOIN and search condition (is that still feasible?)
|
||||
$aConditions[] = $this->RenderCondition();
|
||||
|
||||
$aCondPoint = array();
|
||||
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter)
|
||||
{
|
||||
if ($oFilter->IsAny()) continue;
|
||||
$aCondPoint[] = $this->DescribeConditionPointTo($sExtKeyAttCode);
|
||||
}
|
||||
if (count($aCondPoint) > 0)
|
||||
{
|
||||
$aConditions[] = implode(" and ", $aCondPoint);
|
||||
}
|
||||
|
||||
$aCondReferred= array();
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
if ($oForeignFilter->IsAny()) continue;
|
||||
$aCondReferred[] = $this->DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode);
|
||||
}
|
||||
}
|
||||
foreach ($this->m_aRelatedTo as $aRelInfo)
|
||||
{
|
||||
$aCondReferred[] = $this->DescribeConditionRelTo($aRelInfo);
|
||||
}
|
||||
if (count($aCondReferred) > 0)
|
||||
{
|
||||
$aConditions[] = implode(" and ", $aCondReferred);
|
||||
}
|
||||
|
||||
return implode(" and ", $aConditions);
|
||||
}
|
||||
|
||||
public function __DescribeHTML()
|
||||
{
|
||||
$sConditionDesc = $this->DescribeConditions();
|
||||
if (!empty($sConditionDesc))
|
||||
{
|
||||
return "Objects of class '$this->m_sClass', $sConditionDesc";
|
||||
}
|
||||
return "Any object of class '$this->m_sClass'";
|
||||
}
|
||||
|
||||
protected function TransferConditionExpression($oFilter, $aTranslation)
|
||||
{
|
||||
$oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false);
|
||||
$this->AddConditionExpression($oTranslated);
|
||||
}
|
||||
|
||||
public function ResetCondition()
|
||||
{
|
||||
$this->m_oSearchCondition = new TrueExpression();
|
||||
// ? is that enough, do I need to rebuild the list after the subqueries ?
|
||||
$this->m_aClasses = array($this->m_sClassAlias => $this->m_sClass);
|
||||
}
|
||||
|
||||
public function AddConditionExpression($oExpression)
|
||||
{
|
||||
$this->m_oSearchCondition = $this->m_oSearchCondition->LogAnd($oExpression);
|
||||
}
|
||||
|
||||
public function AddCondition($sFilterCode, $value, $sOpCode = null)
|
||||
{
|
||||
MyHelpers::CheckKeyInArray('filter code', $sFilterCode, MetaModel::GetClassFilterDefs($this->m_sClass));
|
||||
$oFilterDef = MetaModel::GetClassFilterDef($this->m_sClass, $sFilterCode);
|
||||
|
||||
if (empty($sOpCode))
|
||||
{
|
||||
$sOpCode = $oFilterDef->GetLooseOperator();
|
||||
}
|
||||
MyHelpers::CheckKeyInArray('operator', $sOpCode, $oFilterDef->GetOperators());
|
||||
|
||||
// Preserve backward compatibility - quick n'dirty way to change that API semantic
|
||||
//
|
||||
$oField = new FieldExpression($sFilterCode, $this->m_sClassAlias);
|
||||
switch($sOpCode)
|
||||
{
|
||||
case 'SameDay':
|
||||
case 'SameMonth':
|
||||
case 'SameYear':
|
||||
case 'Today':
|
||||
case '>|':
|
||||
case '<|':
|
||||
case '=|':
|
||||
throw new CoreException('Deprecated operator, please consider using OQL (SQL) expressions like "(TO_DAYS(NOW()) - TO_DAYS(x)) AS AgeDays"', array('operator' => $sOpCode));
|
||||
break;
|
||||
|
||||
case "IN":
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
break;
|
||||
|
||||
case "NOTIN":
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
|
||||
$sOQLCondition = $oField->Render()." NOT IN $sListExpr";
|
||||
break;
|
||||
|
||||
case 'Contains':
|
||||
$oRightExpr = new ScalarExpression("%$value%");
|
||||
$sOperator = 'LIKE';
|
||||
break;
|
||||
|
||||
case 'Begins with':
|
||||
$oRightExpr = new ScalarExpression("$value%");
|
||||
$sOperator = 'LIKE';
|
||||
break;
|
||||
|
||||
case 'Finishes with':
|
||||
$oRightExpr = new ScalarExpression("%$value");
|
||||
$sOperator = 'LIKE';
|
||||
break;
|
||||
|
||||
default:
|
||||
$oRightExpr = new ScalarExpression($value);
|
||||
$sOperator = $sOpCode;
|
||||
}
|
||||
|
||||
switch($sOpCode)
|
||||
{
|
||||
case "IN":
|
||||
case "NOTIN":
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
break;
|
||||
|
||||
case 'Contains':
|
||||
case 'Begins with':
|
||||
case 'Finishes with':
|
||||
default:
|
||||
$oNewCondition = new BinaryExpression($oField, $sOperator, $oRightExpr);
|
||||
}
|
||||
|
||||
$this->AddConditionExpression($oNewCondition);
|
||||
}
|
||||
|
||||
public function AddCondition_FullText($sFullText)
|
||||
{
|
||||
$this->m_aFullText[] = $sFullText;
|
||||
}
|
||||
|
||||
protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
$sOrigAlias = $this->m_sClassAlias;
|
||||
if (array_key_exists($sOrigAlias, $aClassAliases))
|
||||
{
|
||||
$this->m_sClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $oFilter->GetClass());
|
||||
// Translate the condition expression with the new alias
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $this->m_sClassAlias;
|
||||
}
|
||||
|
||||
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter)
|
||||
{
|
||||
$oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
$oForeignFilter->AddToNameSpace($aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode)
|
||||
{
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->AddCondition_PointingTo_InNameSpace($oFilter, $sExtKeyAttCode, $this->m_aClasses, $aAliasTranslation);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode))
|
||||
{
|
||||
trigger_error("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored", E_USER_WARNING);
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($oFilter->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
trigger_error("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}", E_USER_ERROR);
|
||||
}
|
||||
|
||||
if (array_key_exists($sExtKeyAttCode, $this->m_aPointingTo))
|
||||
{
|
||||
$this->m_aPointingTo[$sExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOrigAlias = $oFilter->GetClassAlias();
|
||||
$sKeyClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $oFilter->GetClass());
|
||||
if ($sKeyClassAlias != $sOrigAlias)
|
||||
{
|
||||
// Translate the condition expression with the new alias
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $sKeyClassAlias;
|
||||
}
|
||||
|
||||
// #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!!
|
||||
$oNewFilter = clone $oFilter;
|
||||
$oNewFilter->ResetCondition();
|
||||
|
||||
$this->m_aPointingTo[$sExtKeyAttCode] = $oNewFilter;
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode)
|
||||
{
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->AddCondition_ReferencedBy_InNameSpace($oFilter, $sForeignExtKeyAttCode, $this->m_aClasses, $aAliasTranslation);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
$sForeignClass = $oFilter->GetClass();
|
||||
$sForeignClassAlias = $oFilter->GetClassAlias();
|
||||
if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode))
|
||||
{
|
||||
trigger_error("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}' - the condition will be ignored", E_USER_WARNING);
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($this->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
trigger_error("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}", E_USER_ERROR);
|
||||
}
|
||||
if (array_key_exists($sForeignClass, $this->m_aReferencedBy) && array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sForeignClass]))
|
||||
{
|
||||
$this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOrigAlias = $oFilter->GetClassAlias();
|
||||
$sKeyClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $oFilter->GetClass());
|
||||
if ($sKeyClassAlias != $sOrigAlias)
|
||||
{
|
||||
// Translate the condition expression with the new alias
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $sKeyClassAlias;
|
||||
}
|
||||
|
||||
// #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!!
|
||||
$oNewFilter = clone $oFilter;
|
||||
$oNewFilter->ResetCondition();
|
||||
|
||||
$this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]= $oNewFilter;
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_LinkedTo(DBObjectSearch $oLinkFilter, $sExtKeyAttCodeToMe, $sExtKeyAttCodeTarget, DBObjectSearch $oFilterTarget)
|
||||
{
|
||||
$oLinkFilterFinal = clone $oLinkFilter;
|
||||
$oLinkFilterFinal->AddCondition_PointingTo($sExtKeyAttCodeToMe);
|
||||
|
||||
$this->AddCondition_ReferencedBy($oLinkFilterFinal, $sExtKeyAttCodeToMe);
|
||||
}
|
||||
|
||||
public function AddCondition_RelatedTo(DBObjectSearch $oFilter, $sRelCode, $iMaxDepth)
|
||||
{
|
||||
MyHelpers::CheckValueInArray('relation code', $sRelCode, MetaModel::EnumRelations());
|
||||
$this->m_aRelatedTo[] = array('flt'=>$oFilter, 'relcode'=>$sRelCode, 'maxdepth'=>$iMaxDepth);
|
||||
}
|
||||
|
||||
public function MergeWith($oFilter)
|
||||
{
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->MergeWith_InNamespace($oFilter, $this->m_aClasses, $aAliasTranslation);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function MergeWith_InNamespace($oFilter, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
if ($this->GetClass() != $oFilter->GetClass())
|
||||
{
|
||||
trigger_error("Attempting to merge a filter of class '{$this->GetClass()}' with a filter of class '{$oFilter->GetClass()}'", E_USER_ERROR);
|
||||
}
|
||||
|
||||
// Translate search condition into our aliasing scheme
|
||||
$aAliasTranslation[$oFilter->GetClassAlias()]['*'] = $this->GetClassAlias();
|
||||
|
||||
$this->m_aFullText = array_merge($this->m_aFullText, $oFilter->m_aFullText);
|
||||
$this->m_aRelatedTo = array_merge($this->m_aRelatedTo, $oFilter->m_aRelatedTo);
|
||||
|
||||
foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$oExtFilter)
|
||||
{
|
||||
$this->AddCondition_PointingTo_InNamespace($oExtFilter, $sExtKeyAttCode, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
foreach($oFilter->m_aReferencedBy as $sForeignClass => $aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode => $oForeignFilter)
|
||||
{
|
||||
$this->AddCondition_ReferencedBy_InNamespace($oForeignFilter, $sForeignExtKeyAttCode, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];}
|
||||
public function GetClasses() {return $this->m_aClasses;}
|
||||
|
||||
public function GetClass() {return $this->m_sClass;}
|
||||
public function GetClassAlias() {return $this->m_sClassAlias;}
|
||||
public function GetCriteria() {return $this->m_oSearchCondition;}
|
||||
public function GetCriteria_FullText() {return $this->m_aFullText;}
|
||||
public function GetCriteria_PointingTo($sKeyAttCode = "")
|
||||
{
|
||||
if (empty($sKeyAttCode))
|
||||
{
|
||||
return $this->m_aPointingTo;
|
||||
}
|
||||
if (!array_key_exists($sKeyAttCode, $this->m_aPointingTo)) return null;
|
||||
return $this->m_aPointingTo[$sKeyAttCode];
|
||||
}
|
||||
public function GetCriteria_ReferencedBy($sRemoteClass = "", $sForeignExtKeyAttCode = "")
|
||||
{
|
||||
if (empty($sRemoteClass))
|
||||
{
|
||||
return $this->m_aReferencedBy;
|
||||
}
|
||||
if (!array_key_exists($sRemoteClass, $this->m_aReferencedBy)) return null;
|
||||
if (empty($sForeignExtKeyAttCode))
|
||||
{
|
||||
return $this->m_aReferencedBy[$sRemoteClass];
|
||||
}
|
||||
if (!array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sRemoteClass])) return null;
|
||||
return $this->m_aReferencedBy[$sRemoteClass][$sForeignExtKeyAttCode];
|
||||
}
|
||||
public function GetCriteria_RelatedTo()
|
||||
{
|
||||
return $this->m_aRelatedTo;
|
||||
}
|
||||
|
||||
public function RenderCondition()
|
||||
{
|
||||
return $this->m_oSearchCondition->Render();
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
// Efficient but resulting in long strings:
|
||||
// -> return (base64_encode(serialize($this)));
|
||||
|
||||
$sValue = $this->GetClass()."\n";
|
||||
$sValue .= $this->GetClassAlias()."\n";
|
||||
|
||||
foreach($this->m_aClasses as $sClassAlias => $sClass)
|
||||
{
|
||||
// A stands for "Aliases"
|
||||
$sValue .= "A:$sClassAlias:$sClass\n";
|
||||
}
|
||||
foreach($this->m_aFullText as $sFullText)
|
||||
{
|
||||
// F stands for "Full text"
|
||||
$sValue .= "F:".$sFullText."\n";
|
||||
}
|
||||
$sValue .= "C:".$this->m_oSearchCondition->serialize()."\n";
|
||||
|
||||
foreach($this->m_aPointingTo as $sExtKey=>$oFilter)
|
||||
{
|
||||
// P stands for "Pointing to"
|
||||
$sValue .= "P:".$sExtKey.":".$oFilter->serialize()."\n";
|
||||
}
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
// R stands for "Referenced by"
|
||||
$sValue .= "R:".$sForeignExtKeyAttCode.":".$oForeignFilter->serialize()."\n";
|
||||
}
|
||||
}
|
||||
foreach($this->m_aRelatedTo as $aRelatedTo)
|
||||
{
|
||||
$oFilter = $aRelatedTo['flt'];
|
||||
$sRelCode = $aRelatedTo['relcode'];
|
||||
$iMaxDepth = $aRelatedTo['maxdepth'];
|
||||
|
||||
$sValue .= "T:".$oFilter->serialize().":$sRelCode:$iMaxDepth";
|
||||
}
|
||||
return base64_encode($sValue);
|
||||
}
|
||||
|
||||
static public function unserialize($sValue)
|
||||
{
|
||||
// See comment above...
|
||||
// -> return (unserialize(base64_decode($sValue)));
|
||||
|
||||
$sClearText = base64_decode($sValue);
|
||||
$aValues = split("\n", $sClearText);
|
||||
$i = 0;
|
||||
$sClass = $aValues[$i++];
|
||||
$sClassAlias = $aValues[$i++];
|
||||
$oFilter = new DBObjectSearch($sClass, $sClassAlias);
|
||||
while($i < count($aValues) && !empty($aValues[$i]))
|
||||
{
|
||||
$aCondition = split(":", $aValues[$i++]);
|
||||
switch ($aCondition[0])
|
||||
{
|
||||
case "A":
|
||||
$oFilter->m_aClasses[$aCondition[1]] = $aCondition[2];
|
||||
break;
|
||||
case "F":
|
||||
$oFilter->AddCondition_FullText($aCondition[1]);
|
||||
break;
|
||||
case "C":
|
||||
$oFilter->m_oSearchCondition = Expression::unserialize($aCondition[1]);
|
||||
break;
|
||||
case "P":
|
||||
//$oAtt = DBObject::GetAttributeDef($sClass, $aCondition[1]);
|
||||
//$sRemoteClass = $oAtt->GetTargetClass();
|
||||
$oSubFilter = self::unserialize($aCondition[2]);
|
||||
$sExtKeyAttCode = $aCondition[1];
|
||||
$oFilter->AddCondition_PointingTo($oSubFilter, $sExtKeyAttCode);
|
||||
break;
|
||||
case "R":
|
||||
$oRemoteFilter = self::unserialize($aCondition[2]);
|
||||
$sExtKeyAttCodeToMe = $aCondition[1];
|
||||
$oFilter->AddCondition_ReferencedBy($oRemoteFilter, $sExtKeyAttCodeToMe);
|
||||
break;
|
||||
case "T":
|
||||
$oSubFilter = self::unserialize($aCondition[1]);
|
||||
$sRelCode = $aCondition[2];
|
||||
$iMaxDepth = $aCondition[3];
|
||||
$oFilter->AddCondition_RelatedTo($oSubFilter, $sRelCode, $iMaxDepth);
|
||||
default:
|
||||
trigger_error("invalid filter definition (cannot unserialize the data, clear text = '$sClearText')", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
return $oFilter;
|
||||
}
|
||||
|
||||
// SImple BUt Structured Query Languag - SubuSQL
|
||||
//
|
||||
static private function Value2Expression($value)
|
||||
{
|
||||
$sRet = $value;
|
||||
if (is_array($value))
|
||||
{
|
||||
$sRet = VS_START.implode(', ', $value).VS_END;
|
||||
}
|
||||
else if (!is_numeric($value))
|
||||
{
|
||||
$sRet = "'".addslashes($value)."'";
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
static private function Expression2Value($sExpr)
|
||||
{
|
||||
$retValue = $sExpr;
|
||||
if ((substr($sExpr, 0, 1) == "'") && (substr($sExpr, -1, 1) == "'"))
|
||||
{
|
||||
$sNoQuotes = substr($sExpr, 1, -1);
|
||||
return stripslashes($sNoQuotes);
|
||||
}
|
||||
if ((substr($sExpr, 0, 1) == VS_START) && (substr($sExpr, -1, 1) == VS_END))
|
||||
{
|
||||
$sNoBracket = substr($sExpr, 1, -1);
|
||||
$aRetValue = array();
|
||||
foreach (explode(",", $sNoBracket) as $sItem)
|
||||
{
|
||||
$aRetValue[] = self::Expression2Value(trim($sItem));
|
||||
}
|
||||
return $aRetValue;
|
||||
}
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
public function ToSibusQL()
|
||||
{
|
||||
$aConds = array(); // string conditions, to be merged into a logical AND
|
||||
foreach($this->m_aFullText as $sFullText)
|
||||
{
|
||||
$aConds[] = "* HAS ".self::Value2Expression($sFullText);
|
||||
}
|
||||
// #@# todo - changer ToSibusQL en ToOQL et l'implementer !
|
||||
// $aConds[] = $this->m_oSearchCondition->ToSibusQL
|
||||
/*
|
||||
foreach($this->m_aCriteria as $aCritInfo)
|
||||
{
|
||||
$aConds[] = $aCritInfo["filtercode"]." ".$aCritInfo["opcode"]." ".self::Value2Expression($aCritInfo["value"]);
|
||||
}
|
||||
*/
|
||||
foreach($this->m_aPointingTo as $sExtKey=>$oFilter)
|
||||
{
|
||||
$aConds[] = $sExtKey." IN (".$oFilter->ToSibusQL().")";
|
||||
}
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
$aConds[] = "PKEY IS ".$sForeignExtKeyAttCode." IN (".$oForeignFilter->ToSibusQL().")";
|
||||
}
|
||||
}
|
||||
foreach($this->m_aRelatedTo as $aRelatedTo)
|
||||
{
|
||||
$oFilter = $aRelatedTo['flt'];
|
||||
$sRelCode = $aRelatedTo['relcode'];
|
||||
$iMaxDepth = $aRelatedTo['maxdepth'];
|
||||
|
||||
$aConds[] = "RELATED ($sRelCode, $iMaxDepth) TO (".$oFilter->ToSibuSQL().")";
|
||||
}
|
||||
|
||||
$sValue = $this->GetClass();
|
||||
if (count($aConds) > 0)
|
||||
{
|
||||
$sValue .= ": ".implode(" AND ", $aConds);
|
||||
}
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
static private function privProcessParams($sQuery, array $aParams, $oDbObject)
|
||||
{
|
||||
$iPlaceHoldersCount = preg_match_all(SIBUSQLPARAMREGEXP, $sQuery, $aMatches, PREG_SET_ORDER);
|
||||
if ($iPlaceHoldersCount > 0)
|
||||
{
|
||||
foreach($aMatches as $aMatch)
|
||||
{
|
||||
$sStringToSearch = $aMatch[0];
|
||||
$sParameterName = $aMatch[1];
|
||||
$sDefaultValue = $aMatch[2];
|
||||
$sDescription = $aMatch[3];
|
||||
|
||||
$sValue = $sDefaultValue;
|
||||
if (array_key_exists($sParameterName, $aParams))
|
||||
{
|
||||
$sValue = $aParams[$sParameterName];
|
||||
unset($aParams[$sParameterName]);
|
||||
}
|
||||
else if (is_object($oDbObject))
|
||||
{
|
||||
if (strpos($sParameterName, "this.") === 0)
|
||||
{
|
||||
$sAttCode = substr($sParameterName, strlen("this."));
|
||||
if ($sAttCode == 'pkey')
|
||||
{
|
||||
$sValue = $oDbObject->GetKey();
|
||||
}
|
||||
else if ($sAttCode == 'class')
|
||||
{
|
||||
$sValue = get_class($oDbObject);
|
||||
}
|
||||
else if (MetaModel::IsValidAttCode(get_class($oDbObject), $sAttCode))
|
||||
{
|
||||
$sValue = $oDbObject->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
$sQuery = str_replace($sStringToSearch, $sValue, $sQuery);
|
||||
}
|
||||
}
|
||||
if (count($aParams) > 0)
|
||||
{
|
||||
trigger_error("Unused parameter(s) for this SibusQL expression: (".implode(', ', array_keys($aParams)).")");
|
||||
}
|
||||
return $sQuery;
|
||||
}
|
||||
|
||||
static public function ListSibusQLParams($sQuery)
|
||||
{
|
||||
$aRet = array();
|
||||
$iPlaceHoldersCount = preg_match_all(SIBUSQLPARAMREGEXP, $sQuery, $aMatches, PREG_SET_ORDER);
|
||||
if ($iPlaceHoldersCount > 0)
|
||||
{
|
||||
foreach($aMatches as $aMatch)
|
||||
{
|
||||
$sStringToSearch = $aMatch[0];
|
||||
$sParameterName = $aMatch[1];
|
||||
$sDefaultValue = $aMatch[2];
|
||||
$sDescription = $aMatch[3];
|
||||
$aRet[$sParameterName]["description"] = $sDescription;
|
||||
$aRet[$sParameterName]["default"] = $sDefaultValue;
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
protected function OQLExpressionToCondition($sQuery, $oExpression, $aClassAliases)
|
||||
{
|
||||
if ($oExpression instanceof BinaryOqlExpression)
|
||||
{
|
||||
$sOperator = $oExpression->GetOperator();
|
||||
$oLeft = $this->OQLExpressionToCondition($sQuery, $oExpression->GetLeftExpr(), $aClassAliases);
|
||||
$oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases);
|
||||
return new BinaryExpression($oLeft, $sOperator, $oRight);
|
||||
}
|
||||
elseif ($oExpression instanceof FieldOqlExpression)
|
||||
{
|
||||
$sClassAlias = $oExpression->GetParent();
|
||||
$sFltCode = $oExpression->GetName();
|
||||
if (empty($sClassAlias))
|
||||
{
|
||||
$iPos = $oExpression->GetPosition();
|
||||
throw new OqlNormalizeException('Missing class specification', $sQuery, 0, $iPos, '');
|
||||
}
|
||||
if (!array_key_exists($sClassAlias, $aClassAliases))
|
||||
{
|
||||
$iPos = $oExpression->GetPosition();
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, 0, $iPos, $sClassAlias, array_keys($aClassAliases));
|
||||
}
|
||||
$sClass = $aClassAliases[$sClassAlias];
|
||||
if (!MetaModel::IsValidFilterCode($sClass, $sFltCode))
|
||||
{
|
||||
$iPos = $oExpression->GetPosition();
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, 0, $iPos, "$sFltCode in class $sClassAlias", MetaModel::GetFiltersList($sClass));
|
||||
}
|
||||
|
||||
return new FieldExpression($sFltCode, $sClassAlias);
|
||||
}
|
||||
elseif ($oExpression instanceof TrueOqlExpression)
|
||||
{
|
||||
return new TrueExpression;
|
||||
}
|
||||
elseif ($oExpression instanceof ScalarOqlExpression)
|
||||
{
|
||||
return new ScalarExpression($oExpression->GetValue());
|
||||
}
|
||||
elseif ($oExpression instanceof ListOqlExpression)
|
||||
{
|
||||
return new ListExpression($oExpression->GetItems());
|
||||
}
|
||||
elseif ($oExpression instanceof FunctionOqlExpression)
|
||||
{
|
||||
return new FunctionExpression($oExpression->GetVerb(), $oExpression->GetArgs());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CoreException('Unknown expression type', array('class'=>get_class($oExpression), 'query'=>$sQuery));
|
||||
}
|
||||
}
|
||||
|
||||
static public function FromOQL($sQuery, array $aParams = array(), $oObject = null)
|
||||
{
|
||||
if (empty($sQuery)) return null;
|
||||
|
||||
$oOql = new OqlInterpreter($sQuery);
|
||||
$oOqlQuery = $oOql->ParseQuery();
|
||||
|
||||
$sClass = $oOqlQuery->GetClass();
|
||||
$sClassAlias = $oOqlQuery->GetClassAlias();
|
||||
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, 0, 0, $sClass, MetaModel::GetClasses());
|
||||
}
|
||||
|
||||
$oResultFilter = new DBObjectSearch($sClass, $sClassAlias);
|
||||
$oResultFilter->m_aClasses = array($sClassAlias => $sClass);
|
||||
|
||||
// Maintain an array of filters, because the flat list is in fact referring to a tree
|
||||
// And this will be an easy way to dispatch the conditions
|
||||
// $oResultFilter will be referenced by the other filters, or the other way around...
|
||||
$aJoinItems = array($sClassAlias => $oResultFilter);
|
||||
|
||||
$aJoinSpecs = $oOqlQuery->GetJoins();
|
||||
if (is_array($aJoinSpecs))
|
||||
{
|
||||
foreach ($aJoinSpecs as $oJoinSpec)
|
||||
{
|
||||
$sJoinClass = $oJoinSpec->GetClass();
|
||||
$sJoinClassAlias = $oJoinSpec->GetClassAlias();
|
||||
if (!MetaModel::IsValidClass($sJoinClass))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, 0, 0, $sJoinClass, MetaModel::GetClasses());
|
||||
}
|
||||
if (array_key_exists($sJoinClassAlias, $oResultFilter->m_aClasses))
|
||||
{
|
||||
if ($sJoinClassAlias != $sJoinClass)
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class alias', $sQuery, 0, 0, $sJoinClassAlias);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class name', $sQuery, 0, 0, $sJoinClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Assumption: ext key on the left only !!!
|
||||
// normalization should take care of this
|
||||
$oLeftField = $oJoinSpec->GetLeftField();
|
||||
$sFromClass = $oLeftField->GetParent();
|
||||
$sExtKeyAttCode = $oLeftField->GetName();
|
||||
|
||||
$oRightField = $oJoinSpec->GetRightField();
|
||||
$sToClass = $oRightField->GetParent();
|
||||
$sPKeyDescriptor = $oRightField->GetName();
|
||||
if ($sPKeyDescriptor != 'id')
|
||||
{
|
||||
throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sQuery, 0, $oRightField->GetPosition(), $sPKeyDescriptor, array('id'));
|
||||
}
|
||||
|
||||
$oResultFilter->m_aClasses[$sJoinClassAlias] = $sJoinClass;
|
||||
$aJoinItems[$sJoinClassAlias] = new DBObjectSearch($sJoinClass, $sJoinClassAlias);
|
||||
|
||||
if (!array_key_exists($sFromClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sQuery, 0, $oLeftField->GetPosition(), $sFromClass, array_keys($aJoinItems));
|
||||
}
|
||||
if (!array_key_exists($sToClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sQuery, 0, $oRightField->GetPosition(), $sToClass, array_keys($aJoinItems));
|
||||
}
|
||||
$aExtKeys = array_keys(MetaModel::GetExternalKeys($oResultFilter->m_aClasses[$sFromClass]));
|
||||
if (!in_array($sExtKeyAttCode, $aExtKeys))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sQuery, 0, $oLeftField->GetPosition(), $sExtKeyAttCode, $aExtKeys);
|
||||
}
|
||||
|
||||
if ($sFromClass == $sJoinClassAlias)
|
||||
{
|
||||
$aJoinItems[$sToClass]->AddCondition_ReferencedBy($aJoinItems[$sFromClass], $sExtKeyAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aJoinItems[$sFromClass]->AddCondition_PointingTo($aJoinItems[$sToClass], $sExtKeyAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oConditionTree = $oOqlQuery->GetCondition();
|
||||
if ($oConditionTree instanceof Expression)
|
||||
{
|
||||
$oResultFilter->m_oSearchCondition = $oResultFilter->OQLExpressionToCondition($sQuery, $oConditionTree, $oResultFilter->m_aClasses);
|
||||
}
|
||||
|
||||
return $oResultFilter;
|
||||
}
|
||||
|
||||
static public function FromSibusQL($sQuery, array $aParams = array(), $oObject = null)
|
||||
{
|
||||
if (empty($sQuery)) return null;
|
||||
$sQuery = self::privProcessParams($sQuery, $aParams, $oObject);
|
||||
|
||||
$iSepPos = strpos($sQuery, ":");
|
||||
if ($iSepPos === false)
|
||||
{
|
||||
if (preg_match('@^\\s*SELECT@', $sQuery))
|
||||
{
|
||||
return self::FromOQL($sQuery, $aParams, $oObject);
|
||||
}
|
||||
// Only the class was specified -> all rows are required
|
||||
$sClass = trim($sQuery);
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sClass = trim(substr($sQuery, 0, $iSepPos));
|
||||
$sConds = trim(substr($sQuery, $iSepPos + 1));
|
||||
$aValues = split(" AND ", $sConds);
|
||||
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
|
||||
foreach ($aValues as $sCond)
|
||||
{
|
||||
$sCond = trim($sCond);
|
||||
|
||||
if (strpos($sCond, "* HAS ") === 0)
|
||||
{
|
||||
$sValue = self::Expression2Value(substr($sCond, strlen("* HAS ")));
|
||||
$oFilter->AddCondition_FullText($sValue);
|
||||
}
|
||||
else if (preg_match("@^(\S+) IN \\((.+)\\)$@", $sCond, $aMatches))
|
||||
{
|
||||
$sExtKeyAttCode = $aMatches[1];
|
||||
$sFilterExp = $aMatches[2];
|
||||
|
||||
$oSubFilter = self::FromSibuSQL($sFilterExp);
|
||||
$oFilter->AddCondition_PointingTo($oSubFilter, $sExtKeyAttCode);
|
||||
}
|
||||
else if (strpos($sCond, "PKEY IS ") === 0)
|
||||
{
|
||||
if (preg_match("@^PKEY IS (\S+) IN \\((.+)\\)$@", $sCond, $aMatches))
|
||||
{
|
||||
$sExtKeyAttCodeToMe = $aMatches[1];
|
||||
$sFilterExp = $aMatches[2];
|
||||
$oRemoteFilter = self::FromSibuSQL($sFilterExp);
|
||||
$oFilter->AddCondition_ReferencedBy($oRemoteFilter, $sExtKeyAttCodeToMe);
|
||||
}
|
||||
}
|
||||
else if (strpos($sCond, "RELATED") === 0)
|
||||
{
|
||||
if (preg_match("@^RELATED\s*\\((.+)\\)\s*TO\s*\\((.+)\\)@", trim($sCond), $aMatches))
|
||||
{
|
||||
$aRelation = explode(',', trim($aMatches[1]));
|
||||
$sRelCode = trim($aRelation[0]);
|
||||
$iMaxDepth = intval(trim($aRelation[1]));
|
||||
$sFilterExp = trim($aMatches[2]);
|
||||
|
||||
$oSubFilter = self::FromSibuSQL($sFilterExp);
|
||||
$oFilter->AddCondition_RelatedTo($oSubFilter, $sRelCode, $iMaxDepth);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOperandExpr = "'.*'|\d+|-\d+|".VS_START.".+".VS_END;
|
||||
if (preg_match("@^(\S+)\s+(.*)\s+($sOperandExpr)$@", $sCond, $aMatches))
|
||||
{
|
||||
$sFltCode = trim($aMatches[1]);
|
||||
$sOpCode = trim($aMatches[2]);
|
||||
$value = self::Expression2Value($aMatches[3]);
|
||||
$oFilter->AddCondition($sFltCode, $value, $sOpCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_error("Wrong format for filter definition: '$sQuery'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $oFilter;
|
||||
}
|
||||
|
||||
// Sexy display of a SibuSQL expression
|
||||
static public function SibuSQLAsHtml($sQuery)
|
||||
{
|
||||
$sQuery = htmlentities($sQuery);
|
||||
$aParams = self::ListSibusQLParams($sQuery);
|
||||
$aParamValues = array();
|
||||
foreach ($aParams as $sParamName => $aParamInfo)
|
||||
{
|
||||
$sDescription = $aParamInfo["description"];
|
||||
$sDefaultValue = $aParamInfo["default"];
|
||||
$aParamValues[$sParamName] = "<span style=\"background-color:#aaa;\" title\"$sDescription (default to '$sDefaultValue')\">$sParamName</span>";
|
||||
}
|
||||
$sQuery = self::privProcessParams($sQuery, $aParamValues, null);
|
||||
return $sQuery;
|
||||
}
|
||||
|
||||
public function toxpath()
|
||||
{
|
||||
// #@# a voir...
|
||||
}
|
||||
static public function fromxpath()
|
||||
{
|
||||
// #@# a voir...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
255
core/dbobjectset.class.php
Normal file
255
core/dbobjectset.class.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A set of persistent objects, could be heterogeneous
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
class DBObjectSet
|
||||
{
|
||||
private $m_oFilter;
|
||||
private $m_aOrderBy;
|
||||
public $m_bLoaded;
|
||||
private $m_aData;
|
||||
private $m_aId2Row;
|
||||
private $m_iCurrRow;
|
||||
|
||||
public function __construct($oFilter, $aOrderBy = array())
|
||||
{
|
||||
$this->m_oFilter = $oFilter;
|
||||
$this->m_aOrderBy = $aOrderBy;
|
||||
|
||||
$this->m_bLoaded = false;
|
||||
$this->m_aData = array();
|
||||
$this->m_aId2Row = array();
|
||||
$this->m_iCurrRow = 0;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$sRet = '';
|
||||
$this->Rewind();
|
||||
$sRet .= "Set (".$this->m_oFilter->ToSibuSQL().")<br/>\n";
|
||||
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".MetaModel::MakeSelectQuery($this->m_oFilter, array()).")</pre>\n";
|
||||
|
||||
$sRet .= $this->Count()." records<br/>\n";
|
||||
if ($this->Count() > 0)
|
||||
{
|
||||
$sRet .= "<ul class=\"treeview\">\n";
|
||||
while ($oObj = $this->Fetch())
|
||||
{
|
||||
$sRet .= "<li>".$oObj->__toString()."</li>\n";
|
||||
}
|
||||
$sRet .= "</ul>\n";
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new self($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
static public function FromArray($sClass, $aObjects)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new self($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
$oRetSet->AddObjectArray($aObjects);
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
public function ToArray($bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
$this->Rewind();
|
||||
while ($oObject = $this->Fetch())
|
||||
{
|
||||
if ($bWithId)
|
||||
{
|
||||
$aRet[$oObject->GetKey()] = $oObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRet[] = $oObject;
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
public function GetFilter()
|
||||
{
|
||||
return $this->m_oFilter;
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_oFilter->GetClass();
|
||||
}
|
||||
|
||||
public function GetRootClass()
|
||||
{
|
||||
return MetaModel::GetRootClass($this->GetClass());
|
||||
}
|
||||
|
||||
public function Load()
|
||||
{
|
||||
if ($this->m_bLoaded) return;
|
||||
// #@# debug - echo "Loading (".$this->m_oFilter->ToSibuSQL().")....</br>\n";
|
||||
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->m_aOrderBy);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return;
|
||||
|
||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||
{
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$oObject = MetaModel::GetObjectByRow($sClass, $aRow);
|
||||
$this->AddObject($oObject);
|
||||
}
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
|
||||
$this->m_bLoaded = true;
|
||||
}
|
||||
|
||||
public function Count()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
return count($this->m_aData);
|
||||
}
|
||||
|
||||
public function Fetch()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
if ($this->m_iCurrRow >= count($this->m_aData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
$oRetObj = $this->m_aData[$this->m_iCurrRow];
|
||||
$this->m_iCurrRow++;
|
||||
return $oRetObj;
|
||||
}
|
||||
|
||||
public function Rewind()
|
||||
{
|
||||
$this->Seek(0);
|
||||
}
|
||||
|
||||
public function Seek($iRow)
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$this->m_iCurrRow = min($iRow, count($this->m_aData));
|
||||
return $this->m_iCurrRow;
|
||||
}
|
||||
|
||||
public function AddObject($oObject)
|
||||
{
|
||||
// ?usefull? if ($oObject->GetClass() != $this->GetClass()) return;
|
||||
|
||||
// it is mandatory to avoid duplicates
|
||||
if (array_key_exists($oObject->GetKey(), $this->m_aId2Row)) return;
|
||||
|
||||
// Do not load here, because the load uses that method too
|
||||
$iNextPos = count($this->m_aData);
|
||||
$this->m_aData[$iNextPos] = $oObject;
|
||||
$this->m_aId2Row[$oObject->GetKey()] = $iNextPos;
|
||||
}
|
||||
|
||||
public function RemoveObject($iRow)
|
||||
{
|
||||
trigger_error("#@# not implemented! ca sert a quoi ?");
|
||||
}
|
||||
|
||||
public function AddObjectArray($aObjects)
|
||||
{
|
||||
foreach ($aObjects as $oObj)
|
||||
{
|
||||
$this->AddObject($oObj);
|
||||
}
|
||||
}
|
||||
|
||||
public function Merge($oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
trigger_error("Could not merge two objects sets if they don't have the same root class");
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$oObjectSet->Seek(0);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
$this->AddObject($oObject);
|
||||
}
|
||||
}
|
||||
|
||||
public function CreateIntersect($oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
trigger_error("Could not 'intersect' two objects sets if they don't have the same root class");
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$oNewSet = DBObjectSet::FromScratch($this->GetClass());
|
||||
|
||||
$oObjectSet->Seek(0);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
if (array_key_exists($oObject->GetKey(), $this->m_aId2Row))
|
||||
{
|
||||
$oNewSet->AddObject($oObject);
|
||||
}
|
||||
}
|
||||
return $oNewSet;
|
||||
}
|
||||
|
||||
public function CreateDelta($oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
trigger_error("Could not 'delta' two objects sets if they don't have the same root class");
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$oNewSet = DBObjectSet::FromScratch($this->GetClass());
|
||||
|
||||
$oObjectSet->Seek(0);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
if (!array_key_exists($oObject->GetKey(), $this->m_aId2Row))
|
||||
{
|
||||
$oNewSet->AddObject($oObject);
|
||||
}
|
||||
}
|
||||
return $oNewSet;
|
||||
}
|
||||
|
||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
|
||||
{
|
||||
$aVisited = array(); // optimization for consecutive calls of MetaModel::GetRelatedObjects
|
||||
$this->Seek(0);
|
||||
while ($oObject = $this->Fetch())
|
||||
{
|
||||
$aRelatedObjs = $oObject->GetRelatedObjects($sRelCode, $iMaxDepth, $aVisited);
|
||||
}
|
||||
return $aRelatedObjs;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
431
core/expression.class.inc.php
Normal file
431
core/expression.class.inc.php
Normal file
@@ -0,0 +1,431 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* General definition of an expression tree (could be OQL, SQL or whatever)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
abstract class Expression
|
||||
{
|
||||
// recursive translation of identifiers
|
||||
abstract public function Translate($aTranslationData, $bMatchAll = true);
|
||||
|
||||
// recursive rendering
|
||||
abstract public function Render();
|
||||
|
||||
// recursively builds an array of class => fieldname
|
||||
abstract public function ListRequiredFields();
|
||||
|
||||
public function RequiresField($sClass, $sFieldName)
|
||||
{
|
||||
// #@# todo - optimize : this is called quite often when building a single query !
|
||||
$aRequired = $this->ListRequiredFields();
|
||||
if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return base64_encode($this->Render());
|
||||
}
|
||||
|
||||
static public function unserialize($sValue)
|
||||
{
|
||||
return self::FromOQL(base64_decode($sValue));
|
||||
}
|
||||
|
||||
static public function FromOQL($sConditionExpr)
|
||||
{
|
||||
$oOql = new OqlInterpreter($sConditionExpr);
|
||||
$oExpression = $oOql->ParseExpression();
|
||||
|
||||
return $oExpression;
|
||||
}
|
||||
|
||||
public function LogAnd($oExpr)
|
||||
{
|
||||
return new BinaryExpression($this, 'AND', $oExpr);
|
||||
}
|
||||
|
||||
public function LogOr($oExpr)
|
||||
{
|
||||
return new BinaryExpression($this, 'OR', $oExpr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class BinaryExpression extends Expression
|
||||
{
|
||||
protected $m_oLeftExpr; // filter code or an SQL expression (later?)
|
||||
protected $m_oRightExpr;
|
||||
protected $m_sOperator;
|
||||
|
||||
public function __construct($oLeftExpr, $sOperator, $oRightExpr)
|
||||
{
|
||||
if (!is_object($oLeftExpr))
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the left hand', array('found_type' => gettype($oLeftExpr)));
|
||||
}
|
||||
if (!is_object($oRightExpr))
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the right hand', array('found_type' => gettype($oRightExpr)));
|
||||
}
|
||||
if (!$oLeftExpr instanceof Expression)
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the left hand', array('found_class' => get_class($oLeftExpr)));
|
||||
}
|
||||
if (!$oRightExpr instanceof Expression)
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
|
||||
}
|
||||
$this->m_oLeftExpr = $oLeftExpr;
|
||||
$this->m_oRightExpr = $oRightExpr;
|
||||
$this->m_sOperator = $sOperator;
|
||||
}
|
||||
|
||||
public function GetLeftExpr()
|
||||
{
|
||||
return $this->m_oLeftExpr;
|
||||
}
|
||||
|
||||
public function GetRightExpr()
|
||||
{
|
||||
return $this->m_oRightExpr;
|
||||
}
|
||||
|
||||
public function GetOperator()
|
||||
{
|
||||
return $this->m_sOperator;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render()
|
||||
{
|
||||
$sOperator = $this->GetOperator();
|
||||
$sLeft = $this->GetLeftExpr()->Render();
|
||||
$sRight = $this->GetRightExpr()->Render();
|
||||
return "($sLeft $sOperator $sRight)";
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
$oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll);
|
||||
$oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll);
|
||||
return new BinaryExpression($oLeft, $this->GetOperator(), $oRight);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
$aLeft = $this->GetLeftExpr()->ListRequiredFields();
|
||||
$aRight = $this->GetRightExpr()->ListRequiredFields();
|
||||
return array_merge($aLeft, $aRight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class UnaryExpression extends Expression
|
||||
{
|
||||
protected $m_value;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->m_value = $value;
|
||||
}
|
||||
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_value;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render()
|
||||
{
|
||||
return CMDBSource::Quote($this->m_value);
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
class ScalarExpression extends UnaryExpression
|
||||
{
|
||||
public function __construct($value)
|
||||
{
|
||||
if (!is_scalar($value))
|
||||
{
|
||||
throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value)));
|
||||
}
|
||||
parent::__construct($value);
|
||||
}
|
||||
}
|
||||
|
||||
class TrueExpression extends ScalarExpression
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(1);
|
||||
}
|
||||
}
|
||||
|
||||
class FieldExpression extends UnaryExpression
|
||||
{
|
||||
protected $m_sParent;
|
||||
protected $m_sName;
|
||||
|
||||
public function __construct($sName, $sParent = '')
|
||||
{
|
||||
parent::__construct("$sParent.$sName");
|
||||
|
||||
$this->m_sParent = $sParent;
|
||||
$this->m_sName = $sName;
|
||||
}
|
||||
|
||||
public function GetParent() {return $this->m_sParent;}
|
||||
public function GetName() {return $this->m_sName;}
|
||||
|
||||
// recursive rendering
|
||||
public function Render()
|
||||
{
|
||||
if (empty($this->m_sParent))
|
||||
{
|
||||
return "`{$this->m_sName}`";
|
||||
}
|
||||
return "`{$this->m_sParent}`.`{$this->m_sName}`";
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
if (!array_key_exists($this->m_sParent, $aTranslationData))
|
||||
{
|
||||
if ($bMatchAll) throw new CoreException('Unknown parent id in translation table', array('parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData)));
|
||||
return clone $this;
|
||||
}
|
||||
if (!array_key_exists($this->m_sName, $aTranslationData[$this->m_sParent]))
|
||||
{
|
||||
if (!array_key_exists('*', $aTranslationData[$this->m_sParent]))
|
||||
{
|
||||
// #@# debug - if ($bMatchAll) MyHelpers::var_dump_html($aTranslationData, true);
|
||||
if ($bMatchAll) throw new CoreException('Unknown name in translation table', array('name' => $this->m_sName, 'parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData[$this->m_sParent])));
|
||||
return clone $this;
|
||||
}
|
||||
$sNewParent = $aTranslationData[$this->m_sParent]['*'];
|
||||
$sNewName = $this->m_sName;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sNewParent = $aTranslationData[$this->m_sParent][$this->m_sName][0];
|
||||
$sNewName = $aTranslationData[$this->m_sParent][$this->m_sName][1];
|
||||
}
|
||||
return new FieldExpression($sNewName, $sNewParent);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
return array($this->m_sParent.'.'.$this->m_sName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Temporary, until we implement functions and expression casting!
|
||||
// ... or until we implement a real full text search based in the MATCH() expression
|
||||
class ListExpression extends Expression
|
||||
{
|
||||
protected $m_aExpressions;
|
||||
|
||||
public function __construct($aExpressions)
|
||||
{
|
||||
$this->m_aExpressions = $aExpressions;
|
||||
}
|
||||
|
||||
public function GetItems()
|
||||
{
|
||||
return $this->m_aExpressions;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Render();
|
||||
}
|
||||
return '('.implode(', ', $aRes).')';
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
|
||||
}
|
||||
return new ListExpression($aRes);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FunctionExpression extends Expression
|
||||
{
|
||||
protected $m_sVerb;
|
||||
protected $m_aArgs; // array of expressions
|
||||
|
||||
public function __construct($sVerb, $aArgExpressions)
|
||||
{
|
||||
$this->m_sVerb = $sVerb;
|
||||
$this->m_aArgs = $aArgExpressions;
|
||||
}
|
||||
|
||||
public function GetVerb()
|
||||
{
|
||||
return $this->m_sVerb;
|
||||
}
|
||||
|
||||
public function GetArgs()
|
||||
{
|
||||
return $this->m_aArgs;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Render();
|
||||
}
|
||||
return $this->m_sVerb.'('.implode(', ', $aRes).')';
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
|
||||
}
|
||||
return new FunctionExpression($this->m_sVerb, $aRes);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
}
|
||||
|
||||
class IntervalExpression extends Expression
|
||||
{
|
||||
protected $m_oValue; // expression
|
||||
protected $m_sUnit;
|
||||
|
||||
public function __construct($oValue, $sUnit)
|
||||
{
|
||||
$this->m_oValue = $oValue;
|
||||
$this->m_sUnit = $sUnit;
|
||||
}
|
||||
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_oValue;
|
||||
}
|
||||
|
||||
public function GetUnit()
|
||||
{
|
||||
return $this->m_sUnit;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render()
|
||||
{
|
||||
return 'INTERVAL '.$this->m_oValue->Render().' '.$this->m_sUnit;
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
return new IntervalExpression($this->m_oValue->Translate($aTranslationData, $bMatchAll), $this->m_sUnit);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
class CharConcatExpression extends Expression
|
||||
{
|
||||
protected $m_aExpressions;
|
||||
|
||||
public function __construct($aExpressions)
|
||||
{
|
||||
$this->m_aExpressions = $aExpressions;
|
||||
}
|
||||
|
||||
public function GetItems()
|
||||
{
|
||||
return $this->m_aExpressions;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$sCol = $oExpr->Render();
|
||||
// Concat will be globally NULL if one single argument is null !
|
||||
$aRes[] = "COALESCE($sCol, '')";
|
||||
}
|
||||
return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
|
||||
}
|
||||
return new CharConcatExpression($aRes);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
296
core/filterdef.class.inc.php
Normal file
296
core/filterdef.class.inc.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* Definition of a filter (could be made out of an existing attribute, or from an expression)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
abstract class FilterDefinition
|
||||
{
|
||||
abstract public function GetType();
|
||||
abstract public function GetTypeDesc();
|
||||
|
||||
protected $m_sCode;
|
||||
private $m_aParams = array();
|
||||
protected function Get($sParamName) {return $this->m_aParams[$sParamName];}
|
||||
|
||||
public function __construct($sCode, $aParams = array())
|
||||
{
|
||||
$this->m_sCode = $sCode;
|
||||
$this->m_aParams = $aParams;
|
||||
$this->ConsistencyCheck();
|
||||
}
|
||||
|
||||
public function OverloadParams($aParams)
|
||||
{
|
||||
foreach ($aParams as $sParam => $value)
|
||||
{
|
||||
if (!array_key_exists($sParam, $this->m_aParams))
|
||||
{
|
||||
trigger_error("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aParams[$sParam] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// to be overloaded
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
private function ConsistencyCheck()
|
||||
{
|
||||
// Check that any mandatory param has been specified
|
||||
//
|
||||
$aExpectedParams = $this->ListExpectedParams();
|
||||
foreach($aExpectedParams as $sParamName)
|
||||
{
|
||||
if (!array_key_exists($sParamName, $this->m_aParams))
|
||||
{
|
||||
$aBacktrace = debug_backtrace();
|
||||
$sTargetClass = $aBacktrace[2]["class"];
|
||||
$sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"];
|
||||
trigger_error("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)</br>\n", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetCode() {return $this->m_sCode;}
|
||||
abstract public function GetLabel();
|
||||
abstract public function GetValuesDef();
|
||||
|
||||
// returns an array of opcode=>oplabel (e.g. "differs from")
|
||||
abstract public function GetOperators();
|
||||
// returns an opcode
|
||||
abstract public function GetLooseOperator();
|
||||
abstract public function GetFilterSQLExpr($sOpCode, $value);
|
||||
abstract public function TemporaryGetSQLCol();
|
||||
|
||||
// Wrapper - no need for overloading this one
|
||||
public function GetOpDescription($sOpCode)
|
||||
{
|
||||
$aOperators = $this->GetOperators();
|
||||
if (!array_key_exists($sOpCode, $aOperators))
|
||||
{
|
||||
trigger_error("Unknown operator '$sOpCode'", E_USER_ERROR);
|
||||
}
|
||||
|
||||
return $aOperators[$sOpCode];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against the object unique identifier
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class FilterPrivateKey extends FilterDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("pkey_field"));
|
||||
}
|
||||
|
||||
public function GetType() {return "PKey";}
|
||||
public function GetTypeDesc() {return "Match against object identifier";}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return "Object Private Key";
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetOperators()
|
||||
{
|
||||
return array(
|
||||
"="=>"equals",
|
||||
"!="=>"differs from",
|
||||
"IN"=>"in",
|
||||
"NOTIN"=>"not in"
|
||||
);
|
||||
}
|
||||
public function GetLooseOperator()
|
||||
{
|
||||
return "IN";
|
||||
}
|
||||
|
||||
public function GetFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sFieldName = $this->Get("pkey_field");
|
||||
// #@# not obliged to quote... these are numbers !!!
|
||||
$sQValue = CMDBSource::Quote($value);
|
||||
switch($sOpCode)
|
||||
{
|
||||
case "IN":
|
||||
if (!is_array($sQValue)) trigger_error("Expected an array for argument value (sOpCode='$sOpCode')");
|
||||
return "$sFieldName IN (".implode(", ", $sQValue).")";
|
||||
|
||||
case "NOTIN":
|
||||
if (!is_array($sQValue)) trigger_error("Expected an array for argument value (sOpCode='$sOpCode')");
|
||||
return "$sFieldName NOT IN (".implode(", ", $sQValue).")";
|
||||
|
||||
case "!=":
|
||||
return $sFieldName." != ".$sQValue;
|
||||
|
||||
case "=":
|
||||
default:
|
||||
return $sFieldName." = ".$sQValue;
|
||||
}
|
||||
}
|
||||
public function TemporaryGetSQLCol()
|
||||
{
|
||||
return $this->Get("pkey_field");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against an existing attribute (the attribute type will determine the available operators)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class FilterFromAttribute extends FilterDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("refattribute"));
|
||||
}
|
||||
|
||||
public function __construct($oRefAttribute, $aParam = array())
|
||||
{
|
||||
// In this very specific case, the code is the one of the attribute
|
||||
// (this to get a very very simple syntax upon declaration)
|
||||
$aParam["refattribute"] = $oRefAttribute;
|
||||
parent::__construct($oRefAttribute->GetCode(), $aParam);
|
||||
}
|
||||
|
||||
public function GetType() {return "Basic";}
|
||||
public function GetTypeDesc() {return "Match against field contents";}
|
||||
|
||||
public function __GetRefAttribute() // for checking purposes only !!!
|
||||
{
|
||||
return $oAttDef = $this->Get("refattribute");
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetLabel();
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetValuesDef();
|
||||
}
|
||||
|
||||
public function GetOperators()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetBasicFilterOperators();
|
||||
}
|
||||
public function GetLooseOperator()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
}
|
||||
|
||||
public function TemporaryGetSQLCol()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetSQLExpr();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against a given column (experimental -to be cleaned up later)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class FilterDBValues extends FilterDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("dbfield"));
|
||||
}
|
||||
|
||||
public function GetType() {return "Values from DB";}
|
||||
public function GetTypeDesc() {return "Match against the existing values in a field";}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return "enum de valeurs DB";
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetOperators()
|
||||
{
|
||||
return array(
|
||||
"IN"=>"in",
|
||||
);
|
||||
}
|
||||
public function GetLooseOperator()
|
||||
{
|
||||
return "IN";
|
||||
}
|
||||
|
||||
public function GetFilterSQLExpr($sOpCode, $value)
|
||||
{
|
||||
$sFieldName = $this->Get("dbfield");
|
||||
if (is_array($value) && !empty($value))
|
||||
{
|
||||
$sValueList = "'".implode("', '", $value)."'";
|
||||
return "$sFieldName IN ($sValueList)";
|
||||
}
|
||||
return "1=1";
|
||||
}
|
||||
|
||||
public function TemporaryGetSQLCol()
|
||||
{
|
||||
return $this->Get("dbfield");
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
2675
core/metamodel.class.php
Normal file
2675
core/metamodel.class.php
Normal file
File diff suppressed because it is too large
Load Diff
3
core/oql/build.cmd
Normal file
3
core/oql/build.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\LexerGenerator\cli.php" oql-lexer.plex
|
||||
c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\ParserGenerator\cli.php" oql-parser.y
|
||||
pause
|
||||
522
core/oql/oql-lexer.php
Normal file
522
core/oql/oql-lexer.php
Normal file
@@ -0,0 +1,522 @@
|
||||
<?php
|
||||
|
||||
// Notes (from the source file: oql-lexer.plex) - Romain
|
||||
//
|
||||
// The strval rule is a little bit cryptic.
|
||||
// This is due to both a bug in the lexer generator and the complexity of our need
|
||||
// The rule means: either a quoted string with ", or a quoted string with '
|
||||
// literal " (resp. ') must be escaped by a \
|
||||
// \ must be escaped by an additional \
|
||||
//
|
||||
// Here are the issues and limitation found in the lexer generator:
|
||||
// * Matching simple quotes is an issue, because regexp are not correctly escaped (and the ESC code is escaped itself)
|
||||
// Workaround: insert '.chr(39).' which will be a real ' in the end
|
||||
// * Matching an alternate regexp is an issue because you must specify "|^...."
|
||||
// and the regexp parser will not accept that syntax
|
||||
// Workaround: insert '.chr(94).' which will be a real ^
|
||||
//
|
||||
// Let's analyze an overview of the regexp, we have
|
||||
// 1) The strval rule in the lexer definition
|
||||
// /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
// 2) Becomes the php expression in the lexer
|
||||
// (note the escaped double quotes, hopefully having no effect, but showing where the issue is!)
|
||||
// $myRegexp = '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/';
|
||||
//
|
||||
// To be fixed in LexerGenerator/Parser.y, in doLongestMatch (doFirstMatch is ok)
|
||||
//
|
||||
//
|
||||
// Now, let's explain how the regexp has been designed.
|
||||
// Here is a simplified version, dealing with simple quotes, and based on the assumption that the lexer generator has been fixed!
|
||||
// The strval rule in the lexer definition
|
||||
// /'([^\\']*(\\')*(\\\\)*)*'/
|
||||
// This means anything containing \\ or \' or any other char but a standalone ' or \
|
||||
// This means ' or \ could not be found without a preceding \
|
||||
//
|
||||
class OQLLexerRaw
|
||||
{
|
||||
protected $data; // input string
|
||||
public $token; // token id
|
||||
public $value; // token string representation
|
||||
protected $line; // current line
|
||||
protected $count; // current column
|
||||
|
||||
function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->count = 0;
|
||||
$this->line = 1;
|
||||
}
|
||||
|
||||
|
||||
private $_yy_state = 1;
|
||||
private $_yy_stack = array();
|
||||
|
||||
function yylex()
|
||||
{
|
||||
return $this->{'yylex' . $this->_yy_state}();
|
||||
}
|
||||
|
||||
function yypushstate($state)
|
||||
{
|
||||
array_push($this->_yy_stack, $this->_yy_state);
|
||||
$this->_yy_state = $state;
|
||||
}
|
||||
|
||||
function yypopstate()
|
||||
{
|
||||
$this->_yy_state = array_pop($this->_yy_stack);
|
||||
}
|
||||
|
||||
function yybegin($state)
|
||||
{
|
||||
$this->_yy_state = $state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function yylex1()
|
||||
{
|
||||
if ($this->count >= strlen($this->data)) {
|
||||
return false; // end of input
|
||||
}
|
||||
do {
|
||||
$rules = array(
|
||||
'/^[ \t\n]+/',
|
||||
'/^SELECT/',
|
||||
'/^AS/',
|
||||
'/^WHERE/',
|
||||
'/^JOIN/',
|
||||
'/^ON/',
|
||||
'/^\//',
|
||||
'/^\\*/',
|
||||
'/^\\+/',
|
||||
'/^-/',
|
||||
'/^AND/',
|
||||
'/^OR/',
|
||||
'/^,/',
|
||||
'/^\\(/',
|
||||
'/^\\)/',
|
||||
'/^=/',
|
||||
'/^!=/',
|
||||
'/^>/',
|
||||
'/^</',
|
||||
'/^>=/',
|
||||
'/^<=/',
|
||||
'/^LIKE/',
|
||||
'/^NOT LIKE/',
|
||||
'/^IN/',
|
||||
'/^NOT IN/',
|
||||
'/^INTERVAL/',
|
||||
'/^IF/',
|
||||
'/^ELT/',
|
||||
'/^COALESCE/',
|
||||
'/^CONCAT/',
|
||||
'/^SUBSTR/',
|
||||
'/^TRIM/',
|
||||
'/^DATE/',
|
||||
'/^DATE_FORMAT/',
|
||||
'/^CURRENT_DATE/',
|
||||
'/^NOW/',
|
||||
'/^TIME/',
|
||||
'/^TO_DAYS/',
|
||||
'/^FROM_DAYS/',
|
||||
'/^YEAR/',
|
||||
'/^MONTH/',
|
||||
'/^DAY/',
|
||||
'/^DATE_ADD/',
|
||||
'/^DATE_SUB/',
|
||||
'/^ROUND/',
|
||||
'/^FLOOR/',
|
||||
'/^[0-9]+|0x[0-9a-fA-F]+/',
|
||||
'/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/',
|
||||
'/^([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/',
|
||||
'/^\\./',
|
||||
);
|
||||
$match = false;
|
||||
foreach ($rules as $index => $rule) {
|
||||
if (preg_match($rule, substr($this->data, $this->count), $yymatches)) {
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
$match = array($yymatches, $index); // matches, token
|
||||
}
|
||||
} else {
|
||||
$match = array($yymatches, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$match) {
|
||||
throw new Exception('Unexpected input at line' . $this->line .
|
||||
': ' . $this->data[$this->count]);
|
||||
}
|
||||
$this->token = $match[1];
|
||||
$this->value = $match[0][0];
|
||||
$yysubmatches = $match[0];
|
||||
array_shift($yysubmatches);
|
||||
if (!$yysubmatches) {
|
||||
$yysubmatches = array();
|
||||
}
|
||||
$r = $this->{'yy_r1_' . $this->token}($yysubmatches);
|
||||
if ($r === null) {
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
// accept this token
|
||||
return true;
|
||||
} elseif ($r === true) {
|
||||
// we have changed state
|
||||
// process this token in the new state
|
||||
return $this->yylex();
|
||||
} elseif ($r === false) {
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
if ($this->count >= strlen($this->data)) {
|
||||
return false; // end of input
|
||||
}
|
||||
// skip this token
|
||||
continue;
|
||||
} else {
|
||||
$yy_yymore_patterns = array_slice($rules, $this->token, true);
|
||||
// yymore is needed
|
||||
do {
|
||||
if (!isset($yy_yymore_patterns[$this->token])) {
|
||||
throw new Exception('cannot do yymore for the last token');
|
||||
}
|
||||
$match = false;
|
||||
foreach ($yy_yymore_patterns[$this->token] as $index => $rule) {
|
||||
if (preg_match('/' . $rule . '/',
|
||||
substr($this->data, $this->count), $yymatches)) {
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
$match = array($yymatches, $index); // matches, token
|
||||
}
|
||||
} else {
|
||||
$match = array($yymatches, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$match) {
|
||||
throw new Exception('Unexpected input at line' . $this->line .
|
||||
': ' . $this->data[$this->count]);
|
||||
}
|
||||
$this->token = $match[1];
|
||||
$this->value = $match[0][0];
|
||||
$yysubmatches = $match[0];
|
||||
array_shift($yysubmatches);
|
||||
if (!$yysubmatches) {
|
||||
$yysubmatches = array();
|
||||
}
|
||||
$this->line = substr_count($this->value, "\n");
|
||||
$r = $this->{'yy_r1_' . $this->token}();
|
||||
} while ($r !== null || !$r);
|
||||
if ($r === true) {
|
||||
// we have changed state
|
||||
// process this token in the new state
|
||||
return $this->yylex();
|
||||
} else {
|
||||
// accept
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
|
||||
} // end function
|
||||
|
||||
function yy_r1_0($yy_subpatterns)
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
function yy_r1_1($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::SELECT;
|
||||
}
|
||||
function yy_r1_2($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::AS_ALIAS;
|
||||
}
|
||||
function yy_r1_3($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::WHERE;
|
||||
}
|
||||
function yy_r1_4($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::JOIN;
|
||||
}
|
||||
function yy_r1_5($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ON;
|
||||
}
|
||||
function yy_r1_6($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
function yy_r1_7($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
}
|
||||
function yy_r1_8($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
}
|
||||
function yy_r1_9($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
}
|
||||
function yy_r1_10($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
}
|
||||
function yy_r1_11($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
function yy_r1_12($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
function yy_r1_13($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
function yy_r1_14($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
function yy_r1_15($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
function yy_r1_16($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
function yy_r1_17($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
function yy_r1_18($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
function yy_r1_19($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
function yy_r1_20($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
function yy_r1_21($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
function yy_r1_22($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
function yy_r1_23($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
function yy_r1_24($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
function yy_r1_25($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
function yy_r1_26($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
function yy_r1_27($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
function yy_r1_28($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
function yy_r1_29($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
function yy_r1_30($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
function yy_r1_31($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
function yy_r1_32($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
function yy_r1_33($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
function yy_r1_34($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
function yy_r1_35($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
function yy_r1_36($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
function yy_r1_37($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
function yy_r1_38($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
function yy_r1_39($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
function yy_r1_40($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
function yy_r1_41($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
function yy_r1_42($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
function yy_r1_43($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
function yy_r1_44($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
function yy_r1_45($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
function yy_r1_46($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
function yy_r1_47($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
function yy_r1_48($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
function yy_r1_49($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::DOT;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
define('UNEXPECTED_INPUT_AT_LINE', 'Unexpected input at line');
|
||||
|
||||
class OQLLexerException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sUnexpected)
|
||||
{
|
||||
parent::__construct("Syntax error", $sInput, $iLine, $iCol, $sUnexpected);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLLexer extends OQLLexerRaw
|
||||
{
|
||||
public function getTokenPos()
|
||||
{
|
||||
return max(0, $this->count - strlen($this->value));
|
||||
}
|
||||
|
||||
function yylex()
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
305
core/oql/oql-lexer.plex
Normal file
305
core/oql/oql-lexer.plex
Normal file
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
|
||||
// Notes (from the source file: oql-lexer.plex) - Romain
|
||||
//
|
||||
// The strval rule is a little bit cryptic.
|
||||
// This is due to both a bug in the lexer generator and the complexity of our need
|
||||
// The rule means: either a quoted string with ", or a quoted string with '
|
||||
// literal " (resp. ') must be escaped by a \
|
||||
// \ must be escaped by an additional \
|
||||
//
|
||||
// Here are the issues and limitation found in the lexer generator:
|
||||
// * Matching simple quotes is an issue, because regexp are not correctly escaped (and the ESC code is escaped itself)
|
||||
// Workaround: insert '.chr(39).' which will be a real ' in the end
|
||||
// * Matching an alternate regexp is an issue because you must specify "|^...."
|
||||
// and the regexp parser will not accept that syntax
|
||||
// Workaround: insert '.chr(94).' which will be a real ^
|
||||
//
|
||||
// Let's analyze an overview of the regexp, we have
|
||||
// 1) The strval rule in the lexer definition
|
||||
// /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
// 2) Becomes the php expression in the lexer
|
||||
// (note the escaped double quotes, hopefully having no effect, but showing where the issue is!)
|
||||
// $myRegexp = '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/';
|
||||
//
|
||||
// To be fixed in LexerGenerator/Parser.y, in doLongestMatch (doFirstMatch is ok)
|
||||
//
|
||||
//
|
||||
// Now, let's explain how the regexp has been designed.
|
||||
// Here is a simplified version, dealing with simple quotes, and based on the assumption that the lexer generator has been fixed!
|
||||
// The strval rule in the lexer definition
|
||||
// /'([^\\']*(\\')*(\\\\)*)*'/
|
||||
// This means anything containing \\ or \' or any other char but a standalone ' or \
|
||||
// This means ' or \ could not be found without a preceding \
|
||||
//
|
||||
class OQLLexerRaw
|
||||
{
|
||||
protected $data; // input string
|
||||
public $token; // token id
|
||||
public $value; // token string representation
|
||||
protected $line; // current line
|
||||
protected $count; // current column
|
||||
|
||||
function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->count = 0;
|
||||
$this->line = 1;
|
||||
}
|
||||
|
||||
/*!lex2php
|
||||
%input $this->data
|
||||
%counter $this->count
|
||||
%token $this->token
|
||||
%value $this->value
|
||||
%line $this->line
|
||||
%matchlongest 1
|
||||
whitespace = /[ \t\n]+/
|
||||
select = "SELECT"
|
||||
as_alias = "AS"
|
||||
where = "WHERE"
|
||||
join = "JOIN"
|
||||
on = "ON"
|
||||
coma = ","
|
||||
par_open = "("
|
||||
par_close = ")"
|
||||
math_div = "/"
|
||||
math_mult = "*"
|
||||
math_plus = "+"
|
||||
math_minus = "-"
|
||||
log_and = "AND"
|
||||
log_or = "OR"
|
||||
eq = "="
|
||||
not_eq = "!="
|
||||
gt = ">"
|
||||
lt = "<"
|
||||
ge = ">="
|
||||
le = "<="
|
||||
like = "LIKE"
|
||||
not_like = "NOT LIKE"
|
||||
in = "IN"
|
||||
not_in = "NOT IN"
|
||||
interval = "INTERVAL"
|
||||
f_if = "IF"
|
||||
f_elt = "ELT"
|
||||
f_coalesce = "COALESCE"
|
||||
f_concat = "CONCAT"
|
||||
f_substr = "SUBSTR"
|
||||
f_trim = "TRIM"
|
||||
f_date = "DATE"
|
||||
f_date_format = "DATE_FORMAT"
|
||||
f_current_date = "CURRENT_DATE"
|
||||
f_now = "NOW"
|
||||
f_time = "TIME"
|
||||
f_to_days = "TO_DAYS"
|
||||
f_from_days = "FROM_DAYS"
|
||||
f_year = "YEAR"
|
||||
f_month = "MONTH"
|
||||
f_day = "DAY"
|
||||
f_date_add = "DATE_ADD"
|
||||
f_date_sub = "DATE_SUB"
|
||||
f_round = "ROUND"
|
||||
f_floor = "FLOOR"
|
||||
numval = /[0-9]+|0x[0-9a-fA-F]+/
|
||||
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/
|
||||
dot = "."
|
||||
*/
|
||||
|
||||
/*!lex2php
|
||||
whitespace {
|
||||
return false;
|
||||
}
|
||||
select {
|
||||
$this->token = OQLParser::SELECT;
|
||||
}
|
||||
as_alias {
|
||||
$this->token = OQLParser::AS_ALIAS;
|
||||
}
|
||||
where {
|
||||
$this->token = OQLParser::WHERE;
|
||||
}
|
||||
join {
|
||||
$this->token = OQLParser::JOIN;
|
||||
}
|
||||
on {
|
||||
$this->token = OQLParser::ON;
|
||||
}
|
||||
math_div {
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
math_mult {
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
}
|
||||
math_plus {
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
}
|
||||
math_minus {
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
}
|
||||
log_and {
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
}
|
||||
log_or {
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
coma {
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
par_open {
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
par_close {
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
eq {
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
not_eq {
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
gt {
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
lt {
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
ge {
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
le {
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
like {
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
not_like {
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
in {
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
not_in {
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
interval {
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
f_if {
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
f_elt {
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
f_coalesce {
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
f_concat {
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
f_substr {
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
f_trim {
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
f_date {
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
f_date_format {
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
f_current_date {
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
f_now {
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
f_time {
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
f_to_days {
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
f_from_days {
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
f_year {
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
f_month {
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
f_day {
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
f_date_add {
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
f_date_sub {
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
f_round {
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
f_floor {
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
numval {
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
strval {
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
name {
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
dot {
|
||||
$this->token = OQLParser::DOT;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
define('UNEXPECTED_INPUT_AT_LINE', 'Unexpected input at line');
|
||||
|
||||
class OQLLexerException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sUnexpected)
|
||||
{
|
||||
parent::__construct("Syntax error", $sInput, $iLine, $iCol, $sUnexpected);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLLexer extends OQLLexerRaw
|
||||
{
|
||||
public function getTokenPos()
|
||||
{
|
||||
return max(0, $this->count - strlen($this->value));
|
||||
}
|
||||
|
||||
function yylex()
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
1613
core/oql/oql-parser.php
Normal file
1613
core/oql/oql-parser.php
Normal file
File diff suppressed because it is too large
Load Diff
213
core/oql/oql-parser.y
Normal file
213
core/oql/oql-parser.y
Normal file
@@ -0,0 +1,213 @@
|
||||
%name OQLParser_
|
||||
%declare_class {class OQLParserRaw}
|
||||
%syntax_error {
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
}
|
||||
|
||||
result ::= query(X). { $this->my_result = X; }
|
||||
result ::= condition(X). { $this->my_result = X; }
|
||||
|
||||
query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlQuery(X, X, W, J);
|
||||
}
|
||||
query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlQuery(X, Y, W, J);
|
||||
}
|
||||
|
||||
where_statement(A) ::= WHERE condition(C). { A = C;}
|
||||
where_statement(A) ::= . { A = null;}
|
||||
|
||||
join_statement(A) ::= join_item(J) join_statement(S). {
|
||||
// insert the join statement on top of the existing list
|
||||
array_unshift(S, J);
|
||||
// and return the updated array
|
||||
A = S;
|
||||
}
|
||||
join_statement(A) ::= join_item(J). {
|
||||
A = Array(J);
|
||||
}
|
||||
join_statement(A) ::= . { A = null;}
|
||||
|
||||
join_item(A) ::= JOIN class_name(X) AS_ALIAS class_name(Y) ON join_condition(C).
|
||||
{
|
||||
// create an array with one single item
|
||||
A = new OqlJoinSpec(X, Y, C);
|
||||
}
|
||||
join_item(A) ::= JOIN class_name(X) ON join_condition(C).
|
||||
{
|
||||
// create an array with one single item
|
||||
A = new OqlJoinSpec(X, X, C);
|
||||
}
|
||||
|
||||
join_condition(A) ::= field_id(X) EQ field_id(Y). { A = new BinaryOqlExpression(X, '=', Y); }
|
||||
|
||||
condition(A) ::= expression(X). { A = X; }
|
||||
|
||||
expression(A) ::= PAR_OPEN expression(X) PAR_CLOSE. { A = X; }
|
||||
expression(A) ::= expression(X) operator(Y) expression(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
expression(A) ::= scalar(X). { A=X; }
|
||||
expression(A) ::= field_id(X). { A = X; }
|
||||
expression(A) ::= expression(X) list_operator(Y) list(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
expression(A) ::= func_name(X) PAR_OPEN arg_list(Y) PAR_CLOSE. { A = new FunctionOqlExpression(X, Y); }
|
||||
|
||||
|
||||
list(A) ::= PAR_OPEN scalar_list(X) PAR_CLOSE. {
|
||||
A = new ListOqlExpression(X);
|
||||
}
|
||||
scalar_list(A) ::= scalar(X). {
|
||||
A = array(X);
|
||||
}
|
||||
scalar_list(A) ::= scalar_list(L) COMA scalar(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
|
||||
arg_list(A) ::= . {
|
||||
A = array();
|
||||
}
|
||||
arg_list(A) ::= argument(X). {
|
||||
A = array(X);
|
||||
}
|
||||
arg_list(A) ::= arg_list(L) COMA argument(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
argument(A) ::= expression(X). { A = X; }
|
||||
argument(A) ::= INTERVAL expression(X) interval_unit(Y). { A = new IntervalOqlExpression(X, Y); }
|
||||
|
||||
interval_unit(A) ::= F_DAY(X). { A = X; }
|
||||
interval_unit(A) ::= F_MONTH(X). { A = X; }
|
||||
interval_unit(A) ::= F_YEAR(X). { A = X; }
|
||||
|
||||
scalar(A) ::= num_scalar(X). { A = X; }
|
||||
scalar(A) ::= str_scalar(X). { A = X; }
|
||||
|
||||
num_scalar(A) ::= num_value(X). { A = new ScalarOqlExpression(X); }
|
||||
str_scalar(A) ::= str_value(X). { A = new ScalarOqlExpression(X); }
|
||||
|
||||
field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression($this->m_iCol, Y, X); }
|
||||
class_name(A) ::= name(X). {A=X;}
|
||||
|
||||
name(A) ::= NAME(X). {
|
||||
if (X[0] == '`')
|
||||
{
|
||||
A = substr(X, 1, strlen(X) - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
A = X;
|
||||
}
|
||||
}
|
||||
|
||||
num_value(A) ::= NUMVAL(X). {A=X;}
|
||||
str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
|
||||
|
||||
operator(A) ::= log_operator(X). {A=X;}
|
||||
operator(A) ::= num_operator(X). {A=X;}
|
||||
operator(A) ::= str_operator(X). {A=X;}
|
||||
operator(A) ::= EQ(X). {A=X;}
|
||||
operator(A) ::= NOT_EQ(X). {A=X;}
|
||||
|
||||
log_operator(A) ::= LOG_AND(X). {A=X;}
|
||||
log_operator(A) ::= LOG_OR(X). {A=X;}
|
||||
|
||||
num_operator(A) ::= GT(X). {A=X;}
|
||||
num_operator(A) ::= LT(X). {A=X;}
|
||||
num_operator(A) ::= GE(X). {A=X;}
|
||||
num_operator(A) ::= LE(X). {A=X;}
|
||||
num_operator(A) ::= MATH_DIV(X). {A=X;}
|
||||
num_operator(A) ::= MATH_MULT(X). {A=X;}
|
||||
num_operator(A) ::= MATH_PLUS(X). {A=X;}
|
||||
num_operator(A) ::= MATH_MINUS(X). {A=X;}
|
||||
|
||||
str_operator(A) ::= LIKE(X). {A=X;}
|
||||
str_operator(A) ::= NOT_LIKE(X). {A=X;}
|
||||
|
||||
list_operator(A) ::= IN(X). {A=X;}
|
||||
list_operator(A) ::= NOT_IN(X). {A=X;}
|
||||
|
||||
func_name(A) ::= F_IF(X). { A=X; }
|
||||
func_name(A) ::= F_ELT(X). { A=X; }
|
||||
func_name(A) ::= F_COALESCE(X). { A=X; }
|
||||
func_name(A) ::= F_CONCAT(X). { A=X; }
|
||||
func_name(A) ::= F_SUBSTR(X). { A=X; }
|
||||
func_name(A) ::= F_TRIM(X). { A=X; }
|
||||
func_name(A) ::= F_DATE(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_FORMAT(X). { A=X; }
|
||||
func_name(A) ::= F_CURRENT_DATE(X). { A=X; }
|
||||
func_name(A) ::= F_NOW(X). { A=X; }
|
||||
func_name(A) ::= F_TIME(X). { A=X; }
|
||||
func_name(A) ::= F_TO_DAYS(X). { A=X; }
|
||||
func_name(A) ::= F_FROM_DAYS(X). { A=X; }
|
||||
func_name(A) ::= F_YEAR(X). { A=X; }
|
||||
func_name(A) ::= F_MONTH(X). { A=X; }
|
||||
func_name(A) ::= F_DAY(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_ADD(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_SUB(X). { A=X; }
|
||||
func_name(A) ::= F_ROUND(X). { A=X; }
|
||||
func_name(A) ::= F_FLOOR(X). { A=X; }
|
||||
|
||||
|
||||
%code {
|
||||
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
protected $my_result;
|
||||
|
||||
public function GetResult()
|
||||
{
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
// More info on the source query and the current position while parsing it
|
||||
// Data used when an exception is raised
|
||||
protected $m_iLine; // still not used
|
||||
protected $m_iCol;
|
||||
protected $m_sSourceQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_iLine = 0;
|
||||
$this->m_iCol = 0;
|
||||
$this->m_sSourceQuery = $sQuery;
|
||||
// no constructor - parent::__construct();
|
||||
}
|
||||
|
||||
public function doParse($token, $value, $iCurrPosition = 0)
|
||||
{
|
||||
$this->m_iCol = $iCurrPosition;
|
||||
|
||||
return parent::DoParse($token, $value);
|
||||
}
|
||||
|
||||
public function doFinish()
|
||||
{
|
||||
$this->doParse(0, 0);
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Bug in the original destructor, causing an infinite loop !
|
||||
// This is a real issue when a fatal error occurs on the first token (the error could not be seen)
|
||||
if (is_null($this->yyidx))
|
||||
{
|
||||
$this->yyidx = -1;
|
||||
}
|
||||
parent::__destruct();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
38
core/oql/oqlexception.class.inc.php
Normal file
38
core/oql/oqlexception.class.inc.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
class OQLException extends CoreException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sUnexpected, $aExpecting = null)
|
||||
{
|
||||
$this->m_MyIssue = $sIssue;
|
||||
$this->m_sInput = $sInput;
|
||||
$this->m_iLine = $iLine;
|
||||
$this->m_iCol = $iCol;
|
||||
$this->m_sUnexpected = $sUnexpected;
|
||||
$this->m_aExpecting = $aExpecting;
|
||||
|
||||
if (is_null($this->m_aExpecting))
|
||||
{
|
||||
$sMessage = "$sIssue - found '$sUnexpected' at $iCol in '$sInput'";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sExpectations = '{'.implode(', ', $aExpecting).'}';
|
||||
$sMessage = "$sIssue - found '$sUnexpected' at $iCol in '$sInput', expecting $sExpectations";
|
||||
}
|
||||
|
||||
// make sure everything is assigned properly
|
||||
parent::__construct($sMessage, 0);
|
||||
}
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
{
|
||||
$sRet = htmlentities($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: ");
|
||||
$sRet .= htmlentities(substr($this->m_sInput, 0, $this->m_iCol));
|
||||
$sRet .= $sHighlightHtmlBegin.htmlentities(substr($this->m_sInput, $this->m_iCol, strlen($this->m_sUnexpected))).$sHighlightHtmlEnd;
|
||||
$sRet .= htmlentities(substr($this->m_sInput, $this->m_iCol + strlen($this->m_sUnexpected)));
|
||||
return $sRet;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
55
core/oql/oqlinterpreter.class.inc.php
Normal file
55
core/oql/oqlinterpreter.class.inc.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?
|
||||
|
||||
class OqlNormalizeException extends OQLException
|
||||
{
|
||||
}
|
||||
|
||||
class OqlInterpreterException extends OQLException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
class OqlInterpreter
|
||||
{
|
||||
public $m_sQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_sQuery = $sQuery;
|
||||
}
|
||||
|
||||
protected function Parse()
|
||||
{
|
||||
$oLexer = new OQLLexer($this->m_sQuery);
|
||||
$oParser = new OQLParser($this->m_sQuery);
|
||||
|
||||
while($oLexer->yylex())
|
||||
{
|
||||
$oParser->doParse($oLexer->token, $oLexer->value, $oLexer->getTokenPos());
|
||||
}
|
||||
$res = $oParser->doFinish();
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function ParseQuery()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof OqlQuery)
|
||||
{
|
||||
throw new OqlException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes), array('OqlQuery'));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
public function ParseExpression()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof Expression)
|
||||
{
|
||||
throw new OqlException('Expecting an OQL expression', $this->m_sQuery, 0, 0, get_class($oRes), array('Expression'));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
106
core/oql/oqlquery.class.inc.php
Normal file
106
core/oql/oqlquery.class.inc.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?
|
||||
|
||||
class OqlJoinSpec
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sClassAlias;
|
||||
protected $m_oLeftField;
|
||||
protected $m_oRightField;
|
||||
|
||||
protected $m_oNextJoinspec;
|
||||
|
||||
public function __construct($sClass, $sClassAlias, BinaryExpression $oExpression)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sClassAlias = $sClassAlias;
|
||||
$this->m_oLeftField = $oExpression->GetLeftExpr();
|
||||
$this->m_oRightField = $oExpression->GetRightExpr();
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_sClass;
|
||||
}
|
||||
public function GetClassAlias()
|
||||
{
|
||||
return $this->m_sClassAlias;
|
||||
}
|
||||
public function GetLeftField()
|
||||
{
|
||||
return $this->m_oLeftField;
|
||||
}
|
||||
public function GetRightField()
|
||||
{
|
||||
return $this->m_oRightField;
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryOqlExpression extends BinaryExpression
|
||||
{
|
||||
}
|
||||
|
||||
class ScalarOqlExpression extends ScalarExpression
|
||||
{
|
||||
}
|
||||
|
||||
class FieldOqlExpression extends FieldExpression
|
||||
{
|
||||
protected $m_iPosition; // position in the source string
|
||||
|
||||
public function __construct($iPosition, $sName, $sParent = '')
|
||||
{
|
||||
$this->m_iPosition = $iPosition;
|
||||
parent::__construct($sName, $sParent);
|
||||
}
|
||||
|
||||
public function GetPosition()
|
||||
{
|
||||
return $this->m_iPosition;
|
||||
}
|
||||
}
|
||||
|
||||
class ListOqlExpression extends ListExpression
|
||||
{
|
||||
}
|
||||
|
||||
class FunctionOqlExpression extends FunctionExpression
|
||||
{
|
||||
}
|
||||
|
||||
class IntervalOqlExpression extends IntervalExpression
|
||||
{
|
||||
}
|
||||
class OqlQuery
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sClassAlias;
|
||||
protected $m_aJoins; // array of OqlJoinSpec
|
||||
protected $m_oCondition; // condition tree (expressions)
|
||||
|
||||
public function __construct($sClass, $sClassAlias = '', $oCondition = null, $aJoins = null)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sClassAlias = $sClassAlias;
|
||||
$this->m_aJoins = $aJoins;
|
||||
$this->m_oCondition = $oCondition;
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_sClass;
|
||||
}
|
||||
public function GetClassAlias()
|
||||
{
|
||||
return $this->m_sClassAlias;
|
||||
}
|
||||
public function GetJoins()
|
||||
{
|
||||
return $this->m_aJoins;
|
||||
}
|
||||
public function GetCondition()
|
||||
{
|
||||
return $this->m_oCondition;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
399
core/sqlquery.class.inc.php
Normal file
399
core/sqlquery.class.inc.php
Normal file
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SQLQuery
|
||||
* build an mySQL compatible SQL query
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
require_once('cmdbsource.class.inc.php');
|
||||
|
||||
|
||||
class SQLExpression extends BinaryExpression
|
||||
{
|
||||
}
|
||||
class ScalarSQLExpression extends ScalarExpression
|
||||
{
|
||||
}
|
||||
class TrueSQLExpression extends TrueExpression
|
||||
{
|
||||
}
|
||||
class FieldSQLExpression extends FieldExpression
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SQLQuery
|
||||
{
|
||||
private $m_sTable = '';
|
||||
private $m_sTableAlias = '';
|
||||
private $m_aFields = array();
|
||||
private $m_oConditionExpr = null;
|
||||
private $m_aFullTextNeedles = array();
|
||||
private $m_bToDelete = true; // The current table must be listed for deletion ?
|
||||
private $m_aValues = array(); // Values to set in case of an update query
|
||||
private $m_aJoinSelects = array();
|
||||
|
||||
public function __construct($sTable, $sTableAlias, $aFields, $oConditionExpr, $aFullTextNeedles, $bToDelete = true, $aValues = array())
|
||||
{
|
||||
if (!CMDBSource::IsTable($sTable))
|
||||
{
|
||||
trigger_error("Unknown table '$sTable'", E_USER_ERROR);
|
||||
}
|
||||
// $aFields must be an array of "alias"=>"expr"
|
||||
// $oConditionExpr must be a condition tree
|
||||
// $aValues is an array of "alias"=>value
|
||||
|
||||
$this->m_sTable = $sTable;
|
||||
$this->m_sTableAlias = $sTableAlias;
|
||||
$this->m_aFields = $aFields;
|
||||
$this->m_oConditionExpr = $oConditionExpr;
|
||||
if (is_null($oConditionExpr))
|
||||
{
|
||||
$this->m_oConditionExpr = new TrueExpression;
|
||||
}
|
||||
else if (!$oConditionExpr instanceof Expression)
|
||||
{
|
||||
throw new CoreException('Invalid type for condition, expecting an Expression', array('class' => get_class($oConditionExpr)));
|
||||
}
|
||||
$this->m_aFullTextNeedles = $aFullTextNeedles;
|
||||
$this->m_bToDelete = $bToDelete;
|
||||
$this->m_aValues = $aValues;
|
||||
}
|
||||
|
||||
public function DisplayHtml()
|
||||
{
|
||||
if (count($this->m_aFields) == 0) $sFields = "";
|
||||
else
|
||||
{
|
||||
$aFieldDesc = array();
|
||||
foreach ($this->m_aFields as $sAlias => $oExpression)
|
||||
{
|
||||
$aFieldDesc[] = $oExpression->Render()." as <em>$sAlias</em>";
|
||||
}
|
||||
$sFields = " => ".implode(', ', $aFieldDesc);
|
||||
}
|
||||
echo "<b>$this->m_sTable</b>$sFields<br/>\n";
|
||||
// #@# todo - display html of an expression tree
|
||||
//$this->m_oConditionExpr->DisplayHtml()
|
||||
if (count($this->m_aFullTextNeedles) > 0)
|
||||
{
|
||||
echo "Full text criteria...<br/>\n";
|
||||
echo "<ul class=\"treeview\">\n";
|
||||
foreach ($this->m_aFullTextNeedles as $sFTNeedle)
|
||||
{
|
||||
echo "<li>$sFTNeedle</li>\n";
|
||||
}
|
||||
echo "</ul>";
|
||||
}
|
||||
if (count($this->m_aJoinSelects) > 0)
|
||||
{
|
||||
echo "Joined to...<br/>\n";
|
||||
echo "<ul class=\"treeview\">\n";
|
||||
foreach ($this->m_aJoinSelects as $aJoinInfo)
|
||||
{
|
||||
$sJoinType = $aJoinInfo["jointype"];
|
||||
$oSQLQuery = $aJoinInfo["select"];
|
||||
$sLeftField = $aJoinInfo["leftfield"];
|
||||
$sRightField = $aJoinInfo["rightfield"];
|
||||
|
||||
echo "<li>Join '$sJoinType', $sLeftField, $sRightField".$oSQLQuery->DisplayHtml()."</li>\n";
|
||||
}
|
||||
echo "</ul>";
|
||||
}
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
$oCondition = null;
|
||||
$aDelTables = array();
|
||||
$aSetValues = array();
|
||||
$this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues);
|
||||
echo "From ...<br/>\n";
|
||||
echo "<pre style=\"font-size: smaller;\">\n";
|
||||
print_r($aFrom);
|
||||
echo "</pre>";
|
||||
}
|
||||
|
||||
public function SetCondition($oConditionExpr)
|
||||
{
|
||||
$this->m_oConditionExpr = $oConditionExpr;
|
||||
}
|
||||
|
||||
public function AddCondition($oConditionExpr)
|
||||
{
|
||||
$this->m_oConditionExpr->LogAnd($oConditionExpr);
|
||||
}
|
||||
|
||||
private function AddJoin($sJoinType, $oSQLQuery, $sLeftField, $sRightField)
|
||||
{
|
||||
assert((get_class($oSQLQuery) == __CLASS__) || is_subclass_of($oSQLQuery, __CLASS__));
|
||||
if (!CMDBSource::IsField($this->m_sTable, $sLeftField))
|
||||
{
|
||||
trigger_error("Unknown field '$sLeftField' in table '".$this->m_sTable, E_USER_ERROR);
|
||||
}
|
||||
if (!CMDBSource::IsField($oSQLQuery->m_sTable, $sRightField))
|
||||
{
|
||||
trigger_error("Unknown field '$sRightField' in table '".$oSQLQuery->m_sTable."'", E_USER_ERROR);
|
||||
}
|
||||
|
||||
$this->m_aJoinSelects[] = array(
|
||||
"jointype" => $sJoinType,
|
||||
"select" => $oSQLQuery,
|
||||
"leftfield" => $sLeftField,
|
||||
"rightfield" => $sRightField
|
||||
);
|
||||
}
|
||||
public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField)
|
||||
{
|
||||
$this->AddJoin("inner", $oSQLQuery, $sLeftField, $sRightField);
|
||||
}
|
||||
public function AddLeftJoin($oSQLQuery, $sLeftField, $sRightField)
|
||||
{
|
||||
return $this->AddJoin("left", $oSQLQuery, $sLeftField, $sRightField);
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderDelete()
|
||||
{
|
||||
// The goal will be to complete the list as we build the Joins
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
$oCondition = null;
|
||||
$aDelTables = array();
|
||||
$aSetValues = array();
|
||||
$this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues);
|
||||
|
||||
// Target: DELETE myAlias1, myAlias2 FROM t1 as myAlias1, t2 as myAlias2, t3 as topreserve WHERE ...
|
||||
|
||||
$sDelete = self::ClauseDelete($aDelTables);
|
||||
$sFrom = self::ClauseFrom($aFrom);
|
||||
// #@# safety net to redo ?
|
||||
/*
|
||||
if ($this->m_oConditionExpr->IsAny())
|
||||
-- if (count($aConditions) == 0) --
|
||||
{
|
||||
trigger_error("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead...", E_USER_ERROR);
|
||||
}
|
||||
*/
|
||||
$sWhere = self::ClauseWhere($oCondition);
|
||||
return "DELETE $sDelete FROM $sFrom WHERE $sWhere";
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderUpdate()
|
||||
{
|
||||
// The goal will be to complete the list as we build the Joins
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
$oCondition = null;
|
||||
$aDelTables = array();
|
||||
$aSetValues = array();
|
||||
$this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues);
|
||||
|
||||
$sFrom = self::ClauseFrom($aFrom);
|
||||
$sValues = self::ClauseValues($aSetValues);
|
||||
$sWhere = self::ClauseWhere($oCondition);
|
||||
return "UPDATE $sFrom SET $sValues WHERE $sWhere";
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderSelect($aOrderBy = array())
|
||||
{
|
||||
// The goal will be to complete the lists as we build the Joins
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
$oCondition = null;
|
||||
$aDelTables = array();
|
||||
$aSetValues = array();
|
||||
$this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues);
|
||||
|
||||
$sSelect = self::ClauseSelect($aFields);
|
||||
$sFrom = self::ClauseFrom($aFrom);
|
||||
$sWhere = self::ClauseWhere($oCondition);
|
||||
$sOrderBy = self::ClauseOrderBy($aOrderBy);
|
||||
if (!empty($sOrderBy))
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy";
|
||||
}
|
||||
return "SELECT DISTINCT $sSelect FROM $sFrom WHERE $sWhere $sOrderBy";
|
||||
}
|
||||
|
||||
private static function ClauseSelect($aFields)
|
||||
{
|
||||
$aSelect = array();
|
||||
foreach ($aFields as $sFieldAlias => $sSQLExpr)
|
||||
{
|
||||
$aSelect[] = "$sSQLExpr AS $sFieldAlias";
|
||||
}
|
||||
$sSelect = implode(', ', $aSelect);
|
||||
return $sSelect;
|
||||
}
|
||||
|
||||
private static function ClauseDelete($aDelTableAliases)
|
||||
{
|
||||
$aDelTables = array();
|
||||
foreach ($aDelTableAliases as $sTableAlias)
|
||||
{
|
||||
$aDelTables[] = "$sTableAlias";
|
||||
}
|
||||
$sDelTables = implode(', ', $aDelTables);
|
||||
return $sDelTables;
|
||||
}
|
||||
|
||||
private static function ClauseFrom($aFrom)
|
||||
{
|
||||
$sFrom = "";
|
||||
foreach ($aFrom as $sTableAlias => $aJoinInfo)
|
||||
{
|
||||
switch ($aJoinInfo["jointype"])
|
||||
{
|
||||
case "first":
|
||||
$sFrom .= "`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
break;
|
||||
case "inner":
|
||||
$sFrom .= " INNER JOIN `".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ON ".$aJoinInfo["joincondition"];
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
break;
|
||||
case "left":
|
||||
$sFrom .= " LEFT JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
$sFrom .= ") ON ".$aJoinInfo["joincondition"];
|
||||
break;
|
||||
default:
|
||||
trigger_error("Unknown jointype: '".$aJoinInfo["jointype"]."'");
|
||||
}
|
||||
}
|
||||
return $sFrom;
|
||||
}
|
||||
|
||||
private static function ClauseValues($aValues)
|
||||
{
|
||||
$aSetValues = array();
|
||||
foreach ($aValues as $sFieldSpec => $value)
|
||||
{
|
||||
$aSetValues[] = "$sFieldSpec = ".CMDBSource::Quote($value);
|
||||
}
|
||||
$sSetValues = implode(', ', $aSetValues);
|
||||
return $sSetValues;
|
||||
}
|
||||
|
||||
private static function ClauseWhere($oConditionExpr)
|
||||
{
|
||||
return $oConditionExpr->Render();
|
||||
}
|
||||
|
||||
private static function ClauseOrderBy($aOrderBy)
|
||||
{
|
||||
$aOrderBySpec = array();
|
||||
foreach($aOrderBy as $sFieldAlias => $bAscending)
|
||||
{
|
||||
$aOrderBySpec[] = '`'.$sFieldAlias.'`'.($bAscending ? " ASC" : " DESC");
|
||||
}
|
||||
$sOrderBy = implode(", ", $aOrderBySpec);
|
||||
return $sOrderBy;
|
||||
}
|
||||
|
||||
// Purpose: prepare the query data, once for all
|
||||
private function privRender(&$aFrom, &$aFields, &$oCondition, &$aDelTables, &$aSetValues)
|
||||
{
|
||||
$sTableAlias = $this->privRenderSingleTable($aFrom, $aFields, $aDelTables, $aSetValues);
|
||||
|
||||
// Add the full text search condition, based on each and every requested field
|
||||
//
|
||||
// To be updated with a real full text search based on the mySQL settings
|
||||
// (then it might move somewhere else !)
|
||||
//
|
||||
$oCondition = $this->m_oConditionExpr;
|
||||
if ((count($aFields) > 0) && (count($this->m_aFullTextNeedles) > 0))
|
||||
{
|
||||
$aFieldExp = array();
|
||||
foreach ($aFields as $sField)
|
||||
{
|
||||
// This is TEMPORARY (that's why it is weird, actually)
|
||||
// Full text match will be done as an expression in the filter condition
|
||||
|
||||
// $sField is already a string `table`.`column`
|
||||
// Let's make an expression out of it (again !)
|
||||
$aFieldExp[] = Expression::FromOQL($sField);
|
||||
}
|
||||
$oFullTextExpr = new CharConcatExpression($aFieldExp);
|
||||
// The cast is necessary because the CONCAT result in a binary string:
|
||||
// if any of the field is a binary string => case sensitive comparison
|
||||
//
|
||||
foreach($this->m_aFullTextNeedles as $sFTNeedle)
|
||||
{
|
||||
$oNewCond = new BinaryExpression($oFullTextExpr, 'LIKE', new ScalarExpression("%$sFTNeedle%"));
|
||||
$oCondition = $oCondition->LogAnd($oNewCond);
|
||||
}
|
||||
}
|
||||
|
||||
return $sTableAlias;
|
||||
}
|
||||
|
||||
private function privRenderSingleTable(&$aFrom, &$aFields, &$aDelTables, &$aSetValues, $sJoinType = "first", $sCallerAlias = "", $sLeftField = "", $sRightField = "")
|
||||
{
|
||||
$aActualTableFields = CMDBSource::GetTableFieldsList($this->m_sTable);
|
||||
|
||||
$aTranslationTable[$this->m_sTable]['*'] = $this->m_sTableAlias;
|
||||
|
||||
// Handle the various kinds of join (or first table in the list)
|
||||
//
|
||||
$sJoinCond = "`$sCallerAlias`.`$sLeftField` = `{$this->m_sTableAlias}`.`$sRightField`";
|
||||
switch ($sJoinType)
|
||||
{
|
||||
case "first":
|
||||
$aFrom[$this->m_sTableAlias] = array("jointype"=>"first", "tablename"=>$this->m_sTable, "joincondition"=>"");
|
||||
break;
|
||||
case "inner":
|
||||
case "left":
|
||||
$aFrom[$this->m_sTableAlias] = array("jointype"=>$sJoinType, "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond");
|
||||
break;
|
||||
}
|
||||
|
||||
// Given the alias, modify the fields and conditions
|
||||
// before adding them into the current lists
|
||||
//
|
||||
foreach($this->m_aFields as $sAlias => $oExpression)
|
||||
{
|
||||
$sTable = $oExpression->GetParent();
|
||||
$sColumn = $oExpression->GetName();
|
||||
$aFields["`$sAlias`"] = $oExpression->Render();
|
||||
}
|
||||
if ($this->m_bToDelete)
|
||||
{
|
||||
$aDelTables[] = "`{$this->m_sTableAlias}`";
|
||||
}
|
||||
foreach($this->m_aValues as $sFieldName=>$value)
|
||||
{
|
||||
$aSetValues["`{$this->m_sTableAlias}`.`$sFieldName`"] = $value; // quoted further!
|
||||
}
|
||||
|
||||
// loop on joins, to complete the list of tables/fields/conditions
|
||||
//
|
||||
$aTempFrom = array(); // temporary subset of 'from' specs, to be grouped in the final query
|
||||
foreach ($this->m_aJoinSelects as $aJoinData)
|
||||
{
|
||||
$sJoinType = $aJoinData["jointype"];
|
||||
$oRightSelect = $aJoinData["select"];
|
||||
$sLeftField = $aJoinData["leftfield"];
|
||||
$sRightField = $aJoinData["rightfield"];
|
||||
|
||||
$sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $sJoinType, $this->m_sTableAlias, $sLeftField, $sRightField);
|
||||
}
|
||||
$aFrom[$this->m_sTableAlias]['subfrom'] = $aTempFrom;
|
||||
|
||||
return $this->m_sTableAlias;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
59
core/stimulus.class.inc.php
Normal file
59
core/stimulus.class.inc.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A stimulus is the trigger that makes the lifecycle go ahead (state machine)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
class ObjectStimulus
|
||||
{
|
||||
private $m_aParams = array();
|
||||
|
||||
public function __construct($aParams)
|
||||
{
|
||||
$this->m_aParams = $aParams;
|
||||
$this->ConsistencyCheck();
|
||||
}
|
||||
|
||||
public function Get($sParamName) {return $this->m_aParams[$sParamName];}
|
||||
|
||||
// Note: I could factorize this code with the parameter management made for the AttributeDef class
|
||||
// to be overloaded
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array("label", "description");
|
||||
}
|
||||
|
||||
private function ConsistencyCheck()
|
||||
{
|
||||
|
||||
// Check that any mandatory param has been specified
|
||||
//
|
||||
$aExpectedParams = $this->ListExpectedParams();
|
||||
foreach($aExpectedParams as $sParamName)
|
||||
{
|
||||
if (!array_key_exists($sParamName, $this->m_aParams))
|
||||
{
|
||||
$aBacktrace = debug_backtrace();
|
||||
$sTargetClass = $aBacktrace[2]["class"];
|
||||
$sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"];
|
||||
trigger_error("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)</br>\n", E_USER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class StimulusUserAction extends ObjectStimulus
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
430
core/test.class.inc.php
Normal file
430
core/test.class.inc.php
Normal file
@@ -0,0 +1,430 @@
|
||||
<?php
|
||||
|
||||
require_once('coreexception.class.inc.php');
|
||||
require_once('attributedef.class.inc.php');
|
||||
require_once('filterdef.class.inc.php');
|
||||
require_once('stimulus.class.inc.php');
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
|
||||
require_once('expression.class.inc.php');
|
||||
require_once('cmdbsource.class.inc.php');
|
||||
require_once('sqlquery.class.inc.php');
|
||||
|
||||
require_once('dbobject.class.php');
|
||||
require_once('dbobjectsearch.class.php');
|
||||
require_once('dbobjectset.class.php');
|
||||
|
||||
require_once('userrights.class.inc.php');
|
||||
|
||||
|
||||
|
||||
// Just to differentiate programmatically triggered exceptions and other kind of errors (usefull?)
|
||||
class UnitTestException extends Exception
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* Improved display of the backtrace
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
class ExceptionFromError extends Exception
|
||||
{
|
||||
public function getTraceAsHtml()
|
||||
{
|
||||
$aBackTrace = $this->getTrace();
|
||||
return MyHelpers::get_callstack_html(0, $this->getTrace());
|
||||
// return "<pre>\n".$this->getTraceAsString()."</pre>\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test handler API and basic helpers
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
abstract class TestHandler
|
||||
{
|
||||
protected $m_aSuccesses;
|
||||
protected $m_aWarnings;
|
||||
protected $m_aErrors;
|
||||
protected $m_sOutput;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->m_aSuccesses = array();
|
||||
$this->m_aWarnings = array();
|
||||
$this->m_aErrors = array();
|
||||
}
|
||||
|
||||
abstract static public function GetName();
|
||||
abstract static public function GetDescription();
|
||||
|
||||
protected function DoPrepare() {return true;}
|
||||
abstract protected function DoExecute();
|
||||
protected function DoCleanup() {return true;}
|
||||
|
||||
protected function ReportSuccess($sMessage, $sSubtestId = '')
|
||||
{
|
||||
$this->m_aSuccesses[] = $sMessage;
|
||||
}
|
||||
|
||||
protected function ReportWarning($sMessage, $sSubtestId = '')
|
||||
{
|
||||
$this->m_aWarnings[] = $sMessage;
|
||||
}
|
||||
|
||||
protected function ReportError($sMessage, $sSubtestId = '')
|
||||
{
|
||||
$this->m_aErrors[] = $sMessage;
|
||||
}
|
||||
|
||||
public function GetResults()
|
||||
{
|
||||
return $this->m_aSuccesses;
|
||||
}
|
||||
|
||||
public function GetWarnings()
|
||||
{
|
||||
return $this->m_aWarnings;
|
||||
}
|
||||
|
||||
public function GetErrors()
|
||||
{
|
||||
return $this->m_aErrors;
|
||||
}
|
||||
|
||||
public function GetOutput()
|
||||
{
|
||||
return $this->m_sOutput;
|
||||
}
|
||||
|
||||
public function error_handler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
// Note: return false to call the default handler (stop the program if an error)
|
||||
|
||||
switch ($errno)
|
||||
{
|
||||
case E_USER_ERROR:
|
||||
$this->ReportError($errstr);
|
||||
//throw new ExceptionFromError("Fatal error in line $errline of file $errfile: $errstr");
|
||||
break;
|
||||
case E_USER_WARNING:
|
||||
$this->ReportWarning($errstr);
|
||||
break;
|
||||
case E_USER_NOTICE:
|
||||
$this->ReportWarning($errstr);
|
||||
break;
|
||||
default:
|
||||
throw new ExceptionFromError("Fatal warning in line $errline of file $errfile: $errstr");
|
||||
$this->ReportWarning("Unknown error type: [$errno] $errstr");
|
||||
echo "Unknown error type: [$errno] $errstr<br />\n";
|
||||
break;
|
||||
}
|
||||
return true; // do not call the default handler
|
||||
}
|
||||
|
||||
public function Execute()
|
||||
{
|
||||
ob_start();
|
||||
set_error_handler(array($this, 'error_handler'));
|
||||
try
|
||||
{
|
||||
$this->DoPrepare();
|
||||
$this->DoExecute();
|
||||
}
|
||||
catch (ExceptionFromError $e)
|
||||
{
|
||||
$this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml());
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
//$this->ReportError($e->getMessage());
|
||||
//$this->ReportError($e->__tostring());
|
||||
$this->ReportError($e->getMessage().' - '.$e->getTraceAsString());
|
||||
}
|
||||
restore_error_handler();
|
||||
$this->m_sOutput = ob_get_clean();
|
||||
return (count($this->GetErrors()) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to execute a piece of code (checks if an error occurs)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class TestFunction extends TestHandler
|
||||
{
|
||||
// simply overload DoExecute (temporary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to check that a function outputs some values depending on its input
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class TestFunctionInOut extends TestFunction
|
||||
{
|
||||
abstract static public function GetCallSpec(); // parameters to call_user_func
|
||||
abstract static public function GetInOut(); // array of input => output
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
$aTests = $this->GetInOut();
|
||||
if (is_array($aTests))
|
||||
{
|
||||
foreach ($aTests as $iTestId => $aTest)
|
||||
{
|
||||
$ret = call_user_func_array($this->GetCallSpec(), $aTest['args']);
|
||||
if ($ret != $aTest['output'])
|
||||
{
|
||||
// Note: to be improved to cope with non string parameters
|
||||
$this->ReportError("Found '$ret' while expecting '".$aTest['output']."'", $iTestId);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->ReportSuccess("Found the expected output '$ret'", $iTestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret = call_user_func($this->GetCallSpec());
|
||||
$this->ReportSuccess('Finished successfully');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to check an URL (Searches for Error/Warning/Etc keywords)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class TestUrl extends TestHandler
|
||||
{
|
||||
abstract static public function GetUrl();
|
||||
abstract static public function GetErrorKeywords();
|
||||
abstract static public function GetWarningKeywords();
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to check a user management module
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class TestUserRights extends TestHandler
|
||||
{
|
||||
protected function DoExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to execute a scenario on a given DB
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class TestScenarioOnDB extends TestHandler
|
||||
{
|
||||
abstract static public function GetDBHost();
|
||||
abstract static public function GetDBUser();
|
||||
abstract static public function GetDBPwd();
|
||||
abstract static public function GetDBName();
|
||||
|
||||
protected function DoPrepare()
|
||||
{
|
||||
$sDBHost = $this->GetDBHost();
|
||||
$sDBUser = $this->GetDBUser();
|
||||
$sDBPwd = $this->GetDBPwd();
|
||||
$sDBName = $this->GetDBName();
|
||||
|
||||
CMDBSource::Init($sDBHost, $sDBUser, $sDBPwd);
|
||||
if (CMDBSource::IsDB($sDBName))
|
||||
{
|
||||
CMDBSource::DropDB($sDBName);
|
||||
}
|
||||
CMDBSource::CreateDB($sDBName);
|
||||
}
|
||||
|
||||
protected function DoCleanup()
|
||||
{
|
||||
// CMDBSource::DropDB($this->GetDBName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to use a business model on a given DB
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class TestBizModel extends TestHandler
|
||||
{
|
||||
// abstract static public function GetDBSubName();
|
||||
// abstract static public function GetBusinessModelFile();
|
||||
abstract static public function GetConfigFile();
|
||||
|
||||
protected function DoPrepare()
|
||||
{
|
||||
MetaModel::Startup($this->GetConfigFile(), true); // allow missing DB
|
||||
MetaModel::CheckDefinitions();
|
||||
|
||||
// something here to create records... but that's another story
|
||||
}
|
||||
|
||||
protected function ResetDB()
|
||||
{
|
||||
if (MetaModel::DBExists())
|
||||
{
|
||||
MetaModel::DBDrop();
|
||||
}
|
||||
MetaModel::DBCreate();
|
||||
}
|
||||
|
||||
static protected function show_list($oObjectSet)
|
||||
{
|
||||
$oObjectSet->Rewind();
|
||||
while ($oItem = $oObjectSet->Fetch())
|
||||
{
|
||||
$aValues = array();
|
||||
foreach(MetaModel::GetAttributesList(get_class($oItem)) as $sAttCode)
|
||||
{
|
||||
$aValues[] = $oItem->GetAsHTML($sAttCode);
|
||||
}
|
||||
echo $oItem->GetKey()." => ".implode(", ", $aValues)."</br>\n";
|
||||
}
|
||||
}
|
||||
|
||||
static protected function search_and_show_list(DBObjectSearch $oMyFilter)
|
||||
{
|
||||
$oObjSet = new CMDBObjectSet($oMyFilter);
|
||||
echo $oMyFilter->__DescribeHTML()."' - Found ".$oObjSet->Count()." items.</br>\n";
|
||||
self::show_list($oObjSet);
|
||||
}
|
||||
|
||||
static protected function search_and_show_list_from_sibusql($sSibuSQL)
|
||||
{
|
||||
echo $sSibuSQL."...<br/>\n";
|
||||
$oNewFilter = DBObjectSearch::FromSibuSQL($sSibuSQL);
|
||||
self::search_and_show_list($oNewFilter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test to execute a scenario common to any business model (tries to build all the possible queries, etc.)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class TestBizModelGeneric extends TestBizModel
|
||||
{
|
||||
static public function GetName()
|
||||
{
|
||||
return 'Full test on a given business model';
|
||||
}
|
||||
|
||||
static public function GetDescription()
|
||||
{
|
||||
return 'Systematic tests: gets each and every existing class and tries every attribute, search filters, etc.';
|
||||
}
|
||||
|
||||
protected function DoPrepare()
|
||||
{
|
||||
parent::DoPrepare();
|
||||
|
||||
if (!MetaModel::DBExists())
|
||||
{
|
||||
MetaModel::DBCreate();
|
||||
}
|
||||
// something here to create records... but that's another story
|
||||
}
|
||||
|
||||
protected function DoExecute()
|
||||
{
|
||||
foreach(MetaModel::GetClasses() as $sClassName)
|
||||
{
|
||||
if (MetaModel::IsAbstract($sClassName)) continue;
|
||||
|
||||
$oNobody = MetaModel::GetObject($sClassName, 123);
|
||||
$oBaby = new $sClassName;
|
||||
$oFilter = new DBObjectSearch($sClassName);
|
||||
|
||||
// Challenge reversibility of SibusQL / filter object
|
||||
//
|
||||
$sExpr1 = $oFilter->ToSibuSQL();
|
||||
$oNewFilter = DBObjectSearch::FromSibuSQL($sExpr1);
|
||||
$sExpr2 = $oNewFilter->ToSibuSQL();
|
||||
if ($sExpr1 != $sExpr2)
|
||||
{
|
||||
$this->ReportError("Found two different SibuSQL expression out of the (same?) filter: <em>$sExpr1</em> != <em>$sExpr2</em>");
|
||||
}
|
||||
|
||||
// Use the filter (perform the query)
|
||||
//
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
205
core/userrights.class.inc.php
Normal file
205
core/userrights.class.inc.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* UserRights
|
||||
* User management API
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
class UserRightException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
define('UR_ALLOWED_NO', 0);
|
||||
define('UR_ALLOWED_YES', 1);
|
||||
define('UR_ALLOWED_DEPENDS', 2);
|
||||
|
||||
define('UR_ACTION_READ', 1); // View an object
|
||||
define('UR_ACTION_MODIFY', 2); // Create/modify an object/attribute
|
||||
define('UR_ACTION_DELETE', 3); // Delete an object
|
||||
|
||||
define('UR_ACTION_BULK_READ', 4); // Export multiple objects
|
||||
define('UR_ACTION_BULK_MODIFY', 5); // Create/modify multiple objects
|
||||
define('UR_ACTION_BULK_DELETE', 6); // Delete multiple objects
|
||||
|
||||
define('UR_ACTION_APPLICATION_DEFINED', 10000); // Application specific actions (CSV import, View schema...)
|
||||
|
||||
/**
|
||||
* User management module API
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
abstract class UserRightsAddOnAPI
|
||||
{
|
||||
abstract public function Setup(); // initial installation
|
||||
abstract public function Init(); // loads data (possible optimizations)
|
||||
abstract public function CheckCredentials($iUserId, $sPassword); // returns the id of the user or false
|
||||
abstract public function GetFilter($iUserId, $sClass); // returns a filter object
|
||||
abstract public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $aInstances);
|
||||
abstract public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $aInstances);
|
||||
abstract public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* User management core API
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class UserRights
|
||||
{
|
||||
protected static $m_oAddOn;
|
||||
protected static $m_sUser;
|
||||
protected static $m_sRealUser;
|
||||
protected static $m_iUserId;
|
||||
protected static $m_iRealUserId;
|
||||
|
||||
public static function SelectModule($sModuleName)
|
||||
{
|
||||
if (!class_exists($sModuleName))
|
||||
{
|
||||
trigger_error("Could not select this module, '$sModuleName' in not a valid class name", E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
if (!is_subclass_of($sModuleName, 'UserRightsAddOnAPI'))
|
||||
{
|
||||
trigger_error("Could not select this module, the class '$sModuleName' is not derived from UserRightsAddOnAPI", E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
self::$m_oAddOn = new $sModuleName;
|
||||
self::$m_oAddOn->Init();
|
||||
self::$m_sUser = '';
|
||||
self::$m_sRealUser = '';
|
||||
self::$m_iUserId = 0;
|
||||
self::$m_iRealUserId = 0;
|
||||
}
|
||||
|
||||
// Installation (e.g: give default values for users)
|
||||
public static function Setup()
|
||||
{
|
||||
// to be discussed...
|
||||
return self::$m_oAddOn->Setup();
|
||||
}
|
||||
|
||||
protected static function IsLoggedIn()
|
||||
{
|
||||
return (!empty(self::$m_sUser));
|
||||
}
|
||||
|
||||
public static function Login($sName, $sPassword)
|
||||
{
|
||||
self::$m_iUserId = self::$m_oAddOn->CheckCredentials($sName, $sPassword);
|
||||
if ( self::$m_iUserId !== false )
|
||||
{
|
||||
self::$m_sUser = $sName;
|
||||
self::$m_iRealUserId = self::$m_iUserId;
|
||||
self::$m_sRealUser = $sName;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function Impersonate($sName, $sPassword)
|
||||
{
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
self::$m_iRealUserId = self::$m_oAddOn->CheckCredentials($sName, $sPassword);
|
||||
if ( self::$m_iRealUserId !== false)
|
||||
{
|
||||
self::$m_sUser = $sName;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetUser()
|
||||
{
|
||||
return self::$m_sUser;
|
||||
}
|
||||
|
||||
public static function GetUserId()
|
||||
{
|
||||
return self::$m_iUserId;
|
||||
}
|
||||
|
||||
public static function GetRealUser()
|
||||
{
|
||||
return self::$m_sRealUser;
|
||||
}
|
||||
|
||||
public static function GetRealUserId()
|
||||
{
|
||||
return self::$m_iRealUserId;
|
||||
}
|
||||
|
||||
protected static function CheckLogin()
|
||||
{
|
||||
if (!self::IsLoggedIn())
|
||||
{
|
||||
//throw new UserRightException('No user logged in', array());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static function GetFilter($sClass)
|
||||
{
|
||||
if (!MetaModel::HasCategory($sClass, 'bizModel')) return new DBObjectSearch($sClass);
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
return self::$m_oAddOn->GetFilter(self::$m_iUserId, $sClass);
|
||||
}
|
||||
|
||||
public static function IsActionAllowed($sClass, $iActionCode, dbObjectSet $aInstances)
|
||||
{
|
||||
if (!MetaModel::HasCategory($sClass, 'bizModel')) return true;
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
return self::$m_oAddOn->IsActionAllowed(self::$m_iUserId, $sClass, $iActionCode, $aInstances);
|
||||
}
|
||||
|
||||
public static function IsStimulusAllowed($sClass, $sStimulusCode, dbObjectSet $aInstances)
|
||||
{
|
||||
if (!MetaModel::HasCategory($sClass, 'bizModel')) return true;
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
return self::$m_oAddOn->IsStimulusAllowed(self::$m_iUserId, $sClass, $sStimulusCode, $aInstances);
|
||||
}
|
||||
|
||||
public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances)
|
||||
{
|
||||
if (!MetaModel::HasCategory($sClass, 'bizModel')) return true;
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
return self::$m_oAddOn->IsActionAllowedOnAttribute(self::$m_iUserId, $sClass, $sAttCode, $iActionCode, $aInstances);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
238
core/valuesetdef.class.inc.php
Normal file
238
core/valuesetdef.class.inc.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ValueSetDefinition
|
||||
* value sets API and implementations
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @author Denis Flaven <denisflave@free.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version 1.1.1.1 $
|
||||
*/
|
||||
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
|
||||
abstract class ValueSetDefinition
|
||||
{
|
||||
protected $m_bIsLoaded = false;
|
||||
protected $m_aValues = array();
|
||||
protected $m_aArgsObj = array();
|
||||
protected $m_aArgsApp = array();
|
||||
|
||||
|
||||
// Displayable description that could be computed out of the std usage context
|
||||
public function GetValuesDescription()
|
||||
{
|
||||
$aValues = $this->GetValues(array(), '');
|
||||
$aDisplayedValues = array();
|
||||
foreach($aValues as $key => $value)
|
||||
{
|
||||
$aDisplayedValues[] = "$key => $value";
|
||||
}
|
||||
$sAllowedValues = implode(', ', $aDisplayedValues);
|
||||
return $sAllowedValues;
|
||||
}
|
||||
|
||||
|
||||
public function GetValues($aArgs, $sBeginsWith)
|
||||
{
|
||||
if (!$this->m_bIsLoaded)
|
||||
{
|
||||
$this->LoadValues($aArgs);
|
||||
$this->m_bIsLoaded = true;
|
||||
}
|
||||
if (strlen($sBeginsWith) == 0)
|
||||
{
|
||||
$aRet = $this->m_aValues;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCheckedLen = strlen($sBeginsWith);
|
||||
$sBeginsWith = strtolower($sBeginsWith);
|
||||
$aRet = array();
|
||||
foreach ($this->m_aValues as $sKey=>$sValue)
|
||||
{
|
||||
if (strtolower(substr($sValue, 0, $iCheckedLen)) == $sBeginsWith)
|
||||
{
|
||||
$aRet[$sKey] = $sValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
public function ListArgsFromContextApp()
|
||||
{
|
||||
return $this->m_aArgsObj;
|
||||
}
|
||||
public function ListArgsFromContextObj()
|
||||
{
|
||||
return $this->m_aArgsApp;
|
||||
}
|
||||
|
||||
abstract protected function LoadValues($aArgs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set of existing values for an attribute, given a search filter
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class ValueSetObjects extends ValueSetDefinition
|
||||
{
|
||||
protected $m_sFilterExpr; // in SibuSQL
|
||||
protected $m_sValueAttCode;
|
||||
protected $m_aOrderBy;
|
||||
|
||||
public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array())
|
||||
{
|
||||
$this->m_sFilterExpr = $sFilterExp;
|
||||
$this->m_sValueAttCode = $sValueAttCode;
|
||||
$this->m_aOrderBy = $aOrderBy;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
{
|
||||
$this->m_aValues = array();
|
||||
|
||||
$oFilter = DBObjectSearch::FromSibuSQL($this->m_sFilterExpr, $aArgs);
|
||||
if (!$oFilter) return false;
|
||||
|
||||
if (empty($this->m_sValueAttCode))
|
||||
{
|
||||
$this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass());
|
||||
}
|
||||
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy);
|
||||
while ($oObject = $oObjects->Fetch())
|
||||
{
|
||||
$this->m_aValues[$oObject->GetKey()] = $oObject->GetAsHTML($this->m_sValueAttCode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetValuesDescription()
|
||||
{
|
||||
return 'Filter: '.$this->m_sFilterExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set of existing values for an attribute, given a search filter and a relation id
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class ValueSetRelatedObjects extends ValueSetObjects
|
||||
{
|
||||
public function __construct($sFilterExp, $sRelCode, $sClass, $sValueAttCode = '', $aOrderBy = array())
|
||||
{
|
||||
$sFullFilterExp = "$sClass: RELATED ($sRelCode, 1) TO ($sFilterExp)";
|
||||
parent::__construct($sFullFilterExp, $sValueAttCode, $aOrderBy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set oof existing values for an attribute, given a set of objects (AttributeLinkedSet)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class ValueSetRelatedObjectsFromLinkedSet extends ValueSetDefinition
|
||||
{
|
||||
protected $m_sLinkedSetAttCode;
|
||||
protected $m_sRelCode;
|
||||
protected $m_sValueAttCode;
|
||||
protected $m_aOrderBy;
|
||||
|
||||
public function __construct($sLinkedSetAttCode, $sRelCode, $sValueAttCode = '', $aOrderBy = array())
|
||||
{
|
||||
$this->m_sLinkedSetAttCode = $sLinkedSetAttCode;
|
||||
$this->m_sRelCode = $sRelCode;
|
||||
$this->m_sValueAttCode = $sValueAttCode;
|
||||
$this->m_aOrderBy = $aOrderBy;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
{
|
||||
$this->m_aValues = array();
|
||||
|
||||
if (empty($this->m_sValueAttCode))
|
||||
{
|
||||
$this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass());
|
||||
}
|
||||
|
||||
$oCurrentObject = @$aArgs['*this*'];
|
||||
if (!is_object($oCurrentObject)) return false;
|
||||
|
||||
$oObjects = $oCurrentObject->Get($this->m_sLinkedSetAttCode);
|
||||
while ($oObject = $oObjects->Fetch())
|
||||
{
|
||||
$this->m_aValues[$oObject->GetKey()] = $oObject->Get($this->m_sValueAttCode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetValuesDescription()
|
||||
{
|
||||
return 'Objects related ('.$this->m_sRelCode.') to objects linked through '.$this->m_sLinkedSetAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fixed set values (could be hardcoded in the business model)
|
||||
*
|
||||
* @package iTopORM
|
||||
* @author Romain Quetiez <romainquetiez@yahoo.fr>
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.itop.com
|
||||
* @since 1.0
|
||||
* @version $itopversion$
|
||||
*/
|
||||
class ValueSetEnum extends ValueSetDefinition
|
||||
{
|
||||
public function __construct($Values)
|
||||
{
|
||||
if (is_array($Values))
|
||||
{
|
||||
$aValues = $Values;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues = array();
|
||||
foreach (explode(",", $Values) as $sVal)
|
||||
{
|
||||
$sVal = trim($sVal);
|
||||
$sKey = $sVal;
|
||||
$aValues[$sKey] = $sVal;
|
||||
}
|
||||
}
|
||||
$this->m_aValues = $aValues;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user