oxutilsobject.php

Go to the documentation of this file.
00001 <?php
00002 
00006 class oxUtilsObject
00007 {
00008 
00012     const CACHE_FILE_PREFIX = "config";
00013 
00019     protected $_aClassNameCache = array();
00020 
00026     protected static $_aLoadedArticles = array();
00027 
00033     protected static $_aInstanceCache = array();
00034 
00040     protected static $_aModuleVars = array();
00041 
00047     protected static $_aClassInstances = array();
00048 
00054     private static $_instance = null;
00055 
00061     public static function getInstance()
00062     {
00063         // disable caching for test modules
00064         if ( defined( 'OXID_PHP_UNIT' ) ) {
00065             self::$_instance = null;
00066         }
00067 
00068         if ( !self::$_instance instanceof oxUtilsObject ) {
00069 
00070             // allow modules
00071             $oUtilsObject = new oxUtilsObject();
00072             self::$_instance = $oUtilsObject->oxNew( 'oxUtilsObject' );
00073         }
00074         return self::$_instance;
00075     }
00076 
00086     public static function setClassInstance( $sClassName, $oInstance )
00087     {
00088         $sClassName = strtolower( $sClassName );
00089         self::$_aClassInstances[$sClassName] = $oInstance;
00090     }
00091 
00097     public static function resetClassInstances()
00098     {
00099         self::$_aClassInstances = array();
00100     }
00101 
00109     public static function resetModuleVars()
00110     {
00111         self::$_aModuleVars = array();
00112 
00113         $sMask = oxRegistry::get("oxConfigFile")->getVar("sCompileDir") . "/" . self::CACHE_FILE_PREFIX . ".*.txt" ;
00114         $aFiles = glob( $sMask );
00115         if ( is_array( $aFiles ) ) {
00116             foreach ( $aFiles as $sFile ) {
00117                 if (is_file($sFile)) {
00118                     @unlink($sFile);
00119                 }
00120             }
00121         }
00122     }
00123 
00134     public function oxNew( $sClassName )
00135     {
00136         $aArgs = func_get_args();
00137         array_shift( $aArgs );
00138         $iArgCnt = count( $aArgs );
00139         $blCacheObj = $iArgCnt < 2;
00140         $sClassName = strtolower( $sClassName );
00141 
00142         if ( isset( self::$_aClassInstances[$sClassName] ) ) {
00143             return self::$_aClassInstances[$sClassName];
00144         }
00145         if ( !defined( 'OXID_PHP_UNIT' ) && $blCacheObj ) {
00146             $sCacheKey  = ( $iArgCnt )?$sClassName.md5( serialize( $aArgs ) ):$sClassName;
00147             if ( isset( self::$_aInstanceCache[$sCacheKey] ) ) {
00148                 return clone self::$_aInstanceCache[$sCacheKey];
00149             }
00150         }
00151 
00152         // performance
00153         if ( !defined( 'OXID_PHP_UNIT' ) && isset( $this->_aClassNameCache[$sClassName] ) ) {
00154             $sActionClassName = $this->_aClassNameCache[$sClassName];
00155         } else {
00156             $sActionClassName = $this->getClassName( $sClassName );
00157             //expect __autoload() (oxfunctions.php) to do its job when class_exists() is called
00158             if ( !class_exists( $sActionClassName ) ) {
00162                 $oEx = oxNew( "oxSystemComponentException" );
00163                 $oEx->setMessage('EXCEPTION_SYSTEMCOMPONENT_CLASSNOTFOUND');
00164                 $oEx->setComponent($sClassName);
00165                 $oEx->debugOut();
00166                 throw $oEx;
00167             }
00168             // performance
00169             $this->_aClassNameCache[$sClassName] = $sActionClassName;
00170         }
00171 
00172         $oActionObject = $this->_getObject( $sActionClassName, $iArgCnt, $aArgs );
00173         if ( $blCacheObj && $oActionObject instanceof oxBase ) {
00174             self::$_aInstanceCache[$sCacheKey] = clone $oActionObject;
00175         }
00176 
00177         return $oActionObject;
00178     }
00179 
00192     protected function _getObject( $sClassName, $iArgCnt, $aParams )
00193     {
00194         // dynamic creation (if parameter count < 4) gives more performance for regular objects
00195         switch( $iArgCnt ) {
00196             case 0:
00197                 $oObj = new $sClassName();
00198                 break;
00199             case 1:
00200                 $oObj = new $sClassName( $aParams[0] );
00201                 break;
00202             case 2:
00203                 $oObj = new $sClassName( $aParams[0], $aParams[1] );
00204                 break;
00205             case 3:
00206                 $oObj = new $sClassName( $aParams[0], $aParams[1], $aParams[2] );
00207                 break;
00208             default:
00209                 try {
00210                     // unlimited constructor arguments support
00211                     $oRo = new ReflectionClass( $sClassName );
00212                     $oObj = $oRo->newInstanceArgs( $aParams );
00213                 } catch ( ReflectionException $oRefExcp ) {
00214                     // something went wrong?
00218                     $oEx = oxNew( "oxSystemComponentException" );
00219                     $oEx->setMessage( $oRefExcp->getMessage() );
00220                     $oEx->setComponent( $sClassName );
00221                     $oEx->debugOut();
00222                     throw $oEx;
00223                 }
00224         }
00225 
00226         return $oObj;
00227     }
00228 
00239     public function oxNewArticle( $sOxID, $aProperties = array())
00240     {
00241         if ( $sOxID && isset( self::$_aLoadedArticles[$sOxID] ) ) {
00242             return self::$_aLoadedArticles[$sOxID];
00243         }
00244 
00245         $oActionObject = $this->oxNew( 'oxarticle' );
00246 
00247         // adding object prioperties
00248         foreach ( $aProperties as $sPropertyName => $sPropertyVal ) {
00249             $oActionObject->$sPropertyName = $sPropertyVal;
00250         }
00251 
00252         $oActionObject->load( $sOxID );
00253 
00254         self::$_aLoadedArticles[$sOxID] = $oActionObject;
00255         return $oActionObject;
00256     }
00257 
00265     public function resetInstanceCache($sClassName = null)
00266     {
00267         if ($sClassName && isset(self::$_aInstanceCache[$sClassName])) {
00268             unset(self::$_aInstanceCache[$sClassName]);
00269             return;
00270         }
00271 
00272         //looping due to possible memory "leak".
00273         if (is_array(self::$_aInstanceCache)) {
00274             foreach (self::$_aInstanceCache as $sKey => $oInstance) {
00275                 unset(self::$_aInstanceCache[$sKey]);
00276             }
00277         }
00278 
00279         self::$_aInstanceCache = array();
00280     }
00281 
00287     public function generateUId()
00288     {
00289         return /*substr( $this->getSession()->getId(), 0, 3 ) . */ substr( md5( uniqid( '', true ).'|'.microtime() ), 0, 32 );
00290     }
00291 
00292 
00293 
00301     public function getClassName( $sClassName )
00302     {
00303         //$aModules = $this->getConfig()->getConfigParam( 'aModules' );
00304         $aModules = $this->getModuleVar('aModules');
00305         $aClassChain = array();
00306 
00307 
00308         if (is_array( $aModules )) {
00309 
00310             $aModules = array_change_key_case( $aModules );
00311 
00312             if ( array_key_exists( $sClassName, $aModules ) ) {
00313                 //multiple inheritance implementation
00314                 //in case we have multiple modules:
00315                 //like oxoutput => sub/suboutput1&sub/suboutput2&sub/suboutput3
00316                 $aClassChain = explode( "&", $aModules[$sClassName] );
00317                 $aClassChain = $this->_getActiveModuleChain( $aClassChain );
00318             }
00319 
00320             if (count($aClassChain)) {
00321                 $sParent = $sClassName;
00322 
00323                 //security: just preventing string termination
00324                 $sParent = str_replace(chr(0), '', $sParent);
00325 
00326                 //building middle classes if needed
00327                 $sClassName = $this->_makeSafeModuleClassParents( $aClassChain, $sParent );
00328             }
00329         }
00330 
00331         // check if there is a path, if yes, remove it
00332         $sClassName = basename( $sClassName );
00333 
00334         return $sClassName;
00335     }
00336 
00344     protected function _getActiveModuleChain( $aClassChain )
00345     {
00346         $aDisabledModules = $this->getModuleVar( 'aDisabledModules' );
00347         $aModulePaths     = $this->getModuleVar( 'aModulePaths' );
00348 
00349         if (is_array( $aDisabledModules ) && count($aDisabledModules) > 0) {
00350             foreach ($aDisabledModules as $sId) {
00351                 $sPath = $aModulePaths[$sId];
00352                 if (!isset($sPath)) {
00353                     $sPath = $sId;
00354                 }
00355                 foreach ( $aClassChain as $sKey => $sModuleClass ) {
00356                     if ( strpos($sModuleClass, $sPath."/" ) === 0 ) {
00357                         unset( $aClassChain[$sKey] );
00358                     }
00359                     // If module consists of one file without own dir (getting module.php as id, instead of module)
00360                     elseif ( strpos( $sPath, "."  ) ) {
00361                         if ( strpos( $sPath, strtolower( $sModuleClass ) ) === 0 ) {
00362                             unset( $aClassChain[$sKey] );
00363                         }
00364                     }
00365                 }
00366             }
00367         }
00368 
00369         return $aClassChain;
00370     }
00371 
00379     protected function _disableModule( $sModule )
00380     {
00384         $oModule = oxNew("oxModule");
00385         $sModuleId = $oModule->getIdByPath($sModule);
00386         $oModule->deactivate($sModuleId);
00387     }
00388 
00399     protected function _makeSafeModuleClassParents( $aClassChain, $sBaseModule )
00400     {
00401         $sParent = $sBaseModule;
00402         $sClassName = $sBaseModule;
00403 
00404         //building middle classes if needed
00405         foreach ($aClassChain as $sModule) {
00406             //creating middle classes
00407             //e.g. class suboutput1_parent extends oxoutput {}
00408             //     class suboutput2_parent extends suboutput1 {}
00409             //$sModuleClass = $this->getClassName($sModule);
00410 
00411             //security: just preventing string termination
00412             $sModule = str_replace(chr(0), '', $sModule);
00413 
00414             //get parent and module class names from sub/suboutput2
00415             $sModuleClass = basename($sModule);
00416 
00417             if ( !class_exists( $sModuleClass, false ) ) {
00418                 $sParentClass       = basename($sParent);
00419                 $sModuleParentClass = $sModuleClass."_parent";
00420 
00421                 //initializing middle class
00422                 if (!class_exists($sModuleParentClass, false)) {
00423                     // If possible using alias instead if eval (since php 5.3).
00424                     if (function_exists('class_alias')) {
00425                         class_alias($sParentClass, $sModuleParentClass);
00426                     } else {
00427                         eval("abstract class $sModuleParentClass extends $sParentClass {}");
00428                     }
00429                 }
00430                 $sParentPath = oxRegistry::get( "oxConfigFile" )->getVar( "sShopDir" ) . "/modules/".$sModule.".php";
00431 
00432                 //including original file
00433                 if ( file_exists( $sParentPath ) ) {
00434                     include_once $sParentPath;
00435                 } elseif ( !class_exists( $sModuleClass ) ) {
00436                     // special case is when oxconfig class is extended: we cant call "_disableModule" as it requires valid config object
00437                     // but we can't create it as module class extending it does not exist. So we will use orginal oxConfig object instead.
00438                     if ( $sParentClass == "oxconfig" ) {
00439                         $oConfig = $this->_getObject( "oxconfig", 0, null );
00440                         oxRegistry::set( "oxconfig", $oConfig );
00441                     }
00442 
00443                     // disable module if extended class is not found
00444                     $blDisableModuleOnError = !oxRegistry::get( "oxConfigFile" )->getVar( "blDoNotDisableModuleOnError" );
00445                     if ( $blDisableModuleOnError ) {
00446                         $this->_disableModule( $sModule );
00447                     } else {
00448                         //to avoid problems with unitest and only throw a exception if class does not exists MAFI
00449                         $oEx = oxNew( "oxSystemComponentException" );
00450                         $oEx->setMessage("EXCEPTION_SYSTEMCOMPONENT_CLASSNOTFOUND");
00451                         $oEx->setComponent( $sModuleClass );
00452                         throw $oEx;
00453                     }
00454                     continue;
00455                 }
00456             }
00457             $sParent = $sModule;
00458             $sClassName = $sModule;
00459         }
00460 
00461         //returning the last module from the chain
00462         return $sClassName;
00463     }
00464 
00470     public function getShopId()
00471     {
00472             return 'oxbaseshop';
00473     }
00474 
00484     public function getModuleVar( $sModuleVarName )
00485     {
00486         //static cache
00487         if ( isset(self::$_aModuleVars[$sModuleVarName]) ) {
00488             return self::$_aModuleVars[$sModuleVarName];
00489         }
00490 
00491         //first try to get it from cache
00492         //we do not use any of our cache APIs, as we want to prevent any class dependencies here
00493         $aValue = $this->_getFromCache( $sModuleVarName );
00494 
00495         if ( is_null( $aValue ) ) {
00496             $aValue = $this->_getModuleVarFromDB($sModuleVarName);
00497             $this->_setToCache( $sModuleVarName, $aValue);
00498         }
00499 
00500         //static cache
00501         self::$_aModuleVars[$sModuleVarName] = $aValue;
00502 
00503         return $aValue;
00504     }
00505 
00514     public function setModuleVar( $sModuleVarName, $aValues )
00515     {
00516         if ( is_null( $aValues) ) {
00517             self::$_aModuleVars = null;
00518         } else {
00519             self::$_aModuleVars[$sModuleVarName] = $aValues;
00520         }
00521 
00522         $this->_setToCache($sModuleVarName, $aValues);
00523     }
00524 
00530     protected function _getConfKey()
00531     {
00532         $sFileName = dirname( __FILE__ ) . "/oxconfk.php";
00533         $sCfgFile = new oxConfigFile( $sFileName );
00534         return $sCfgFile->getVar("sConfigKey");
00535     }
00536 
00542     protected function _getShopUrlMap( )
00543     {
00544 
00545         //get from static cache
00546         if (isset( self::$_aModuleVars["urlMap"] )) {
00547             return self::$_aModuleVars["urlMap"];
00548         }
00549 
00550         //get from file cache
00551         $aMap = $this->_getFromCache("urlMap", false);
00552         if (!is_null($aMap)) {
00553             self::$_aModuleVars["urlMap"] = $aMap;
00554             return $aMap;
00555         }
00556 
00557         $aMap = array();
00558 
00559         $oDb = oxDb::getDb();
00560         $sConfKey = $this->_getConfKey();
00561 
00562         $sSelect = "SELECT oxshopid, oxvarname, DECODE( oxvarvalue , ".$oDb->quote($sConfKey)." ) as oxvarvalue ".
00563                    "FROM oxconfig WHERE oxvarname in ('aLanguageURLs','sMallShopURL','sMallSSLShopURL')";
00564 
00565         $oRs = $oDb->select($sSelect, false, false);
00566 
00567         if ( $oRs && $oRs->recordCount() > 0) {
00568             while ( !$oRs->EOF ) {
00569                 $iShp = (int) $oRs->fields[0];
00570                 $sVar = $oRs->fields[1];
00571                 $sURL = $oRs->fields[2];
00572 
00573                 if ($sVar == 'aLanguageURLs') {
00574                     $aUrls = unserialize($sURL);
00575                     if (is_array($aUrls) && count($aUrls)) {
00576                         $aUrls = array_filter($aUrls);
00577                         $aUrls = array_fill_keys($aUrls, $iShp);
00578                         $aMap  = array_merge($aMap, $aUrls);
00579                     }
00580                 } elseif ($sURL) {
00581                     $aMap[$sURL] = $iShp;
00582                 }
00583 
00584                 $oRs->moveNext();
00585             }
00586         }
00587 
00588         //save to cache
00589         $this->_setToCache("urlMap", $aMap, false);
00590         self::$_aModuleVars["urlMap"] = $aMap;
00591 
00592         return $aMap;
00593     }
00594 
00600     protected function _getCacheDir()
00601     {
00602         $sDir = oxRegistry::get("oxConfigFile")->getVar("sCompileDir");
00603         return $sDir;
00604     }
00605 
00614     protected function _getCacheFileName($sModuleVarName, $sShopId = null)
00615     {
00616         if (is_null($sShopId)) {
00617             $sShopId = $this->getShopId();
00618         }
00619 
00620         $sDir = $this->_getCacheDir();
00621         $sVar  = strtolower( basename ($sModuleVarName) );
00622         $sShop = strtolower( basename ($sShopId) );
00623 
00624         $sFileName = $sDir . "/" . self::CACHE_FILE_PREFIX . "." . $sShop . '.' . $sVar . ".txt";
00625 
00626         return $sFileName;
00627     }
00628 
00636     protected function _getModuleVarFromDB( $sModuleVarName )
00637     {
00638         $oDb = oxDb::getDb();
00639 
00640         $sShopId  = $this->getShopId();
00641         $sConfKey = $this->_getConfKey();
00642 
00643         $sSelect = "SELECT DECODE( oxvarvalue , ".$oDb->quote($sConfKey)." ) FROM oxconfig ".
00644                    "WHERE oxvarname = ".$oDb->quote( $sModuleVarName )." AND oxshopid = ".$oDb->quote($sShopId);
00645 
00646         $sModuleVarName = $oDb->getOne($sSelect, false, false);
00647 
00648         $sModuleVarName = unserialize( $sModuleVarName );
00649 
00650         return $sModuleVarName;
00651     }
00652 
00662     protected function _getFromCache( $sModuleVarName, $blSubshopSpecific = true )
00663     {
00664         $sShopId = null;
00665         if ( !$blSubshopSpecific ) {
00666             $sShopId = "all";
00667         }
00668 
00669         $sFileName = $this->_getCacheFileName($sModuleVarName, $sShopId);
00670         $sValue = null;
00671         if ( is_readable( $sFileName ) ) {
00672             $sValue = file_get_contents( $sFileName );
00673             if ( $sValue == serialize( false ) ) {
00674                 return false;
00675             }
00676 
00677             $sValue = unserialize( $sValue );
00678             if ( $sValue === false ) {
00679                 $sValue = null;
00680             }
00681         }
00682 
00683         return $sValue ;
00684     }
00685 
00695     protected function _setToCache( $sVarName, $sValue,  $blSubshopSpecific = true )
00696     {
00697         $sShopId = null;
00698         if ( !$blSubshopSpecific ) {
00699             $sShopId = "all";
00700         }
00701 
00702         $sFileName = $this->_getCacheFileName($sVarName, $sShopId);
00703         file_put_contents( $sFileName, serialize($sValue), LOCK_EX );
00704     }
00705 
00706 }