eZ Publish  [trunk]
ezcontentcachemanager.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZContentCacheManager class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package kernel
00009  */
00010 
00011 /*!
00012   \class eZContentCacheManager ezcontentcachemanager.php
00013   \brief Figures out relations between objects, nodes and classes for cache management
00014 
00015   This class works together with eZContentCache to manage the cache files
00016   for content viewing. This class takes care of finding out the relationship
00017   and then passes a list of nodes to eZContentCache which does the actual
00018   clearing.
00019 
00020   The manager uses special rules in 'viewcache.ini' to figure relationships.
00021   \sa eZContentCache
00022 */
00023 
00024 class eZContentCacheManager
00025 {
00026     // Clear cache types
00027     const CLEAR_NO_CACHE       = 0;
00028     const CLEAR_NODE_CACHE     = 1;
00029     const CLEAR_PARENT_CACHE   = 2;
00030     const CLEAR_RELATING_CACHE = 4;
00031     const CLEAR_KEYWORD_CACHE  = 8;
00032     const CLEAR_SIBLINGS_CACHE = 16;
00033     const CLEAR_CHILDREN_CACHE = 32;
00034     const CLEAR_ALL_CACHE      = 63;
00035     const CLEAR_DEFAULT        = 15; // CLEAR_NODE_CACHE and CLEAR_PARENT_CACHE and CLEAR_RELATING_CACHE and CLEAR_KEYWORD_CACHE
00036 
00037     /*!
00038      \static
00039      Appends parent nodes ids of \a $object to \a $nodeIDList array.
00040      \param $versionNum The version of the object to use or \c true for current version
00041      \param[out] $nodeIDList Array with node IDs
00042     */
00043     static function appendParentNodeIDs( $object, $versionNum, &$nodeIDList )
00044     {
00045         foreach ( $object->parentNodeIDArray() as $parentNodeID )
00046         {
00047             $nodeIDList[] = $parentNodeID;
00048         }
00049     }
00050 
00051     /*!
00052      \static
00053      Appends nodes ids from \a $nodeList list to \a $nodeIDList
00054      \param[out] $nodeIDList Array with node IDs
00055     */
00056     static function appendNodeIDs( $nodeList, &$nodeIDList )
00057     {
00058         foreach ( $nodeList as $node )
00059         {
00060             $nodeIDList[] = $node->attribute( 'node_id' );
00061         }
00062     }
00063 
00064     /*!
00065      \static
00066      Goes through all content nodes in \a $nodeList and extracts the \c 'path_string'.
00067      \return An array with \c 'path_string' information.
00068     */
00069     static function fetchNodePathString( $nodeList )
00070     {
00071         $pathList = array();
00072         foreach ( $nodeList as $node )
00073         {
00074             $pathList[] = $node->attribute( 'path_string' );
00075         }
00076         return $pathList;
00077     }
00078 
00079     /*!
00080      \static
00081      Find all content objects that relates \a $object and appends
00082      their node IDs to \a $nodeIDList.
00083      \param[out] $nodeIDList Array with node IDs
00084     */
00085     static function appendRelatingNodeIDs( $object, &$nodeIDList )
00086     {
00087         $viewCacheIni = eZINI::instance( 'viewcache.ini' );
00088         if ( $viewCacheIni->hasVariable( 'ViewCacheSettings', 'ClearRelationTypes' ) )
00089         {
00090             $relTypes = $viewCacheIni->variable( 'ViewCacheSettings', 'ClearRelationTypes' );
00091 
00092             if ( !count( $relTypes ) )
00093                 return;
00094 
00095             $relatedObjects = array();
00096 
00097             $relationsMask = 0;
00098             if ( in_array( 'object', $relTypes ) )
00099                 $relationsMask |= eZContentObject::RELATION_COMMON | eZContentObject::RELATION_EMBED;
00100 
00101             if ( in_array( 'common', $relTypes ) )
00102                 $relationsMask |= eZContentObject::RELATION_COMMON;
00103 
00104             if ( in_array( 'embedded', $relTypes ) )
00105                 $relationsMask |= eZContentObject::RELATION_EMBED;
00106 
00107             if ( in_array( 'linked', $relTypes ) )
00108                 $relationsMask |= eZContentObject::RELATION_LINK;
00109 
00110             if ( in_array( 'attribute', $relTypes ) )
00111                 $relationsMask |= eZContentObject::RELATION_ATTRIBUTE;
00112 
00113             if ( $relationsMask )
00114             {
00115                 $objects = $object->relatedContentObjectList( false, false, false, false,
00116                                                               array( 'AllRelations' => $relationsMask ) );
00117                 $previousVersionObjects = array();
00118                 $previousVersion = $object->previousVersion();
00119                 if ( $previousVersion )
00120                 {
00121                     $previousVersionObjects = $object->relatedContentObjectList( $previousVersion, false, false, false,
00122                                                               array( 'AllRelations' => $relationsMask ) );
00123                 }
00124                 $relatedObjects = array_merge( $relatedObjects, $objects, $previousVersionObjects );
00125             }
00126 
00127             $relationsMask = 0;
00128             if ( in_array( 'reverse_object', $relTypes ) )
00129                 $relationsMask |= eZContentObject::RELATION_COMMON | eZContentObject::RELATION_EMBED;
00130 
00131             if ( in_array( 'reverse_common', $relTypes ) )
00132                 $relationsMask |= eZContentObject::RELATION_COMMON;
00133 
00134             if ( in_array( 'reverse_embedded', $relTypes ) )
00135                 $relationsMask |= eZContentObject::RELATION_EMBED;
00136 
00137             if ( in_array( 'reverse_linked', $relTypes ) )
00138                 $relationsMask |= eZContentObject::RELATION_LINK;
00139 
00140             if ( in_array( 'reverse_attribute', $relTypes ) )
00141                 $relationsMask |= eZContentObject::RELATION_ATTRIBUTE;
00142 
00143             if ( $relationsMask )
00144             {
00145                 $objects = $object->reverseRelatedObjectList( false, false, false,
00146                                                               array( 'AllRelations' => $relationsMask ) );
00147                 $previousVersionObjects = array();
00148                 $previousVersion = $object->previousVersion();
00149                 if ( $previousVersion )
00150                 {
00151                     $previousVersionObjects = $object->relatedContentObjectList( $previousVersion, false, false, false,
00152                                                               array( 'AllRelations' => $relationsMask ) );
00153                 }
00154                 $relatedObjects = array_merge( $relatedObjects, $objects, $previousVersionObjects );
00155             }
00156         }
00157         else
00158         {
00159             $normalRelated = $object->relatedContentObjectArray();
00160             $reversedRelated = $object->contentObjectListRelatingThis();
00161 
00162             $relatedObjects = array_merge( $normalRelated, $reversedRelated );
00163         }
00164 
00165         foreach ( $relatedObjects as $relatedObject )
00166         {
00167             $assignedNodes = $relatedObject->assignedNodes( false );
00168             foreach ( $assignedNodes as $assignedNode )
00169             {
00170                 $nodeIDList[] = $assignedNode['node_id'];
00171             }
00172         }
00173         $nodeIDList = array_unique( $nodeIDList );
00174     }
00175 
00176     /*!
00177      \static
00178      Appends node ids of objects with the same keyword(s) as \a $object to \a $nodeIDList array.
00179      \param $versionNum The version of the object to use or \c true for current version
00180      \param[out] $nodeIDList Array with node IDs
00181     */
00182     static function appendKeywordNodeIDs( $object, $versionNum, &$nodeIDList, $limit = null )
00183     {
00184         if ( $limit === 0 )
00185             return;
00186         if ( $versionNum === true )
00187             $versionNum = false;
00188         $keywordArray = array();
00189         $attributes = $object->contentObjectAttributes( true, $versionNum );
00190         foreach ( array_keys( $attributes ) as $key )  // Looking for ezkeyword attributes
00191         {
00192             if ( $attributes[$key] instanceof eZContentObjectAttribute and
00193                  $attributes[$key]->attribute( 'data_type_string' ) == 'ezkeyword' )  // Found one
00194             {
00195                 $keywordObject = $attributes[$key]->content();
00196                 if ( $keywordObject instanceof eZKeyword )
00197                 {
00198                     foreach ( $keywordObject->attribute( 'keywords' ) as $keyword )
00199                     {
00200                         $keywordArray[] = $keyword;
00201                     }
00202                 }
00203             }
00204         }
00205 
00206         // Find all nodes that have the given keywords
00207         if ( count( $keywordArray ) > 0 )
00208         {
00209             $db = eZDB::instance();
00210             foreach( $keywordArray as $k => $keyword )
00211             {
00212                 $keywordArray[$k] = "'" . $db->escapeString( $keyword ) . "'";
00213             }
00214             $keywordString = implode( ', ', $keywordArray );
00215             $params = $limit ? array( 'offset' => 0, 'limit'  => $limit ) : array();
00216             $rows = $db->arrayQuery( "SELECT DISTINCT ezcontentobject_tree.node_id
00217                                        FROM
00218                                          ezcontentobject_tree,
00219                                          ezcontentobject_attribute,
00220                                          ezkeyword_attribute_link,
00221                                          ezkeyword
00222                                        WHERE
00223                                          ezcontentobject_tree.contentobject_id = ezcontentobject_attribute.contentobject_id AND
00224                                          ezcontentobject_attribute.id = ezkeyword_attribute_link.objectattribute_id AND
00225                                          ezkeyword_attribute_link.keyword_id = ezkeyword.id AND
00226                                          ezkeyword.keyword IN ( $keywordString )",
00227                                         $params );
00228 
00229             foreach ( $rows as $row )
00230             {
00231                 $nodeIDList[] = $row['node_id'];
00232             }
00233         }
00234     }
00235 
00236     /*
00237      \static
00238      For each node in \a $nodeList finds its sibling nodes and adds its ids to
00239      the \a $nodeIDList
00240     */
00241     static function appendSiblingsNodeIDs( &$nodeList, &$nodeIDList )
00242     {
00243         $params = array( 'Depth' => 1,
00244                          'AsObject' => false );
00245         foreach ( $nodeList as $node )
00246         {
00247             $siblingNodeList = eZContentObjectTreeNode::subTreeByNodeID( $params, $node->attribute( 'parent_node_id' ) );
00248             if ( count( $siblingNodeList ) > 0 )
00249             {
00250                 foreach ( array_keys( $siblingNodeList ) as $siblingKey )
00251                 {
00252                     $nodeIDList[] = $siblingNodeList[$siblingKey]['node_id'];
00253                 }
00254             }
00255         }
00256     }
00257 
00258     /**
00259      * For each node in $nodeList finds its children nodes and adds its ids to
00260      * the $nodeIDList.
00261      *
00262      * @param array(eZContentObjectTreeNode) $nodeList
00263      * @param array(int) $nodeIDList
00264      */
00265     public static function appendChildrenNodeIDs( &$nodeList, &$nodeIDList )
00266     {
00267         $params = array( 'Depth' => 1,
00268                          'AsObject' => false );
00269         foreach ( $nodeList as $node )
00270         {
00271             $childNodeList = eZContentObjectTreeNode::subTreeByNodeID( $params, $node->attribute( 'node_id' ) );
00272             if ( !empty( $childNodeList ) )
00273             {
00274                 foreach ( $childNodeList as $childNode )
00275                 {
00276                     $nodeIDList[] = $childNode['node_id'];
00277                 }
00278             }
00279         }
00280     }
00281 
00282     /*
00283      \static
00284      Reads 'viewcache.ini' file and determines relation between
00285      \a $classID and another class.
00286 
00287      \return An associative array with information on the class, containsL:
00288              - dependent_class_identifier - The class identifier of objects that depend on this class
00289              - additional_objects - Array of additional arbitrary object ids to clear
00290              - max_parents - The maxium number of parent nodes to check, or \c 0 for no limit
00291              - clear_cache_type - Bitfield of clear types, see nodeListForObject() for more details
00292              - object_filter - Array with object IDs, if there are entries only these objects should be checked.
00293     */
00294     static function dependencyInfo( $classID, $ignoreINISettings = false )
00295     {
00296         $ini = eZINI::instance( 'viewcache.ini' );
00297         $info = false;
00298 
00299         if ( $ignoreINISettings || $ini->variable( 'ViewCacheSettings', 'SmartCacheClear' ) !== 'disabled' )
00300         {
00301             if ( $ini->hasGroup( $classID ) )
00302             {
00303                 $info = array();
00304                 $info['clear_cache_exclusive'] = $ini->variable( 'ViewCacheSettings', 'SmartCacheClear' ) === 'exclusive';
00305 
00306                 if ( $ini->hasVariable( $classID, 'DependentClassIdentifier' ) )
00307                     $info['dependent_class_identifier'] = $ini->variable( $classID, 'DependentClassIdentifier' );
00308 
00309                 if ( $ini->hasVariable( $classID, 'MaxParents' ) )
00310                     $info['max_parents'] = $ini->variable( $classID, 'MaxParents' );
00311                 else
00312                     $info['max_parents'] = 0;
00313 
00314                 if ( $ini->hasVariable( $classID, 'AdditionalObjectIDs' ) )
00315                     $info['additional_objects'] = $ini->variable( $classID, 'AdditionalObjectIDs' );
00316 
00317                 $info['clear_cache_type'] = 0;
00318                 if ( $ini->hasVariable( $classID, 'ClearCacheMethod' ) )
00319                 {
00320                     $type = $ini->variable( $classID, 'ClearCacheMethod' );
00321 
00322                     if ( is_array( $type ) )
00323                     {
00324                         if ( in_array( 'none', $type ) )
00325                         {
00326                             $info['clear_cache_type'] = self::CLEAR_NO_CACHE;
00327                         }
00328                         elseif ( in_array( 'all', $type ) )
00329                         {
00330                             $info['clear_cache_type'] = self::CLEAR_ALL_CACHE;
00331                         }
00332                         else
00333                         {
00334                             if ( in_array( 'object', $type ) )
00335                                 $info['clear_cache_type'] |= self::CLEAR_NODE_CACHE;
00336 
00337                             if ( in_array( 'parent', $type ) )
00338                                 $info['clear_cache_type'] |= self::CLEAR_PARENT_CACHE;
00339 
00340                             if ( in_array( 'relating', $type ) )
00341                                 $info['clear_cache_type'] |= self::CLEAR_RELATING_CACHE;
00342 
00343                             if ( in_array( 'keyword', $type ) )
00344                                 $info['clear_cache_type'] |= self::CLEAR_KEYWORD_CACHE;
00345 
00346                             if ( in_array( 'siblings', $type ) )
00347                                 $info['clear_cache_type'] |= self::CLEAR_SIBLINGS_CACHE;
00348 
00349                             if ( in_array( 'children', $type ) )
00350                                 $info['clear_cache_type'] |= self::CLEAR_CHILDREN_CACHE;
00351                         }
00352                     }
00353                     else
00354                     {
00355                         // deprecated
00356                         if ( $type == 'clear_all_caches' )
00357                         {
00358                             $info['clear_cache_type'] = self::CLEAR_ALL_CACHE;
00359                         }
00360                         else
00361                         {
00362                             if ( $type == 'clear_object_caches_only' ||
00363                                  $type == 'clear_object_and_parent_nodes_caches' ||
00364                                  $type == 'clear_object_and_relating_objects_caches' )
00365                             {
00366                                 $info['clear_cache_type'] |= self::CLEAR_NODE_CACHE;
00367                             }
00368 
00369                             if ( $type == 'clear_object_and_parent_nodes_caches' ||
00370                                  $type == 'clear_parent_nodes_caches_only' ||
00371                                  $type == 'clear_parent_nodes_and_relating_caches' )
00372                             {
00373                                 $info['clear_cache_type'] |= self::CLEAR_PARENT_CACHE;
00374                             }
00375 
00376                             if ( $type == 'clear_object_and_relating_objects_caches' ||
00377                                  $type == 'clear_parent_nodes_and_relating_caches' ||
00378                                  $type == 'clear_relating_caches_only' )
00379                             {
00380                                 $info['clear_cache_type'] |= self::CLEAR_RELATING_CACHE;
00381                             }
00382 
00383                             if ( $type == 'clear_keyword_caches_only' )
00384                             {
00385                                 $info['clear_cache_type'] |= self::CLEAR_KEYWORD_CACHE;
00386                             }
00387                         }
00388                     }
00389                 }
00390                 else
00391                 {
00392                     $info['clear_cache_type'] = self::CLEAR_DEFAULT;
00393                 }
00394 
00395                 $info['object_filter'] = array();
00396                 if ( $ini->hasVariable( $classID, 'ObjectFilter' ) )
00397                 {
00398                     $info['object_filter'] = $ini->variable( $classID, 'ObjectFilter' );
00399                 }
00400             }
00401         }
00402 
00403         return $info;
00404     }
00405 
00406     /*
00407      Can be used to debug the \a $handledObjectList parameter of nodeListForObject()
00408     */
00409     static function writeDebugBits( $handledObjectList, $highestBit )
00410     {
00411         $bitPadLength = (int)( pow( $highestBit, 0.5 ) + 1 );
00412         //$bitPadLength = strlen( decbin( $highestBit ) );
00413 
00414         $objectIDList = array_keys( $handledObjectList );
00415         $maxObjectID = max( $objectIDList );
00416         $padLength = strlen( $maxObjectID ) + 2;
00417 
00418         $msg = '';
00419         foreach ( $handledObjectList as $objectID => $clearCacheType )
00420         {
00421             $bitString = decbin( $clearCacheType );
00422             $msg .= str_pad( $objectID, $padLength, ' ', STR_PAD_RIGHT ) . str_pad( $bitString, $bitPadLength, '0', STR_PAD_LEFT );
00423             $msg .= "\r\n";
00424         }
00425 
00426         eZDebug::writeDebug( $msg, __METHOD__ );
00427     }
00428 
00429     /*!
00430      \static
00431      Use \a $clearCacheType to include different kind of nodes( parent, relating, etc ).
00432      If \a $versionNum is true, then current version will be used.
00433 
00434      \param $contentObject Current content object that is checked.
00435      \param $versionNum The version of the object to use or \c true for current version
00436      \param $clearCacheType Bit field which controls the the extra nodes to include,
00437                             use bitwise or with one of these defines:
00438                             - self::CLEAR_NODE_CACHE - Clear the nodes of the object
00439                             - self::CLEAR_PARENT_CACHE - Clear the parent nodes of the object
00440                             - self::CLEAR_RELATING_CACHE - Clear nodes of objects that relate this object
00441                             - self::CLEAR_KEYWORD_CACHE - Clear nodes of objects that have the same keyword as this object
00442                             - self::CLEAR_SIBLINGS_CACHE - Clear caches for siblings of the node.
00443                             - self::CLEAR_ALL_CACHE - Enables all of the above
00444                             - self::CLEAR_NO_CACHE - Do not clear cache for current object.
00445      \param[out] $nodeList An array with node IDs that are affected by the current object change.
00446      \param[out] $handledObjectList An associative array with object IDs and the cache types that were handled for these objects already.
00447                                     Used to avoid infinite recursion.
00448 
00449      \note This function is recursive.
00450     */
00451     static function nodeListForObject( $contentObject, $versionNum, $clearCacheType, &$nodeList, &$handledObjectList )
00452     {
00453         $contentObjectID = $contentObject->attribute( 'id' );
00454 
00455         if ( isset( $handledObjectList[$contentObjectID] ) )
00456         {
00457             $handledObjectList[$contentObjectID] |= $clearCacheType;
00458         }
00459         else
00460         {
00461             $handledObjectList[$contentObjectID] = $clearCacheType;
00462         }
00463         //self::writeDebugBits( $handledObjectList, self::CLEAR_SIBLINGS_CACHE );
00464 
00465         $assignedNodes = $contentObject->assignedNodes();
00466 
00467         // determine if $contentObject has dependent objects for which cache should be cleared too.
00468         $objectClassIdentifier = $contentObject->attribute( 'class_identifier' );
00469         $dependentClassInfo = eZContentCacheManager::dependencyInfo( $objectClassIdentifier );
00470 
00471         if ( $dependentClassInfo['clear_cache_type'] === self::CLEAR_NO_CACHE )
00472         {
00473             // BC: Allow smart cache clear setting to specify no caching setting
00474             $clearCacheType = self::CLEAR_NO_CACHE;
00475         }
00476         else if ( $dependentClassInfo['clear_cache_exclusive'] === true )
00477         {
00478             // use class specific smart cache rules exclusivly
00479             $clearCacheType = $dependentClassInfo['clear_cache_type'];
00480         }
00481 
00482         if ( $clearCacheType === self::CLEAR_NO_CACHE )
00483         {
00484             // when recursing we will never have to handle this object again for other cache types
00485             // because types of caches to clear will always be set to none
00486             $handledObjectList[$contentObjectID] = self::CLEAR_ALL_CACHE;
00487         }
00488 
00489         if ( $clearCacheType & self::CLEAR_NODE_CACHE )
00490         {
00491             eZContentCacheManager::appendNodeIDs( $assignedNodes, $nodeList );
00492         }
00493 
00494         if ( $clearCacheType & self::CLEAR_PARENT_CACHE )
00495         {
00496             eZContentCacheManager::appendParentNodeIDs( $contentObject, $versionNum, $nodeList );
00497         }
00498 
00499         if ( $clearCacheType & self::CLEAR_RELATING_CACHE )
00500         {
00501             eZContentCacheManager::appendRelatingNodeIDs( $contentObject, $nodeList );
00502         }
00503 
00504         if ( $clearCacheType & self::CLEAR_KEYWORD_CACHE )
00505         {
00506             $keywordClearLimit = null;
00507             $viewcacheini = eZINI::instance( 'viewcache.ini' );
00508             if ( is_numeric( $viewcacheini->variable( 'ViewCacheSettings', 'KeywordNodesCacheClearLimit' ) ) )
00509                 $keywordClearLimit = (int) $viewcacheini->variable( 'ViewCacheSettings', 'KeywordNodesCacheClearLimit' );
00510 
00511             eZContentCacheManager::appendKeywordNodeIDs( $contentObject, $versionNum, $nodeList, $keywordClearLimit );
00512         }
00513 
00514         if ( $clearCacheType & self::CLEAR_SIBLINGS_CACHE )
00515         {
00516             eZContentCacheManager::appendSiblingsNodeIDs( $assignedNodes, $nodeList );
00517         }
00518 
00519         if ( $dependentClassInfo['clear_cache_type'] & self::CLEAR_SIBLINGS_CACHE )
00520         {
00521             if ( !( $clearCacheType & self::CLEAR_SIBLINGS_CACHE ) )
00522             {
00523                 eZContentCacheManager::appendSiblingsNodeIDs( $assignedNodes, $nodeList );
00524                 $handledObjectList[$contentObjectID] |= self::CLEAR_SIBLINGS_CACHE;
00525             }
00526 
00527             // drop 'siblings' bit and process parent nodes.
00528             // since 'sibling' mode is affected to the current object
00529             $dependentClassInfo['clear_cache_type'] &= ~self::CLEAR_SIBLINGS_CACHE;
00530         }
00531 
00532         if ( $clearCacheType & self::CLEAR_CHILDREN_CACHE )
00533         {
00534             eZContentCacheManager::appendChildrenNodeIDs( $assignedNodes, $nodeList );
00535         }
00536 
00537         if ( $dependentClassInfo['clear_cache_type'] & self::CLEAR_CHILDREN_CACHE )
00538         {
00539             if ( !( $clearCacheType & self::CLEAR_CHILDREN_CACHE ) )
00540             {
00541                 eZContentCacheManager::appendChildrenNodeIDs( $assignedNodes, $nodeList );
00542                 $handledObjectList[$contentObjectID] |= self::CLEAR_CHILDREN_CACHE;
00543             }
00544             // drop 'children' bit and process parent nodes.
00545             // since 'children' mode is affected to the current object
00546             $dependentClassInfo['clear_cache_type'] &= ~self::CLEAR_CHILDREN_CACHE;
00547         }
00548 
00549         if ( isset( $dependentClassInfo['additional_objects'] ) )
00550         {
00551             foreach( $dependentClassInfo['additional_objects'] as $objectID )
00552             {
00553                 // skip if cache type is already handled for this object
00554                 if ( isset( $handledObjectList[$objectID] ) && $handledObjectList[$objectID] & self::CLEAR_NODE_CACHE )
00555                 {
00556                     continue;
00557                 }
00558 
00559                 $object = eZContentObject::fetch( $objectID );
00560                 if ( $object )
00561                 {
00562                     //eZDebug::writeDebug( 'adding additional object ' . $objectID, 'eZContentCacheManager::nodeListForObject() for object ' . $contentObjectID );
00563                     eZContentCacheManager::nodeListForObject( $object, true, self::CLEAR_NODE_CACHE, $nodeList, $handledObjectList );
00564                 }
00565             }
00566         }
00567 
00568         if ( isset( $dependentClassInfo['dependent_class_identifier'] ) )
00569         {
00570             $maxParents = $dependentClassInfo['max_parents'];
00571             $dependentClassIdentifiers = $dependentClassInfo['dependent_class_identifier'];
00572             $smartClearType = $dependentClassInfo['clear_cache_type'];
00573 
00574             // getting 'path_string's for all locations.
00575             $nodePathList = eZContentCacheManager::fetchNodePathString( $assignedNodes );
00576 
00577             foreach ( $nodePathList as $nodePath )
00578             {
00579                 $step = 0;
00580 
00581                 // getting class identifier and node ID for each node in the $nodePath, up to $maxParents
00582                 $nodeInfoList = eZContentObjectTreeNode::fetchClassIdentifierListByPathString( $nodePath, false, $maxParents );
00583 
00584                 // for each node in $nodeInfoList determine if this node belongs to $dependentClassIdentifiers. If
00585                 // so then clear cache for this node.
00586                 foreach ( $nodeInfoList as $item )
00587                 {
00588                     if ( in_array( $item['class_identifier'], $dependentClassIdentifiers ) )
00589                     {
00590                         $object = eZContentObject::fetchByNodeID( $item['node_id'] );
00591                         if ( !$object instanceof eZContentObject )
00592                         {
00593                             continue;
00594                         }
00595                         $objectID = $object->attribute( 'id' );
00596 
00597                         if ( isset( $handledObjectList[$objectID] ) )
00598                         {
00599                             // remove cache types that were already handled
00600                             $smartClearType &= ~$handledObjectList[$objectID];
00601 
00602                             // if there are no cache types remaining, then skip
00603                             if ( $smartClearType == self::CLEAR_NO_CACHE )
00604                             {
00605                                 continue;
00606                             }
00607                         }
00608 
00609                         if ( count( $dependentClassInfo['object_filter'] ) > 0 )
00610                         {
00611                             if ( in_array( $objectID, $dependentClassInfo['object_filter'] ) )
00612                             {
00613                                 //eZDebug::writeDebug( 'adding parent ' . $objectID, 'eZContentCacheManager::nodeListForObject() for object ' . $contentObjectID );
00614                                 eZContentCacheManager::nodeListForObject( $object, true, $smartClearType, $nodeList, $handledObjectList );
00615                             }
00616                         }
00617                         else
00618                         {
00619                             //eZDebug::writeDebug( 'adding parent ' . $objectID, 'eZContentCacheManager::nodeListForObject() for object ' . $contentObjectID );
00620                             eZContentCacheManager::nodeListForObject( $object, true, $smartClearType, $nodeList, $handledObjectList );
00621                         }
00622                     }
00623                 }
00624             }
00625         }
00626 
00627         //self::writeDebugBits( $handledObjectList, self::CLEAR_SIBLINGS_CACHE );
00628     }
00629 
00630     /*!
00631      \static
00632      Figures out all nodes that are affected by the change of object \a $objectID.
00633      This involves finding all nodes, parent nodes and nodes of objects
00634      that relate this object.
00635      The 'viewcache.ini' file is also checked to see if some special content classes
00636      has dependencies to the current object, if this is true extra nodes might be
00637      included.
00638 
00639      \param $versionNum The version of the object to use or \c true for current version
00640      \param $additionalNodeList An array with node IDs to add to clear list,
00641                                 or \c false for no additional nodes.
00642      \return An array with node IDs that must have their viewcaches cleared.
00643     */
00644     static function nodeList( $objectID, $versionNum )
00645     {
00646         $nodeList = array();
00647 
00648         $object = eZContentObject::fetch( $objectID );
00649         if ( !$object )
00650         {
00651             return false;
00652         }
00653 
00654         eZContentCacheManager::nodeListForObject( $object, $versionNum, self::CLEAR_DEFAULT, $nodeList, $handledObjectList );
00655 
00656         return $nodeList;
00657     }
00658 
00659     /*!
00660      \static
00661      Deprecated. Use 'clearObjectViewCache' instead
00662     */
00663     static function clearViewCache( $objectID, $versionNum = true , $additionalNodeList = false )
00664     {
00665         eZDebug::writeWarning( "'clearViewCache' function was deprecated. Use 'clearObjectViewCache' instead", __METHOD__ );
00666         eZContentCacheManager::clearObjectViewCache( $objectID, $versionNum, $additionalNodeList );
00667     }
00668 
00669     /*!
00670      \static
00671      Clears view caches of nodes, parent nodes and relating nodes
00672      of content objects with id \a $objectID.
00673      It will use 'viewcache.ini' to determine additional nodes.
00674 
00675      \param $versionNum The version of the object to use or \c true for current version
00676      \param $additionalNodeList An array with node IDs to add to clear list,
00677                                 or \c false for no additional nodes.
00678     */
00679     static function clearObjectViewCache( $objectID, $versionNum = true, $additionalNodeList = false )
00680     {
00681         eZDebug::accumulatorStart( 'node_cleanup_list', '', 'Node cleanup list' );
00682 
00683         $nodeList = eZContentCacheManager::nodeList( $objectID, $versionNum );
00684 
00685         if ( $nodeList === false and !is_array( $additionalNodeList ) )
00686             return false;
00687 
00688         if ( $nodeList === false )
00689         {
00690             $nodeList = array();
00691         }
00692 
00693         if ( is_array( $additionalNodeList ) )
00694         {
00695             array_splice( $nodeList, count( $nodeList ), 0, $additionalNodeList );
00696         }
00697 
00698         if ( count( $nodeList ) == 0 )
00699         {
00700             return false;
00701         }
00702 
00703         $nodeList = array_unique( $nodeList );
00704 
00705         eZDebug::accumulatorStop( 'node_cleanup_list' );
00706 
00707         eZDebugSetting::writeDebug( 'kernel-content-edit', count( $nodeList ), "count in nodeList" );
00708 
00709         $ini = eZINI::instance();
00710         if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' )
00711         {
00712             $optionArray = array( 'iniFile'      => 'site.ini',
00713                                   'iniSection'   => 'ContentSettings',
00714                                   'iniVariable'  => 'StaticCacheHandler' );
00715 
00716             $options = new ezpExtensionOptions( $optionArray );
00717 
00718             $staticCacheHandler = eZExtension::getHandlerClass( $options );
00719 
00720             $staticCacheHandler->generateAlwaysUpdatedCache();
00721             $staticCacheHandler->generateNodeListCache( $nodeList );
00722         }
00723 
00724         eZDebug::accumulatorStart( 'node_cleanup', '', 'Node cleanup' );
00725 
00726         $nodeList = ezpEvent::getInstance()->filter( 'content/cache', $nodeList );
00727 
00728         eZContentObject::expireComplexViewModeCache();
00729         $cleanupValue = eZContentCache::calculateCleanupValue( count( $nodeList ) );
00730 
00731         if ( eZContentCache::inCleanupThresholdRange( $cleanupValue ) )
00732             eZContentCache::cleanup( $nodeList );
00733         else
00734         {
00735             eZDebug::writeDebug( "Expiring all view cache since list of nodes({$cleanupValue}) related to object({$objectID}) exeeds site.ini\[ContentSettings]\CacheThreshold", __METHOD__ );
00736             eZContentObject::expireAllViewCache();
00737         }
00738 
00739         eZDebug::accumulatorStop( 'node_cleanup' );
00740         return true;
00741     }
00742 
00743     /*!
00744      \static
00745      Clears view cache for specified object.
00746      Checks 'ViewCaching' ini setting to determine whether cache is enabled or not.
00747     */
00748     static function clearObjectViewCacheIfNeeded( $objectID, $versionNum = true, $additionalNodeList = false )
00749     {
00750         $ini = eZINI::instance();
00751         if ( $ini->variable( 'ContentSettings', 'ViewCaching' ) === 'enabled' )
00752             eZContentCacheManager::clearObjectViewCache( $objectID, $versionNum, $additionalNodeList );
00753     }
00754 
00755     /*!
00756      \static
00757      Clears template-block cache and template-block with subtree_expiry parameter caches for specified object.
00758      Checks 'TemplateCache' ini setting to determine whether cache is enabled or not.
00759      If $objectID is \c false all template block caches will be cleared.
00760     */
00761     static function clearTemplateBlockCacheIfNeeded( $objectID )
00762     {
00763         $ini = eZINI::instance();
00764         if ( $ini->variable( 'TemplateSettings', 'TemplateCache' ) === 'enabled' )
00765             eZContentCacheManager::clearTemplateBlockCache( $objectID, true );
00766     }
00767 
00768     /*!
00769      \static
00770      Clears template-block cache and template-block with subtree_expiry parameter caches for specified object
00771      without checking 'TemplateCache' ini setting. If $objectID is \c false all template block caches will be cleared.
00772     */
00773     static function clearTemplateBlockCache( $objectID, $checkViewCacheClassSettings = false )
00774     {
00775         // ordinary template block cache
00776         eZContentObject::expireTemplateBlockCache();
00777 
00778         // subtree template block cache
00779         $nodeList = false;
00780         $object = false;
00781         if ( $objectID )
00782             $object = eZContentObject::fetch( $objectID );
00783         if ( $object instanceof eZContentObject )
00784         {
00785             $getAssignedNodes = true;
00786             if ( $checkViewCacheClassSettings )
00787             {
00788                 $ini = eZINI::instance('viewcache.ini');
00789                 $objectClassIdentifier = $object->attribute('class_identifier');
00790                 if ( $ini->hasVariable( $objectClassIdentifier, 'ClearCacheBlocks' )
00791                   && $ini->variable( $objectClassIdentifier, 'ClearCacheBlocks' ) === 'disabled' )
00792                 {
00793                     $getAssignedNodes = false;
00794                 }
00795             }
00796 
00797             if ( $getAssignedNodes )
00798             {
00799                 $nodeList = $object->assignedNodes();
00800             }
00801         }
00802 
00803         eZSubtreeCache::cleanup( $nodeList );
00804     }
00805 
00806     /*!
00807      \static
00808      Generates the related viewcaches (PreGeneration) for the content object.
00809      It will only do this if [ContentSettings]/PreViewCache in site.ini is enabled.
00810 
00811      \param $objectID The ID of the content object to generate caches for.
00812     */
00813     static function generateObjectViewCache( $objectID )
00814     {
00815         // Generate the view cache
00816         $ini = eZINI::instance();
00817         $object = eZContentObject::fetch( $objectID );
00818         $user = eZUser::currentUser();
00819 
00820         eZDebug::accumulatorStart( 'generate_cache', '', 'Generating view cache' );
00821         if ( $ini->variable( 'ContentSettings', 'PreViewCache' ) == 'enabled' )
00822         {
00823             $preCacheSiteaccessArray = $ini->variable( 'ContentSettings', 'PreCacheSiteaccessArray' );
00824 
00825             $currentSiteAccess = $GLOBALS['eZCurrentAccess'];
00826 
00827             // This is the default view parameters for content/view
00828             $viewParameters = array( 'offset' => false,
00829                                      'year' => false,
00830                                      'month' => false,
00831                                      'day' => false,
00832                                      'namefilter' => false );
00833             if ( is_array( $preCacheSiteaccessArray ) && count( $preCacheSiteaccessArray ) > 0 )
00834             {
00835                 foreach ( $preCacheSiteaccessArray as $changeToSiteAccess )
00836                 {
00837                     $newSiteAccess = $currentSiteAccess;
00838                     $newSiteAccess['name'] = $changeToSiteAccess;
00839                     unset( $newSiteAccess['uri_part'] );//eZSiteAccess::load() will take care of setting correct one
00840                     eZSiteAccess::load( $newSiteAccess );
00841 
00842                     $tpl = eZTemplate::factory();
00843 
00844                     // Get the sitedesign and cached view preferences for this siteaccess
00845                     $siteini = eZINI::instance( 'site.ini');
00846                     $cachedViewPreferences = $siteini->variable( 'ContentSettings', 'CachedViewPreferences' );
00847 
00848                     $language = false; // Needs to be specified if you want to generate the cache for a specific language
00849                     $viewMode = 'full';
00850 
00851                     $assignedNodes = $object->assignedNodes();
00852                     foreach ( $assignedNodes as $node )
00853                     {
00854                         // We want to generate the cache for the specified user
00855                         $previewCacheUsers = $ini->variable( 'ContentSettings', 'PreviewCacheUsers' );
00856                         foreach ( $previewCacheUsers as $previewCacheUserID )
00857                         {
00858                             // If the text is 'anon' we need to fetch the Anonymous user ID.
00859                             if ( $previewCacheUserID === 'anonymous' )
00860                             {
00861                                 $previewCacheUserID = $siteini->variable( "UserSettings", "AnonymousUserID" );
00862                                 $previewCacheUser = eZUser::fetch( $previewCacheUserID  );
00863                             }
00864                             else if ( $previewCacheUserID === 'current' )
00865                             {
00866                                 $previewCacheUser = $user;
00867                             }
00868                             else
00869                             {
00870                                 $previewCacheUser = eZUser::fetch( $previewCacheUserID  );
00871                             }
00872                             if ( !$previewCacheUser )
00873                                 continue;
00874 
00875                             // Before we generate the view cache we must change the currently logged in user to $previewCacheUser
00876                             // If not the templates might read in wrong personalized data (preferences etc.)
00877                             eZUser::setCurrentlyLoggedInUser( $previewCacheUser, $previewCacheUser->attribute( 'contentobject_id' ), eZUser::NO_SESSION_REGENERATE );
00878 
00879                             // Cache the current node
00880                             $cacheFileArray = eZNodeviewfunctions::generateViewCacheFile( $previewCacheUser, $node->attribute( 'node_id' ), false, false, $language, $viewMode, $viewParameters, $cachedViewPreferences );
00881                             $tmpRes = eZNodeviewfunctions::generateNodeView( $tpl, $node, $node->attribute( 'object' ), $language, $viewMode, false, $cacheFileArray['cache_dir'], $cacheFileArray['cache_path'], true, $viewParameters );
00882 
00883                             // Cache the parent node
00884                             $parentNode = $node->attribute( 'parent' );
00885                             $objectID = $parentNode->attribute( 'contentobject_id' );
00886                             // if parent objectID is null or is 0 we should not create cache.
00887                             if ( $objectID )
00888                             {
00889                                 $cacheFileArray = eZNodeviewfunctions::generateViewCacheFile( $previewCacheUser, $parentNode->attribute( 'node_id' ), 0, false, $language, $viewMode, $viewParameters, $cachedViewPreferences );
00890                                 $tmpRes = eZNodeviewfunctions::generateNodeView( $tpl, $parentNode, $parentNode->attribute( 'object' ), $language, $viewMode, 0, $cacheFileArray['cache_dir'], $cacheFileArray['cache_path'], true, $viewParameters );
00891                             }
00892                         }
00893                     }
00894                 }
00895                 // Restore the old user as the current one
00896                 eZUser::setCurrentlyLoggedInUser( $user, $user->attribute( 'contentobject_id' ), eZUser::NO_SESSION_REGENERATE );
00897 
00898                 // restore siteaccess
00899                 eZSiteAccess::load( $currentSiteAccess );
00900             }
00901         }
00902 
00903         if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' )
00904         {
00905             $nodes = array();
00906             $ini = eZINI::instance();
00907             $useURLAlias =& $GLOBALS['eZContentObjectTreeNodeUseURLAlias'];
00908             $pathPrefix = $ini->variable( 'SiteAccessSettings', 'PathPrefix' );
00909             
00910             // get staticCacheHandler instance
00911             $optionArray = array( 'iniFile'      => 'site.ini',
00912                                   'iniSection'   => 'ContentSettings',
00913                                   'iniVariable'  => 'StaticCacheHandler' );
00914 
00915             $options = new ezpExtensionOptions( $optionArray );
00916             $staticCacheHandler = eZExtension::getHandlerClass( $options );
00917 
00918             if ( !isset( $useURLAlias ) )
00919             {
00920                 $useURLAlias = $ini->variable( 'URLTranslator', 'Translation' ) == 'enabled';
00921             }
00922 
00923             eZContentCacheManager::nodeListForObject( $object, true, self::CLEAR_DEFAULT, $nodes, $handledObjectList );
00924 
00925             // If no nodes returns it means that ClearCacheMethod = self::CLEAR_NO_CACHE
00926             if ( count( $nodes ) )
00927             {
00928                 foreach ( $nodes as $nodeID )
00929                 {
00930                     if ( $useURLAlias )
00931                     {
00932                         $oNode = eZContentObjectTreeNode::fetch( $nodeID, false, true );
00933                         if ( !isset( $oNode ) )
00934                             continue;
00935 
00936                         $urlAlias = $oNode->urlAlias();
00937                         if ( $pathPrefix != '' )
00938                         {
00939                             $tempAlias = substr( $pathPrefix, strlen( $pathPrefix ) -1 ) == '/'
00940                                             ? $urlAlias . '/'
00941                                             : $urlAlias;
00942                             if ( strncmp( $tempAlias, $pathPrefix, strlen( $tempAlias) ) == 0 )
00943                                 $urlAlias = substr( $tempAlias, strlen( $pathPrefix ) );
00944                         }
00945                     }
00946                     else
00947                     {
00948                         $urlAlias = 'content/view/full/' . $nodeID;
00949                     }
00950                     $staticCacheHandler->cacheURL( '/' . $urlAlias, $nodeID );
00951                 }
00952                 $staticCacheHandler->generateAlwaysUpdatedCache();
00953             }
00954         }
00955         eZDebug::accumulatorStop( 'generate_cache' );
00956     }
00957 
00958     /*!
00959      \static
00960      Clears content cache if needed by \a $sectionID
00961     */
00962     static function clearContentCacheIfNeededBySectionID( $sectionID )
00963     {
00964         // fetch all objects of this section
00965         $objectList = eZContentObject::fetchList( false, array( 'section_id' => "$sectionID" ) );
00966         // Clear cache
00967         foreach ( $objectList as $object )
00968         {
00969             eZContentCacheManager::clearContentCacheIfNeeded( $object['id'] );
00970         }
00971         return true;
00972     }
00973 
00974     /*!
00975      \static
00976      Clears content cache for specified object: view cache, template-block cache, template-block with subtree_expiry parameter cache.
00977      Checks appropriate ini settings to determine whether caches are enabled or not.
00978     */
00979     static function clearContentCacheIfNeeded( $objectID, $versionNum = true, $additionalNodeList = false )
00980     {
00981         eZDebug::accumulatorStart( 'check_cache', '', 'Check cache' );
00982 
00983         eZContentCacheManager::clearObjectViewCacheIfNeeded( $objectID, $versionNum, $additionalNodeList );
00984         eZContentCacheManager::clearTemplateBlockCacheIfNeeded( $objectID );
00985 
00986         // Clear cached path strings of content SSL zones.
00987         eZSSLZone::clearCacheIfNeeded();
00988 
00989         eZDebug::accumulatorStop( 'check_cache' );
00990         return true;
00991     }
00992 
00993     /*!
00994      \static
00995      Clears content cache for specified object: view cache, template-block cache, template-block with subtree_expiry parameter cache
00996      without checking of ini settings.
00997     */
00998     static function clearContentCache( $objectID, $versionNum = true, $additionalNodeList = false )
00999     {
01000         eZDebug::accumulatorStart( 'check_cache', '', 'Check cache' );
01001 
01002         eZContentCacheManager::clearObjectViewCache( $objectID, $versionNum, $additionalNodeList );
01003         eZContentCacheManager::clearTemplateBlockCache( $objectID );
01004 
01005         // Clear cached path strings of content SSL zones.
01006         eZSSLZone::clearCache();
01007 
01008         eZDebug::accumulatorStop( 'check_cache' );
01009         return true;
01010     }
01011 
01012     /*!
01013      \static
01014      Clears all content cache: view cache, template-block cache, template-block with subtree_expiry parameter cache.
01015     */
01016     static function clearAllContentCache( $ignoreINISettings = false )
01017     {
01018         if ( !$ignoreINISettings )
01019         {
01020             $ini = eZINI::instance();
01021             $viewCacheEnabled = ( $ini->variable( 'ContentSettings', 'ViewCaching' ) === 'enabled' );
01022             $templateCacheEnabled = ( $ini->variable( 'TemplateSettings', 'TemplateCache' ) === 'enabled' );
01023         }
01024         else
01025         {
01026             $viewCacheEnabled = true;
01027             $templateCacheEnabled = true;
01028         }
01029 
01030         if ( $viewCacheEnabled || $templateCacheEnabled )
01031         {
01032             // view cache and/or ordinary template block cache
01033             eZContentObject::expireAllCache();
01034 
01035             // subtree template block caches
01036             if ( $templateCacheEnabled )
01037             {
01038                 eZSubtreeCache::cleanupAll();
01039             }
01040         }
01041     }
01042 }
01043 
01044 ?>