Source for file cache.php

Documentation is available at cache.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  Cache
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM'or die;
  11.  
  12. /**
  13.  * Joomla! Cache base object
  14.  *
  15.  * @package     Joomla.Platform
  16.  * @subpackage  Cache
  17.  * @since       11.1
  18.  */
  19. class JCache
  20. {
  21.     /**
  22.      * @var    object  Storage handler
  23.      * @since  11.1
  24.      */
  25.     public static $_handler array();
  26.  
  27.     /**
  28.      * @var    array  Options
  29.      * @since  11.1
  30.      */
  31.     public $_options;
  32.  
  33.     /**
  34.      * Constructor
  35.      *
  36.      * @param   array  $options  options
  37.      *
  38.      * @since   11.1
  39.      */
  40.     public function __construct($options)
  41.     {
  42.         $conf JFactory::getConfig();
  43.  
  44.         $this->_options = array(
  45.             'cachebase' => $conf->get('cache_path'JPATH_CACHE),
  46.             'lifetime' => (int) $conf->get('cachetime'),
  47.             'language' => $conf->get('language''en-GB'),
  48.             'storage' => $conf->get('cache_handler'''),
  49.             'defaultgroup' => 'default',
  50.             'locking' => true,
  51.             'locktime' => 15,
  52.             'checkTime' => true,
  53.             'caching' => ($conf->get('caching'>= 1true false);
  54.  
  55.         // Overwrite default options with given options
  56.         foreach ($options as $option => $value)
  57.         {
  58.             if (isset($options[$option]&& $options[$option!== '')
  59.             {
  60.                 $this->_options[$option$options[$option];
  61.             }
  62.         }
  63.  
  64.         if (empty($this->_options['storage']))
  65.         {
  66.             $this->_options['caching'false;
  67.         }
  68.     }
  69.  
  70.     /**
  71.      * Returns a reference to a cache adapter object, always creating it
  72.      *
  73.      * @param   string  $type     The cache object type to instantiate
  74.      * @param   array   $options  The array of options
  75.      *
  76.      * @return  JCache  A JCache object
  77.      *
  78.      * @since   11.1
  79.      */
  80.     public static function getInstance($type 'output'$options array())
  81.     {
  82.         return JCacheController::getInstance($type$options);
  83.     }
  84.  
  85.     /**
  86.      * Get the storage handlers
  87.      *
  88.      * @return  array    An array of available storage handlers
  89.      *
  90.      * @since   11.1
  91.      */
  92.     public static function getStores()
  93.     {
  94.         $handlers array();
  95.  
  96.         // Get an iterator and loop trough the driver classes.
  97.         $iterator new DirectoryIterator(__DIR__ . '/storage');
  98.  
  99.         foreach ($iterator as $file)
  100.         {
  101.             $fileName $file->getFilename();
  102.  
  103.             // Only load for php files.
  104.             // Note: DirectoryIterator::getExtension only available PHP >= 5.3.6
  105.             if (!$file->isFile()
  106.                 || substr($fileNamestrrpos($fileName'.'1!= 'php'
  107.                 || $fileName == 'helper.php')
  108.             {
  109.                 continue;
  110.             }
  111.  
  112.             // Derive the class name from the type.
  113.             $class str_ireplace('.php''''JCacheStorage' ucfirst(trim($fileName)));
  114.  
  115.             // If the class doesn't exist we have nothing left to do but look at the next type. We did our best.
  116.             if (!class_exists($class))
  117.             {
  118.                 continue;
  119.             }
  120.  
  121.             // Sweet!  Our class exists, so now we just need to know if it passes its test method.
  122.             if ($class::isSupported())
  123.             {
  124.                 // Connector names should not have file extensions.
  125.                 $handlers[str_ireplace('.php'''$fileName);
  126.             }
  127.         }
  128.  
  129.         return $handlers;
  130.     }
  131.  
  132.     /**
  133.      * Set caching enabled state
  134.      *
  135.      * @param   boolean  $enabled  True to enable caching
  136.      *
  137.      * @return  void 
  138.      *
  139.      * @since   11.1
  140.      */
  141.     public function setCaching($enabled)
  142.     {
  143.         $this->_options['caching'$enabled;
  144.     }
  145.  
  146.     /**
  147.      * Get caching state
  148.      *
  149.      * @return  boolean  Caching state
  150.      *
  151.      * @since   11.1
  152.      */
  153.     public function getCaching()
  154.     {
  155.         return $this->_options['caching'];
  156.     }
  157.  
  158.     /**
  159.      * Set cache lifetime
  160.      *
  161.      * @param   integer  $lt  Cache lifetime
  162.      *
  163.      * @return  void 
  164.      *
  165.      * @since   11.1
  166.      */
  167.     public function setLifeTime($lt)
  168.     {
  169.         $this->_options['lifetime'$lt;
  170.     }
  171.  
  172.     /**
  173.      * Get cached data by id and group
  174.      *
  175.      * @param   string  $id     The cache data id
  176.      * @param   string  $group  The cache data group
  177.      *
  178.      * @return  mixed  boolean  False on failure or a cached data string
  179.      *
  180.      * @since   11.1
  181.      */
  182.     public function get($id$group null)
  183.     {
  184.         // Get the default group
  185.         $group ($group$group $this->_options['defaultgroup'];
  186.  
  187.         // Get the storage
  188.         $handler $this->_getStorage();
  189.         if (!($handler instanceof Exception&& $this->_options['caching'])
  190.         {
  191.             return $handler->get($id$group$this->_options['checkTime']);
  192.         }
  193.         return false;
  194.     }
  195.  
  196.     /**
  197.      * Get a list of all cached data
  198.      *
  199.      * @return  mixed    Boolean false on failure or an object with a list of cache groups and data
  200.      *
  201.      * @since   11.1
  202.      */
  203.     public function getAll()
  204.     {
  205.         // Get the storage
  206.         $handler $this->_getStorage();
  207.         if (!($handler instanceof Exception&& $this->_options['caching'])
  208.         {
  209.             return $handler->getAll();
  210.         }
  211.         return false;
  212.     }
  213.  
  214.     /**
  215.      * Store the cached data by id and group
  216.      *
  217.      * @param   mixed   $data   The data to store
  218.      * @param   string  $id     The cache data id
  219.      * @param   string  $group  The cache data group
  220.      *
  221.      * @return  boolean  True if cache stored
  222.      *
  223.      * @since   11.1
  224.      */
  225.     public function store($data$id$group null)
  226.     {
  227.         // Get the default group
  228.         $group ($group$group $this->_options['defaultgroup'];
  229.  
  230.         // Get the storage and store the cached data
  231.         $handler $this->_getStorage();
  232.         if (!($handler instanceof Exception&& $this->_options['caching'])
  233.         {
  234.             $handler->_lifetime $this->_options['lifetime'];
  235.             return $handler->store($id$group$data);
  236.         }
  237.         return false;
  238.     }
  239.  
  240.     /**
  241.      * Remove a cached data entry by id and group
  242.      *
  243.      * @param   string  $id     The cache data id
  244.      * @param   string  $group  The cache data group
  245.      *
  246.      * @return  boolean  True on success, false otherwise
  247.      *
  248.      * @since   11.1
  249.      */
  250.     public function remove($id$group null)
  251.     {
  252.         // Get the default group
  253.         $group ($group$group $this->_options['defaultgroup'];
  254.  
  255.         // Get the storage
  256.         $handler $this->_getStorage();
  257.         if (!($handler instanceof Exception))
  258.         {
  259.             return $handler->remove($id$group);
  260.         }
  261.         return false;
  262.     }
  263.  
  264.     /**
  265.      * Clean cache for a group given a mode.
  266.      *
  267.      * group mode       : cleans all cache in the group
  268.      * notgroup mode    : cleans all cache not in the group
  269.      *
  270.      * @param   string  $group  The cache data group
  271.      * @param   string  $mode   The mode for cleaning cache [group|notgroup]
  272.      *
  273.      * @return  boolean  True on success, false otherwise
  274.      *
  275.      * @since   11.1
  276.      */
  277.     public function clean($group null$mode 'group')
  278.     {
  279.         // Get the default group
  280.         $group ($group$group $this->_options['defaultgroup'];
  281.  
  282.         // Get the storage handler
  283.         $handler $this->_getStorage();
  284.         if (!($handler instanceof Exception))
  285.         {
  286.             return $handler->clean($group$mode);
  287.         }
  288.         return false;
  289.     }
  290.  
  291.     /**
  292.      * Garbage collect expired cache data
  293.      *
  294.      * @return  boolean  True on success, false otherwise.
  295.      *
  296.      * @since   11.1
  297.      */
  298.     public function gc()
  299.     {
  300.         // Get the storage handler
  301.         $handler $this->_getStorage();
  302.         if (!($handler instanceof Exception))
  303.         {
  304.             return $handler->gc();
  305.         }
  306.         return false;
  307.     }
  308.  
  309.     /**
  310.      * Set lock flag on cached item
  311.      *
  312.      * @param   string  $id        The cache data id
  313.      * @param   string  $group     The cache data group
  314.      * @param   string  $locktime  The default locktime for locking the cache.
  315.      *
  316.      * @return  object  Properties are lock and locklooped
  317.      *
  318.      * @since   11.1
  319.      */
  320.     public function lock($id$group null$locktime null)
  321.     {
  322.         $returning new stdClass;
  323.         $returning->locklooped false;
  324.  
  325.         // Get the default group
  326.         $group ($group$group $this->_options['defaultgroup'];
  327.  
  328.         // Get the default locktime
  329.         $locktime ($locktime$locktime $this->_options['locktime'];
  330.  
  331.         // Allow storage handlers to perform locking on their own
  332.         // NOTE drivers with lock need also unlock or unlocking will fail because of false $id
  333.         $handler $this->_getStorage();
  334.         if (!($handler instanceof Exception&& $this->_options['locking'== true && $this->_options['caching'== true)
  335.         {
  336.             $locked $handler->lock($id$group$locktime);
  337.             if ($locked !== false)
  338.             {
  339.                 return $locked;
  340.             }
  341.         }
  342.  
  343.         // Fallback
  344.         $curentlifetime $this->_options['lifetime'];
  345.  
  346.         // Set lifetime to locktime for storing in children
  347.         $this->_options['lifetime'$locktime;
  348.  
  349.         $looptime $locktime 10;
  350.         $id2 $id '_lock';
  351.  
  352.         if ($this->_options['locking'== true && $this->_options['caching'== true)
  353.         {
  354.             $data_lock $this->get($id2$group);
  355.  
  356.         }
  357.         else
  358.         {
  359.             $data_lock false;
  360.             $returning->locked false;
  361.         }
  362.  
  363.         if ($data_lock !== false)
  364.         {
  365.             $lock_counter 0;
  366.  
  367.             // Loop until you find that the lock has been released.
  368.             // That implies that data get from other thread has finished
  369.             while ($data_lock !== false)
  370.             {
  371.  
  372.                 if ($lock_counter $looptime)
  373.                 {
  374.                     $returning->locked false;
  375.                     $returning->locklooped true;
  376.                     break;
  377.                 }
  378.  
  379.                 usleep(100);
  380.                 $data_lock $this->get($id2$group);
  381.                 $lock_counter++;
  382.             }
  383.         }
  384.  
  385.         if ($this->_options['locking'== true && $this->_options['caching'== true)
  386.         {
  387.             $returning->locked $this->store(1$id2$group);
  388.         }
  389.  
  390.         // Revert lifetime to previous one
  391.         $this->_options['lifetime'$curentlifetime;
  392.  
  393.         return $returning;
  394.     }
  395.  
  396.     /**
  397.      * Unset lock flag on cached item
  398.      *
  399.      * @param   string  $id     The cache data id
  400.      * @param   string  $group  The cache data group
  401.      *
  402.      * @return  boolean  True on success, false otherwise.
  403.      *
  404.      * @since   11.1
  405.      */
  406.     public function unlock($id$group null)
  407.     {
  408.         $unlock false;
  409.  
  410.         // Get the default group
  411.         $group ($group$group $this->_options['defaultgroup'];
  412.  
  413.         // Allow handlers to perform unlocking on their own
  414.         $handler $this->_getStorage();
  415.         if (!($handler instanceof Exception&& $this->_options['caching'])
  416.         {
  417.             $unlocked $handler->unlock($id$group);
  418.             if ($unlocked !== false)
  419.             {
  420.                 return $unlocked;
  421.             }
  422.         }
  423.  
  424.         // Fallback
  425.         if ($this->_options['caching'])
  426.         {
  427.             $unlock $this->remove($id '_lock'$group);
  428.         }
  429.  
  430.         return $unlock;
  431.     }
  432.  
  433.     /**
  434.      * Get the cache storage handler
  435.      *
  436.      * @return  JCacheStorage   A JCacheStorage object
  437.      *
  438.      * @since   11.1
  439.      */
  440.     public function &_getStorage()
  441.     {
  442.         $hash md5(serialize($this->_options));
  443.  
  444.         if (isset(self::$_handler[$hash]))
  445.         {
  446.             return self::$_handler[$hash];
  447.         }
  448.  
  449.         self::$_handler[$hashJCacheStorage::getInstance($this->_options['storage']$this->_options);
  450.         return self::$_handler[$hash];
  451.     }
  452.  
  453.     /**
  454.      * Perform workarounds on retrieved cached data
  455.      *
  456.      * @param   string  $data     Cached data
  457.      * @param   array   $options  Array of options
  458.      *
  459.      * @return  string  Body of cached data
  460.      *
  461.      * @since   11.1
  462.      */
  463.     public static function getWorkarounds($data$options array())
  464.     {
  465.         $app JFactory::getApplication();
  466.         $document JFactory::getDocument();
  467.         $body null;
  468.  
  469.         // Get the document head out of the cache.
  470.         if (isset($options['mergehead']&& $options['mergehead'== && isset($data['head']&& !empty($data['head']))
  471.         {
  472.             $document->mergeHeadData($data['head']);
  473.         }
  474.         elseif (isset($data['head']&& method_exists($document'setHeadData'))
  475.         {
  476.             $document->setHeadData($data['head']);
  477.         }
  478.  
  479.         // If the pathway buffer is set in the cache data, get it.
  480.         if (isset($data['pathway']&& is_array($data['pathway']))
  481.         {
  482.             // Push the pathway data into the pathway object.
  483.             $pathway $app->getPathWay();
  484.             $pathway->setPathway($data['pathway']);
  485.         }
  486.  
  487.         // @todo check if the following is needed, seems like it should be in page cache
  488.         // If a module buffer is set in the cache data, get it.
  489.         if (isset($data['module']&& is_array($data['module']))
  490.         {
  491.             // Iterate through the module positions and push them into the document buffer.
  492.             foreach ($data['module'as $name => $contents)
  493.             {
  494.                 $document->setBuffer($contents'module'$name);
  495.             }
  496.         }
  497.  
  498.         // Set cached headers.
  499.         if (isset($data['headers']&& $data['headers'])
  500.         {
  501.             foreach($data['headers'as $header)
  502.             {
  503.                 $app->setHeader($header['name']$header['value']);
  504.             }
  505.         }
  506.  
  507.         // The following code searches for a token in the cached page and replaces it with the
  508.         // proper token.
  509.         if (isset($data['body']))
  510.         {
  511.             $token             JSession::getFormToken();
  512.             $search         '#<input type="hidden" name="[0-9a-f]{32}" value="1" />#';
  513.             $replacement     '<input type="hidden" name="' $token '" value="1" />';
  514.  
  515.             $data['body'preg_replace($search$replacement$data['body']);
  516.             $body $data['body'];
  517.         }
  518.  
  519.         // Get the document body out of the cache.
  520.         return $body;
  521.     }
  522.  
  523.     /**
  524.      * Create workarounded data to be cached
  525.      *
  526.      * @param   string  $data     Cached data
  527.      * @param   array   $options  Array of options
  528.      *
  529.      * @return  string  Data to be cached
  530.      *
  531.      * @since   11.1
  532.      */
  533.     public static function setWorkarounds($data$options array())
  534.     {
  535.         $loptions array(
  536.             'nopathway'     => 0,
  537.             'nohead'         => 0,
  538.             'nomodules'     => 0,
  539.             'modulemode'     => 0,
  540.         );
  541.  
  542.         if (isset($options['nopathway']))
  543.         {
  544.             $loptions['nopathway'$options['nopathway'];
  545.         }
  546.  
  547.         if (isset($options['nohead']))
  548.         {
  549.             $loptions['nohead'$options['nohead'];
  550.         }
  551.  
  552.         if (isset($options['nomodules']))
  553.         {
  554.             $loptions['nomodules'$options['nomodules'];
  555.         }
  556.  
  557.         if (isset($options['modulemode']))
  558.         {
  559.             $loptions['modulemode'$options['modulemode'];
  560.         }
  561.  
  562.         $app JFactory::getApplication();
  563.         $document JFactory::getDocument();
  564.  
  565.         if ($loptions['nomodules'!= 1)
  566.         {
  567.             // Get the modules buffer before component execution.
  568.             $buffer1 $document->getBuffer();
  569.             if (!is_array($buffer1))
  570.             {
  571.                 $buffer1 array();
  572.             }
  573.  
  574.             // Make sure the module buffer is an array.
  575.             if (!isset($buffer1['module']|| !is_array($buffer1['module']))
  576.             {
  577.                 $buffer1['module'array();
  578.             }
  579.         }
  580.  
  581.         // View body data
  582.         $cached['body'$data;
  583.  
  584.         // Document head data
  585.         if ($loptions['nohead'!= && method_exists($document'getHeadData'))
  586.         {
  587.  
  588.             if ($loptions['modulemode'== 1)
  589.             {
  590.                 $headnow $document->getHeadData();
  591.                 $unset array('title''description''link''links''metaTags');
  592.  
  593.                 foreach ($unset as $un)
  594.                 {
  595.                     unset($headnow[$un]);
  596.                     unset($options['headerbefore'][$un]);
  597.                 }
  598.  
  599.                 $cached['head'array();
  600.  
  601.                 // Only store what this module has added
  602.                 foreach ($headnow as $now => $value)
  603.                 {
  604.                     if (isset($options['headerbefore'][$now]))
  605.                     {
  606.                         // We have to serialize the content of the arrays because the may contain other arrays which is a notice in PHP 5.4 and newer
  607.                         $nowvalue array_map('serialize'$headnow[$now]);
  608.                         $beforevalue array_map('serialize'$options['headerbefore'][$now]);
  609.                         $newvalue array_diff_assoc($nowvalue$beforevalue);
  610.                         $newvalue array_map('unserialize'$newvalue);
  611.                     }
  612.                     else
  613.                     {
  614.                         $newvalue $headnow[$now];
  615.                     }
  616.  
  617.                     if (!empty($newvalue))
  618.                     {
  619.                         $cached['head'][$now$newvalue;
  620.                     }
  621.                 }
  622.  
  623.             }
  624.             else
  625.             {
  626.                 $cached['head'$document->getHeadData();
  627.             }
  628.         }
  629.  
  630.         // Pathway data
  631.         if ($app->isSite(&& $loptions['nopathway'!= 1)
  632.         {
  633.             $pathway $app->getPathWay();
  634.             $cached['pathway'= isset($data['pathway']$data['pathway'$pathway->getPathway();
  635.         }
  636.  
  637.         if ($loptions['nomodules'!= 1)
  638.         {
  639.             // @todo Check if the following is needed, seems like it should be in page cache
  640.             // Get the module buffer after component execution.
  641.             $buffer2 $document->getBuffer();
  642.             if (!is_array($buffer2))
  643.             {
  644.                 $buffer2 array();
  645.             }
  646.  
  647.             // Make sure the module buffer is an array.
  648.             if (!isset($buffer2['module']|| !is_array($buffer2['module']))
  649.             {
  650.                 $buffer2['module'array();
  651.             }
  652.  
  653.             // Compare the second module buffer against the first buffer.
  654.             $cached['module'array_diff_assoc($buffer2['module']$buffer1['module']);
  655.         }
  656.  
  657.         // Headers data
  658.         if (isset($options['headers']&& $options['headers'])
  659.         {
  660.             $cached['headers'$app->getHeaders();
  661.         }
  662.  
  663.         return $cached;
  664.     }
  665.  
  666.     /**
  667.      * Create safe id for cached data from url parameters set by plugins and framework
  668.      *
  669.      * @return  string   md5 encoded cacheid
  670.      *
  671.      * @since   11.1
  672.      */
  673.     public static function makeId()
  674.     {
  675.         $app JFactory::getApplication();
  676.  
  677.         // Get url parameters set by plugins
  678.         if (!empty($app->registeredurlparams))
  679.         {
  680.             $registeredurlparams $app->registeredurlparams;
  681.         }
  682.  
  683.         // Platform defaults
  684.         $registeredurlparams->format 'WORD';
  685.         $registeredurlparams->option 'WORD';
  686.         $registeredurlparams->view 'WORD';
  687.         $registeredurlparams->layout 'WORD';
  688.         $registeredurlparams->tpl 'CMD';
  689.         $registeredurlparams->id 'INT';
  690.  
  691.         $safeuriaddon new stdClass;
  692.  
  693.         foreach ($registeredurlparams as $key => $value)
  694.         {
  695.             $safeuriaddon->$key $app->input->get($keynull$value);
  696.         }
  697.  
  698.         return md5(serialize($safeuriaddon));
  699.     }
  700.  
  701.     /**
  702.      * Add a directory where JCache should search for handlers. You may
  703.      * either pass a string or an array of directories.
  704.      *
  705.      * @param   string  $path  A path to search.
  706.      *
  707.      * @return  array   An array with directory elements
  708.      *
  709.      * @since   11.1
  710.      */
  711.     public static function addIncludePath($path '')
  712.     {
  713.         static $paths;
  714.  
  715.         if (!isset($paths))
  716.         {
  717.             $paths array();
  718.         }
  719.         if (!empty($path&& !in_array($path$paths))
  720.         {
  721.             jimport('joomla.filesystem.path');
  722.             array_unshift($pathsJPath::clean($path));
  723.         }
  724.         return $paths;
  725.     }
  726. }

Documentation generated on Tue, 19 Nov 2013 14:54:52 +0100 by phpDocumentor 1.4.3