Apache Zeta Components Manual :: File Source for stack.php

Source for file stack.php

Documentation is available at stack.php

  1. <?php
  2. /**
  3.  * File containing the ezcCacheStack class.
  4.  *
  5.  * Licensed to the Apache Software Foundation (ASF) under one
  6.  * or more contributor license agreements.  See the NOTICE file
  7.  * distributed with this work for additional information
  8.  * regarding copyright ownership.  The ASF licenses this file
  9.  * to you under the Apache License, Version 2.0 (the
  10.  * "License"); you may not use this file except in compliance
  11.  * with the License.  You may obtain a copy of the License at
  12.  * 
  13.  *   http://www.apache.org/licenses/LICENSE-2.0
  14.  * 
  15.  * Unless required by applicable law or agreed to in writing,
  16.  * software distributed under the License is distributed on an
  17.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18.  * KIND, either express or implied.  See the License for the
  19.  * specific language governing permissions and limitations
  20.  * under the License.
  21.  *
  22.  * @package Cache
  23.  * @version //autogentag//
  24.  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  25.  * @filesource
  26.  */
  27.  
  28. /**
  29.  * Hierarchical caching class using multiple storages.
  30.  *
  31.  * An instance of this class can be used to achieve so called "hierarchical
  32.  * caching". A cache stack consists of an arbitrary number of cache storages,
  33.  * being sorted from top to bottom. Usually this order reflects the speed of
  34.  * access for the caches: The fastest cache is at the top, the slowest at the
  35.  * bottom. Whenever data is stored in the stack, it is stored in all contained
  36.  * storages. When data is to be restored, the stack will restore it from the
  37.  * highest storage it is found in. Data is removed from a storage, whenever the
  38.  * storage reached a configured number of items, using a {@link }
  39.  * ezcCacheStackReplacementStrategy}.
  40.  *
  41.  * To create a cache stack, multiple storages are necessary. The following
  42.  * examples assume that $storage1, $storage2 and $storage3 contain objects that
  43.  * implement the {@link ezcCacheStackableStorage} interface.
  44.  *
  45.  * The slowest cache should be at the very bottom of the stack, so that items
  46.  * don't need to be restored from it that frequently.
  47.  * <code>
  48.  * <?php
  49.  *  $stack = new ezcCacheStack( 'stack_id' );
  50.  *  $stack->pushStorage(
  51.  *      new ezcCacheStackStorageConfiguration(
  52.  *          'filesystem_cache',
  53.  *          $storage1,
  54.  *          1000000,
  55.  *          .5
  56.  *      )
  57.  *  );
  58.  * ?>
  59.  * </code>
  60.  * This operations create a new cache stack and add $storage1 to its very
  61.  * bottom. The first parameter for {@linke ezcCacheStackStorageConfiguration}
  62.  * are a unique ID for the storage inside the stack, which should never change.
  63.  * The second parameter is the storage object itself. Parameter 3 is the
  64.  * maximum number of items the storage might contain. As soon as this limit ist
  65.  * reached, 500000 items will be purged from the cache. The latter number is
  66.  * defined through the fourth parameter, indicating the fraction of the stored
  67.  * item number, that is to be freed. For freeing, first already outdated items
  68.  * will be purged. If this does not last, more items will be freed using the
  69.  * {@link ezcCacheStackReplacementStrategy} used by the stack.
  70.  *
  71.  * The following code adds the other 2 storages to the stack, where $storage2
  72.  * is a memory storage {@link ezcCacheStackMemoryStorage} and $storage3 is a
  73.  * custom storage implementation, that stores objects in the current requests
  74.  * memory.
  75.  * <code>
  76.  * <?php
  77.  *  $stack->pushStorage(
  78.  *      new ezcCacheStackStorageConfiguration(
  79.  *          'apc_storage',
  80.  *          $storage2,
  81.  *          10000,
  82.  *          .8
  83.  *      )
  84.  *  );
  85.  *  $stack->pushStorage(
  86.  *      new ezcCacheStackStorageConfiguration(
  87.  *          'custom_storage',
  88.  *          $storage3,
  89.  *          50,
  90.  *          .3
  91.  *      )
  92.  *  );
  93.  * ?>
  94.  * </code>
  95.  * The second level of the cache build by $storage2. This storage will contain
  96.  * 10000 items maximum and when it runs full, 8000 items will be deleted from
  97.  * it. The top level of the cache is the custom cache storage, that will only
  98.  * contain 50 objects in the currently processed request. If this storage ever
  99.  * runs full, which should not happen within a request, 15 items will be
  100.  * removed from it.
  101.  *
  102.  * Since the top most storage in this example does not implement {@link }
  103.  * ezcCacheStackMetaDataStorage}, another storage must be defined to be used
  104.  * for storing meta data about the stack:
  105.  * <code>
  106.  * <?php
  107.  *  $stack->options->metaStorage = $storage2;
  108.  *  $stack->options->replacementStrategy = 'ezcCacheStackLfuReplacementStrategy';
  109.  * ?>
  110.  * </code>
  111.  * $storage2 is defined to store the meta information for the stack. In
  112.  * addition, a different replacement strategy than the default {@link }
  113.  * ezcCacheStackLruReplacementStrategy} replacement strategy is defined. LFU
  114.  * removes least frequently used items, while LRU deletes least recently used
  115.  * items from a storage, if it runs full.
  116.  *
  117.  * Using the $bubbleUpOnRestore option you can determine, that data which is
  118.  * restored from a lower storage will be automatically stored in all higher
  119.  * storages again. The problem with this is, that only the attributes that are
  120.  * used for restoring will be assigned to the item in higher storages. In
  121.  * addition, the items will be stored with a fresh TTL. Therefore, this
  122.  * behavior is not recommended.
  123.  *
  124.  * If you want to use a cache stack in combination with {@ezcCacheManager}, you
  125.  * will most likely want to use {@link ezcCacheStackConfigurator}. This allows
  126.  * you to let the stack be configured on the fly, when it is used for the first
  127.  * time in a request.
  128.  *
  129.  * Beware, that whenever you change the structure of your stack or the
  130.  * replacement strategy between 2 requests, you should always perform a
  131.  * complete {@link ezcCacheStack::reset()} on it. Otherwise, your cache data
  132.  * might be seriously broken which will result in undefined behaviour of the
  133.  * stack. Changing the replacement strategy will most possibly result in an
  134.  * {@link ezcCacheInvalidMetaDataException}.
  135.  * 
  136.  * @property ezcCacheStackOptions $options 
  137.  *            Options for the cache stack.
  138.  * @property string $location 
  139.  *            Location of this stack. Unused.
  140.  * @mainclass
  141.  * @package Cache
  142.  * @version //autogentag//
  143.  */
  144. {
  145.     /**
  146.      * Stack of storages.
  147.      * 
  148.      * @var array(int=>ezcCacheStackableStorage) 
  149.      */
  150.     private $storageStack array();
  151.  
  152.     /**
  153.      * Mapping if IDs to storages.
  154.      *
  155.      * Mainly used to validate an ID is not taken twice and a storage is not
  156.      * added twice.
  157.      * 
  158.      * @var array(string=>ezcCacheStackableStorage) 
  159.      */
  160.     private $storageIdMap array();
  161.  
  162.     /**
  163.      * Creates a new cache stack.
  164.      *
  165.      * Usually you will want to use the {@link ezcCacheManager} to take care of
  166.      * your caches. The $options ({@link ezcCacheStackOptions}) stored in the
  167.      * manager and given here can contain a class reference to an
  168.      * implementation of {@link ezcCacheStackConfigurator} that will be used to
  169.      * initially configure the stack instance directly after construction.
  170.      *
  171.      * To perform manual configuration of the stack the {@link }
  172.      * ezcCacheStack::pushStorage()} and {@link ezcCacheStack::popStorage()}
  173.      * methods can be used.
  174.      *
  175.      * The location can be a free form string that identifies the stack
  176.      * uniquely in the {@link ezcCacheManager}. It is currently not used
  177.      * internally in the stack.
  178.      * 
  179.      * @param string $location 
  180.      * @param ezcCacheStackOptions $options 
  181.      */
  182.     public function __construct$locationezcCacheStackOptions $options null )
  183.     {
  184.         if $options === null )
  185.         {
  186.             $options new ezcCacheStackOptions();
  187.         }
  188.  
  189.         $this->properties['location'$location;
  190.         $this->properties['options']  $options;
  191.  
  192.         if $options->configurator !== null )
  193.         {
  194.             // Configure this instance
  195.             call_user_func(
  196.                 array$options->configurator'configure' ),
  197.                 $this
  198.             );
  199.         }
  200.     }
  201.  
  202.     /**
  203.      * Stores data in the cache stack.
  204.      *
  205.      * This method will store the given data across the complete stack. It can
  206.      * afterwards be restored using the {@link ezcCacheStack::restore()} method
  207.      * and be deleted through the {@link ezcCacheStack::delete()} method.
  208.      *
  209.      * The data that can be stored in the cache depends on the configured
  210.      * storages. Usually it is save to store arrays and scalars. However, some
  211.      * caches support objects or do not even support arrays. You need to make
  212.      * sure that the inner caches of the stack *all* support the data you want
  213.      * to store!
  214.      *
  215.      * The $attributes array is optional and can be used to describe the stack
  216.      * further.
  217.      *
  218.      * @param string $id 
  219.      * @param mixed $data 
  220.      * @param array $attributes 
  221.      */
  222.     public function store$id$data$attributes array() )
  223.     {
  224.         $metaStorage $this->getMetaDataStorage();
  225.         $metaStorage->lock();
  226.         
  227.         $metaData $this->getMetaData$metaStorage );
  228.  
  229.         foreach$this->storageStack as $storageConf )
  230.         {
  231.             call_user_func(
  232.                 array(
  233.                     $this->properties['options']->replacementStrategy,
  234.                     'store'
  235.                 ),
  236.                 $storageConf,
  237.                 $metaData,
  238.                 $id,
  239.                 $data,
  240.                 $attributes
  241.             );
  242.         }
  243.  
  244.         $metaStorage->storeMetaData$metaData );
  245.         $metaStorage->unlock();
  246.     }
  247.  
  248.     /**
  249.      * Returns the meta data to use.
  250.      *
  251.      * Returns the meta data to use with the configured {@link }
  252.      * ezcCacheStackStackReplacementStrategy}.
  253.      * 
  254.      * @param ezcCacheStackMetaDataStorage $metaStorage 
  255.      * @return ezcCacheMetaData 
  256.      */
  257.     private function getMetaDataezcCacheStackMetaDataStorage $metaStorage )
  258.     {
  259.         $metaData $metaStorage->restoreMetaData();
  260.         
  261.         if $metaData === null )
  262.         {
  263.             $metaData call_user_func(
  264.                 array(
  265.                     $this->properties['options']->replacementStrategy,
  266.                     'createMetaData'
  267.                 )
  268.             );
  269.         }
  270.  
  271.         return $metaData;
  272.     }
  273.  
  274.     /**
  275.      * Restores an item from the stack.
  276.      *
  277.      * This method tries to restore an item from the cache stack and returns
  278.      * the found data, if any. If no data is found, boolean false is returned.
  279.      * Given the ID of an object will restore exactly the desired object. If
  280.      * additional $attributes are given, this may speed up the restore process
  281.      * for some caches. If only attributes are given, the $search parameter
  282.      * makes sense, since it will instruct the inner cach storages to search
  283.      * their content for the given attribute combination and will restore the
  284.      * first item found. However, this process is much slower then restoring
  285.      * with an ID and therefore is not recommended.
  286.      * 
  287.      * @param string $id 
  288.      * @param array $attributes 
  289.      * @param bool $search 
  290.      * @return mixed The restored data or false on failure.
  291.      */
  292.     public function restore$id$attributes array()$search false )
  293.     {
  294.         $metaStorage $this->getMetaDataStorage();
  295.         $metaStorage->lock();
  296.  
  297.         $metaData $this->getMetaData$metaStorage );
  298.  
  299.         $item false;
  300.         foreach $this->storageStack as $storageConf )
  301.         {
  302.             $item call_user_func(
  303.                 array(
  304.                     $this->properties['options']->replacementStrategy,
  305.                     'restore'
  306.                 ),
  307.                 $storageConf,
  308.                 $metaData,
  309.                 $id,
  310.                 $attributes,
  311.                 $search
  312.             );
  313.             if $item !== false )
  314.             {
  315.                 if $this->properties['options']->bubbleUpOnRestore )
  316.                 {
  317.                     $this->bubbleUp$id$attributes$item$storageConf$metaData );
  318.                 }
  319.                 // Found, so end.
  320.                 break;
  321.             }
  322.         }
  323.  
  324.         $metaStorage->storeMetaData$metaData );
  325.         $metaStorage->unlock();
  326.  
  327.         return $item;
  328.     }
  329.  
  330.     /**
  331.      * Bubbles a restored $item up to all storages above $foundStorageConf.
  332.      * 
  333.      * @param string $id 
  334.      * @param array $attributes 
  335.      * @param mixed $item 
  336.      * @param ezcCacheStackStorageConfiguration $foundStorageConf 
  337.      * @param ezcCacheStackMetaData $metaData 
  338.      */
  339.     private function bubbleUp$idarray $attributes$itemezcCacheStackStorageConfiguration $foundStorageConfezcCacheStackMetaData $metaData )
  340.     {
  341.         foreach$this->storageStack as $storageConf )
  342.         {
  343.             if $storageConf === $foundStorageConf )
  344.             {
  345.                 // This was the storage where we restored
  346.                 break;
  347.             }
  348.             call_user_func(
  349.                 array(
  350.                     $this->properties['options']->replacementStrategy,
  351.                     'store'
  352.                 ),
  353.                 $storageConf,
  354.                 $metaData,
  355.                 $id,
  356.                 $item,
  357.                 $attributes
  358.             );
  359.         }
  360.     }
  361.  
  362.     /**
  363.      * Deletes an item from the stack.
  364.      *
  365.      * This method deletes an item from the cache stack. The item will
  366.      * afterwards no more be stored in any of the inner cache storages. Giving
  367.      * the ID of the cache item will delete exactly 1 desired item. Giving an
  368.      * attribute array, describing the desired item in more detail, can speed
  369.      * up the deletion in some caches.
  370.      *
  371.      * Giving null as the ID and just an attribibute array, with the $search
  372.      * attribute in addition, will delete *all* items that comply to the
  373.      * attributes from all storages. This might be much slower than just
  374.      * deleting a single item.
  375.      *
  376.      * Deleting items from a cache stack is not recommended at all. Instead,
  377.      * items should expire on their own or be overwritten with more actual
  378.      * data.
  379.      *
  380.      * The method returns an array containing all deleted item IDs.
  381.      * 
  382.      * @param string $id 
  383.      * @param array $attributes 
  384.      * @param bool $search 
  385.      * @return array(string) 
  386.      */
  387.     public function delete$id null$attributes array()$search false )
  388.     {
  389.         $metaStorage $this->getMetaDataStorage();
  390.         $metaStorage->lock();
  391.  
  392.         $metaData $metaStorage->restoreMetaData();
  393.  
  394.         $deletedIds array();
  395.         foreach $this->storageStack as $storageConf )
  396.         {
  397.             $deletedIds array_merge(
  398.                 $deletedIds,
  399.                 call_user_func(
  400.                     array(
  401.                         $this->properties['options']->replacementStrategy,
  402.                         'delete'
  403.                     ),
  404.                     $storageConf,
  405.                     $metaData,
  406.                     $id,
  407.                     $attributes,
  408.                     $search
  409.                 )
  410.             );
  411.         }
  412.  
  413.         $metaStorage->storeMetaData$metaData );
  414.         $metaStorage->unlock();
  415.  
  416.         return array_unique$deletedIds );
  417.     }
  418.  
  419.     /**
  420.      * Returns the meta data storage to be used.
  421.      *
  422.      * Determines the meta data storage to be used by the stack and returns it.
  423.      *
  424.      * @return ezcCacheStackMetaData 
  425.      */
  426.     private function getMetaDataStorage()
  427.     {
  428.         $metaStorage $this->options->metaStorage;
  429.         if $metaStorage === null )
  430.         {
  431.             $metaStorage reset$this->storageStack )->storage;
  432.             if !$metaStorage instanceof ezcCacheStackMetaDataStorage ) )
  433.             {
  434.                 throw new ezcBaseValueException(
  435.                     'metaStorage',
  436.                     $metaStorage,
  437.                     'ezcCacheStackMetaDataStorage',
  438.                     'top of storage stack'
  439.                 );
  440.             }
  441.         }
  442.         return $metaStorage;
  443.     }
  444.  
  445.     /**
  446.      * Counts how many items are stored, fulfilling certain criteria.
  447.      *
  448.      * This method counts how many data items fulfilling the given criteria are
  449.      * stored overall. Note: The items of all contained storages are counted
  450.      * independantly and summarized.
  451.      * 
  452.      * @param string $id 
  453.      * @param array $attributes 
  454.      * @return int 
  455.      */
  456.     public function countDataItems$id null$attributes array() )
  457.     {
  458.         $sum 0;
  459.         foreach$this->storageStack as $storageConf )
  460.         {
  461.             $sum += $storageConf->storage->countDataItems$id$attributes );
  462.         }
  463.         return $sum;
  464.     }
  465.  
  466.     /**
  467.      * Returns the remaining lifetime for the given item ID.
  468.      *
  469.      * This method returns the lifetime in seconds for the item identified by $item
  470.      * and optionally described by $attributes. Definining the $attributes
  471.      * might lead to faster results with some caches.
  472.      *
  473.      * The first internal storage that is found for the data item is chosen to
  474.      * detemine the lifetime. If no storage contains the item or the item is
  475.      * outdated in all found caches, 0 is returned.
  476.      * 
  477.      * @param string $id 
  478.      * @param array $attributes 
  479.      * @return int 
  480.      */
  481.     public function getRemainingLifetime$id$attributes array() )
  482.     {
  483.         foreach $this->storageStack as $storageConf )
  484.         {
  485.             $lifetime $storageConf->storage->getRemainingLifetime(
  486.                 $id,
  487.                 $attributes
  488.             );
  489.             if $lifetime )
  490.             {
  491.                 return $lifetime;
  492.             }
  493.         }
  494.         return 0;
  495.     }
  496.  
  497.     /**
  498.      * Add a storage to the top of the stack.
  499.      *
  500.      * This method is used to add a new storage to the top of the cache. The
  501.      * $storageConf of type {@link ezcCacheStackStorageConfiguration} consists
  502.      * of the actual {@link ezcCacheStackableStorage} and other information.
  503.      *
  504.      * Most importantly, the configuration object contains an ID, which must be
  505.      * unique within the whole storage. The itemLimit setting determines how
  506.      * many items might be stored in the storage at all. freeRate determines
  507.      * which fraction of itemLimit will be freed by the {@link }
  508.      * ezcCacheStackStackReplacementStrategy} of the stack, when the storage
  509.      * runs full.
  510.      *
  511.      * @param ezcCacheStackStorageConfiguration $storageConf 
  512.      */
  513.     public function pushStorageezcCacheStackStorageConfiguration $storageConf )
  514.     {
  515.         if isset$this->storageIdMap[$storageConf->id) )
  516.         {
  517.             throw new ezcCacheStackIdAlreadyUsedException(
  518.                 $storageConf->id
  519.             );
  520.         }
  521.         if in_array$storageConf->storage$this->storageIdMaptrue ) )
  522.         {
  523.             throw new ezcCacheStackStorageUsedTwiceException(
  524.                 $storageConf->storage
  525.             );
  526.         }
  527.         array_unshift$this->storageStack$storageConf );
  528.         $this->storageIdMap[$storageConf->id$storageConf->storage;
  529.     }
  530.  
  531.     /**
  532.      * Removes a storage from the top of the stack.
  533.      *
  534.      * This method can be used to remove the top most {@link }
  535.      * ezcCacheStackableStorage} from the stack. This is commonly done to
  536.      * remove caches or to insert new ones into lower positions. In both cases,
  537.      * it is recommended to {@link ezcCacheStack::reset()} the whole cache
  538.      * afterwards to avoid any kind of inconsistency.
  539.      *
  540.      * @return ezcCacheStackStorageConfiguration 
  541.      *
  542.      * @throws ezcCacheStackUnderflowException
  543.      *          if called on an empty stack.
  544.      */
  545.     public function popStorage()
  546.     {
  547.         if count$this->storageStack === )
  548.         {
  549.             throw new ezcCacheStackUnderflowException();
  550.         }
  551.         $storageConf array_shift$this->storageStack );
  552.         unset$this->storageIdMap[$storageConf->id);
  553.         return $storageConf;
  554.     }
  555.  
  556.     /**
  557.      * Returns the number of storages on the stack.
  558.      *
  559.      * Returns the number of storages currently on the stack.
  560.      * 
  561.      * @return int 
  562.      */
  563.     public function countStorages()
  564.     {
  565.         return count$this->storageStack );
  566.     }
  567.  
  568.     /**
  569.      * Returns all stacked storages.
  570.      *
  571.      * This method returns the whole stack of {@link ezcCacheStackableStorage}
  572.      * as an array. This maybe useful to adjust options of the storages after
  573.      * they have been added to the stack. However, it is not recommended to
  574.      * perform any drastical changes to the configurations. Performing manual
  575.      * stores, restores and deletes on the storages is *highly discouraged*,
  576.      * since it may lead to irrepairable inconsistencies of the stack.
  577.      * 
  578.      * @return array(ezcCacheStackableStorage) 
  579.      */
  580.     public function getStorages()
  581.     {
  582.         return $this->storageStack;
  583.     }
  584.  
  585.     /**
  586.      * Resets the complete stack.
  587.      *
  588.      * This method is used to reset the complete stack of storages. It will
  589.      * reset all storages, using {@link ezcCacheStackableStorage::reset()}, and
  590.      * therefore purge the complete content of the stack. In addition, it will
  591.      * kill the complete meta data.
  592.      *
  593.      * The stack is in a consistent, but empty, state afterwards.
  594.      * 
  595.      * @return void 
  596.      */
  597.     public function reset()
  598.     {
  599.         foreach $this->storageStack as $storageConf )
  600.         {
  601.             $storageConf->storage->reset();
  602.         }
  603.     }
  604.  
  605.     /**
  606.      * Validates the $location parameter of the constructor.
  607.      *
  608.      * Returns true, since $location is not necessary for this storage.
  609.      * 
  610.      * @return bool 
  611.      */
  612.     protected function validateLocation()
  613.     {
  614.         // Does not utilize $location
  615.         return true;
  616.     }
  617.  
  618.     /**
  619.      * Sets the options for this stack instance.
  620.      *
  621.      * Overwrites the parent implementation to only allow instances of {@link }
  622.      * ezcCacheStackOptions}.
  623.      * 
  624.      * @param ezcCacheStackOptions $options 
  625.      *
  626.      * @apichange Use $stack->options instead.
  627.      */
  628.     public function setOptions$options )
  629.     {
  630.         // Overloading
  631.         $this->options $options;
  632.     }
  633.     
  634.     /**
  635.      * Property write access.
  636.      * 
  637.      * @param string $propertyName Name of the property.
  638.      * @param mixed $propertyValue  The value for the property.
  639.      *
  640.      * @throws ezcBaseValueException
  641.      *          If the value for the property options is not an instance of
  642.      *          ezcCacheStackOptions.
  643.      * @ignore
  644.      */
  645.     public function __set$propertyName$propertyValue )
  646.     {
  647.         switch $propertyName 
  648.         {
  649.             case 'options':
  650.                 if !$propertyValue instanceof ezcCacheStackOptions ) )
  651.                 {
  652.                     throw new ezcBaseValueException$propertyName$propertyValue'ezcCacheStackOptions' );
  653.                 }
  654.                 break;
  655.             default:
  656.                 parent::__set$propertyName$propertyValue );
  657.                 return;
  658.         }
  659.         $this->properties[$propertyName$propertyValue;
  660.     }
  661. }
  662.  
  663. ?>
Documentation generated by phpDocumentor 1.4.3