3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Cache\Pattern
;
12 use Zend\Cache\Exception
;
13 use Zend\Stdlib\ErrorHandler
;
15 class CaptureCache
extends AbstractPattern
20 * @param string $pageId Page identifier
23 public function start($pageId = null)
25 if ($pageId === null) {
26 $pageId = $this->detectPageId();
30 ob_start(function ($content) use ($that, $pageId) {
31 $that->set($content, $pageId);
33 // http://php.net/manual/function.ob-start.php
34 // -> If output_callback returns FALSE original input is sent to the browser.
38 ob_implicit_flush(false);
42 * Write content to page identity
44 * @param string $content
45 * @param null|string $pageId
46 * @throws Exception\LogicException
48 public function set($content, $pageId = null)
50 $publicDir = $this->getOptions()->getPublicDir();
51 if ($publicDir === null) {
52 throw new Exception\
LogicException("Option 'public_dir' no set");
55 if ($pageId === null) {
56 $pageId = $this->detectPageId();
59 $path = $this->pageId2Path($pageId);
60 $file = $path . DIRECTORY_SEPARATOR
. $this->pageId2Filename($pageId);
62 $this->createDirectoryStructure($publicDir . DIRECTORY_SEPARATOR
. $path);
63 $this->putFileContent($publicDir . DIRECTORY_SEPARATOR
. $file, $content);
69 * @param null|string $pageId
71 * @throws Exception\LogicException
72 * @throws Exception\RuntimeException
74 public function get($pageId = null)
76 $publicDir = $this->getOptions()->getPublicDir();
77 if ($publicDir === null) {
78 throw new Exception\
LogicException("Option 'public_dir' no set");
81 if ($pageId === null) {
82 $pageId = $this->detectPageId();
86 . DIRECTORY_SEPARATOR
. $this->pageId2Path($pageId)
87 . DIRECTORY_SEPARATOR
. $this->pageId2Filename($pageId);
89 if (file_exists($file)) {
90 ErrorHandler
::start();
91 $content = file_get_contents($file);
92 $error = ErrorHandler
::stop();
93 if ($content === false) {
94 throw new Exception\
RuntimeException(
95 "Failed to read cached pageId '{$pageId}'", 0, $error
103 * Checks if a cache with given id exists
105 * @param null|string $pageId
106 * @throws Exception\LogicException
109 public function has($pageId = null)
111 $publicDir = $this->getOptions()->getPublicDir();
112 if ($publicDir === null) {
113 throw new Exception\
LogicException("Option 'public_dir' no set");
116 if ($pageId === null) {
117 $pageId = $this->detectPageId();
121 . DIRECTORY_SEPARATOR
. $this->pageId2Path($pageId)
122 . DIRECTORY_SEPARATOR
. $this->pageId2Filename($pageId);
124 return file_exists($file);
130 * @param null|string $pageId
131 * @throws Exception\LogicException
132 * @throws Exception\RuntimeException
135 public function remove($pageId = null)
137 $publicDir = $this->getOptions()->getPublicDir();
138 if ($publicDir === null) {
139 throw new Exception\
LogicException("Option 'public_dir' no set");
142 if ($pageId === null) {
143 $pageId = $this->detectPageId();
147 . DIRECTORY_SEPARATOR
. $this->pageId2Path($pageId)
148 . DIRECTORY_SEPARATOR
. $this->pageId2Filename($pageId);
150 if (file_exists($file)) {
151 ErrorHandler
::start();
152 $res = unlink($file);
153 $err = ErrorHandler
::stop();
155 throw new Exception\
RuntimeException(
156 "Failed to remove cached pageId '{$pageId}'", 0, $err
166 * Clear cached pages matching glob pattern
168 * @param string $pattern
169 * @throws Exception\LogicException
171 public function clearByGlob($pattern = '**')
173 $publicDir = $this->getOptions()->getPublicDir();
174 if ($publicDir === null) {
175 throw new Exception\
LogicException("Option 'public_dir' no set");
178 $it = new \
GlobIterator(
179 $publicDir . '/' . $pattern,
180 \GlobIterator
::CURRENT_AS_SELF | \GlobIterator
::SKIP_DOTS | \GlobIterator
::UNIX_PATHS
182 foreach ($it as $pathname => $entry) {
183 if ($entry->isFile()) {
190 * Determine the page to save from the request
192 * @throws Exception\RuntimeException
195 protected function detectPageId()
197 if (!isset($_SERVER['REQUEST_URI'])) {
198 throw new Exception\
RuntimeException("Can't auto-detect current page identity");
201 return $_SERVER['REQUEST_URI'];
205 * Get filename for page id
207 * @param string $pageId
210 protected function pageId2Filename($pageId)
212 if (substr($pageId, -1) === '/') {
213 return $this->getOptions()->getIndexFilename();
216 return basename($pageId);
220 * Get path for page id
222 * @param string $pageId
225 protected function pageId2Path($pageId)
227 if (substr($pageId, -1) == '/') {
228 $path = rtrim($pageId, '/');
230 $path = dirname($pageId);
233 // convert requested "/" to the valid local directory separator
234 if ('/' != DIRECTORY_SEPARATOR
) {
235 $path = str_replace('/', DIRECTORY_SEPARATOR
, $path);
242 * Write content to a file
244 * @param string $file File complete path
245 * @param string $data Data to write
247 * @throws Exception\RuntimeException
249 protected function putFileContent($file, $data)
251 $options = $this->getOptions();
252 $locking = $options->getFileLocking();
253 $perm = $options->getFilePermission();
254 $umask = $options->getUmask();
255 if ($umask !== false && $perm !== false) {
256 $perm = $perm & ~
$umask;
259 ErrorHandler
::start();
261 $umask = ($umask !== false) ?
umask($umask) : false;
262 $rs = file_put_contents($file, $data, $locking ? LOCK_EX
: 0);
268 $err = ErrorHandler
::stop();
269 throw new Exception\
RuntimeException(
270 "Error writing file '{$file}'", 0, $err
274 if ($perm !== false && !chmod($file, $perm)) {
275 $oct = decoct($perm);
276 $err = ErrorHandler
::stop();
277 throw new Exception\
RuntimeException("chmod('{$file}', 0{$oct}) failed", 0, $err);
280 ErrorHandler
::stop();
284 * Creates directory if not already done.
286 * @param string $pathname
288 * @throws Exception\RuntimeException
290 protected function createDirectoryStructure($pathname)
292 // Directory structure already exists
293 if (file_exists($pathname)) {
297 $options = $this->getOptions();
298 $perm = $options->getDirPermission();
299 $umask = $options->getUmask();
300 if ($umask !== false && $perm !== false) {
301 $perm = $perm & ~
$umask;
304 ErrorHandler
::start();
306 if ($perm === false) {
307 // build-in mkdir function is enough
309 $umask = ($umask !== false) ?
umask($umask) : false;
310 $res = mkdir($pathname, ($perm !== false) ?
$perm : 0777, true);
312 if ($umask !== false) {
317 $oct = ($perm === false) ?
'777' : decoct($perm);
318 $err = ErrorHandler
::stop();
319 throw new Exception\
RuntimeException(
320 "mkdir('{$pathname}', 0{$oct}, true) failed", 0, $err
324 if ($perm !== false && !chmod($pathname, $perm)) {
325 $oct = decoct($perm);
326 $err = ErrorHandler
::stop();
327 throw new Exception\
RuntimeException(
328 "chmod('{$pathname}', 0{$oct}) failed", 0, $err
333 // build-in mkdir function sets permission together with current umask
334 // which doesn't work well on multo threaded webservers
335 // -> create directories one by one and set permissions
337 // find existing path and missing path parts
340 while (!file_exists($path)) {
341 array_unshift($parts, basename($path));
342 $nextPath = dirname($path);
343 if ($nextPath === $path) {
349 // make all missing path parts
350 foreach ($parts as $part) {
351 $path.= DIRECTORY_SEPARATOR
. $part;
353 // create a single directory, set and reset umask immediately
354 $umask = ($umask !== false) ?
umask($umask) : false;
355 $res = mkdir($path, ($perm === false) ?
0777 : $perm, false);
356 if ($umask !== false) {
361 $oct = ($perm === false) ?
'777' : decoct($perm);
362 $err = ErrorHandler
::stop();
363 throw new Exception\
RuntimeException(
364 "mkdir('{$path}', 0{$oct}, false) failed"
368 if ($perm !== false && !chmod($path, $perm)) {
369 $oct = decoct($perm);
370 $err = ErrorHandler
::stop();
371 throw new Exception\
RuntimeException(
372 "chmod('{$path}', 0{$oct}) failed"
378 ErrorHandler
::stop();
382 * Returns the generated file name.
384 * @param null|string $pageId
387 public function getFilename($pageId = null)
389 if ($pageId === null) {
390 $pageId = $this->detectPageId();
393 $publicDir = $this->getOptions()->getPublicDir();
394 $path = $this->pageId2Path($pageId);
395 $file = $path . DIRECTORY_SEPARATOR
. $this->pageId2Filename($pageId);
397 return $publicDir . $file;