PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/modx/core/model/modx/modcachemanager.class.php

https://bitbucket.org/argnist/mohana
PHP | 594 lines | 457 code | 27 blank | 110 comment | 107 complexity | 0237cd157c3bf1cf4f30034817457dd0 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, LGPL-2.1, GPL-2.0, GPL-3.0, LGPL-2.0
  1. <?php
  2. /**
  3. * Contains the xPDOCacheManager implementation for MODX.
  4. * @package modx
  5. */
  6. /**
  7. * The default xPDOCacheManager instance for MODX.
  8. *
  9. * Through this class, MODX provides several types of default, file-based
  10. * caching to reduce load and dependencies on the database, including:
  11. * <ul>
  12. * <li>partial modResource caching, which stores the object properties,
  13. * along with individual modElement cache items</li>
  14. * <li>full caching of modContext and modSystemSetting data</li>
  15. * <li>object-level caching</li>
  16. * <li>db query-level caching</li>
  17. * <li>optional JSON object caching for increased Ajax performance
  18. * possibilities</li>
  19. * </ul>
  20. *
  21. * @package modx
  22. */
  23. class modCacheManager extends xPDOCacheManager {
  24. public $modx= null;
  25. function __construct(& $xpdo, array $options = array()) {
  26. parent :: __construct($xpdo, $options);
  27. $this->modx =& $this->xpdo;
  28. }
  29. /**
  30. * Generates a cache entry for a MODX site Context.
  31. *
  32. * Context cache entries can override site configuration settings and are responsible for
  33. * loading the various listings and maps in the modX class, including resourceMap, aliasMap,
  34. * and eventMap. It can also be used to setup or transform any other modX properties.
  35. *
  36. * @todo Further refactor the generation of aliasMap and resourceMap so it uses less memory/file size.
  37. *
  38. * @param modContext $obj The modContext instance to be cached.
  39. * @param array $options Options for system settings generation.
  40. * @return array An array containing all the context variable values.
  41. */
  42. public function generateContext($key, array $options = array()) {
  43. $results = array();
  44. $obj= $this->modx->getObject('modContext', $key, true);
  45. if (is_object($obj) && $obj instanceof modContext && $obj->get('key')) {
  46. $contextKey = is_object($this->modx->context) ? $this->modx->context->get('key') : $key;
  47. $contextConfig= $this->modx->_systemConfig;
  48. /* generate the ContextSettings */
  49. $results['config']= array();
  50. if ($settings= $obj->getMany('ContextSettings')) {
  51. foreach ($settings as $setting) {
  52. $k= $setting->get('key');
  53. $v= $setting->get('value');
  54. $matches = array();
  55. if (preg_match_all('~\{(.*?)\}~', $v, $matches, PREG_SET_ORDER)) {
  56. foreach ($matches as $match) {
  57. if (array_key_exists("{$match[1]}", $contextConfig)) {
  58. $matchValue= $contextConfig["{$match[1]}"];
  59. } else {
  60. $matchValue= '';
  61. }
  62. $v= str_replace($match[0], $matchValue, $v);
  63. }
  64. }
  65. $results['config'][$k]= $v;
  66. $contextConfig[$k]= $v;
  67. }
  68. }
  69. /* generate the aliasMap and resourceMap */
  70. $tblResource= $this->modx->getTableName('modResource');
  71. $tblContextResource= $this->modx->getTableName('modContextResource');
  72. $resourceFields= array('id','parent','uri');
  73. $resourceCols= $this->modx->getSelectColumns('modResource', 'r', '', $resourceFields);
  74. $bindings= array (
  75. ':context_key1' => array('value' => $obj->get('key'), 'type' => PDO::PARAM_STR)
  76. ,':context_key2' => array('value' => $obj->get('key'), 'type' => PDO::PARAM_STR)
  77. );
  78. $sql = "SELECT {$resourceCols} FROM {$tblResource} r LEFT JOIN {$tblContextResource} cr ON cr.context_key = :context_key1 AND r.id = cr.resource WHERE r.id != r.parent AND (r.context_key = :context_key2 OR cr.context_key IS NOT NULL) AND r.deleted = 0 GROUP BY {$resourceCols}, r.menuindex ORDER BY r.parent ASC, r.menuindex ASC";
  79. $criteria= new xPDOCriteria($this->modx, $sql, $bindings, false);
  80. $collResources = null;
  81. if ($criteria->stmt && $criteria->stmt->execute()) {
  82. $collResources= & $criteria->stmt;
  83. }
  84. $results['resourceMap']= array ();
  85. $results['aliasMap']= array ();
  86. if ($collResources) {
  87. while ($r = $collResources->fetch(PDO::FETCH_OBJ)) {
  88. $results['resourceMap'][(string) $r->parent][] = (string) $r->id;
  89. if ($this->modx->getOption('friendly_urls', $contextConfig, false)) {
  90. if (array_key_exists($r->uri, $results['aliasMap'])) {
  91. $this->modx->log(xPDO::LOG_LEVEL_ERROR, "Resource URI {$r->uri} already exists for resource id = {$results['aliasMap'][$r->uri]}; skipping duplicate resource URI for resource id = {$r->id}");
  92. continue;
  93. }
  94. $results['aliasMap'][$r->uri]= $r->id;
  95. }
  96. }
  97. }
  98. /* generate the eventMap and pluginCache */
  99. $results['eventMap'] = array();
  100. $results['pluginCache'] = array();
  101. $eventMap= $this->modx->getEventMap($obj->get('key'));
  102. if (is_array ($eventMap) && !empty($eventMap)) {
  103. $results['eventMap'] = $eventMap;
  104. $pluginIds= array();
  105. $plugins= array();
  106. $this->modx->loadClass('modScript');
  107. foreach ($eventMap as $pluginKeys) {
  108. foreach ($pluginKeys as $pluginKey) {
  109. if (isset ($pluginIds[$pluginKey])) {
  110. continue;
  111. }
  112. $pluginIds[$pluginKey]= $pluginKey;
  113. }
  114. }
  115. if (!empty($pluginIds)) {
  116. $pluginQuery = $this->modx->newQuery('modPlugin', array('id:IN' => array_keys($pluginIds)), true);
  117. $pluginQuery->select($this->modx->getSelectColumns('modPlugin', 'modPlugin'));
  118. if ($pluginQuery->prepare() && $pluginQuery->stmt->execute()) {
  119. $plugins= $pluginQuery->stmt->fetchAll(PDO::FETCH_ASSOC);
  120. }
  121. }
  122. if (!empty($plugins)) {
  123. foreach ($plugins as $plugin) {
  124. $results['pluginCache'][(string) $plugin['id']]= $plugin;
  125. }
  126. }
  127. }
  128. /* cache the Context ACL policies */
  129. $results['policies'] = $obj->findPolicy($contextKey);
  130. if ($this->getOption('cache_context_settings', $options, true)) {
  131. $options[xPDO::OPT_CACHE_KEY] = $this->getOption('cache_context_settings_key', $options, 'context_settings');
  132. $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption('cache_context_settings_handler', $options, $this->getOption(xPDO::OPT_CACHE_HANDLER, $options));
  133. $options[xPDO::OPT_CACHE_FORMAT] = (integer) $this->getOption('cache_context_settings_format', $options, $this->getOption(xPDO::OPT_CACHE_FORMAT, $options, xPDOCacheManager::CACHE_PHP));
  134. $options[xPDO::OPT_CACHE_ATTEMPTS] = (integer) $this->getOption('cache_context_settings_attempts', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 10));
  135. $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (integer) $this->getOption('cache_context_settings_attempt_delay', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000));
  136. $lifetime = (integer) $this->getOption('cache_context_settings_expires', $options, $this->getOption(xPDO::OPT_CACHE_EXPIRES, $options, 0));
  137. if (!$this->set($obj->getCacheKey(), $results, $lifetime, $options)) {
  138. $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not cache context settings for ' . $obj->get('key') . '.');
  139. }
  140. }
  141. }
  142. return $results;
  143. }
  144. /**
  145. * Generates the system settings cache for a MODX site.
  146. *
  147. * @param array $options Options for system settings generation.
  148. * @return array The generated system settings array.
  149. */
  150. public function generateConfig(array $options = array()) {
  151. $config = array();
  152. if ($collection= $this->modx->getCollection('modSystemSetting')) {
  153. foreach ($collection as $setting) {
  154. $k= $setting->get('key');
  155. $v= $setting->get('value');
  156. $matches= array();
  157. if (preg_match_all('~\{(.*?)\}~', $v, $matches, PREG_SET_ORDER)) {
  158. $matchValue= '';
  159. foreach ($matches as $match) {
  160. if (isset ($this->modx->config["{$match[1]}"])) {
  161. $matchValue= $this->modx->config["{$match[1]}"];
  162. } else {
  163. /* this causes problems with JSON in settings, disabling for now */
  164. //$matchValue= '';
  165. }
  166. if (!empty($matchValue)) {
  167. $v= str_replace($match[0], $matchValue, $v);
  168. }
  169. }
  170. }
  171. $config[$k]= $v;
  172. }
  173. }
  174. if (!empty($config) && $this->getOption('cache_system_settings', $options, true)) {
  175. $options[xPDO::OPT_CACHE_KEY] = $this->getOption('cache_system_settings_key', $options, 'system_settings');
  176. $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption('cache_system_settings_handler', $options, $this->getOption(xPDO::OPT_CACHE_HANDLER, $options));
  177. $options[xPDO::OPT_CACHE_FORMAT] = (integer) $this->getOption('cache_system_settings_format', $options, $this->getOption(xPDO::OPT_CACHE_FORMAT, $options, xPDOCacheManager::CACHE_PHP));
  178. $options[xPDO::OPT_CACHE_ATTEMPTS] = (integer) $this->getOption('cache_system_settings_attempts', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 10));
  179. $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (integer) $this->getOption('cache_system_settings_attempt_delay', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000));
  180. $lifetime = (integer) $this->getOption('cache_system_settings_expires', $options, $this->getOption(xPDO::OPT_CACHE_EXPIRES, $options, 0));
  181. if (!$this->set('config', $config, $lifetime, $options)) {
  182. $this->modx->log(modX::LOG_LEVEL_ERROR, 'Could not cache system settings.');
  183. }
  184. }
  185. return $config;
  186. }
  187. /**
  188. * Generates a cache entry for a Resource or Resource-derived object.
  189. *
  190. * Resource classes can define their own cacheKey.
  191. *
  192. * @param modResource $obj The Resource instance to be cached.
  193. * @param array $options Options for resource generation.
  194. * @return array The generated resource representation.
  195. */
  196. public function generateResource(modResource & $obj, array $options = array()) {
  197. $results= array();
  198. if ($this->getOption('cache_resource', $options, true)) {
  199. if (is_object($obj) && $obj instanceof modResource && $obj->getProcessed() && $obj->get('cacheable') && $obj->get('id')) {
  200. $results['resourceClass']= $obj->_class;
  201. $results['resource']['_processed']= $obj->getProcessed();
  202. $results['resource']= $obj->toArray('', true);
  203. $results['resource']['_content']= $obj->_content;
  204. $results['resource']['_isForward']= $obj->_isForward;
  205. if ($contentType = $obj->getOne('ContentType')) {
  206. $results['contentType']= $contentType->toArray('', true);
  207. }
  208. /* TODO: remove legacy docGroups and cache ABAC policies instead */
  209. if ($docGroups= $obj->getMany('ResourceGroupResources')) {
  210. $groups= array();
  211. foreach ($docGroups as $docGroupPk => $docGroup) {
  212. $groups[(string) $docGroupPk] = $docGroup->toArray('', true);
  213. }
  214. $results['resourceGroups']= $groups;
  215. }
  216. $context = $obj->_contextKey ? $obj->_contextKey : 'web';
  217. $policies = $obj->findPolicy($context);
  218. if (is_array($policies)) {
  219. $results['policyCache']= $policies;
  220. }
  221. if (!empty($this->modx->elementCache)) {
  222. $results['elementCache']= $this->modx->elementCache;
  223. }
  224. if (!empty($this->modx->sourceCache)) {
  225. $results['sourceCache']= $this->modx->sourceCache;
  226. }
  227. if (!empty($obj->_sjscripts)) {
  228. $results['resource']['_sjscripts']= $obj->_sjscripts;
  229. }
  230. if (!empty($obj->_jscripts)) {
  231. $results['resource']['_jscripts']= $obj->_jscripts;
  232. }
  233. if (!empty($obj->_loadedjscripts)) {
  234. $results['resource']['_loadedjscripts']= $obj->_loadedjscripts;
  235. }
  236. }
  237. if (!empty($results)) {
  238. $options[xPDO::OPT_CACHE_KEY] = $this->getOption('cache_resource_key', $options, 'resource');
  239. $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption('cache_resource_handler', $options, $this->getOption(xPDO::OPT_CACHE_HANDLER, $options));
  240. $options[xPDO::OPT_CACHE_FORMAT] = (integer) $this->getOption('cache_resource_format', $options, $this->getOption(xPDO::OPT_CACHE_FORMAT, $options, xPDOCacheManager::CACHE_PHP));
  241. $options[xPDO::OPT_CACHE_ATTEMPTS] = (integer) $this->getOption('cache_resource_attempts', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 1));
  242. $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (integer) $this->getOption('cache_resource_attempt_delay', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000));
  243. $lifetime = (integer) $this->getOption('cache_resource_expires', $options, $this->getOption(xPDO::OPT_CACHE_EXPIRES, $options, 0));
  244. if (!$this->set($obj->getCacheKey(), $results, $lifetime, $options)) {
  245. $this->modx->log(modX::LOG_LEVEL_ERROR, "Could not cache resource " . $obj->get('id'));
  246. }
  247. } else {
  248. $this->modx->log(modX::LOG_LEVEL_ERROR, "Could not retrieve data to cache for resource " . $obj->get('id'));
  249. }
  250. }
  251. return $results;
  252. }
  253. /**
  254. * Generates a lexicon topic cache file from a collection of entries
  255. *
  256. * @access public
  257. * @param string $cacheKey The key to use when caching the lexicon topic.
  258. * @param array $entries An array of key => value pairs of lexicon entries.
  259. * @param array $options An optional array of caching options.
  260. * @return array An array representing the lexicon topic cache.
  261. */
  262. public function generateLexiconTopic($cacheKey, $entries = array(), $options = array()) {
  263. if (!empty($entries) && $this->getOption('cache_lexicon_topics', $options, true)) {
  264. $options[xPDO::OPT_CACHE_KEY] = $this->getOption('cache_lexicon_topics_key', $options, 'lexicon_topics');
  265. $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption('cache_lexicon_topics_handler', $options, $this->getOption(xPDO::OPT_CACHE_HANDLER, $options));
  266. $options[xPDO::OPT_CACHE_FORMAT] = (integer) $this->getOption('cache_lexicon_topics_format', $options, $this->getOption(xPDO::OPT_CACHE_FORMAT, $options, xPDOCacheManager::CACHE_PHP));
  267. $options[xPDO::OPT_CACHE_ATTEMPTS] = (integer) $this->getOption('cache_lexicon_topics_attempts', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 1));
  268. $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (integer) $this->getOption('cache_lexicon_topics_attempt_delay', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000));
  269. $lifetime = (integer) $this->getOption('cache_lexicon_topics_expires', $options, $this->getOption(xPDO::OPT_CACHE_EXPIRES, $options, 0));
  270. if (!$this->set($cacheKey, $entries, $lifetime, $options)) {
  271. $this->modx->log(modX::LOG_LEVEL_ERROR, "Error caching lexicon topic " . $cacheKey);
  272. }
  273. }
  274. return $entries;
  275. }
  276. /**
  277. * Generates a cache file for the manager actions.
  278. *
  279. * @access public
  280. * @param string $cacheKey The key to use when caching the action map.
  281. * @return array An array representing the action map.
  282. */
  283. public function generateActionMap($cacheKey, array $options = array()) {
  284. $results= array();
  285. $c = $this->modx->newQuery('modAction');
  286. $c->select(array(
  287. $this->modx->getSelectColumns('modAction', 'modAction'),
  288. $this->modx->getSelectColumns('modNamespace', 'Namespace', 'namespace_', array('name','path'))
  289. ));
  290. $c->innerJoin('modNamespace','Namespace');
  291. $c->sortby('namespace','ASC');
  292. $c->sortby('controller','ASC');
  293. if ($c->prepare() && $c->stmt->execute()) {
  294. $actions = $c->stmt->fetchAll(PDO::FETCH_ASSOC);
  295. foreach ($actions as $action) {
  296. if (empty($action['namespace_path']) || $action['namespace_name'] == 'core') {
  297. $action['namespace_path'] = $this->modx->getOption('manager_path');
  298. }
  299. if ($action['namespace_name'] != 'core') {
  300. $nsPath = $action['namespace_path'];
  301. if (!empty($nsPath)) {
  302. $nsPath = str_replace(array(
  303. '{core_path}',
  304. '{base_path}',
  305. '{assets_path}',
  306. ),array(
  307. $this->modx->getOption('core_path'),
  308. $this->modx->getOption('base_path'),
  309. $this->modx->getOption('assets_path'),
  310. ),$nsPath);
  311. $action['namespace_path'] = $nsPath;
  312. }
  313. }
  314. $results[$action['id']] = $action;
  315. }
  316. }
  317. if (!empty($results) && $this->getOption('cache_action_map', $options, true)) {
  318. $options[xPDO::OPT_CACHE_KEY] = $this->getOption('cache_action_map_key', $options, 'action_map');
  319. $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption('cache_action_map_handler', $options, $this->getOption(xPDO::OPT_CACHE_HANDLER, $options));
  320. $options[xPDO::OPT_CACHE_FORMAT] = (integer) $this->getOption('cache_action_map_format', $options, $this->getOption(xPDO::OPT_CACHE_FORMAT, $options, xPDOCacheManager::CACHE_PHP));
  321. $options[xPDO::OPT_CACHE_ATTEMPTS] = (integer) $this->getOption('cache_action_map_attempts', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 1));
  322. $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (integer) $this->getOption('cache_action_map_attempt_delay', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000));
  323. $lifetime = (integer) $this->getOption('cache_action_map_expires', $options, $this->getOption(xPDO::OPT_CACHE_EXPIRES, $options, 0));
  324. if (!$this->set($cacheKey, $results, $lifetime, $options)) {
  325. $this->modx->log(modX::LOG_LEVEL_ERROR, "Error caching action map {$cacheKey}");
  326. }
  327. }
  328. return $results;
  329. }
  330. /**
  331. * Generates a file representing an executable modScript function.
  332. *
  333. * @param modScript $objElement A {@link modScript} instance to generate the
  334. * script file for.
  335. * @param string $objContent Optional script content to override the
  336. * persistent instance.
  337. * @param array $options An array of additional options for the operation.
  338. * @return boolean|string The actual generated source content representing the modScript or
  339. * false if the source content could not be generated.
  340. */
  341. public function generateScript(modScript &$objElement, $objContent= null, array $options= array()) {
  342. $results= false;
  343. if (is_object($objElement) && $objElement instanceof modScript) {
  344. $scriptContent= $objElement->getContent(is_string($objContent) ? array('content' => $objContent) : array());
  345. $scriptName= $objElement->getScriptName();
  346. $content = "function {$scriptName}(\$scriptProperties= array()) {\n";
  347. $content .= "global \$modx;\n";
  348. $content .= "if (is_array(\$scriptProperties)) {\n";
  349. $content .= "extract(\$scriptProperties, EXTR_SKIP);\n";
  350. $content .= "}\n";
  351. $content .= $scriptContent . "\n";
  352. $content .= "}\n";
  353. if ($this->getOption('returnFunction', $options, false)) {
  354. return $content;
  355. }
  356. $results = $content;
  357. if ($this->getOption('cache_scripts', $options, true)) {
  358. $options[xPDO::OPT_CACHE_KEY] = $this->getOption('cache_scripts_key', $options, 'scripts');
  359. $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption('cache_scripts_handler', $options, $this->getOption(xPDO::OPT_CACHE_HANDLER, $options));
  360. $options[xPDO::OPT_CACHE_FORMAT] = (integer) $this->getOption('cache_scripts_format', $options, $this->getOption(xPDO::OPT_CACHE_FORMAT, $options, xPDOCacheManager::CACHE_PHP));
  361. $options[xPDO::OPT_CACHE_ATTEMPTS] = (integer) $this->getOption('cache_scripts_attempts', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 1));
  362. $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (integer) $this->getOption('cache_scripts_attempt_delay', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000));
  363. $lifetime = (integer) $this->getOption('cache_scripts_expires', $options, $this->getOption(xPDO::OPT_CACHE_EXPIRES, $options, 0));
  364. if (empty($results) || !$this->set($objElement->getScriptCacheKey(), $results, $lifetime, $options)) {
  365. $this->modx->log(modX::LOG_LEVEL_ERROR, "Error caching script " . $objElement->getScriptCacheKey());
  366. }
  367. }
  368. }
  369. return $results;
  370. }
  371. /**
  372. * Implements MODX cache refresh process, converting cache partitions to cache providers.
  373. */
  374. public function refresh(array $providers = array(), array &$results = array()) {
  375. if (empty($providers)) {
  376. $contexts = array();
  377. $query = $this->xpdo->newQuery('modContext');
  378. $query->select($this->xpdo->escape('key'));
  379. if ($query->prepare() && $query->stmt->execute()) {
  380. $contexts = $query->stmt->fetchAll(PDO::FETCH_COLUMN);
  381. }
  382. $providers = array(
  383. 'auto_publish' => array('contexts' => array_diff($contexts, array('mgr'))),
  384. 'system_settings' => array(),
  385. 'context_settings' => array('contexts' => $contexts),
  386. 'db' => array(),
  387. 'scripts' => array(),
  388. 'default' => array(),
  389. 'resource' => array('contexts' => array_diff($contexts, array('mgr'))),
  390. 'menu' => array(),
  391. 'action_map' => array(),
  392. 'lexicon_topics' => array()
  393. );
  394. }
  395. $cleared = array();
  396. foreach ($providers as $partition => $partOptions) {
  397. $partKey = $this->xpdo->getOption("cache_{$partition}_key", $partOptions, $partition);
  398. if (array_search($partKey, $cleared) !== false) {
  399. $results[$partition] = false;
  400. continue;
  401. }
  402. $partHandler = $this->xpdo->getOption("cache_{$partition}_handler", $partOptions, $this->xpdo->getOption(xPDO::OPT_CACHE_HANDLER));
  403. if (!is_array($partOptions)) $partOptions = array();
  404. $partOptions = array_merge($partOptions, array(xPDO::OPT_CACHE_KEY => $partKey, xPDO::OPT_CACHE_HANDLER => $partHandler));
  405. switch ($partition) {
  406. case 'auto_publish':
  407. $results['auto_publish'] = $this->autoPublish($partOptions);
  408. break;
  409. case 'system_settings':
  410. $results['system_settings'] = ($this->generateConfig($partOptions) ? true : false);
  411. break;
  412. case 'context_settings':
  413. if (array_key_exists('contexts', $partOptions)) {
  414. $contextResults = array();
  415. foreach ($partOptions['contexts'] as $context) {
  416. $contextResults[$context] = ($this->generateContext($context) ? true : false);
  417. }
  418. $results['context_settings'] = $contextResults;
  419. } else {
  420. $results['context_settings'] = false;
  421. }
  422. break;
  423. case 'scripts':
  424. /* clean the configurable source cache and remove the include files */
  425. $results[$partition] = $this->clean($partOptions);
  426. $this->deleteTree($this->getCachePath() . 'includes/');
  427. break;
  428. default:
  429. $results[$partition] = $this->clean($partOptions);
  430. break;
  431. }
  432. $cleared[] = $partKey;
  433. }
  434. return (array_search(false, $results, true) === false);
  435. }
  436. /**
  437. * Check for and process Resources with pub_date or unpub_date set to now or in past.
  438. *
  439. * @todo Implement Context-isolated auto-publishing.
  440. * @param array $options An array of options for the process.
  441. * @return array An array containing published and unpublished Resource counts.
  442. */
  443. public function autoPublish(array $options = array()) {
  444. $publishingResults= array();
  445. /* publish and unpublish resources using pub_date and unpub_date checks */
  446. $tblResource= $this->modx->getTableName('modResource');
  447. $timeNow= time() + $this->modx->getOption('server_offset_time', null, 0);
  448. $publishingResults['published']= $this->modx->exec("UPDATE {$tblResource} SET published=1, publishedon=pub_date, pub_date=0 WHERE published = 0 AND pub_date IS NOT NULL AND pub_date < {$timeNow} AND pub_date > 0");
  449. $publishingResults['unpublished']= $this->modx->exec("UPDATE $tblResource SET published=0, publishedon=0, pub_date=0, unpub_date=0 WHERE published = 1 AND unpub_date IS NOT NULL AND unpub_date < {$timeNow} AND unpub_date > 0");
  450. /* update publish time file */
  451. $timesArr= array ();
  452. $minpub= 0;
  453. $minunpub= 0;
  454. $sql= "SELECT MIN(pub_date) FROM {$tblResource} WHERE pub_date > ?";
  455. $stmt= $this->modx->prepare($sql);
  456. if ($stmt) {
  457. $stmt->bindValue(1, time());
  458. if ($stmt->execute()) {
  459. foreach ($stmt->fetchAll(PDO::FETCH_NUM) as $value) {
  460. $minpub= $value[0];
  461. unset($value);
  462. break;
  463. }
  464. } else {
  465. $publishingResults['errors'][]= $this->modx->lexicon('cache_publish_event_error',array('info' => $stmt->errorInfo()));
  466. }
  467. }
  468. else {
  469. $publishingResults['errors'][]= $this->modx->lexicon('cache_publish_event_error',array('info' => $sql));
  470. }
  471. if ($minpub) $timesArr[]= $minpub;
  472. $sql= "SELECT MIN(unpub_date) FROM {$tblResource} WHERE unpub_date > ?";
  473. $stmt= $this->modx->prepare($sql);
  474. if ($stmt) {
  475. $stmt->bindValue(1, time());
  476. if ($stmt->execute()) {
  477. foreach ($stmt->fetchAll(PDO::FETCH_NUM) as $value) {
  478. $minunpub= $value[0];
  479. unset($value);
  480. break;
  481. }
  482. } else {
  483. $publishingResults['errors'][]= $this->modx->lexicon('cache_unpublish_event_error',array('info' => $stmt->errorInfo()));
  484. }
  485. } else {
  486. $publishingResults['errors'][]= $this->modx->lexicon('cache_unpublish_event_error',array('info' => $sql));
  487. }
  488. if ($minunpub) $timesArr[]= $minunpub;
  489. if (count($timesArr) > 0) {
  490. $nextevent= min($timesArr);
  491. } else {
  492. $nextevent= 0;
  493. }
  494. /* cache the time of the next auto_publish event */
  495. $options[xPDO::OPT_CACHE_KEY] = $this->getOption('cache_auto_publish_key', $options, 'auto_publish');
  496. $options[xPDO::OPT_CACHE_HANDLER] = $this->getOption('cache_auto_publish_handler', $options, $this->getOption(xPDO::OPT_CACHE_HANDLER, $options));
  497. $options[xPDO::OPT_CACHE_ATTEMPTS] = (integer) $this->getOption('cache_auto_publish_attempts', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPTS, $options, 1));
  498. $options[xPDO::OPT_CACHE_ATTEMPT_DELAY] = (integer) $this->getOption('cache_auto_publish_attempt_delay', $options, $this->getOption(xPDO::OPT_CACHE_ATTEMPT_DELAY, $options, 1000));
  499. if (!$this->set('auto_publish', $nextevent, 0, $options)) {
  500. $this->modx->log(modX::LOG_LEVEL_ERROR, "Error caching time of next auto publishing event");
  501. $publishingResults['errors'][]= $this->modx->lexicon('cache_sitepublishing_file_error');
  502. }
  503. return $publishingResults;
  504. }
  505. /**
  506. * Clear part or all of the MODX cache.
  507. *
  508. * @deprecated Use refresh()
  509. * @param array $paths An optional array of paths, relative to the cache
  510. * path, to be deleted.
  511. * @param array $options An optional associative array of cache clearing options: <ul>
  512. * <li><strong>objects</strong>: an array of objects or paths to flush from the db object cache</li>
  513. * <li><strong>extensions</strong>: an array of file extensions to match when deleting the cache directories</li>
  514. * </ul>
  515. */
  516. public function clearCache(array $paths= array(), array $options= array()) {
  517. $results= array();
  518. $delObjs= array();
  519. if ($clearObjects = $this->getOption('objects', $options)) {
  520. $objectOptions = array_merge($options, array('cache_prefix' => $this->getOption('cache_db_prefix', $options, xPDOCacheManager::CACHE_DIR)));
  521. /* clear object cache by key, or * = flush entire object cache */
  522. if (is_array($clearObjects)) {
  523. foreach ($clearObjects as $key) {
  524. if ($this->delete($key, $objectOptions))
  525. $delObjs[]= $key;
  526. }
  527. }
  528. elseif (is_string($clearObjects) && $clearObjects == '*') {
  529. $delObjs= $this->clean($objectOptions);
  530. }
  531. }
  532. $results['deleted_objects']= $delObjs;
  533. $extensions= $this->getOption('extensions', $options, array('.cache.php'));
  534. if (empty($paths)) {
  535. $paths= array('');
  536. }
  537. $delFiles= array();
  538. foreach ($paths as $pathIdx => $path) {
  539. $deleted= false;
  540. $abspath= $this->modx->getOption(xPDO::OPT_CACHE_PATH) . $path;
  541. if (file_exists($abspath)) {
  542. if (is_dir($abspath)) {
  543. $deleted= $this->deleteTree($abspath, array('deleteTop' => false, 'skipDirs' => false, 'extensions' => $extensions));
  544. } else {
  545. if (unlink($abspath)) {
  546. $deleted= array($path);
  547. }
  548. }
  549. if (is_array($deleted))
  550. $delFiles= array_merge($delFiles, $deleted);
  551. }
  552. if ($path == '') break;
  553. }
  554. $results['deleted_files']= $delFiles;
  555. $results['deleted_files_count']= count($delFiles);
  556. if (isset($options['publishing']) && $options['publishing']) {
  557. $results['publishing']= $this->autoPublish($options);
  558. }
  559. /* invoke OnCacheUpdate event */
  560. $this->modx->invokeEvent('OnCacheUpdate', array(
  561. 'results' => $results,
  562. 'paths' => $paths,
  563. 'options' => $options,
  564. ));
  565. return $results;
  566. }
  567. }