PageRenderTime 59ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/kernel/classes/ezcontentcachemanager.php

https://github.com/granitegreg/ezpublish
PHP | 1053 lines | 687 code | 132 blank | 234 comment | 126 complexity | b71aec84b5f0169c61fc42c0031318ac MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. //
  3. // Definition of eZContentCacheManager class
  4. //
  5. // Created on: <23-Sep-2004 12:52:38 jb>
  6. //
  7. // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  8. // SOFTWARE NAME: eZ Publish
  9. // SOFTWARE RELEASE: 4.1.x
  10. // COPYRIGHT NOTICE: Copyright (C) 1999-2011 eZ Systems AS
  11. // SOFTWARE LICENSE: GNU General Public License v2.0
  12. // NOTICE: >
  13. // This program is free software; you can redistribute it and/or
  14. // modify it under the terms of version 2.0 of the GNU General
  15. // Public License as published by the Free Software Foundation.
  16. //
  17. // This program is distributed in the hope that it will be useful,
  18. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. // GNU General Public License for more details.
  21. //
  22. // You should have received a copy of version 2.0 of the GNU General
  23. // Public License along with this program; if not, write to the Free
  24. // Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  25. // MA 02110-1301, USA.
  26. //
  27. //
  28. // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  29. //
  30. /*! \file
  31. */
  32. /*!
  33. \class eZContentCacheManager ezcontentcachemanager.php
  34. \brief Figures out relations between objects, nodes and classes for cache management
  35. This class works together with eZContentCache to manage the cache files
  36. for content viewing. This class takes care of finding out the relationship
  37. and then passes a list of nodes to eZContentCache which does the actual
  38. clearing.
  39. The manager uses special rules in 'viewcache.ini' to figure relationships.
  40. \sa eZContentCache
  41. */
  42. class eZContentCacheManager
  43. {
  44. // Clear cache types
  45. const CLEAR_NO_CACHE = 0;
  46. const CLEAR_NODE_CACHE = 1;
  47. const CLEAR_PARENT_CACHE = 2;
  48. const CLEAR_RELATING_CACHE = 4;
  49. const CLEAR_KEYWORD_CACHE = 8;
  50. const CLEAR_SIBLINGS_CACHE = 16;
  51. const CLEAR_CHILDREN_CACHE = 32;
  52. const CLEAR_ALL_CACHE = 63;
  53. const CLEAR_DEFAULT = 15; // CLEAR_NODE_CACHE and CLEAR_PARENT_CACHE and CLEAR_RELATING_CACHE and CLEAR_KEYWORD_CACHE
  54. /*!
  55. \static
  56. Appends parent nodes ids of \a $object to \a $nodeIDList array.
  57. \param $versionNum The version of the object to use or \c true for current version
  58. \param[out] $nodeIDList Array with node IDs
  59. */
  60. static function appendParentNodeIDs( $object, $versionNum, &$nodeIDList )
  61. {
  62. foreach ( $object->parentNodeIDArray() as $parentNodeID )
  63. {
  64. $nodeIDList[] = $parentNodeID;
  65. }
  66. }
  67. /*!
  68. \static
  69. Appends nodes ids from \a $nodeList list to \a $nodeIDList
  70. \param[out] $nodeIDList Array with node IDs
  71. */
  72. static function appendNodeIDs( $nodeList, &$nodeIDList )
  73. {
  74. foreach ( $nodeList as $node )
  75. {
  76. $nodeIDList[] = $node->attribute( 'node_id' );
  77. }
  78. }
  79. /*!
  80. \static
  81. Goes through all content nodes in \a $nodeList and extracts the \c 'path_string'.
  82. \return An array with \c 'path_string' information.
  83. */
  84. static function fetchNodePathString( $nodeList )
  85. {
  86. $pathList = array();
  87. foreach ( $nodeList as $node )
  88. {
  89. $pathList[] = $node->attribute( 'path_string' );
  90. }
  91. return $pathList;
  92. }
  93. /*!
  94. \static
  95. Find all content objects that relates \a $object and appends
  96. their node IDs to \a $nodeIDList.
  97. \param[out] $nodeIDList Array with node IDs
  98. */
  99. static function appendRelatingNodeIDs( $object, &$nodeIDList )
  100. {
  101. $viewCacheIni = eZINI::instance( 'viewcache.ini' );
  102. if ( $viewCacheIni->hasVariable( 'ViewCacheSettings', 'ClearRelationTypes' ) )
  103. {
  104. $relTypes = $viewCacheIni->variable( 'ViewCacheSettings', 'ClearRelationTypes' );
  105. if ( !count( $relTypes ) )
  106. return;
  107. $relatedObjects = array();
  108. $relationsMask = 0;
  109. if ( in_array( 'object', $relTypes ) )
  110. $relationsMask |= eZContentObject::RELATION_COMMON | eZContentObject::RELATION_EMBED;
  111. if ( in_array( 'common', $relTypes ) )
  112. $relationsMask |= eZContentObject::RELATION_COMMON;
  113. if ( in_array( 'embedded', $relTypes ) )
  114. $relationsMask |= eZContentObject::RELATION_EMBED;
  115. if ( in_array( 'linked', $relTypes ) )
  116. $relationsMask |= eZContentObject::RELATION_LINK;
  117. if ( in_array( 'attribute', $relTypes ) )
  118. $relationsMask |= eZContentObject::RELATION_ATTRIBUTE;
  119. if ( $relationsMask )
  120. {
  121. $objects = $object->relatedContentObjectList( false, false, false, false,
  122. array( 'AllRelations' => $relationsMask ) );
  123. $previousVersionObjects = array();
  124. $previousVersion = $object->previousVersion();
  125. if ( $previousVersion )
  126. {
  127. $previousVersionObjects = $object->relatedContentObjectList( $previousVersion, false, false, false,
  128. array( 'AllRelations' => $relationsMask ) );
  129. }
  130. $relatedObjects = array_merge( $relatedObjects, $objects, $previousVersionObjects );
  131. }
  132. $relationsMask = 0;
  133. if ( in_array( 'reverse_object', $relTypes ) )
  134. $relationsMask |= eZContentObject::RELATION_COMMON | eZContentObject::RELATION_EMBED;
  135. if ( in_array( 'reverse_common', $relTypes ) )
  136. $relationsMask |= eZContentObject::RELATION_COMMON;
  137. if ( in_array( 'reverse_embedded', $relTypes ) )
  138. $relationsMask |= eZContentObject::RELATION_EMBED;
  139. if ( in_array( 'reverse_linked', $relTypes ) )
  140. $relationsMask |= eZContentObject::RELATION_LINK;
  141. if ( in_array( 'reverse_attribute', $relTypes ) )
  142. $relationsMask |= eZContentObject::RELATION_ATTRIBUTE;
  143. if ( $relationsMask )
  144. {
  145. $objects = $object->reverseRelatedObjectList( false, false, false,
  146. array( 'AllRelations' => $relationsMask ) );
  147. $previousVersionObjects = array();
  148. $previousVersion = $object->previousVersion();
  149. if ( $previousVersion )
  150. {
  151. $previousVersionObjects = $object->relatedContentObjectList( $previousVersion, false, false, false,
  152. array( 'AllRelations' => $relationsMask ) );
  153. }
  154. $relatedObjects = array_merge( $relatedObjects, $objects, $previousVersionObjects );
  155. }
  156. }
  157. else
  158. {
  159. $normalRelated = $object->relatedContentObjectArray();
  160. $reversedRelated = $object->contentObjectListRelatingThis();
  161. $relatedObjects = array_merge( $normalRelated, $reversedRelated );
  162. }
  163. foreach ( $relatedObjects as $relatedObject )
  164. {
  165. $assignedNodes = $relatedObject->assignedNodes( false );
  166. foreach ( $assignedNodes as $assignedNode )
  167. {
  168. $nodeIDList[] = $assignedNode['node_id'];
  169. }
  170. }
  171. $nodeIDList = array_unique( $nodeIDList );
  172. }
  173. /*!
  174. \static
  175. Appends node ids of objects with the same keyword(s) as \a $object to \a $nodeIDList array.
  176. \param $versionNum The version of the object to use or \c true for current version
  177. \param[out] $nodeIDList Array with node IDs
  178. */
  179. static function appendKeywordNodeIDs( $object, $versionNum, &$nodeIDList, $limit = null )
  180. {
  181. if ( $limit === 0 )
  182. return;
  183. if ( $versionNum === true )
  184. $versionNum = false;
  185. $keywordArray = array();
  186. $attributes = $object->contentObjectAttributes( true, $versionNum );
  187. foreach ( array_keys( $attributes ) as $key ) // Looking for ezkeyword attributes
  188. {
  189. if ( $attributes[$key] instanceof eZContentObjectAttribute and
  190. $attributes[$key]->attribute( 'data_type_string' ) == 'ezkeyword' ) // Found one
  191. {
  192. $keywordObject = $attributes[$key]->content();
  193. if ( $keywordObject instanceof eZKeyword )
  194. {
  195. foreach ( $keywordObject->attribute( 'keywords' ) as $keyword )
  196. {
  197. $keywordArray[] = $keyword;
  198. }
  199. }
  200. }
  201. }
  202. // Find all nodes that have the given keywords
  203. if ( count( $keywordArray ) > 0 )
  204. {
  205. $db = eZDB::instance();
  206. foreach( $keywordArray as $k => $keyword )
  207. {
  208. $keywordArray[$k] = "'" . $db->escapeString( $keyword ) . "'";
  209. }
  210. $keywordString = implode( ', ', $keywordArray );
  211. $params = $limit ? array( 'offset' => 0, 'limit' => $limit ) : array();
  212. $rows = $db->arrayQuery( "SELECT DISTINCT ezcontentobject_tree.node_id
  213. FROM
  214. ezcontentobject_tree,
  215. ezcontentobject_attribute,
  216. ezkeyword_attribute_link,
  217. ezkeyword
  218. WHERE
  219. ezcontentobject_tree.contentobject_id = ezcontentobject_attribute.contentobject_id AND
  220. ezcontentobject_attribute.id = ezkeyword_attribute_link.objectattribute_id AND
  221. ezkeyword_attribute_link.keyword_id = ezkeyword.id AND
  222. ezkeyword.keyword IN ( $keywordString )",
  223. $params );
  224. foreach ( $rows as $row )
  225. {
  226. $nodeIDList[] = $row['node_id'];
  227. }
  228. }
  229. }
  230. /*
  231. \static
  232. For each node in \a $nodeList finds its sibling nodes and adds its ids to
  233. the \a $nodeIDList
  234. */
  235. static function appendSiblingsNodeIDs( &$nodeList, &$nodeIDList )
  236. {
  237. $params = array( 'Depth' => 1,
  238. 'AsObject' => false );
  239. foreach ( $nodeList as $node )
  240. {
  241. $siblingNodeList = eZContentObjectTreeNode::subTreeByNodeID( $params, $node->attribute( 'parent_node_id' ) );
  242. if ( count( $siblingNodeList ) > 0 )
  243. {
  244. foreach ( array_keys( $siblingNodeList ) as $siblingKey )
  245. {
  246. $nodeIDList[] = $siblingNodeList[$siblingKey]['node_id'];
  247. }
  248. }
  249. }
  250. }
  251. /**
  252. * For each node in $nodeList finds its children nodes and adds its ids to
  253. * the $nodeIDList.
  254. *
  255. * @param array(eZContentObjectTreeNode) $nodeList
  256. * @param array(int) $nodeIDList
  257. */
  258. public static function appendChildrenNodeIDs( &$nodeList, &$nodeIDList )
  259. {
  260. $params = array( 'Depth' => 1,
  261. 'AsObject' => false );
  262. foreach ( $nodeList as $node )
  263. {
  264. $childNodeList = eZContentObjectTreeNode::subTreeByNodeID( $params, $node->attribute( 'node_id' ) );
  265. if ( !empty( $childNodeList ) )
  266. {
  267. foreach ( $childNodeList as $childNode )
  268. {
  269. $nodeIDList[] = $childNode['node_id'];
  270. }
  271. }
  272. }
  273. }
  274. /*
  275. \static
  276. Reads 'viewcache.ini' file and determines relation between
  277. \a $classID and another class.
  278. \return An associative array with information on the class, containsL:
  279. - dependent_class_identifier - The class identifier of objects that depend on this class
  280. - additional_objects - Array of additional arbitrary object ids to clear
  281. - max_parents - The maxium number of parent nodes to check, or \c 0 for no limit
  282. - clear_cache_type - Bitfield of clear types, see nodeListForObject() for more details
  283. - object_filter - Array with object IDs, if there are entries only these objects should be checked.
  284. */
  285. static function dependencyInfo( $classID, $ignoreINISettings = false )
  286. {
  287. $ini = eZINI::instance( 'viewcache.ini' );
  288. $info = false;
  289. if ( $ignoreINISettings || $ini->variable( 'ViewCacheSettings', 'SmartCacheClear' ) !== 'disabled' )
  290. {
  291. if ( $ini->hasGroup( $classID ) )
  292. {
  293. $info = array();
  294. $info['clear_cache_exclusive'] = $ini->variable( 'ViewCacheSettings', 'SmartCacheClear' ) === 'exclusive';
  295. if ( $ini->hasVariable( $classID, 'DependentClassIdentifier' ) )
  296. $info['dependent_class_identifier'] = $ini->variable( $classID, 'DependentClassIdentifier' );
  297. if ( $ini->hasVariable( $classID, 'MaxParents' ) )
  298. $info['max_parents'] = $ini->variable( $classID, 'MaxParents' );
  299. else
  300. $info['max_parents'] = 0;
  301. if ( $ini->hasVariable( $classID, 'AdditionalObjectIDs' ) )
  302. $info['additional_objects'] = $ini->variable( $classID, 'AdditionalObjectIDs' );
  303. $info['clear_cache_type'] = 0;
  304. if ( $ini->hasVariable( $classID, 'ClearCacheMethod' ) )
  305. {
  306. $type = $ini->variable( $classID, 'ClearCacheMethod' );
  307. if ( is_array( $type ) )
  308. {
  309. if ( in_array( 'none', $type ) )
  310. {
  311. $info['clear_cache_type'] = self::CLEAR_NO_CACHE;
  312. }
  313. elseif ( in_array( 'all', $type ) )
  314. {
  315. $info['clear_cache_type'] = self::CLEAR_ALL_CACHE;
  316. }
  317. else
  318. {
  319. if ( in_array( 'object', $type ) )
  320. $info['clear_cache_type'] |= self::CLEAR_NODE_CACHE;
  321. if ( in_array( 'parent', $type ) )
  322. $info['clear_cache_type'] |= self::CLEAR_PARENT_CACHE;
  323. if ( in_array( 'relating', $type ) )
  324. $info['clear_cache_type'] |= self::CLEAR_RELATING_CACHE;
  325. if ( in_array( 'keyword', $type ) )
  326. $info['clear_cache_type'] |= self::CLEAR_KEYWORD_CACHE;
  327. if ( in_array( 'siblings', $type ) )
  328. $info['clear_cache_type'] |= self::CLEAR_SIBLINGS_CACHE;
  329. if ( in_array( 'children', $type ) )
  330. $info['clear_cache_type'] |= self::CLEAR_CHILDREN_CACHE;
  331. }
  332. }
  333. else
  334. {
  335. // deprecated
  336. if ( $type == 'clear_all_caches' )
  337. {
  338. $info['clear_cache_type'] = self::CLEAR_ALL_CACHE;
  339. }
  340. else
  341. {
  342. if ( $type == 'clear_object_caches_only' ||
  343. $type == 'clear_object_and_parent_nodes_caches' ||
  344. $type == 'clear_object_and_relating_objects_caches' )
  345. {
  346. $info['clear_cache_type'] |= self::CLEAR_NODE_CACHE;
  347. }
  348. if ( $type == 'clear_object_and_parent_nodes_caches' ||
  349. $type == 'clear_parent_nodes_caches_only' ||
  350. $type == 'clear_parent_nodes_and_relating_caches' )
  351. {
  352. $info['clear_cache_type'] |= self::CLEAR_PARENT_CACHE;
  353. }
  354. if ( $type == 'clear_object_and_relating_objects_caches' ||
  355. $type == 'clear_parent_nodes_and_relating_caches' ||
  356. $type == 'clear_relating_caches_only' )
  357. {
  358. $info['clear_cache_type'] |= self::CLEAR_RELATING_CACHE;
  359. }
  360. if ( $type == 'clear_keyword_caches_only' )
  361. {
  362. $info['clear_cache_type'] |= self::CLEAR_KEYWORD_CACHE;
  363. }
  364. }
  365. }
  366. }
  367. else
  368. {
  369. $info['clear_cache_type'] = self::CLEAR_DEFAULT;
  370. }
  371. $info['object_filter'] = array();
  372. if ( $ini->hasVariable( $classID, 'ObjectFilter' ) )
  373. {
  374. $info['object_filter'] = $ini->variable( $classID, 'ObjectFilter' );
  375. }
  376. }
  377. }
  378. return $info;
  379. }
  380. /*
  381. Can be used to debug the \a $handledObjectList parameter of nodeListForObject()
  382. */
  383. static function writeDebugBits( $handledObjectList, $highestBit )
  384. {
  385. $bitPadLength = (int)( pow( $highestBit, 0.5 ) + 1 );
  386. //$bitPadLength = strlen( decbin( $highestBit ) );
  387. $objectIDList = array_keys( $handledObjectList );
  388. $maxObjectID = max( $objectIDList );
  389. $padLength = strlen( $maxObjectID ) + 2;
  390. $msg = '';
  391. foreach ( $handledObjectList as $objectID => $clearCacheType )
  392. {
  393. $bitString = decbin( $clearCacheType );
  394. $msg .= str_pad( $objectID, $padLength, ' ', STR_PAD_RIGHT ) . str_pad( $bitString, $bitPadLength, '0', STR_PAD_LEFT );
  395. $msg .= "\r\n";
  396. }
  397. eZDebug::writeDebug( $msg, __METHOD__ );
  398. }
  399. /*!
  400. \static
  401. Use \a $clearCacheType to include different kind of nodes( parent, relating, etc ).
  402. If \a $versionNum is true, then current version will be used.
  403. \param $contentObject Current content object that is checked.
  404. \param $versionNum The version of the object to use or \c true for current version
  405. \param $clearCacheType Bit field which controls the the extra nodes to include,
  406. use bitwise or with one of these defines:
  407. - self::CLEAR_NODE_CACHE - Clear the nodes of the object
  408. - self::CLEAR_PARENT_CACHE - Clear the parent nodes of the object
  409. - self::CLEAR_RELATING_CACHE - Clear nodes of objects that relate this object
  410. - self::CLEAR_KEYWORD_CACHE - Clear nodes of objects that have the same keyword as this object
  411. - self::CLEAR_SIBLINGS_CACHE - Clear caches for siblings of the node.
  412. - self::CLEAR_ALL_CACHE - Enables all of the above
  413. - self::CLEAR_NO_CACHE - Do not clear cache for current object.
  414. \param[out] $nodeList An array with node IDs that are affected by the current object change.
  415. \param[out] $handledObjectList An associative array with object IDs and the cache types that were handled for these objects already.
  416. Used to avoid infinite recursion.
  417. \note This function is recursive.
  418. */
  419. static function nodeListForObject( $contentObject, $versionNum, $clearCacheType, &$nodeList, &$handledObjectList )
  420. {
  421. $contentObjectID = $contentObject->attribute( 'id' );
  422. if ( isset( $handledObjectList[$contentObjectID] ) )
  423. {
  424. $handledObjectList[$contentObjectID] |= $clearCacheType;
  425. }
  426. else
  427. {
  428. $handledObjectList[$contentObjectID] = $clearCacheType;
  429. }
  430. //self::writeDebugBits( $handledObjectList, self::CLEAR_SIBLINGS_CACHE );
  431. $assignedNodes = $contentObject->assignedNodes();
  432. // determine if $contentObject has dependent objects for which cache should be cleared too.
  433. $objectClassIdentifier = $contentObject->attribute( 'class_identifier' );
  434. $dependentClassInfo = eZContentCacheManager::dependencyInfo( $objectClassIdentifier );
  435. if ( $dependentClassInfo['clear_cache_type'] === self::CLEAR_NO_CACHE )
  436. {
  437. // BC: Allow smart cache clear setting to specify no caching setting
  438. $clearCacheType = self::CLEAR_NO_CACHE;
  439. }
  440. else if ( $dependentClassInfo['clear_cache_exclusive'] === true )
  441. {
  442. // use class specific smart cache rules exclusivly
  443. $clearCacheType = $dependentClassInfo['clear_cache_type'];
  444. }
  445. if ( $clearCacheType === self::CLEAR_NO_CACHE )
  446. {
  447. // when recursing we will never have to handle this object again for other cache types
  448. // because types of caches to clear will always be set to none
  449. $handledObjectList[$contentObjectID] = self::CLEAR_ALL_CACHE;
  450. }
  451. if ( $clearCacheType & self::CLEAR_NODE_CACHE )
  452. {
  453. eZContentCacheManager::appendNodeIDs( $assignedNodes, $nodeList );
  454. }
  455. if ( $clearCacheType & self::CLEAR_PARENT_CACHE )
  456. {
  457. eZContentCacheManager::appendParentNodeIDs( $contentObject, $versionNum, $nodeList );
  458. }
  459. if ( $clearCacheType & self::CLEAR_RELATING_CACHE )
  460. {
  461. eZContentCacheManager::appendRelatingNodeIDs( $contentObject, $nodeList );
  462. }
  463. if ( $clearCacheType & self::CLEAR_KEYWORD_CACHE )
  464. {
  465. $keywordClearLimit = null;
  466. $viewcacheini = eZINI::instance( 'viewcache.ini' );
  467. if ( is_numeric( $viewcacheini->variable( 'ViewCacheSettings', 'KeywordNodesCacheClearLimit' ) ) )
  468. $keywordClearLimit = (int) $viewcacheini->variable( 'ViewCacheSettings', 'KeywordNodesCacheClearLimit' );
  469. eZContentCacheManager::appendKeywordNodeIDs( $contentObject, $versionNum, $nodeList, $keywordClearLimit );
  470. }
  471. if ( $clearCacheType & self::CLEAR_SIBLINGS_CACHE )
  472. {
  473. eZContentCacheManager::appendSiblingsNodeIDs( $assignedNodes, $nodeList );
  474. }
  475. if ( $dependentClassInfo['clear_cache_type'] & self::CLEAR_SIBLINGS_CACHE )
  476. {
  477. if ( !( $clearCacheType & self::CLEAR_SIBLINGS_CACHE ) )
  478. {
  479. eZContentCacheManager::appendSiblingsNodeIDs( $assignedNodes, $nodeList );
  480. $handledObjectList[$contentObjectID] |= self::CLEAR_SIBLINGS_CACHE;
  481. }
  482. // drop 'siblings' bit and process parent nodes.
  483. // since 'sibling' mode is affected to the current object
  484. $dependentClassInfo['clear_cache_type'] &= ~self::CLEAR_SIBLINGS_CACHE;
  485. }
  486. if ( $clearCacheType & self::CLEAR_CHILDREN_CACHE )
  487. {
  488. eZContentCacheManager::appendChildrenNodeIDs( $assignedNodes, $nodeList );
  489. }
  490. if ( $dependentClassInfo['clear_cache_type'] & self::CLEAR_CHILDREN_CACHE )
  491. {
  492. if ( !( $clearCacheType & self::CLEAR_CHILDREN_CACHE ) )
  493. {
  494. eZContentCacheManager::appendChildrenNodeIDs( $assignedNodes, $nodeList );
  495. $handledObjectList[$contentObjectID] |= self::CLEAR_CHILDREN_CACHE;
  496. }
  497. // drop 'children' bit and process parent nodes.
  498. // since 'children' mode is affected to the current object
  499. $dependentClassInfo['clear_cache_type'] &= ~self::CLEAR_CHILDREN_CACHE;
  500. }
  501. if ( isset( $dependentClassInfo['additional_objects'] ) )
  502. {
  503. foreach( $dependentClassInfo['additional_objects'] as $objectID )
  504. {
  505. // skip if cache type is already handled for this object
  506. if ( isset( $handledObjectList[$objectID] ) && $handledObjectList[$objectID] & self::CLEAR_NODE_CACHE )
  507. {
  508. continue;
  509. }
  510. $object = eZContentObject::fetch( $objectID );
  511. if ( $object )
  512. {
  513. //eZDebug::writeDebug( 'adding additional object ' . $objectID, 'eZContentCacheManager::nodeListForObject() for object ' . $contentObjectID );
  514. eZContentCacheManager::nodeListForObject( $object, true, self::CLEAR_NODE_CACHE, $nodeList, $handledObjectList );
  515. }
  516. }
  517. }
  518. if ( isset( $dependentClassInfo['dependent_class_identifier'] ) )
  519. {
  520. $maxParents = $dependentClassInfo['max_parents'];
  521. $dependentClassIdentifiers = $dependentClassInfo['dependent_class_identifier'];
  522. $smartClearType = $dependentClassInfo['clear_cache_type'];
  523. // getting 'path_string's for all locations.
  524. $nodePathList = eZContentCacheManager::fetchNodePathString( $assignedNodes );
  525. foreach ( $nodePathList as $nodePath )
  526. {
  527. $step = 0;
  528. // getting class identifier and node ID for each node in the $nodePath, up to $maxParents
  529. $nodeInfoList = eZContentObjectTreeNode::fetchClassIdentifierListByPathString( $nodePath, false, $maxParents );
  530. // for each node in $nodeInfoList determine if this node belongs to $dependentClassIdentifiers. If
  531. // so then clear cache for this node.
  532. foreach ( $nodeInfoList as $item )
  533. {
  534. if ( in_array( $item['class_identifier'], $dependentClassIdentifiers ) )
  535. {
  536. $object = eZContentObject::fetchByNodeID( $item['node_id'] );
  537. if ( !$object instanceof eZContentObject )
  538. {
  539. continue;
  540. }
  541. $objectID = $object->attribute( 'id' );
  542. if ( isset( $handledObjectList[$objectID] ) )
  543. {
  544. // remove cache types that were already handled
  545. $smartClearType &= ~$handledObjectList[$objectID];
  546. // if there are no cache types remaining, then skip
  547. if ( $smartClearType == self::CLEAR_NO_CACHE )
  548. {
  549. continue;
  550. }
  551. }
  552. if ( count( $dependentClassInfo['object_filter'] ) > 0 )
  553. {
  554. if ( in_array( $objectID, $dependentClassInfo['object_filter'] ) )
  555. {
  556. //eZDebug::writeDebug( 'adding parent ' . $objectID, 'eZContentCacheManager::nodeListForObject() for object ' . $contentObjectID );
  557. eZContentCacheManager::nodeListForObject( $object, true, $smartClearType, $nodeList, $handledObjectList );
  558. }
  559. }
  560. else
  561. {
  562. //eZDebug::writeDebug( 'adding parent ' . $objectID, 'eZContentCacheManager::nodeListForObject() for object ' . $contentObjectID );
  563. eZContentCacheManager::nodeListForObject( $object, true, $smartClearType, $nodeList, $handledObjectList );
  564. }
  565. }
  566. }
  567. }
  568. }
  569. //self::writeDebugBits( $handledObjectList, self::CLEAR_SIBLINGS_CACHE );
  570. }
  571. /*!
  572. \static
  573. Figures out all nodes that are affected by the change of object \a $objectID.
  574. This involves finding all nodes, parent nodes and nodes of objects
  575. that relate this object.
  576. The 'viewcache.ini' file is also checked to see if some special content classes
  577. has dependencies to the current object, if this is true extra nodes might be
  578. included.
  579. \param $versionNum The version of the object to use or \c true for current version
  580. \param $additionalNodeList An array with node IDs to add to clear list,
  581. or \c false for no additional nodes.
  582. \return An array with node IDs that must have their viewcaches cleared.
  583. */
  584. static function nodeList( $objectID, $versionNum )
  585. {
  586. $nodeList = array();
  587. $object = eZContentObject::fetch( $objectID );
  588. if ( !$object )
  589. {
  590. return false;
  591. }
  592. eZContentCacheManager::nodeListForObject( $object, $versionNum, self::CLEAR_DEFAULT, $nodeList, $handledObjectList );
  593. return $nodeList;
  594. }
  595. /*!
  596. \static
  597. Depreciated. Use 'clearObjectViewCache' instead
  598. */
  599. static function clearViewCache( $objectID, $versionNum = true , $additionalNodeList = false )
  600. {
  601. eZDebug::writeWarning( "'clearViewCache' function was depreciated. Use 'clearObjectViewCache' instead", __METHOD__ );
  602. eZContentCacheManager::clearObjectViewCache( $objectID, $versionNum, $additionalNodeList );
  603. }
  604. /*!
  605. \static
  606. Clears view caches of nodes, parent nodes and relating nodes
  607. of content objects with id \a $objectID.
  608. It will use 'viewcache.ini' to determine additional nodes.
  609. \param $versionNum The version of the object to use or \c true for current version
  610. \param $additionalNodeList An array with node IDs to add to clear list,
  611. or \c false for no additional nodes.
  612. */
  613. static function clearObjectViewCache( $objectID, $versionNum = true, $additionalNodeList = false )
  614. {
  615. eZDebug::accumulatorStart( 'node_cleanup_list', '', 'Node cleanup list' );
  616. $nodeList = eZContentCacheManager::nodeList( $objectID, $versionNum );
  617. if ( $nodeList === false and !is_array( $additionalNodeList ) )
  618. return false;
  619. if ( $nodeList === false )
  620. {
  621. $nodeList = array();
  622. }
  623. if ( is_array( $additionalNodeList ) )
  624. {
  625. array_splice( $nodeList, count( $nodeList ), 0, $additionalNodeList );
  626. }
  627. if ( count( $nodeList ) == 0 )
  628. {
  629. return false;
  630. }
  631. $nodeList = array_unique( $nodeList );
  632. eZDebug::accumulatorStop( 'node_cleanup_list' );
  633. eZDebugSetting::writeDebug( 'kernel-content-edit', count( $nodeList ), "count in nodeList" );
  634. $ini = eZINI::instance();
  635. if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' )
  636. {
  637. $staticCache = new eZStaticCache();
  638. $staticCache->generateAlwaysUpdatedCache();
  639. $staticCache->generateNodeListCache( $nodeList );
  640. }
  641. eZDebug::accumulatorStart( 'node_cleanup', '', 'Node cleanup' );
  642. $nodeList = ezpEvent::getInstance()->filter( 'content/cache', $nodeList );
  643. eZContentObject::expireComplexViewModeCache();
  644. $cleanupValue = eZContentCache::calculateCleanupValue( count( $nodeList ) );
  645. if ( eZContentCache::inCleanupThresholdRange( $cleanupValue ) )
  646. eZContentCache::cleanup( $nodeList );
  647. else
  648. {
  649. eZDebug::writeDebug( "Expiring all view cache since list of nodes({$cleanupValue}) related to object({$objectID}) exeeds site.ini\[ContentSettings]\CacheThreshold", __METHOD__ );
  650. eZContentObject::expireAllViewCache();
  651. }
  652. eZDebug::accumulatorStop( 'node_cleanup' );
  653. return true;
  654. }
  655. /*!
  656. \static
  657. Clears view cache for specified object.
  658. Checks 'ViewCaching' ini setting to determine whether cache is enabled or not.
  659. */
  660. static function clearObjectViewCacheIfNeeded( $objectID, $versionNum = true, $additionalNodeList = false )
  661. {
  662. $ini = eZINI::instance();
  663. if ( $ini->variable( 'ContentSettings', 'ViewCaching' ) === 'enabled' )
  664. eZContentCacheManager::clearObjectViewCache( $objectID, $versionNum, $additionalNodeList );
  665. }
  666. /*!
  667. \static
  668. Clears template-block cache and template-block with subtree_expiry parameter caches for specified object.
  669. Checks 'TemplateCache' ini setting to determine whether cache is enabled or not.
  670. If $objectID is \c false all template block caches will be cleared.
  671. */
  672. static function clearTemplateBlockCacheIfNeeded( $objectID )
  673. {
  674. $ini = eZINI::instance();
  675. if ( $ini->variable( 'TemplateSettings', 'TemplateCache' ) === 'enabled' )
  676. eZContentCacheManager::clearTemplateBlockCache( $objectID, true );
  677. }
  678. /*!
  679. \static
  680. Clears template-block cache and template-block with subtree_expiry parameter caches for specified object
  681. without checking 'TemplateCache' ini setting. If $objectID is \c false all template block caches will be cleared.
  682. */
  683. static function clearTemplateBlockCache( $objectID, $checkViewCacheClassSettings = false )
  684. {
  685. // ordinary template block cache
  686. eZContentObject::expireTemplateBlockCache();
  687. // subtree template block cache
  688. $nodeList = false;
  689. $object = false;
  690. if ( $objectID )
  691. $object = eZContentObject::fetch( $objectID );
  692. if ( $object instanceof eZContentObject )
  693. {
  694. $getAssignedNodes = true;
  695. if ( $checkViewCacheClassSettings )
  696. {
  697. $ini = eZINI::instance('viewcache.ini');
  698. $objectClassIdentifier = $object->attribute('class_identifier');
  699. if ( $ini->hasVariable( $objectClassIdentifier, 'ClearCacheBlocks' )
  700. && $ini->variable( $objectClassIdentifier, 'ClearCacheBlocks' ) === 'disabled' )
  701. {
  702. $getAssignedNodes = false;
  703. }
  704. }
  705. if ( $getAssignedNodes )
  706. {
  707. $nodeList = $object->assignedNodes();
  708. }
  709. }
  710. eZSubtreeCache::cleanup( $nodeList );
  711. }
  712. /*!
  713. \static
  714. Generates the related viewcaches (PreGeneration) for the content object.
  715. It will only do this if [ContentSettings]/PreViewCache in site.ini is enabled.
  716. \param $objectID The ID of the content object to generate caches for.
  717. */
  718. static function generateObjectViewCache( $objectID )
  719. {
  720. // Generate the view cache
  721. $ini = eZINI::instance();
  722. $object = eZContentObject::fetch( $objectID );
  723. $user = eZUser::currentUser();
  724. eZDebug::accumulatorStart( 'generate_cache', '', 'Generating view cache' );
  725. if ( $ini->variable( 'ContentSettings', 'PreViewCache' ) == 'enabled' )
  726. {
  727. $preCacheSiteaccessArray = $ini->variable( 'ContentSettings', 'PreCacheSiteaccessArray' );
  728. $currentSiteAccess = $GLOBALS['eZCurrentAccess'];
  729. // This is the default view parameters for content/view
  730. $viewParameters = array( 'offset' => 0,
  731. 'year' => false,
  732. 'month' => false,
  733. 'day' => false,
  734. 'namefilter' => false );
  735. if ( is_array( $preCacheSiteaccessArray ) && count( $preCacheSiteaccessArray ) > 0 )
  736. {
  737. foreach ( $preCacheSiteaccessArray as $changeToSiteAccess )
  738. {
  739. $newSiteAccess = $currentSiteAccess;
  740. $newSiteAccess['name'] = $changeToSiteAccess;
  741. unset( $newSiteAccess['uri_part'] );//eZSiteAccess::load() will take care of setting correct one
  742. eZSiteAccess::load( $newSiteAccess );
  743. $tpl = eZTemplate::factory();
  744. // Get the sitedesign and cached view preferences for this siteaccess
  745. $siteini = eZINI::instance( 'site.ini');
  746. $cachedViewPreferences = $siteini->variable( 'ContentSettings', 'CachedViewPreferences' );
  747. $language = false; // Needs to be specified if you want to generate the cache for a specific language
  748. $viewMode = 'full';
  749. $assignedNodes = $object->assignedNodes();
  750. foreach ( $assignedNodes as $node )
  751. {
  752. // We want to generate the cache for the specified user
  753. $previewCacheUsers = $ini->variable( 'ContentSettings', 'PreviewCacheUsers' );
  754. foreach ( $previewCacheUsers as $previewCacheUserID )
  755. {
  756. // If the text is 'anon' we need to fetch the Anonymous user ID.
  757. if ( $previewCacheUserID === 'anonymous' )
  758. {
  759. $previewCacheUserID = $siteini->variable( "UserSettings", "AnonymousUserID" );
  760. $previewCacheUser = eZUser::fetch( $previewCacheUserID );
  761. }
  762. else if ( $previewCacheUserID === 'current' )
  763. {
  764. $previewCacheUser = $user;
  765. }
  766. else
  767. {
  768. $previewCacheUser = eZUser::fetch( $previewCacheUserID );
  769. }
  770. if ( !$previewCacheUser )
  771. continue;
  772. // Before we generate the view cache we must change the currently logged in user to $previewCacheUser
  773. // If not the templates might read in wrong personalized data (preferences etc.)
  774. eZUser::setCurrentlyLoggedInUser( $previewCacheUser, $previewCacheUser->attribute( 'contentobject_id' ) );
  775. // Cache the current node
  776. $cacheFileArray = eZNodeviewfunctions::generateViewCacheFile( $previewCacheUser, $node->attribute( 'node_id' ), 0, false, $language, $viewMode, $viewParameters, $cachedViewPreferences );
  777. $tmpRes = eZNodeviewfunctions::generateNodeView( $tpl, $node, $node->attribute( 'object' ), $language, $viewMode, 0, $cacheFileArray['cache_dir'], $cacheFileArray['cache_path'], true, $viewParameters );
  778. // Cache the parent node
  779. $parentNode = $node->attribute( 'parent' );
  780. $objectID = $parentNode->attribute( 'contentobject_id' );
  781. // if parent objectID is null or is 0 we should not create cache.
  782. if ( $objectID )
  783. {
  784. $cacheFileArray = eZNodeviewfunctions::generateViewCacheFile( $previewCacheUser, $parentNode->attribute( 'node_id' ), 0, false, $language, $viewMode, $viewParameters, $cachedViewPreferences );
  785. $tmpRes = eZNodeviewfunctions::generateNodeView( $tpl, $parentNode, $parentNode->attribute( 'object' ), $language, $viewMode, 0, $cacheFileArray['cache_dir'], $cacheFileArray['cache_path'], true, $viewParameters );
  786. }
  787. }
  788. }
  789. }
  790. // Restore the old user as the current one
  791. eZUser::setCurrentlyLoggedInUser( $user, $user->attribute( 'contentobject_id' ) );
  792. // restore siteaccess
  793. eZSiteAccess::load( $currentSiteAccess );
  794. }
  795. }
  796. if ( $ini->variable( 'ContentSettings', 'StaticCache' ) == 'enabled' )
  797. {
  798. $nodes = array();
  799. $ini = eZINI::instance();
  800. $staticCache = new eZStaticCache();
  801. $useURLAlias =& $GLOBALS['eZContentObjectTreeNodeUseURLAlias'];
  802. $pathPrefix = $ini->variable( 'SiteAccessSettings', 'PathPrefix' );
  803. if ( !isset( $useURLAlias ) )
  804. {
  805. $useURLAlias = $ini->variable( 'URLTranslator', 'Translation' ) == 'enabled';
  806. }
  807. eZContentCacheManager::nodeListForObject( $object, true, self::CLEAR_DEFAULT, $nodes, $handledObjectList );
  808. // If no nodes returns it means that ClearCacheMethod = self::CLEAR_NO_CACHE
  809. if ( count( $nodes ) )
  810. {
  811. foreach ( $nodes as $nodeID )
  812. {
  813. if ( $useURLAlias )
  814. {
  815. $oNode = eZContentObjectTreeNode::fetch( $nodeID, false, true );
  816. if ( !isset( $oNode ) )
  817. continue;
  818. $urlAlias = $oNode->urlAlias();
  819. if ( $pathPrefix != '' )
  820. {
  821. $tempAlias = substr( $pathPrefix, strlen( $pathPrefix ) -1 ) == '/'
  822. ? $urlAlias . '/'
  823. : $urlAlias;
  824. if ( strncmp( $tempAlias, $pathPrefix, strlen( $tempAlias) ) == 0 )
  825. $urlAlias = substr( $tempAlias, strlen( $pathPrefix ) );
  826. }
  827. }
  828. else
  829. {
  830. $urlAlias = 'content/view/full/' . $nodeID;
  831. }
  832. $staticCache->cacheURL( '/' . $urlAlias, $nodeID );
  833. }
  834. $staticCache->generateAlwaysUpdatedCache();
  835. }
  836. }
  837. eZDebug::accumulatorStop( 'generate_cache' );
  838. }
  839. /*!
  840. \static
  841. Clears content cache if needed by \a $sectionID
  842. */
  843. static function clearContentCacheIfNeededBySectionID( $sectionID )
  844. {
  845. // fetch all objects of this section
  846. $objectList = eZContentObject::fetchList( false, array( 'section_id' => "$sectionID" ) );
  847. // Clear cache
  848. foreach ( $objectList as $object )
  849. {
  850. eZContentCacheManager::clearContentCacheIfNeeded( $object['id'] );
  851. }
  852. return true;
  853. }
  854. /*!
  855. \static
  856. Clears content cache for specified object: view cache, template-block cache, template-block with subtree_expiry parameter cache.
  857. Checks appropriate ini settings to determine whether caches are enabled or not.
  858. */
  859. static function clearContentCacheIfNeeded( $objectID, $versionNum = true, $additionalNodeList = false )
  860. {
  861. eZDebug::accumulatorStart( 'check_cache', '', 'Check cache' );
  862. eZContentCacheManager::clearObjectViewCacheIfNeeded( $objectID, $versionNum, $additionalNodeList );
  863. eZContentCacheManager::clearTemplateBlockCacheIfNeeded( $objectID );
  864. // Clear cached path strings of content SSL zones.
  865. eZSSLZone::clearCacheIfNeeded();
  866. eZDebug::accumulatorStop( 'check_cache' );
  867. return true;
  868. }
  869. /*!
  870. \static
  871. Clears content cache for specified object: view cache, template-block cache, template-block with subtree_expiry parameter cache
  872. without checking of ini settings.
  873. */
  874. static function clearContentCache( $objectID, $versionNum = true, $additionalNodeList = false )
  875. {
  876. eZDebug::accumulatorStart( 'check_cache', '', 'Check cache' );
  877. eZContentCacheManager::clearObjectViewCache( $objectID, $versionNum, $additionalNodeList );
  878. eZContentCacheManager::clearTemplateBlockCache( $objectID );
  879. // Clear cached path strings of content SSL zones.
  880. eZSSLZone::clearCache();
  881. eZDebug::accumulatorStop( 'check_cache' );
  882. return true;
  883. }
  884. /*!
  885. \static
  886. Clears all content cache: view cache, template-block cache, template-block with subtree_expiry parameter cache.
  887. */
  888. static function clearAllContentCache( $ignoreINISettings = false )
  889. {
  890. if ( !$ignoreINISettings )
  891. {
  892. $ini = eZINI::instance();
  893. $viewCacheEnabled = ( $ini->variable( 'ContentSettings', 'ViewCaching' ) === 'enabled' );
  894. $templateCacheEnabled = ( $ini->variable( 'TemplateSettings', 'TemplateCache' ) === 'enabled' );
  895. }
  896. else
  897. {
  898. $viewCacheEnabled = true;
  899. $templateCacheEnabled = true;
  900. }
  901. if ( $viewCacheEnabled || $templateCacheEnabled )
  902. {
  903. // view cache and/or ordinary template block cache
  904. eZContentObject::expireAllCache();
  905. // subtree template block caches
  906. if ( $templateCacheEnabled )
  907. {
  908. eZSubtreeCache::cleanupAll();
  909. }
  910. }
  911. }
  912. }
  913. ?>