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

https://github.com/zucchi/zf2 · PHP · 209 lines · 129 code · 19 blank · 61 comment · 14 complexity · b4abd1d2cf83c276091b55ad3201b719 MD5 · raw file

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