/library/Zend/Cache/Pattern/CallbackCache.php

https://github.com/jtai/zf2 · PHP · 223 lines · 119 code · 21 blank · 83 comment · 17 complexity · edeb85e718de98369477512ce706def6 MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Cache
  17. * @subpackage Pattern
  18. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. namespace Zend\Cache\Pattern;
  22. use Zend\Cache\Exception,
  23. Zend\Cache\StorageFactory,
  24. Zend\Cache\Storage\Adapter as StorageAdapter;
  25. /**
  26. * @category Zend
  27. * @package Zend_Cache
  28. * @subpackage Pattern
  29. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  30. * @license http://framework.zend.com/license/new-bsd New BSD License
  31. */
  32. class CallbackCache extends AbstractPattern
  33. {
  34. /**
  35. * Set options
  36. *
  37. * @param PatternOptions $options
  38. * @return CallbackCache
  39. * @throws Exception\InvalidArgumentException if missing storage option
  40. */
  41. public function setOptions(PatternOptions $options)
  42. {
  43. parent::setOptions($options);
  44. if (!$options->getStorage()) {
  45. throw new Exception\InvalidArgumentException("Missing option 'storage'");
  46. }
  47. return $this;
  48. }
  49. /**
  50. * Call the specified callback or get the result from cache
  51. *
  52. * @param callback $callback A valid callback
  53. * @param array $args Callback arguments
  54. * @param array $options Options
  55. * @return mixed Result
  56. * @throws Exception
  57. */
  58. public function call($callback, array $args = array(), array $options = array())
  59. {
  60. $classOptions = $this->getOptions();
  61. $key = $this->_generateKey($callback, $args, $options);
  62. if ( ($rs = $classOptions->getStorage()->getItem($key, $options)) !== false ) {
  63. if (!isset($rs[0])) {
  64. throw new Exception\RuntimeException("Invalid cached data for key '{$key}'");
  65. }
  66. echo isset($rs[1]) ? $rs[1] : '';
  67. return $rs[0];
  68. }
  69. if ( ($cacheOutput = $classOptions->getCacheOutput()) ) {
  70. ob_start();
  71. ob_implicit_flush(false);
  72. }
  73. // TODO: do not cache on errors using [set|restore]_error_handler
  74. try {
  75. if ($args) {
  76. $ret = call_user_func_array($callback, $args);
  77. } else {
  78. $ret = call_user_func($callback);
  79. }
  80. } catch (\Exception $e) {
  81. if ($cacheOutput) {
  82. ob_end_flush();
  83. }
  84. throw $e;
  85. }
  86. if ($cacheOutput) {
  87. $data = array($ret, ob_get_flush());
  88. } else {
  89. $data = array($ret);
  90. }
  91. $classOptions->getStorage()->setItem($key, $data, $options);
  92. return $ret;
  93. }
  94. /**
  95. * function call handler
  96. *
  97. * @param string $function Function name to call
  98. * @param array $args Function arguments
  99. * @return mixed
  100. * @throws Exception
  101. */
  102. public function __call($function, array $args)
  103. {
  104. return $this->call($function, $args);
  105. }
  106. /**
  107. * Generate a unique key in base of a key representing the callback part
  108. * and a key representing the arguments part merged using md5($callbackKey.$argumentsKey).
  109. *
  110. * Options:
  111. * callback_key A string representing the callback part of the key
  112. * or NULL to autogenerate the callback key part
  113. * argument_key A string representing the arguments part of the key
  114. * or NULL to autogenerate the arguments key part
  115. *
  116. * @param callback $callback A valid callback
  117. * @param array $args Callback arguments
  118. * @param array $options Options
  119. * @return string
  120. * @throws Exception
  121. */
  122. public function generateKey($callback, array $args = array(), array $options = array())
  123. {
  124. return $this->_generateKey($callback, $args, $options);
  125. }
  126. /**
  127. * Generate a unique key in base of a key representing the callback part
  128. * and a key representing the arguments part merged using md5($callbackKey.$argumentsKey).
  129. *
  130. * @param callback $callback A valid callback
  131. * @param array $args Callback arguments
  132. * @param array $options Options
  133. * @return string
  134. * @throws Exception\InvalidArgumentException
  135. * @throws Exception\RuntimeException
  136. */
  137. protected function _generateKey($callback, array $args, array $options)
  138. {
  139. $callbackKey = '';
  140. $argumentKey = '';
  141. // generate callback key part
  142. if (isset($options['callback_key'])) {
  143. $callbackKey = (string) $options['callback_key'];
  144. if (!is_callable($callback, false)) {
  145. throw new Exception\InvalidArgumentException('Invalid callback');
  146. }
  147. } else {
  148. if (!is_callable($callback, false, $callbackKey)) {
  149. throw new Exception\InvalidArgumentException('Invalid callback');
  150. }
  151. // functions, methods and classnames are case-insensitive
  152. $callbackKey = strtolower($callbackKey);
  153. // generate a unique key of object callbacks
  154. if (is_object($callback)) { // Closures & __invoke
  155. $object = $callback;
  156. } elseif (isset($callback[0])) { // array($object, 'method')
  157. $object = $callback[0];
  158. }
  159. if (isset($object)) {
  160. try {
  161. $serializedObject = @serialize($object);
  162. } catch (\Exception $e) {
  163. throw new Exception\RuntimeException(
  164. "Can't serialize callback: see previous exception"
  165. , 0, $e);
  166. }
  167. if (!$serializedObject) {
  168. $lastErr = error_get_last();
  169. throw new Exception\RuntimeException(
  170. "Can't serialize callback: "
  171. . $lastErr['message']
  172. );
  173. }
  174. $callbackKey.= $serializedObject;
  175. }
  176. }
  177. // generate argument key part
  178. if (isset($options['argument_key'])) {
  179. $argumentKey = (string)$options['argument_key'];
  180. } elseif ($args) {
  181. try {
  182. $serializedArgs = @serialize(array_values($args));
  183. } catch (\Exception $e) {
  184. throw new Exception\RuntimeException(
  185. "Can't serialize arguments: see previous exception"
  186. , 0, $e);
  187. }
  188. if (!$serializedArgs) {
  189. $lastErr = error_get_last();
  190. throw new Exception\RuntimeException(
  191. "Can't serialize arguments: "
  192. . $lastErr['message']
  193. );
  194. }
  195. $argumentKey = $serializedArgs;
  196. }
  197. // merge and return the key parts
  198. return md5($callbackKey.$argumentKey);
  199. }
  200. }