1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26:
27: 28: 29: 30:
31:
32: class Mage_Core_Model_Cache
33: {
34: const DEFAULT_LIFETIME = 7200;
35: const OPTIONS_CACHE_ID = 'core_cache_options';
36: const INVALIDATED_TYPES = 'core_cache_invalidate';
37: const XML_PATH_TYPES = 'global/cache/types';
38:
39: 40: 41:
42: protected $_idPrefix = '';
43:
44: 45: 46: 47: 48:
49: protected $_frontend = null;
50:
51: 52: 53: 54: 55:
56: protected $_shmBackends = array(
57: 'apc', 'memcached', 'xcache',
58: 'zendserver_shmem', 'zendserver_disk', 'varien_eaccelerator',
59: );
60:
61: 62: 63: 64: 65:
66: protected $_defaultBackend = 'File';
67:
68: 69: 70: 71: 72:
73: protected $_defaultBackendOptions = array(
74: 'hashed_directory_level' => 1,
75: 'hashed_directory_umask' => 0777,
76: 'file_name_prefix' => 'mage',
77: );
78:
79: 80: 81: 82: 83:
84: protected $_requestProcessors = array();
85:
86: 87: 88: 89: 90:
91: protected $_disallowSave = false;
92:
93: 94: 95: 96: 97:
98: protected $_allowedCacheOptions = null;
99:
100: 101: 102: 103: 104:
105: public function __construct(array $options = array())
106: {
107: $this->_defaultBackendOptions['cache_dir'] = Mage::getBaseDir('cache');
108: 109: 110:
111: $this->_idPrefix = isset($options['id_prefix']) ? $options['id_prefix'] : '';
112: if (!$this->_idPrefix && isset($options['prefix'])) {
113: $this->_idPrefix = $options['prefix'];
114: }
115: if (empty($this->_idPrefix)) {
116: $this->_idPrefix = substr(md5(Mage::getConfig()->getOptions()->getEtcDir()), 0, 3).'_';
117: }
118:
119: $backend = $this->_getBackendOptions($options);
120: $frontend = $this->_getFrontendOptions($options);
121:
122: $this->_frontend = Zend_Cache::factory('Varien_Cache_Core', $backend['type'], $frontend, $backend['options'],
123: true, true, true
124: );
125:
126: if (isset($options['request_processors'])) {
127: $this->_requestProcessors = $options['request_processors'];
128: }
129:
130: if (isset($options['disallow_save'])) {
131: $this->_disallowSave = $options['disallow_save'];
132: }
133: }
134:
135: 136: 137: 138: 139: 140:
141: protected function _getBackendOptions(array $cacheOptions)
142: {
143: $enable2levels = false;
144: $type = isset($cacheOptions['backend']) ? $cacheOptions['backend'] : $this->_defaultBackend;
145: if (isset($cacheOptions['backend_options']) && is_array($cacheOptions['backend_options'])) {
146: $options = $cacheOptions['backend_options'];
147: } else {
148: $options = array();
149: }
150:
151: $backendType = false;
152: switch (strtolower($type)) {
153: case 'sqlite':
154: if (extension_loaded('sqlite') && isset($options['cache_db_complete_path'])) {
155: $backendType = 'Sqlite';
156: }
157: break;
158: case 'memcached':
159: if (extension_loaded('memcached')) {
160: if (isset($cacheOptions['memcached'])) {
161: $options = $cacheOptions['memcached'];
162: }
163: $enable2levels = true;
164: $backendType = 'Libmemcached';
165: } elseif (extension_loaded('memcache')) {
166: if (isset($cacheOptions['memcached'])) {
167: $options = $cacheOptions['memcached'];
168: }
169: $enable2levels = true;
170: $backendType = 'Memcached';
171: }
172: break;
173: case 'apc':
174: if (extension_loaded('apc') && ini_get('apc.enabled')) {
175: $enable2levels = true;
176: $backendType = 'Apc';
177: }
178: break;
179: case 'xcache':
180: if (extension_loaded('xcache')) {
181: $enable2levels = true;
182: $backendType = 'Xcache';
183: }
184: break;
185: case 'eaccelerator':
186: case 'varien_cache_backend_eaccelerator':
187: if (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) {
188: $enable2levels = true;
189: $backendType = 'Varien_Cache_Backend_Eaccelerator';
190: }
191: break;
192: case 'database':
193: $backendType = 'Varien_Cache_Backend_Database';
194: $options = $this->getDbAdapterOptions();
195: break;
196: default:
197: if ($type != $this->_defaultBackend) {
198: try {
199: if (class_exists($type, true)) {
200: $implements = class_implements($type, true);
201: if (in_array('Zend_Cache_Backend_Interface', $implements)) {
202: $backendType = $type;
203: }
204: }
205: } catch (Exception $e) {
206: }
207: }
208: }
209:
210: if (!$backendType) {
211: $backendType = $this->_defaultBackend;
212: foreach ($this->_defaultBackendOptions as $option => $value) {
213: if (!array_key_exists($option, $options)) {
214: $options[$option] = $value;
215: }
216: }
217: }
218:
219: $backendOptions = array('type' => $backendType, 'options' => $options);
220: if ($enable2levels) {
221: $backendOptions = $this->_getTwoLevelsBackendOptions($backendOptions, $cacheOptions);
222: }
223: return $backendOptions;
224: }
225:
226: 227: 228: 229: 230:
231: protected function getDbAdapterOptions()
232: {
233: $options['adapter_callback'] = array($this, 'getDbAdapter');
234: $options['data_table'] = Mage::getSingleton('core/resource')->getTableName('core/cache');
235: $options['tags_table'] = Mage::getSingleton('core/resource')->getTableName('core/cache_tag');
236: return $options;
237: }
238:
239: 240: 241: 242: 243: 244: 245:
246: protected function _getTwoLevelsBackendOptions($fastOptions, $cacheOptions)
247: {
248: $options = array();
249: $options['fast_backend'] = $fastOptions['type'];
250: $options['fast_backend_options'] = $fastOptions['options'];
251: $options['fast_backend_custom_naming'] = true;
252: $options['fast_backend_autoload'] = true;
253: $options['slow_backend_custom_naming'] = true;
254: $options['slow_backend_autoload'] = true;
255:
256: if (isset($cacheOptions['auto_refresh_fast_cache'])) {
257: $options['auto_refresh_fast_cache'] = (bool)$cacheOptions['auto_refresh_fast_cache'];
258: } else {
259: $options['auto_refresh_fast_cache'] = false;
260: }
261: if (isset($cacheOptions['slow_backend'])) {
262: $options['slow_backend'] = $cacheOptions['slow_backend'];
263: } else {
264: $options['slow_backend'] = $this->_defaultBackend;
265: }
266: if (isset($cacheOptions['slow_backend_options'])) {
267: $options['slow_backend_options'] = $cacheOptions['slow_backend_options'];
268: } else {
269: $options['slow_backend_options'] = $this->_defaultBackendOptions;
270: }
271: if ($options['slow_backend'] == 'database') {
272: $options['slow_backend'] = 'Varien_Cache_Backend_Database';
273: $options['slow_backend_options'] = $this->getDbAdapterOptions();
274: if (isset($cacheOptions['slow_backend_store_data'])) {
275: $options['slow_backend_options']['store_data'] = (bool)$cacheOptions['slow_backend_store_data'];
276: } else {
277: $options['slow_backend_options']['store_data'] = false;
278: }
279: }
280:
281: $backend = array(
282: 'type' => 'TwoLevels',
283: 'options' => $options
284: );
285: return $backend;
286: }
287:
288: 289: 290: 291: 292: 293:
294: protected function _getFrontendOptions(array $cacheOptions)
295: {
296: $options = isset($cacheOptions['frontend_options']) ? $cacheOptions['frontend_options'] : array();
297: if (!array_key_exists('caching', $options)) {
298: $options['caching'] = true;
299: }
300: if (!array_key_exists('lifetime', $options)) {
301: $options['lifetime'] = isset($cacheOptions['lifetime']) ? $cacheOptions['lifetime']
302: : self::DEFAULT_LIFETIME;
303: }
304: if (!array_key_exists('automatic_cleaning_factor', $options)) {
305: $options['automatic_cleaning_factor'] = 0;
306: }
307: $options['cache_id_prefix'] = $this->_idPrefix;
308: return $options;
309: }
310:
311: 312: 313: 314: 315: 316:
317: protected function _id($id)
318: {
319: if ($id) {
320: $id = strtoupper($id);
321: }
322: return $id;
323: }
324:
325: 326: 327: 328: 329: 330:
331: protected function _tags($tags = array())
332: {
333: foreach ($tags as $key => $value) {
334: $tags[$key] = $this->_id($value);
335: }
336: return $tags;
337: }
338:
339: 340: 341: 342: 343:
344: public function getFrontend()
345: {
346: return $this->_frontend;
347: }
348:
349: 350: 351: 352: 353: 354:
355: public function load($id)
356: {
357: return $this->_frontend->load($this->_id($id));
358: }
359:
360: 361: 362: 363: 364: 365: 366: 367: 368:
369: public function save($data, $id, $tags=array(), $lifeTime=null)
370: {
371: 372: 373:
374: if (!in_array(Mage_Core_Model_Config::CACHE_TAG, $tags)) {
375: $tags[] = Mage_Core_Model_App::CACHE_TAG;
376: }
377: if ($this->_disallowSave) {
378: return true;
379: }
380: return $this->_frontend->save((string)$data, $this->_id($id), $this->_tags($tags), $lifeTime);
381: }
382:
383: 384: 385: 386: 387: 388:
389: public function remove($id)
390: {
391: return $this->_frontend->remove($this->_id($id));
392: }
393:
394: 395: 396: 397: 398: 399:
400: public function clean($tags=array())
401: {
402: $mode = Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG;
403: if (!empty($tags)) {
404: if (!is_array($tags)) {
405: $tags = array($tags);
406: }
407: $res = $this->_frontend->clean($mode, $this->_tags($tags));
408: } else {
409: $res = $this->_frontend->clean($mode, array(Mage_Core_Model_App::CACHE_TAG));
410: $res = $res && $this->_frontend->clean($mode, array(Mage_Core_Model_Config::CACHE_TAG));
411: }
412: return $res;
413: }
414:
415: 416: 417: 418: 419:
420: public function flush()
421: {
422: $res = $this->_frontend->clean();
423: return $res;
424: }
425:
426: 427: 428: 429: 430:
431: public function getDbAdapter()
432: {
433: return Mage::getSingleton('core/resource')->getConnection('core_write');
434: }
435:
436: 437: 438: 439: 440:
441: protected function _getResource()
442: {
443: return Mage::getResourceSingleton('core/cache');
444: }
445:
446: 447: 448: 449: 450:
451: protected function _initOptions()
452: {
453: $options = $this->load(self::OPTIONS_CACHE_ID);
454: if ($options === false) {
455: $options = $this->_getResource()->getAllOptions();
456: if (is_array($options)) {
457: $this->_allowedCacheOptions = $options;
458: $this->save(serialize($this->_allowedCacheOptions), self::OPTIONS_CACHE_ID);
459: } else {
460: $this->_allowedCacheOptions = array();
461: }
462: } else {
463: $this->_allowedCacheOptions = unserialize($options);
464: }
465:
466: if (Mage::getConfig()->getOptions()->getData('global_ban_use_cache')) {
467: foreach ($this->_allowedCacheOptions as $key => $val) {
468: $this->_allowedCacheOptions[$key] = false;
469: }
470: }
471:
472: return $this;
473: }
474:
475: 476: 477: 478: 479: 480:
481: public function saveOptions($options)
482: {
483: $this->remove(self::OPTIONS_CACHE_ID);
484: $options = $this->_getResource()->saveAllOptions($options);
485: return $this;
486: }
487:
488: 489: 490: 491: 492: 493:
494: public function canUse($typeCode)
495: {
496: if (is_null($this->_allowedCacheOptions)) {
497: $this->_initOptions();
498: }
499:
500: if (empty($typeCode)) {
501: return $this->_allowedCacheOptions;
502: } else {
503: if (isset($this->_allowedCacheOptions[$typeCode])) {
504: return (bool)$this->_allowedCacheOptions[$typeCode];
505: } else {
506: return false;
507: }
508: }
509: }
510:
511: 512: 513: 514: 515:
516: public function banUse($typeCode)
517: {
518: $this->_allowedCacheOptions[$typeCode] = false;
519: return $this;
520: }
521:
522: 523: 524: 525: 526: 527:
528: public function getTagsByType($type)
529: {
530: $path = self::XML_PATH_TYPES.'/'.$type.'/tags';
531: $tagsConfig = Mage::getConfig()->getNode($path);
532: if ($tagsConfig) {
533: $tags = (string) $tagsConfig;
534: $tags = explode(',', $tags);
535: } else {
536: $tags = false;
537: }
538: return $tags;
539: }
540:
541: 542: 543: 544: 545:
546: public function getTypes()
547: {
548: $types = array();
549: $config = Mage::getConfig()->getNode(self::XML_PATH_TYPES);
550: if ($config) {
551: foreach ($config->children() as $type=>$node) {
552: $types[$type] = new Varien_Object(array(
553: 'id' => $type,
554: 'cache_type' => Mage::helper('core')->__((string)$node->label),
555: 'description' => Mage::helper('core')->__((string)$node->description),
556: 'tags' => strtoupper((string) $node->tags),
557: 'status' => (int)$this->canUse($type),
558: ));
559: }
560: }
561: return $types;
562: }
563:
564: 565: 566: 567: 568:
569: protected function _getInvalidatedTypes()
570: {
571: $types = $this->load(self::INVALIDATED_TYPES);
572: if ($types) {
573: $types = unserialize($types);
574: } else {
575: $types = array();
576: }
577: return $types;
578: }
579:
580: 581: 582: 583: 584: 585:
586: protected function _saveInvalidatedTypes($types)
587: {
588: $this->save(serialize($types), self::INVALIDATED_TYPES);
589: return $this;
590: }
591:
592: 593: 594: 595: 596:
597: public function getInvalidatedTypes()
598: {
599: $invalidatedTypes = array();
600: $types = $this->_getInvalidatedTypes();
601: if ($types) {
602: $allTypes = $this->getTypes();
603: foreach ($types as $type => $flag) {
604: if (isset($allTypes[$type]) && $this->canUse($type)) {
605: $invalidatedTypes[$type] = $allTypes[$type];
606: }
607: }
608: }
609: return $invalidatedTypes;
610: }
611:
612: 613: 614: 615: 616: 617:
618: public function invalidateType($typeCode)
619: {
620: $types = $this->_getInvalidatedTypes();
621: if (!is_array($typeCode)) {
622: $typeCode = array($typeCode);
623: }
624: foreach ($typeCode as $code) {
625: $types[$code] = 1;
626: }
627: $this->_saveInvalidatedTypes($types);
628: return $this;
629: }
630:
631: 632: 633: 634: 635: 636:
637: public function cleanType($typeCode)
638: {
639: $tags = $this->getTagsByType($typeCode);
640: $this->clean($tags);
641:
642: $types = $this->_getInvalidatedTypes();
643: unset($types[$typeCode]);
644: $this->_saveInvalidatedTypes($types);
645: return $this;
646: }
647:
648: 649: 650: 651: 652:
653: public function processRequest()
654: {
655: if (empty($this->_requestProcessors)) {
656: return false;
657: }
658:
659: $content = false;
660: foreach ($this->_requestProcessors as $processor) {
661: $processor = $this->_getProcessor($processor);
662: if ($processor) {
663: $content = $processor->extractContent($content);
664: }
665: }
666:
667: if ($content) {
668: Mage::app()->getResponse()->appendBody($content);
669: return true;
670: }
671: return false;
672: }
673:
674: 675: 676:
677: protected function _getProcessor($processor)
678: {
679: $processor = new $processor;
680: return $processor;
681: }
682: }
683: