diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index 710f741b8..de90baa47 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -173,8 +173,8 @@ class UserRightsMatrix extends UserRightsAddOnAPI $oUser->Set('login', $sAdminUser); $oUser->Set('password', $sAdminPwd); $oUser->Set('userid', 1); // one is for root ! - $oUser->DBInsert(); - $this->SetupUser($oUser, true); + $iUserId = $oUser->DBInsertNoReload(); + $this->SetupUser($iUserId, true); return true; } @@ -185,15 +185,13 @@ class UserRightsMatrix extends UserRightsAddOnAPI $oUserSet = new DBObjectSet(DBObjectSearch::FromSibuSQL("UserRightsMatrixUsers")); while ($oUser = $oUserSet->Fetch()) { - $this->SetupUser($oUser); + $this->SetupUser($oUser->GetKey()); } return true; } - protected function SetupUser($oUser, $bNewUser = false) + protected function SetupUser($iUserId, $bNewUser = false) { - $iUserId = $oUser->GetKey(); - foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory) { foreach (MetaModel::GetClasses($sCategory) as $sClass) @@ -213,11 +211,11 @@ class UserRightsMatrix extends UserRightsAddOnAPI { // Create a new entry $oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassGrant"); - $oMyClassGrant->Set("userid", $oUser->GetKey()); + $oMyClassGrant->Set("userid", $iUserId); $oMyClassGrant->Set("class", $sClass); $oMyClassGrant->Set("action", $sAction); $oMyClassGrant->Set("permission", "yes"); - $iId = $oMyClassGrant->DBInsert(); + $iId = $oMyClassGrant->DBInsertNoReload(); } } foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) @@ -235,11 +233,11 @@ class UserRightsMatrix extends UserRightsAddOnAPI { // Create a new entry $oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassStimulusGrant"); - $oMyClassGrant->Set("userid", $oUser->GetKey()); + $oMyClassGrant->Set("userid", $iUserId); $oMyClassGrant->Set("class", $sClass); $oMyClassGrant->Set("stimulus", $sStimulusCode); $oMyClassGrant->Set("permission", "yes"); - $iId = $oMyClassGrant->DBInsert(); + $iId = $oMyClassGrant->DBInsertNoReload(); } } foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) @@ -259,12 +257,12 @@ class UserRightsMatrix extends UserRightsAddOnAPI { // Create a new entry $oMyAttGrant = MetaModel::NewObject("UserRightsMatrixAttributeGrant"); - $oMyAttGrant->Set("userid", $oUser->GetKey()); + $oMyAttGrant->Set("userid", $iUserId); $oMyAttGrant->Set("class", $sClass); $oMyAttGrant->Set("attcode", $sAttCode); $oMyAttGrant->Set("action", $sAction); $oMyAttGrant->Set("permission", "yes"); - $iId = $oMyAttGrant->DBInsert(); + $iId = $oMyAttGrant->DBInsertNoReload(); } } } diff --git a/application/dialogstack.class.inc.php b/application/dialogstack.class.inc.php index a81f62166..f3e54bbf3 100644 --- a/application/dialogstack.class.inc.php +++ b/application/dialogstack.class.inc.php @@ -122,7 +122,7 @@ class dialogstack if (self::$m_bCurrPageDeclared) { - trigger_error("DeclareCaller() must not be called before StartDialog()", E_USER_ERROR); + throw new Exception("DeclareCaller() must not be called before StartDialog()"); } $aCall = array( @@ -242,7 +242,7 @@ class dialogstack } else { - trigger_error("Wrong value for button style ($iButtonStyle)", E_USER_ERROR); + throw new Exception("Wrong value for button style ($iButtonStyle)"); } $sRet .= "\n"; $sRet .= "\n"; diff --git a/application/utils.inc.php b/application/utils.inc.php index fb7e0ab08..00aa4c8d3 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -39,7 +39,7 @@ class utils self::$m_aConfig = array(); $sConfigContents = self::ReadFromFile(CONFIGFILE); - if (!$sConfigContents) trigger_error("Could not load file ".CONFIGFILE); + if (!$sConfigContents) throw new Exception("Could not load file ".CONFIGFILE); foreach (explode("\n", $sConfigContents) as $sLine) { diff --git a/business/business_test.class.inc.php b/business/business_test.class.inc.php index 6255208fe..3a51b8f93 100644 --- a/business/business_test.class.inc.php +++ b/business/business_test.class.inc.php @@ -310,7 +310,7 @@ class cmdbLiens extends cmdbObjectHomeMade public static function GetRelationQueries($sRelCode) { - trigger_error("GetRelationQueries: cmdbLiens"); + throw new CoreException("GetRelationQueries: cmdbLiens"); return array("Relies on" => array("sQuery"=>"", "bPropagate"=>true, "iDistance"=>3)); } } @@ -357,7 +357,7 @@ class cmdbWorkshop extends cmdbObjectHomeMade public static function GetRelationQueries($sRelCode) { - trigger_error("GetRelationQueries: cmdbWorkshop"); + throw new CoreException("GetRelationQueries: cmdbWorkshop"); return array("Relies on" => array("sQuery"=>"", "bPropagate"=>true, "iDistance"=>3)); } } diff --git a/core/MyHelpers.class.inc.php b/core/MyHelpers.class.inc.php index 59ba892d0..8ee89a5ff 100644 --- a/core/MyHelpers.class.inc.php +++ b/core/MyHelpers.class.inc.php @@ -42,7 +42,7 @@ class MyHelpers $sArrayDesc = "{".implode(", ", $aData)."}"; } // exit! - trigger_error("Wrong value for $sDescription, found '$value' while expecting a value in $sArrayDesc", E_USER_ERROR); + throw new CoreException("Wrong value for $sDescription, found '$value' while expecting a value in $sArrayDesc"); } // getmicrotime() @@ -281,10 +281,10 @@ class MyHelpers /////////////////////////////////////////////////////////////////////////////// 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); + if (!is_array($aData)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not an array"); $aFirstRow = reset($aData); if (count($aData) == 0) return ''; - if (!is_array($aFirstRow)) trigger_error("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array", E_USER_ERROR); + if (!is_array($aFirstRow)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array"); $sOutput = ""; $sOutput .= "\n"; @@ -364,7 +364,7 @@ class MyHelpers /////////////////////////////////////////////////////////////////////////////// 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); + if (!is_string($sLongString)) throw new CoreException("beautifulstr: expect a string as 1st argument"); // Nothing to do if the string is short if (strlen($sLongString) <= $iMaxLen) return $sLongString; diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 342e68ee3..d1af378a4 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -53,7 +53,7 @@ abstract class AttributeDefinition { if (!array_key_exists($sParam, $this->m_aParams)) { - trigger_error("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); + throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); } else { @@ -90,7 +90,7 @@ abstract class AttributeDefinition $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)
\n", E_USER_ERROR); + throw new Exception("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); } } } @@ -390,7 +390,7 @@ class AttributeInteger extends AttributeDBField return $this->GetSQLExpr()." <= $sQValue"; break; case 'in': - if (!is_array($value)) trigger_error("Expected an array for argument value (sOpCode='$sOpCode')"); + if (!is_array($value)) throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')"); return $this->GetSQLExpr()." IN ('".implode("', '", $value)."')"; break; @@ -485,7 +485,7 @@ class AttributeString extends AttributeDBField return (string)$proposedValue; // if (!settype($proposedValue, "string")) // { - // trigger_error("Failed to change the type of '$proposedValue' to a string", E_USER_WARNING); + // throw new CoreException("Failed to change the type of '$proposedValue' to a string"); // } } public function RealValueToSQLValue($value) @@ -692,7 +692,7 @@ class AttributeDate extends AttributeDBField { 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); + throw new CoreException("Invalid type for a date (found ".gettype($proposedValue)." and accepting string/int/DateTime)"); return null; } public function RealValueToSQLValue($value) @@ -814,7 +814,7 @@ class AttributeExternalField extends AttributeDefinition public function GetEditClass() {return "ExtField";} public function GetDBFieldType() { - trigger_error("external attribute: does it make any sense to request its type ?", E_USER_WARNING); + // throw new CoreException("external attribute: does it make any sense to request its type ?"); $oExtAttDef = $this->GetExtAttDef(); return $oExtAttDef->GetDBFieldType(); } @@ -832,7 +832,7 @@ class AttributeExternalField extends AttributeDefinition return false; default: - trigger_error("Unexpected value for argument iType: '$iType'", E_USER_ERROR); + throw new CoreException("Unexpected value for argument iType: '$iType'"); } } @@ -866,7 +866,7 @@ class AttributeExternalField extends AttributeDefinition return MetaModel::GetAttributeDef($this->GetHostClass(), $this->Get("extkey_attcode")); default: - trigger_error("Unexpected value for argument iType: '$iType'", E_USER_ERROR); + throw new CoreException("Unexpected value for argument iType: '$iType'"); } } diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php index e52521a7d..491852a22 100644 --- a/core/bulkchange.class.inc.php +++ b/core/bulkchange.class.inc.php @@ -62,6 +62,14 @@ class CellChangeSpec_Unchanged extends CellChangeSpec } } +class CellChangeSpec_Init extends CellChangeSpec +{ + public function GetDescription() + { + return $this->GetValue(); + } +} + class CellChangeSpec_Modify extends CellChangeSpec { protected $m_previousValue; @@ -94,7 +102,7 @@ class CellChangeSpec_Issue extends CellChangeSpec_Modify { return 'Could not be changed - reason: '.$this->m_sReason; } - return 'Could not be changed to "'.$this->GetValue().' - reason:'.$this->m_sReason.' (previous: '.$this->m_previousValue.')'; + return 'Could not be changed to "'.$this->GetValue().'" - reason: '.$this->m_sReason.' (previous: '.$this->m_previousValue.')'; } } @@ -168,7 +176,7 @@ class RowStatus_Issue extends RowStatus { protected $m_sReason; - public function __construct($proposedValue, $previousValue, $sReason) + public function __construct($sReason) { $this->m_sReason = $sReason; } @@ -240,9 +248,16 @@ class BulkChange $oTargetObj->Set($sAttCode, $oForeignObj->GetKey()); // Report it - if (array_key_exists($sAttCode, $oTargetObj->ListChanges(false))) + if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) { - $aResults[$sAttCode]= new CellChangeSpec_Modify($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + if ($oTargetObj->IsNew()) + { + $aResults[$sAttCode]= new CellChangeSpec_Init($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + } + else + { + $aResults[$sAttCode]= new CellChangeSpec_Modify($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + } } else { @@ -259,7 +274,14 @@ class BulkChange // foreach ($this->m_aAttList as $sAttCode => $iCol) { - $oTargetObj->Set($sAttCode, $aRowData[$iCol]); + if (!$oTargetObj->CheckValue($sAttCode, $aRowData[$iCol])) + { + $aErrors[$sAttCode] = "Unexpected value"; + } + else + { + $oTargetObj->Set($sAttCode, $aRowData[$iCol]); + } } // Reporting on fields @@ -269,11 +291,19 @@ class BulkChange { if (isset($aErrors[$sAttCode])) { - $aResults["col$iCol"]= new CellChangeSpec_Issue($aRowData[$iCol], $previousValue, $sReason); + $aResults["col$iCol"]= new CellChangeSpec_Issue($aRowData[$iCol], $oTargetObj->Get($sAttCode), $aErrors[$sAttCode]); } elseif (array_key_exists($sAttCode, $aChangedFields)) { - $aResults["col$iCol"]= new CellChangeSpec_Modify($aRowData[$iCol], $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + $originalValue = $oTargetObj->GetOriginal($sAttCode); + if ($oTargetObj->IsNew()) + { + $aResults["col$iCol"]= new CellChangeSpec_Init($aRowData[$iCol], $oTargetObj->Get($sAttCode), $originalValue); + } + else + { + $aResults["col$iCol"]= new CellChangeSpec_Modify($aRowData[$iCol], $oTargetObj->Get($sAttCode), $originalValue); + } } else { @@ -315,7 +345,7 @@ class BulkChange // if ($oChange) { - $newID = $oTargetObj->DBInsertTracked($oChange); + $newID = $oTargetObj->DBInsertTrackedNoReload($oChange); $aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj($newID); } else diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index 9c019d0c4..0ed8776de 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -150,15 +150,17 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp 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("oldvalue", array("label"=>"Previous value", "description"=>"previous value of the attribute", "allowed_values"=>null, "sql"=>"oldvalue", "default_value"=>null, "is_null_allowed"=>true, "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("oldvalue"); 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 + MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for a list } } diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index 3bd4935f5..d49f7280b 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -144,7 +144,7 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("change", $oChange->GetKey()); $oMyChangeOp->Set("objclass", get_class($this)); $oMyChangeOp->Set("objkey", $this->GetKey()); - $iId = $oMyChangeOp->DBInsert(); + $iId = $oMyChangeOp->DBInsertNoReload(); } private function RecordObjDeletion(CMDBChange $oChange, $objkey) { @@ -152,22 +152,12 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("change", $oChange->GetKey()); $oMyChangeOp->Set("objclass", get_class($this)); $oMyChangeOp->Set("objkey", $objkey); - $iId = $oMyChangeOp->DBInsert(); + $iId = $oMyChangeOp->DBInsertNoReload(); } - private function RecordAttChanges(CMDBChange $oChange, array $aValues = array()) + private function RecordAttChanges(CMDBChange $oChange, array $aValues) { // $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); @@ -177,8 +167,9 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("objclass", get_class($this)); $oMyChangeOp->Set("objkey", $this->GetKey()); $oMyChangeOp->Set("attcode", $sAttCode); + $oMyChangeOp->Set("oldvalue", $this->GetOriginal($sAttCode)); $oMyChangeOp->Set("newvalue", $value); - $iId = $oMyChangeOp->DBInsert(); + $iId = $oMyChangeOp->DBInsertNoReload(); } } @@ -186,7 +177,7 @@ abstract class CMDBObject extends DBObject { if(!is_object(self::$m_oCurrChange)) { - trigger_error("DBInsert() could not be used here, please use DBInsertTracked() instead", E_USER_ERROR); + throw new CoreException("DBInsert() could not be used here, please use DBInsertTracked() instead"); } return $this->DBInsertTracked_Internal(); } @@ -199,11 +190,25 @@ abstract class CMDBObject extends DBObject return $ret; } - protected function DBInsertTracked_Internal() + public function DBInsertTrackedNoReload(CMDBChange $oChange) { - $ret = parent::DBInsert(); + self::$m_oCurrChange = $oChange; + $ret = $this->DBInsertTracked_Internal(true); + self::$m_oCurrChange = null; + return $ret; + } + + protected function DBInsertTracked_Internal($bDoNotReload = false) + { + if ($bDoNotReload) + { + $ret = parent::DBInsertNoReload(); + } + else + { + $ret = parent::DBInsert(); + } $this->RecordObjCreation(self::$m_oCurrChange); - $this->RecordAttChanges(self::$m_oCurrChange); return $ret; } @@ -211,7 +216,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("DBClone() could not be used here, please use DBCloneTracked() instead", E_USER_ERROR); + throw new CoreException("DBClone() could not be used here, please use DBCloneTracked() instead"); } return $this->DBCloneTracked_Internal(); } @@ -229,7 +234,6 @@ abstract class CMDBObject extends DBObject $oClone = MetaModel::GetObject(get_class($this), $newKey); $oClone->RecordObjCreation(self::$m_oCurrChange); - $oClone->RecordAttChanges(self::$m_oCurrChange); return $newKey; } @@ -237,7 +241,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("DBUpdate() could not be used here, please use DBUpdateTracked() instead", E_USER_ERROR); + throw new CoreException("DBUpdate() could not be used here, please use DBUpdateTracked() instead"); } return $this->DBUpdateTracked_internal(); } @@ -255,7 +259,7 @@ abstract class CMDBObject extends DBObject $aChanges = $this->ListChanges(); if (count($aChanges) == 0) { - trigger_error("Attempting to update an unchanged object", E_USER_NOTICE); + throw new CoreWarning("Attempting to update an unchanged object"); return; } @@ -268,7 +272,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("DBDelete() could not be used here, please use DBDeleteTracked() instead", E_USER_ERROR); + throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead"); } return $this->DBDeleteTracked_Internal(); } @@ -292,7 +296,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("BulkDelete() could not be used here, please use BulkDeleteTracked() instead", E_USER_ERROR); + throw new CoreException("BulkDelete() could not be used here, please use BulkDeleteTracked() instead"); } return $this->BulkDeleteTracked_Internal($oFilter); } @@ -306,7 +310,7 @@ abstract class CMDBObject extends DBObject protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter) { - trigger_error("Change tracking not tested for bulk operations", E_USER_WARNING); + throw new CoreWarning("Change tracking not tested for bulk operations"); // Get the list of objects to delete (and record data before deleting the DB records) $oObjSet = new CMDBObjectSet($oFilter); @@ -331,7 +335,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead", E_USER_ERROR); + throw new CoreException("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead"); } return $this->BulkUpdateTracked_Internal($oFilter, $aValues); } diff --git a/core/coreexception.class.inc.php b/core/coreexception.class.inc.php index d720fc419..eb5ae8b9d 100644 --- a/core/coreexception.class.inc.php +++ b/core/coreexception.class.inc.php @@ -47,6 +47,17 @@ class CoreException extends Exception { return $this->getMessage(); } + + public function getTraceAsHtml() + { + $aBackTrace = $this->getTrace(); + return MyHelpers::get_callstack_html(0, $this->getTrace()); + // return "
\n".$this->getTraceAsString()."
\n"; + } +} + +class CoreWarning extends CoreException +{ } ?> diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 732f0d901..c9d69a0b9 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -57,10 +57,25 @@ abstract class DBObject { $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); $this->m_aOrigValues[$sAttCode] = null; - // ??? $this->m_aLoadedAtt[$sAttCode] = true; + if ($oAttDef->IsExternalField()) + { + // This field has to be read from the DB + $this->m_aLoadedAtt[$sAttCode] = false; + } + else + { + // No need to trigger a reload for that attribute + // Let's consider it as being already fully loaded + $this->m_aLoadedAtt[$sAttCode] = true; + } } } + public function IsNew() + { + return (!$this->m_bIsInDB); + } + // Returns an Id for memory objects static protected function GetNextTempId($sClass) { @@ -113,7 +128,7 @@ abstract class DBObject $aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey); if (empty($aRow)) { - trigger_error("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey, E_USER_ERROR); + throw new CoreException("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey); } $this->FromRow($aRow); @@ -161,14 +176,14 @@ abstract class DBObject if (!array_key_exists($sKeyField, $aRow)) { // #@# Bug ? - trigger_error("Missing key for class '".get_class($this)."'", E_USER_ERROR); + throw new CoreException("Missing key for class '".get_class($this)."'"); } else { $iPKey = $aRow[$sKeyField]; if (!self::IsValidPKey($iPKey)) { - trigger_error("An object id must be an integer value ($iPKey)", E_USER_NOTICE); + throw new CoreWarning("An object id must be an integer value ($iPKey)"); } $this->m_iKey = $iPKey; } @@ -204,7 +219,7 @@ abstract class DBObject { 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); + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); } $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); if ($this->m_bIsInDB && !$this->m_bFullyLoaded) @@ -216,7 +231,7 @@ abstract class DBObject } if($oAttDef->IsScalar() && !$oAttDef->IsNullAllowed() && is_null($value)) { - trigger_error("null not allowed for attribute '$sAttCode', setting default value", E_USER_NOTICE); + throw new CoreWarning("null not allowed for attribute '$sAttCode', setting default value"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); return; } @@ -227,7 +242,7 @@ abstract class DBObject // (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); + throw new CoreWarning("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); } else @@ -245,7 +260,7 @@ abstract class DBObject } if(!$oAttDef->IsScalar() && !is_object($value)) { - trigger_error("scalar not allowed for attribute '$sAttCode', setting default value (empty list)", E_USER_NOTICE); + throw new CoreWarning("scalar not allowed for attribute '$sAttCode', setting default value (empty list)"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); return; } @@ -253,7 +268,7 @@ abstract class DBObject { 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); + throw new CoreWarning("expecting a set of persistent objects (found a '".get_class($value)."'), setting default value (empty list)"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); return; } @@ -264,7 +279,7 @@ abstract class DBObject // 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); + throw new CoreWarning("expecting a set of '$sLinkClass' objects (found a set of '$sSetClass'), setting default value (empty list)"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); return; } @@ -276,7 +291,7 @@ abstract class DBObject { 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); + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); } if ($this->m_bIsInDB && !$this->m_aLoadedAtt[$sAttCode]) { @@ -291,7 +306,7 @@ abstract class DBObject { 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); + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); } return $this->m_aOrigValues[$sAttCode]; } @@ -374,12 +389,12 @@ abstract class DBObject { if (!self::IsValidPKey($iNewKey)) { - trigger_error("An object id must be an integer value ($iNewKey)", E_USER_ERROR); + throw new CoreException("An object id must be an integer value ($iNewKey)"); } 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); + throw new CoreException("Changing the key ({$this->m_iKey} to $iNewKey) on an object (class {".get_class($this).") wich already exists in the Database"); } $this->m_iKey = $iNewKey; } @@ -431,15 +446,27 @@ abstract class DBObject // check if the given (or current) value is suitable for the attribute public function CheckValue($sAttCode, $value = null) { + if (!is_null($value)) + { + $toCheck = $value; + } + else + { + $toCheck = $this->Get($sAttCode); + } + $oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode); if ($oAtt->IsExternalKey()) { - if (!$oAtt->IsNullAllowed() || ($this->Get($sAttCode) != 0) ) + if (!$oAtt->IsNullAllowed() || ($toCheck != 0) ) { - $oTargetObj = MetaModel::GetObject($oAtt->GetTargetClass(), $this->Get($sAttCode)); - if (!$oTargetObj) + try + { + $oTargetObj = MetaModel::GetObject($oAtt->GetTargetClass(), $toCheck); + return true; + } + catch (CoreException $e) { - echo "Invalid value (".$this->Get($sAttCode).") for ExtKey $sAttCode."; return false; } } @@ -580,11 +607,11 @@ abstract class DBObject // Insert of record for the new object into the database // Returns the key of the newly created object - public function DBInsert() + public function DBInsertNoReload() { if ($this->m_bIsInDB) { - trigger_error("The object already exists into the Database, you may want to use the clone function", E_USER_ERROR); + throw new CoreException("The object already exists into the Database, you may want to use the clone function"); } $sClass = get_class($this); @@ -604,7 +631,7 @@ abstract class DBObject { 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); + throw new CoreWarning("Missing key for the object to write - This class is supposed to have a user defined key, not an autonumber"); } } @@ -629,6 +656,12 @@ abstract class DBObject // Reload to update the external attributes $this->m_bIsInDB = true; + return $this->m_iKey; + } + + public function DBInsert() + { + $this->DBInsertNoReload(); $this->Reload(); return $this->m_iKey; } @@ -647,12 +680,12 @@ abstract class DBObject { if (!$this->m_bIsInDB) { - trigger_error("DBUpdate: could not update a newly created object, please call DBInsert instead", E_USER_ERROR); + throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead"); } $aChanges = $this->ListChanges(); if (count($aChanges) == 0) { - trigger_error("Attempting to update an unchanged object", E_USER_NOTICE); + throw new CoreWarning("Attempting to update an unchanged object"); return; } $bHasANewExternalKeyValue = false; @@ -741,7 +774,7 @@ abstract class DBObject if (!is_callable($aActionCallSpec)) { - trigger_error("Unable to call action: ".get_class($this)."::$sActionHandler", E_USER_ERROR); + throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler"); return; } $bRet = call_user_func($aActionCallSpec, $sStimulusCode); diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index e7fa9dd95..86d6e057a 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -309,12 +309,12 @@ class DBObjectSearch { 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); + throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored"); } $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); + throw new CoreException("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}"); } if (array_key_exists($sExtKeyAttCode, $this->m_aPointingTo)) @@ -347,12 +347,12 @@ class DBObjectSearch $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); + throw new CoreException("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}' - the condition will be ignored"); } $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); + throw new CoreException("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()}"); } if (array_key_exists($sForeignClass, $this->m_aReferencedBy) && array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sForeignClass])) { @@ -396,7 +396,7 @@ class DBObjectSearch { 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); + throw new CoreException("Attempting to merge a filter of class '{$this->GetClass()}' with a filter of class '{$oFilter->GetClass()}'"); } // Translate search condition into our aliasing scheme @@ -546,7 +546,7 @@ class DBObjectSearch $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); + throw new CoreException("invalid filter definition (cannot unserialize the data, clear text = '$sClearText')"); } } return $oFilter; @@ -662,7 +662,7 @@ class DBObjectSearch } if (count($aParams) > 0) { - trigger_error("Unused parameter(s) for this SibusQL expression: (".implode(', ', array_keys($aParams)).")"); + throw new CoreException("Unused parameter(s) for this SibusQL expression: (".implode(', ', array_keys($aParams)).")"); } return $sQuery; } @@ -931,7 +931,7 @@ class DBObjectSearch } else { - trigger_error("Wrong format for filter definition: '$sQuery'"); + throw new CoreException("Wrong format for filter definition: '$sQuery'"); } } } diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php index 4ab3761a9..d2719c5cd 100644 --- a/core/dbobjectset.class.php +++ b/core/dbobjectset.class.php @@ -170,11 +170,6 @@ class DBObjectSet $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) @@ -187,7 +182,7 @@ class DBObjectSet { if ($this->GetRootClass() != $oObjectSet->GetRootClass()) { - trigger_error("Could not merge two objects sets if they don't have the same root class"); + throw new CoreException("Could not merge two objects sets if they don't have the same root class"); } if (!$this->m_bLoaded) $this->Load(); @@ -202,7 +197,7 @@ class DBObjectSet { if ($this->GetRootClass() != $oObjectSet->GetRootClass()) { - trigger_error("Could not 'intersect' two objects sets if they don't have the same root class"); + throw new CoreException("Could not 'intersect' two objects sets if they don't have the same root class"); } if (!$this->m_bLoaded) $this->Load(); @@ -223,7 +218,7 @@ class DBObjectSet { if ($this->GetRootClass() != $oObjectSet->GetRootClass()) { - trigger_error("Could not 'delta' two objects sets if they don't have the same root class"); + throw new CoreException("Could not 'delta' two objects sets if they don't have the same root class"); } if (!$this->m_bLoaded) $this->Load(); diff --git a/core/filterdef.class.inc.php b/core/filterdef.class.inc.php index ec09f50a0..371eadbd5 100644 --- a/core/filterdef.class.inc.php +++ b/core/filterdef.class.inc.php @@ -37,7 +37,7 @@ abstract class FilterDefinition { if (!array_key_exists($sParam, $this->m_aParams)) { - trigger_error("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); + throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); } else { @@ -64,7 +64,7 @@ abstract class FilterDefinition $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)
\n", E_USER_ERROR); + throw new CoreException("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); } } } @@ -86,7 +86,7 @@ abstract class FilterDefinition $aOperators = $this->GetOperators(); if (!array_key_exists($sOpCode, $aOperators)) { - trigger_error("Unknown operator '$sOpCode'", E_USER_ERROR); + throw new CoreException("Unknown operator '$sOpCode'"); } return $aOperators[$sOpCode]; @@ -145,11 +145,11 @@ class FilterPrivateKey extends FilterDefinition switch($sOpCode) { case "IN": - if (!is_array($sQValue)) trigger_error("Expected an array for argument value (sOpCode='$sOpCode')"); + if (!is_array($sQValue)) throw new CoreException("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')"); + if (!is_array($sQValue)) throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')"); return "$sFieldName NOT IN (".implode(", ", $sQValue).")"; case "!=": diff --git a/core/metamodel.class.php b/core/metamodel.class.php index e1927f8f5..275ad454a 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -108,7 +108,7 @@ abstract class MetaModel // (it is not possible to guess it when called as myderived::...) if (!array_key_exists($sClass, self::$m_aClassParams)) { - trigger_error("Unknown class '$sClass', expected a value in {".implode(', ', array_keys(self::$m_aClassParams))."}", E_USER_ERROR); + throw new CoreException("Unknown class '$sClass', expected a value in {".implode(', ', array_keys(self::$m_aClassParams))."}"); } } @@ -685,7 +685,7 @@ abstract class MetaModel { if (!array_key_exists($sParamName, $aListInfo)) { - trigger_error("Declaration of list $sListCode - missing parameter $sParamName", E_USER_ERROR); + throw new CoreException("Declaration of list $sListCode - missing parameter $sParamName"); } } @@ -704,7 +704,7 @@ abstract class MetaModel { if (!array_key_exists($sParamName, $aRelationInfo)) { - trigger_error("Declaration of relation $sRelCode - missing parameter $sParamName", E_USER_ERROR); + throw new CoreException("Declaration of relation $sRelCode - missing parameter $sParamName"); } } @@ -716,7 +716,7 @@ abstract class MetaModel { if (count(self::GetClasses()) > 0) { - trigger_error("InitClasses should not be called more than once -skipped"); + throw new CoreException("InitClasses should not be called more than once -skipped"); return; } @@ -769,11 +769,11 @@ abstract class MetaModel // if (array_key_exists('id', self::$m_aAttribDefs[$sClass])) { - trigger_error("Class $sClass, 'id' is a reserved keyword, it cannot be used as an attribute code", E_USER_ERROR); + throw new CoreException("Class $sClass, 'id' is a reserved keyword, it cannot be used as an attribute code"); } if (array_key_exists('id', self::$m_aFilterDefs[$sClass])) { - trigger_error("Class $sClass, 'id' is a reserved keyword, it cannot be used as a filter code", E_USER_ERROR); + throw new CoreException("Class $sClass, 'id' is a reserved keyword, it cannot be used as a filter code"); } $oFilter = new FilterPrivateKey('id', array('id_field' => self::DBGetKey($sClass))); self::$m_aFilterDefs[$sClass]['id'] = $oFilter; @@ -785,11 +785,11 @@ abstract class MetaModel { if (array_key_exists('finalclass', self::$m_aAttribDefs[$sClass])) { - trigger_error("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as an attribute code", E_USER_ERROR); + throw new CoreException("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as an attribute code"); } if (array_key_exists('finalclass', self::$m_aFilterDefs[$sClass])) { - trigger_error("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code", E_USER_ERROR); + throw new CoreException("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code"); } $sClassAttCode = 'finalclass'; $sRootClass = self::GetRootClass($sClass); @@ -846,14 +846,14 @@ abstract class MetaModel $sClass = self::GetCallersPHPClass("Init"); if (!array_key_exists("name", $aParams)) { - trigger_error("Declaration of class $sClass: missing name ({$aMandatParams["name"]})", E_USER_ERROR); + throw new CoreException("Declaration of class $sClass: missing name ({$aMandatParams["name"]})"); } foreach($aMandatParams as $sParamName=>$sParamDesc) { if (!array_key_exists($sParamName, $aParams)) { - trigger_error("Declaration of class $sClass - missing parameter $sParamName", E_USER_ERROR); + throw new CoreException("Declaration of class $sClass - missing parameter $sParamName"); } } @@ -938,7 +938,7 @@ abstract class MetaModel if (!self::IsValidAttCode($sTargetClass, $sAttCode)) { - trigger_error("Could not overload '$sAttCode', expecting a code from {".implode(", ", self::GetAttributesList($sTargetClass))."}"); + throw new CoreException("Could not overload '$sAttCode', expecting a code from {".implode(", ", self::GetAttributesList($sTargetClass))."}"); } self::$m_aAttribDefs[$sTargetClass][$sAttCode]->OverloadParams($aParams); } @@ -1000,7 +1000,7 @@ abstract class MetaModel if (!self::IsValidFilterCode($sTargetClass, $sFltCode)) { - trigger_error("Could not overload '$sFltCode', expecting a code from {".implode(", ", self::GetFiltersList($sTargetClass))."}"); + throw new CoreException("Could not overload '$sFltCode', expecting a code from {".implode(", ", self::GetFiltersList($sTargetClass))."}"); } self::$m_aFilterDefs[$sTargetClass][$sFltCode]->OverloadParams($aParams); } @@ -1150,7 +1150,7 @@ abstract class MetaModel if (count(self::$m_Category2Class) > 0) { - trigger_error("unkown class category '$sCategory', expecting a value in {".implode(', ', array_keys(self::$m_Category2Class))."}"); + throw new CoreException("unkown class category '$sCategory', expecting a value in {".implode(', ', array_keys(self::$m_Category2Class))."}"); } return array(); } @@ -1175,7 +1175,7 @@ abstract class MetaModel MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sFieldAlias, self::GetAttributesList($oFilter->GetClass())); if (!is_bool($bAscending)) { - trigger_error("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value"); + throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value"); } } if (empty($aOrderBy)) @@ -1565,8 +1565,7 @@ abstract class MetaModel { if (count(self::GetClasses()) == 0) { - trigger_error("MetaModel::InitClasses() has not been called, or no class has been declared ?!?!"); - exit; + throw new CoreException("MetaModel::InitClasses() has not been called, or no class has been declared ?!?!"); } $aErrors = array(); @@ -2229,7 +2228,7 @@ abstract class MetaModel } if ($iPlannedDel > $iMaxDel) { - trigger_error("DB Integrity Check safety net - Exceeding the limit of $iMaxDel planned record deletion", E_USER_WARNING); + throw new CoreWarning("DB Integrity Check safety net - Exceeding the limit of $iMaxDel planned record deletion"); break; } // Safety net #2 - limit the iterations @@ -2238,7 +2237,7 @@ abstract class MetaModel $iMaxLoops = 10; if ($iLoopCount > $iMaxLoops) { - trigger_error("DB Integrity Check safety net - Reached the limit of $iMaxLoops loops", E_USER_WARNING); + throw new CoreWarning("DB Integrity Check safety net - Reached the limit of $iMaxLoops loops"); break; } } @@ -2410,21 +2409,50 @@ abstract class MetaModel // Building an object // // + private static $aQueryCacheGetObject = array(); + private static $aQueryCacheGetObjectHits = array(); + public static function GetQueryCacheStatus() + { + $aRes = array(); + $iTotalHits = 0; + foreach(self::$aQueryCacheGetObjectHits as $sClass => $iHits) + { + $aRes[] = "$sClass: $iHits"; + $iTotalHits += $iHits; + } + return $iTotalHits.' ('.implode(', ', $aRes).')'; + } public static function MakeSingleRow($sClass, $iKey) { - $oFilter = new DBObjectSearch($sClass); - $oFilter->AddCondition('id', $iKey, '='); - - $sSQL = self::MakeSelectQuery($oFilter); - //echo "$sSQL
\n"; + if (!array_key_exists($sClass, self::$aQueryCacheGetObject)) + { + // NOTE: Quick and VERY dirty caching mechanism which relies on + // the fact that the string '987654321' will never appear in the + // standard query + // This will be replaced for sure with a prepared statement + // or a view... next optimization to come! + $oFilter = new DBObjectSearch($sClass); + $oFilter->AddCondition('id', 987654321, '='); + + $sSQL = self::MakeSelectQuery($oFilter); + self::$aQueryCacheGetObject[$sClass] = $sSQL; + self::$aQueryCacheGetObjectHits[$sClass] = 0; + } + else + { + $sSQL = self::$aQueryCacheGetObject[$sClass]; + self::$aQueryCacheGetObjectHits[$sClass] += 1; +// echo " -load $sClass/$iKey- ".self::$aQueryCacheGetObjectHits[$sClass]."
\n"; + } + $sSQL = str_replace('987654321', CMDBSource::Quote($iKey), $sSQL); $res = CMDBSource::Query($sSQL); $aRow = CMDBSource::FetchArray($res); CMDBSource::FreeResult($res); if (empty($aRow)) { - trigger_error("No result for the single row query: '$sSQL'"); + throw new CoreException("No result for the single row query: '$sSQL'"); } return $aRow; } @@ -2446,7 +2474,7 @@ abstract class MetaModel // @#@ possible improvement: check that the class is valid ! $sRootClass = self::GetRootClass($sClass); $sFinalClassField = self::DBGetClassField($sRootClass); - trigger_error("Empty class name for object $sClass::{$aRow["id"]} (root class '$sRootClass', field '{$sFinalClassField}' is empty)", E_USER_ERROR); + throw new CoreException("Empty class name for object $sClass::{$aRow["id"]} (root class '$sRootClass', field '{$sFinalClassField}' is empty)"); } else { @@ -2462,7 +2490,6 @@ abstract class MetaModel $aRow = self::MakeSingleRow($sClass, $iKey); if (empty($aRow)) { - // #@# exception ? return null; } return self::GetObjectByRow($sClass, $aRow); diff --git a/core/sqlquery.class.inc.php b/core/sqlquery.class.inc.php index 06a8bd35f..8a55d33c8 100644 --- a/core/sqlquery.class.inc.php +++ b/core/sqlquery.class.inc.php @@ -46,7 +46,7 @@ class SQLQuery { if (!CMDBSource::IsTable($sTable)) { - trigger_error("Unknown table '$sTable'", E_USER_ERROR); + throw new CoreException("Unknown table '$sTable'"); } // $aFields must be an array of "alias"=>"expr" // $oConditionExpr must be a condition tree @@ -137,7 +137,7 @@ class SQLQuery 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); + throw new CoreException("Unknown field '$sLeftField' in table '".$this->m_sTable); } if (empty($sRightTableAlias)) { @@ -147,7 +147,7 @@ class SQLQuery // // if (!CMDBSource::IsField($sRightTable, $sRightField)) // { -// trigger_error("Unknown field '$sRightField' in table '".$sRightTable."'", E_USER_ERROR); +// throw new CoreException("Unknown field '$sRightField' in table '".$sRightTable."'"); // } $this->m_aJoinSelects[] = array( @@ -187,7 +187,7 @@ class SQLQuery 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); + throw new CoreException("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead..."); } */ $sWhere = self::ClauseWhere($oCondition); @@ -277,7 +277,7 @@ class SQLQuery $sFrom .= ") ON ".$aJoinInfo["joincondition"]; break; default: - trigger_error("Unknown jointype: '".$aJoinInfo["jointype"]."'"); + throw new CoreException("Unknown jointype: '".$aJoinInfo["jointype"]."'"); } } return $sFrom; diff --git a/core/stimulus.class.inc.php b/core/stimulus.class.inc.php index ee22fdd81..d2326f6fc 100644 --- a/core/stimulus.class.inc.php +++ b/core/stimulus.class.inc.php @@ -44,7 +44,7 @@ class ObjectStimulus $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)
\n", E_USER_ERROR); + throw new CoreException("missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); } } } diff --git a/core/test.class.inc.php b/core/test.class.inc.php index 286228c82..f20ebec07 100644 --- a/core/test.class.inc.php +++ b/core/test.class.inc.php @@ -150,11 +150,17 @@ abstract class TestHandler { $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml()); } + catch (CoreException $e) + { + //$this->ReportError($e->getMessage()); + //$this->ReportError($e->__tostring()); + $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml()); + } catch (Exception $e) { //$this->ReportError($e->getMessage()); //$this->ReportError($e->__tostring()); - $this->ReportError($e->getMessage().' - '.$e->getTraceAsString()); + $this->ReportError('class '.get_class($e).' --- '.$e->getMessage().' - '.$e->getTraceAsString()); } restore_error_handler(); $this->m_sOutput = ob_get_clean(); diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index fd1a0bee3..b62c8a042 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -77,12 +77,12 @@ class UserRights { if (!class_exists($sModuleName)) { - trigger_error("Could not select this module, '$sModuleName' in not a valid class name", E_USER_ERROR); + throw new CoreException("Could not select this module, '$sModuleName' in not a valid class name"); 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); + throw new CoreException("Could not select this module, the class '$sModuleName' is not derived from UserRightsAddOnAPI"); return; } self::$m_oAddOn = new $sModuleName; diff --git a/pages/csvimport.php b/pages/csvimport.php index 64f720726..0110e1f53 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -17,165 +17,6 @@ $oPage = new iTopWebPage("iTop - Bulk import", $currentOrganization); define ('EXTKEY_SEP', '::::'); define ('EXTKEY_LABELSEP', ' -> '); -class CSVParser -{ - private $m_sCSVData; - private $m_sSep; - private $m_iSkip; - - public function __construct($sTxt) - { - $this->m_sCSVData = $sTxt; - } - - public function SetSeparator($sSep) - { - $this->m_sSep = $sSep; - } - public function GetSeparator() - { - return $this->m_sSep; - } - - public function SetSkipLines($iSkip) - { - $this->m_iSkip = $iSkip; - } - public function GetSkipLines() - { - return $this->m_iSkip; - } - - public function GuessSeparator() - { - // Note: skip the first line anyway - - $aKnownSeps = array(';', ',', "\t"); // Use double quote for special chars!!! - $aStatsBySeparator = array(); - foreach ($aKnownSeps as $sSep) - { - $aStatsBySeparator[$sSep] = array(); - } - - foreach(split("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - $aLineCharsCount = count_chars($sLine, 0); - foreach ($aKnownSeps as $sSep) - { - $aStatsBySeparator[$sSep][] = $aLineCharsCount[ord($sSep)]; - } - } - - // Default to ',' - $this->SetSeparator(","); - - foreach ($aKnownSeps as $sSep) - { - // Note: this function is NOT available :-( - // stats_variance($aStatsBySeparator[$sSep]); - $iMin = min($aStatsBySeparator[$sSep]); - $iMax = max($aStatsBySeparator[$sSep]); - if (($iMin == $iMax) && ($iMax > 0)) - { - $this->SetSeparator($sSep); - break; - } - } - return $this->GetSeparator(); - } - - public function GuessSkipLines() - { - // Take the FIRST -valuable- LINE ONLY - // If there is a number, then for sure this is not a header line - // Otherwise, we may consider that there is one line to skip - foreach(split("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - foreach (split($this->m_sSep, $sLine) as $value) - { - if (is_numeric($value)) - { - $this->SetSkipLines(0); - return 0; - } - } - $this->SetSkipLines(1); - return 1; - } - } - - function ToArray($aFieldMap, $iMax = 0) - { - // $aFieldMap is an array of col_index=>col_name - // $iMax is a limit - $aRes = array(); - - $iCount = 0; - $iSkipped = 0; - foreach(split("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - if ($iSkipped < $this->m_iSkip) - { - $iSkipped++; - continue; - } - - foreach (split($this->m_sSep, $sLine) as $iCol=>$sValue) - { - if (is_array($aFieldMap)) $sColRef = $aFieldMap[$iCol]; - else $sColRef = "field$iCol"; - $aRes[$iCount][$sColRef] = $sValue; - } - - $iCount++; - if (($iMax > 0) && ($iCount >= $iMax)) break; - } - return $aRes; - } - - public function ListFields() - { - // Take the first valuable line - foreach(split("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - // We've got the first valuable line, that's it! - break; - } - - $aRet = array(); - foreach (split($this->m_sSep, $sLine) as $iCol=>$value) - { - if ($this->m_iSkip == 0) - { - // No header to help us - $sLabel = "field $iCol"; - } - else - { - $sLabel = "$value"; - } - $aRet[] = $sLabel; - } - return $aRet; - } -} - - /////////////////////////////////////////////////////////////////////////////// // External key/field naming conventions (sharing the naming space with std attributes /////////////////////////////////////////////////////////////////////////////// @@ -291,176 +132,11 @@ function ShowTableForm($oPage, $oCSVParser, $sClass) $sSelField .= " "; $aFields["field$iFieldIndex"]["label"] = $sSelField; - $aFields["field$iFieldIndex"]["value"] = $aColToRow["field$iFieldIndex"]; + $aFields["field$iFieldIndex"]["value"] = $aColToRow[$iFieldIndex]; } $oPage->details($aFields); } -function PrepareObject(&$oTargetObj, $aRowData, $aAttList, $aExtKeys, &$aWarnings, &$aErrors) -{ - $aResults = array(); - $aWarnings = array(); - $aErrors = array(); - - // External keys reconciliation - // - foreach($aExtKeys as $sAttCode=>$aKeyConfig) - { - $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode); - $oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass()); - foreach ($aKeyConfig as $iCol => $sForeignAttCode) - { - // The foreign attribute is one of our reconciliation key - $sFieldId = MakeExtFieldSelectValue($sAttCode, $sForeignAttCode); - $oReconFilter->AddCondition($sForeignAttCode, $aRowData[$sFieldId], '='); - $aResults["col$iCol"]= "
".$aRowData[$sFieldId]."
"; - } - $oExtObjects = new CMDBObjectSet($oReconFilter); - switch($oExtObjects->Count()) - { - case 0: - $aErrors[$sAttCode] = "Object not found"; - $aResults[$sAttCode]= "
".$aErrors[$sAttCode]."
"; - break; - case 1: - // Do change the external key attribute - $oForeignObj = $oExtObjects->Fetch(); - $oTargetObj->Set($sAttCode, $oForeignObj->GetKey()); - - // Report it - if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) - { - $aResults[$sAttCode]= "
".$oForeignObj->GetHyperLink()."
"; - } - else - { - $aResults[$sAttCode]= "
".$oForeignObj->GetHyperLink()."
"; - } - break; - default: - $aErrors[$sAttCode] = "Found ".$oExtObjects->Count()." matches"; - $aResults[$sAttCode]= "
".$aErrors[$sAttCode]."
"; - } - } - - // Set the object attributes - // - foreach ($aAttList as $iCol => $sAttCode) - { - $oTargetObj->Set($sAttCode, $aRowData[$sAttCode]); - } - - // Reporting on fields - // - $aChangedFields = $oTargetObj->ListChanges(); - foreach ($aAttList as $iCol => $sAttCode) - { - // By default... nothing happens - $sClass = ""; - $sMoreInfo = ""; - - // Override if the attribute has changed - if (array_key_exists($sAttCode, $aChangedFields)) - { - $sClass = "csvimport_ok"; - } - - // Override if a warning is found - if (isset($aWarnings[$sAttCode])) - { - $sClass = "csvimport_warning"; - $sMoreInfo .= ", ".$aWarnings[$sAttCode]; - } - - // Override if an error is found - if (isset($aErrors[$sAttCode])) - { - $sClass = "csvimport_error"; - $sMoreInfo = ", ".$aErrors[$sAttCode]; - } - - $aResults["col$iCol"]= "
".$aRowData[$sAttCode].$sMoreInfo."
"; - } - - // Checks - // - if (!$oTargetObj->CheckConsistency()) - { - $aErrors["GLOBAL"] = "Attributes not consistent with each others"; - } - return $aResults; -} - - -function CreateObject(&$aResult, $iRow, $sClass, $aRowData, $aAttList, $aExtKeys, CMDBChange $oChange = null) -{ - $oTargetObj = MetaModel::NewObject($sClass); - $aResult[$iRow] = PrepareObject($oTargetObj, $aRowData, $aAttList, $aExtKeys, $aWarnings, $aErrors); - - if (count($aErrors) > 0) - { - $sErrors = implode(', ', $aErrors); - $aResult[$iRow]["__STATUS__"] = "Unexpected attribute value(s)"; - return; - } - - // Check that any external key will have a value proposed - // Could be said once for all rows !!! - foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAtt) - { - if (!$oAtt->IsExternalKey()) continue; - //if (!in_array($sAttCode, $aAttList)) - //{ - // $aResult[$iRow]["__STATUS__"] = "Could not be created - Missing external key (".$oAtt->GetLabel().")"; - // return; - //} - } - - // Optionaly record the results - // - if ($oChange) - { - $newID = $oTargetObj->DBInsertTracked($oChange); - $aResult[$iRow]["__STATUS__"] = "Created: ".$oTargetObj->GetHyperLink($newID); - } - else - { - $aResult[$iRow]["__STATUS__"] = "Create"; - } - -} - -function UpdateObject(&$aResult, $iRow, $oTargetObj, $aRowData, $aAttList, $aExtKeys, CMDBChange $oChange = null) -{ - $aResult[$iRow] = PrepareObject($oTargetObj, $aRowData, $aAttList, $aExtKeys, $aWarnings, $aErrors); - - // Reporting - // - if (count($aErrors) > 0) - { - $sErrors = implode(', ', $aErrors); - $aResult[$iRow]["__STATUS__"] = "Unexpected attribute value(s)"; - return; - } - - $aChangedFields = $oTargetObj->ListChanges(); - if (count($aChangedFields) > 0) - { - $sVerb = $oChange ? "Updated" : "Update"; - $aResult[$iRow]["__STATUS__"] = "$sVerb ".count($aChangedFields)." cols"; - - // Optionaly record the results - // - if ($oChange) - { - $oTargetObj->DBUpdateTracked($oChange); - } - } - else - { - $aResult[$iRow]["__STATUS__"] = "No change"; - } -} function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CMDBChange $oChange = null) { @@ -487,18 +163,18 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM elseif (IsExtKeyField($sColDesc)) { list($sExtKeyAttCode, $sExtReconcKeyAttCode) = GetExtKeyFieldCodes($sColDesc); - $aExtKeys[$sExtKeyAttCode][$iFieldId] = $sExtReconcKeyAttCode; + $aExtKeys[$sExtKeyAttCode][$sExtReconcKeyAttCode] = $iFieldId; } elseif (array_key_exists($sFieldId, $aIsReconcKey)) { - $aReconcilKeys[$iFieldId] = $sColDesc; - $aAttList[$iFieldId] = $sColDesc; // A reconciliation key is also a field + $aReconcilKeys[$sColDesc] = $iFieldId; + $aAttList[$sColDesc] = $iFieldId; // A reconciliation key is also a field } else { // $sColDesc is an attribute code // - $aAttList[$iFieldId] = $sColDesc; + $aAttList[$sColDesc] = $iFieldId; } } @@ -511,7 +187,7 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM { $aDisplayConfig["col$iPKeyId"] = array("label"=>"pkey", "description"=>""); } - foreach($aReconcilKeys as $iCol=>$sAttCode) + foreach($aReconcilKeys as $sAttCode => $iCol) { $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); @@ -521,7 +197,7 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM $oExtKeyAtt = MetaModel::GetAttributeDef($sClass, $sAttCode); $sLabel = $oExtKeyAtt->GetLabel(); $aDisplayConfig[$sAttCode] = array("label"=>"$sLabel", "description"=>""); - foreach ($aKeyConfig as $iCol => $sForeignAttCode) + foreach ($aKeyConfig as $sForeignAttCode => $iCol) { // The foreign attribute is one of our reconciliation key @@ -529,7 +205,7 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); } } - foreach ($aAttList as $iCol => $sAttCode) + foreach ($aAttList as $sAttCode => $iCol) { $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); @@ -537,53 +213,58 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM // Compute the results // - $aResult = array(); - foreach($oCSVParser->ToArray(array_values($aFieldMap)) as $iRow => $aRowData) - { - $oReconciliationFilter = new CMDBSearchFilter($sClass); - if (isset($iPKeyId)) - { - $oReconciliationFilter->AddCondition("pkey", $aRowData["pkey"], '='); - } - foreach($aReconcilKeys as $iCol=>$sAttCode) - { - $sSearchAttCode = $aFieldMap['field'.$iCol]; - $oReconciliationFilter->AddCondition($sSearchAttCode, $aRowData[$sSearchAttCode], '='); - } - $oReconciliationSet = new CMDBObjectSet($oReconciliationFilter); - switch($oReconciliationSet->Count()) - { - case 0: - CreateObject($aResult, $iRow, $sClass, $aRowData, $aAttList, $aExtKeys, $oChange); - // $aResult[$iRow]["__STATUS__"]=> set in CreateObject - $aResult[$iRow]["__RECONCILIATION__"] = "Object not found"; - break; - case 1: - $oTargetObj = $oReconciliationSet->Fetch(); - UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $aAttList, $aExtKeys, $oChange); - $aResult[$iRow]["__RECONCILIATION__"] = "Found a ".$oTargetObj->GetHyperLink("match"); - // $aResult[$iRow]["__STATUS__"]=> set in UpdateObject - break; - default: - foreach ($aAttList as $iCol => $sAttCode) - { - $aResult[$iRow]["col$iCol"]= $aRowData[$sAttCode]; - } - $aResult[$iRow]["__RECONCILIATION__"] = "Found ".$oReconciliationSet->Count()." matches"; - $aResult[$iRow]["__STATUS__"]= "skipped"; - } + $aData = $oCSVParser->ToArray(); - // Whatever happened, do report the reconciliation values - if (isset($iPKeyId)) + $oBulk = new BulkChange( + $sClass, + $aData, + $aAttList, + array_keys($aReconcilKeys), + $aExtKeys + ); + $aRes = $oBulk->Process($oChange); + + $aResultDisp = array(); // to be displayed + foreach($aRes as $iRow => $aRowData) + { + $aRowDisp = array(); + $aRowDisp["__RECONCILIATION__"] = $aRowData["__RECONCILIATION__"]; + $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription(); + foreach($aRowData as $sKey => $value) { - $aResult[$iRow]["col$iPKeyId"] = "
".$aRowData["pkey"]."
"; - } - foreach($aReconcilKeys as $iCol=>$sAttCode) - { - $aResult[$iRow]["col$iCol"] = "
".$aRowData[$sAttCode]."
"; + if ($sKey == '__RECONCILIATION__') continue; + if ($sKey == '__STATUS__') continue; + + switch (get_class($value)) + { + case 'CellChangeSpec_Void': + $sClass = ''; + break; + case 'CellChangeSpec_Unchanged': + $sClass = ''; + break; + case 'CellChangeSpec_Modify': + $sClass = 'csvimport_ok'; + break; + case 'CellChangeSpec_Init': + $sClass = 'csvimport_init'; + break; + case 'CellChangeSpec_Issue': + $sClass = 'csvimport_error'; + break; + } + if (empty($sClass)) + { + $aRowDisp[$sKey] = $value->GetDescription(); + } + else + { + $aRowDisp[$sKey] = "
".$value->GetDescription()."
"; + } } + $aResultDisp[$iRow] = $aRowDisp; } - $oPage->table($aDisplayConfig, $aResult); + $oPage->table($aDisplayConfig, $aResultDisp); } /////////////////////////////////////////////////////////////////////////////// @@ -710,7 +391,7 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $oPage->p("

Goal summary

"); $oPage->p("Target: $iTarget rows"); - $aSampleData = $oCSVParser->ToArray(null, 5); + $aSampleData = $oCSVParser->ToArray(array_keys($aFieldMap), 5); $aDisplayConfig = array(); foreach ($aFieldMap as $sFieldId=>$sColDesc) { diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index 23f259dc9..947d48489 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -612,8 +612,23 @@ class TestQueriesOnFarm extends TestBizModel MetaModel::DBCheckIntegrity(); } + protected $m_oChange; + protected function ObjectToDB(CMDBObject $oNew) + { + if (!isset($this->m_oChange)) + { + new CMDBChange(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Administrator"); + $iChangeId = $oMyChange->DBInsertNoReload(); + $this->m_oChange = $oMyChange; + } + $iId = $oNew->DBInsertTrackedNoReload($this->m_oChange); + return $iId; +} - private function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) + protected function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) { $oNew = MetaModel::NewObject('Mammal'); $oNew->Set('species', $sSpecies); @@ -624,11 +639,10 @@ class TestQueriesOnFarm extends TestBizModel $oNew->Set('name', $sName); $oNew->Set('height', $iHeight); $oNew->Set('birth', $sBirth); - $iId = $oNew->DBInsert(); - return $iId; + return $this->ObjectToDB($oNew); } - private function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId) + protected function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId) { $oNew = MetaModel::NewObject('Bird'); $oNew->Set('species', $sSpecies); @@ -636,11 +650,10 @@ class TestQueriesOnFarm extends TestBizModel $oNew->Set('speed', $iSpeed); $oNew->Set('mother', $iMotherid); $oNew->Set('father', $iFatherId); - $iId = $oNew->DBInsert(); - return $iId; + return $this->ObjectToDB($oNew); } - private function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed) + protected function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed) { $oNew = MetaModel::NewObject('FlyingBird'); $oNew->Set('species', $sSpecies); @@ -649,8 +662,7 @@ class TestQueriesOnFarm extends TestBizModel $oNew->Set('mother', $iMotherid); $oNew->Set('father', $iFatherId); $oNew->Set('flyingspeed', $iFlyingSpeed); - $iId = $oNew->DBInsert(); - return $iId; + return $this->ObjectToDB($oNew); } private function InsertGroup($sName, $iLeaderId) @@ -658,7 +670,7 @@ class TestQueriesOnFarm extends TestBizModel $oNew = MetaModel::NewObject('Group'); $oNew->Set('name', $sName); $oNew->Set('leader', $iLeaderId); - $iId = $oNew->DBInsert(); + $iId = $oNew->DBInsertNoReload(); return $iId; } @@ -926,6 +938,131 @@ class TestBulkChangeOnFarm extends TestBizModel } } + +/////////////////////////////////////////////////////////////////////////// +// Benchmark queries +/////////////////////////////////////////////////////////////////////////// + +class TestItopEfficiency extends TestBizModel +{ + static public function GetName() + { + return 'Itop - benchmark'; + } + + static public function GetDescription() + { + return 'Measure time to perform the queries'; + } + + static public function GetConfigFile() {return '../config-itop.php';} + + protected function DoBenchmark($sOqlQuery) + { + echo "

Testing query: $sOqlQuery

"; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $oFilter = DBObjectSearch::FromOQL($sOqlQuery); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fParsingDuration = $fDuration / COUNT_BENCHMARK; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $sSQL = MetaModel::MakeSelectQuery($oFilter); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fBuildDuration = $fDuration / COUNT_BENCHMARK; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $res = CMDBSource::Query($sSQL); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fQueryDuration = $fDuration / COUNT_BENCHMARK; + + // The fetch could not be repeated with the same results + // But we've seen so far that is was very very quick to exec + // So it makes sense to benchmark it a single time + $fStart = MyHelpers::getmicrotime(); + $aRow = CMDBSource::FetchArray($res); + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fFetchDuration = $fDuration; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $sOql = $oFilter->ToOQL(); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fToOqlDuration = $fDuration / COUNT_BENCHMARK; + + echo "\n"; + + // Everything but the ToOQL (wich is interesting, anyhow) + $fTotal = $fParsingDuration + $fBuildDuration + $fQueryDuration + $fFetchDuration; + + return array( + 'rows' => CMDBSource::NbRows($res), + 'duration (s)' => round($fTotal, 4), + 'parsing (%)' => round(100 * $fParsingDuration / $fTotal, 1), + 'build SQL (%)' => round(100 * $fBuildDuration / $fTotal, 1), + 'query exec (%)' => round(100 * $fQueryDuration / $fTotal, 1), + 'fetch (%)' => round(100 * $fFetchDuration / $fTotal, 1), + 'to OQL (%)' => round(100 * $fToOqlDuration / $fTotal, 1), + 'parsing+build (%)' => round(100 * ($fParsingDuration + $fBuildDuration) / $fTotal, 1), + ); + } + + protected function DoExecute() + { + define ('COUNT_BENCHMARK', 3); + echo "

The test will be repeated ".COUNT_BENCHMARK." times

"; + + $aQueries = array( + 'SELECT CMDBChangeOpSetAttribute', + 'SELECT CMDBChangeOpSetAttribute WHERE id=10', + 'SELECT CMDBChangeOpSetAttribute WHERE id=123456789', + 'SELECT CMDBChangeOpSetAttribute WHERE CMDBChangeOpSetAttribute.id=10', + 'SELECT bizIncidentTicket', + 'SELECT bizIncidentTicket WHERE id=1', + 'SELECT bizPerson', + 'SELECT bizPerson WHERE id=1', + 'SELECT bizIncidentTicket JOIN bizPerson ON bizIncidentTicket.agent_id = bizPerson.id WHERE bizPerson.id = 5', + ); + $aStats = array(); + foreach ($aQueries as $sOQL) + { + $aStats[$sOQL] = $this->DoBenchmark($sOQL); + } + + $aData = array(); + foreach ($aStats as $sOQL => $aResults) + { + $aValues = array(); + $aValues['OQL'] = htmlentities($sOQL); + + foreach($aResults as $sDesc => $sInfo) + { + $aValues[$sDesc] = htmlentities($sInfo); + } + $aData[] = $aValues; + } + echo MyHelpers::make_table_from_assoc_array($aData); + return true; + } +} + /////////////////////////////////////////////////////////////////////////// // Test data load /////////////////////////////////////////////////////////////////////////// diff --git a/setup/xmldataloader.class.inc.php b/setup/xmldataloader.class.inc.php index 5af0bebd9..f4149aa41 100644 --- a/setup/xmldataloader.class.inc.php +++ b/setup/xmldataloader.class.inc.php @@ -163,10 +163,14 @@ class XMLDataLoader { $iExtKey = -$iDstObj; // Convention: Unresolved keys are stored as negative ! } + // tested by Romain, little impact on perf (not significant on the intial setup) + //$oTargetObj->CheckValue($sAttCode, $iExtKey); $oTargetObj->Set($sAttCode, $iExtKey); } else { + // tested by Romain, little impact on perf (not significant on the intial setup) + //$oTargetObj->CheckValue($sAttCode, (string)$oXmlObj->$sAttCode); $oTargetObj->Set($sAttCode, (string)$oXmlObj->$sAttCode); } } @@ -200,11 +204,11 @@ class XMLDataLoader { if (is_subclass_of($oTargetObj, 'CMDBObject')) { - $iObjId = $oTargetObj->DBInsertTracked($this->m_oChange); + $iObjId = $oTargetObj->DBInsertTrackedNoReload($this->m_oChange); } else { - $iObjId = $oTargetObj->DBInsert(); + $iObjId = $oTargetObj->DBInsertNoReload(); } } diff --git a/webservices/import.php b/webservices/import.php index 34a846297..59b9a2cc6 100644 --- a/webservices/import.php +++ b/webservices/import.php @@ -138,6 +138,9 @@ try case 'CellChangeSpec_Modify': $sClass = 'csvimport_ok'; break; + case 'CellChangeSpec_Init': + $sClass = 'csvimport_init'; + break; case 'CellChangeSpec_Issue': $sClass = 'csvimport_error'; break;