3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 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.
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("Failed to read cached pageId '{$pageId}'", 0, $error);
101 * Checks if a cache with given id exists
103 * @param null|string $pageId
104 * @throws Exception\LogicException
107 public function has($pageId = null)
109 $publicDir = $this->getOptions()->getPublicDir();
110 if ($publicDir === null) {
111 throw new Exception\
LogicException("Option 'public_dir' no set");
114 if ($pageId === null) {
115 $pageId = $this->detectPageId();
119 . DIRECTORY_SEPARATOR
. $this->pageId2Path($pageId)
120 . DIRECTORY_SEPARATOR
. $this->pageId2Filename($pageId);
122 return file_exists($file);
128 * @param null|string $pageId
129 * @throws Exception\LogicException
130 * @throws Exception\RuntimeException
133 public function remove($pageId = null)
135 $publicDir = $this->getOptions()->getPublicDir();
136 if ($publicDir === null) {
137 throw new Exception\
LogicException("Option 'public_dir' no set");
140 if ($pageId === null) {
141 $pageId = $this->detectPageId();
145 . DIRECTORY_SEPARATOR
. $this->pageId2Path($pageId)
146 . DIRECTORY_SEPARATOR
. $this->pageId2Filename($pageId);
148 if (file_exists($file)) {
149 ErrorHandler
::start();
150 $res = unlink($file);
151 $err = ErrorHandler
::stop();
153 throw new Exception\
RuntimeException("Failed to remove cached pageId '{$pageId}'", 0, $err);
162 * Clear cached pages matching glob pattern
164 * @param string $pattern
165 * @throws Exception\LogicException
167 public function clearByGlob($pattern = '**')
169 $publicDir = $this->getOptions()->getPublicDir();
170 if ($publicDir === null) {
171 throw new Exception\
LogicException("Option 'public_dir' no set");
174 $it = new \
GlobIterator(
175 $publicDir . '/' . $pattern,
176 \GlobIterator
::CURRENT_AS_SELF | \GlobIterator
::SKIP_DOTS | \GlobIterator
::UNIX_PATHS
178 foreach ($it as $pathname => $entry) {
179 if ($entry->isFile()) {
186 * Determine the page to save from the request
188 * @throws Exception\RuntimeException
191 protected function detectPageId()
193 if (!isset($_SERVER['REQUEST_URI'])) {
194 throw new Exception\
RuntimeException("Can't auto-detect current page identity");
197 return $_SERVER['REQUEST_URI'];
201 * Get filename for page id
203 * @param string $pageId
206 protected function pageId2Filename($pageId)
208 if (substr($pageId, -1) === '/') {
209 return $this->getOptions()->getIndexFilename();
212 return basename($pageId);
216 * Get path for page id
218 * @param string $pageId
221 protected function pageId2Path($pageId)
223 if (substr($pageId, -1) == '/') {
224 $path = rtrim($pageId, '/');
226 $path = dirname($pageId);
229 // convert requested "/" to the valid local directory separator
230 if ('/' != DIRECTORY_SEPARATOR
) {
231 $path = str_replace('/', DIRECTORY_SEPARATOR
, $path);
238 * Write content to a file
240 * @param string $file File complete path
241 * @param string $data Data to write
243 * @throws Exception\RuntimeException
245 protected function putFileContent($file, $data)
247 $options = $this->getOptions();
248 $locking = $options->getFileLocking();
249 $perm = $options->getFilePermission();
250 $umask = $options->getUmask();
251 if ($umask !== false && $perm !== false) {
252 $perm = $perm & ~
$umask;
255 ErrorHandler
::start();
257 $umask = ($umask !== false) ?
umask($umask) : false;
258 $rs = file_put_contents($file, $data, $locking ? LOCK_EX
: 0);
264 $err = ErrorHandler
::stop();
265 throw new Exception\
RuntimeException("Error writing file '{$file}'", 0, $err);
268 if ($perm !== false && !chmod($file, $perm)) {
269 $oct = decoct($perm);
270 $err = ErrorHandler
::stop();
271 throw new Exception\
RuntimeException("chmod('{$file}', 0{$oct}) failed", 0, $err);
274 ErrorHandler
::stop();
278 * Creates directory if not already done.
280 * @param string $pathname
282 * @throws Exception\RuntimeException
284 protected function createDirectoryStructure($pathname)
286 // Directory structure already exists
287 if (file_exists($pathname)) {
291 $options = $this->getOptions();
292 $perm = $options->getDirPermission();
293 $umask = $options->getUmask();
294 if ($umask !== false && $perm !== false) {
295 $perm = $perm & ~
$umask;
298 ErrorHandler
::start();
300 if ($perm === false) {
301 // build-in mkdir function is enough
303 $umask = ($umask !== false) ?
umask($umask) : false;
304 $res = mkdir($pathname, ($perm !== false) ?
$perm : 0775, true);
306 if ($umask !== false) {
311 $oct = ($perm === false) ?
'775' : decoct($perm);
312 $err = ErrorHandler
::stop();
313 throw new Exception\
RuntimeException("mkdir('{$pathname}', 0{$oct}, true) failed", 0, $err);
316 if ($perm !== false && !chmod($pathname, $perm)) {
317 $oct = decoct($perm);
318 $err = ErrorHandler
::stop();
319 throw new Exception\
RuntimeException("chmod('{$pathname}', 0{$oct}) failed", 0, $err);
322 // build-in mkdir function sets permission together with current umask
323 // which doesn't work well on multo threaded webservers
324 // -> create directories one by one and set permissions
326 // find existing path and missing path parts
329 while (!file_exists($path)) {
330 array_unshift($parts, basename($path));
331 $nextPath = dirname($path);
332 if ($nextPath === $path) {
338 // make all missing path parts
339 foreach ($parts as $part) {
340 $path.= DIRECTORY_SEPARATOR
. $part;
342 // create a single directory, set and reset umask immediately
343 $umask = ($umask !== false) ?
umask($umask) : false;
344 $res = mkdir($path, ($perm === false) ?
0775 : $perm, false);
345 if ($umask !== false) {
350 $oct = ($perm === false) ?
'775' : decoct($perm);
351 ErrorHandler
::stop();
352 throw new Exception\
RuntimeException(
353 "mkdir('{$path}', 0{$oct}, false) failed"
357 if ($perm !== false && !chmod($path, $perm)) {
358 $oct = decoct($perm);
359 ErrorHandler
::stop();
360 throw new Exception\
RuntimeException(
361 "chmod('{$path}', 0{$oct}) failed"
367 ErrorHandler
::stop();
371 * Returns the generated file name.
373 * @param null|string $pageId
376 public function getFilename($pageId = null)
378 if ($pageId === null) {
379 $pageId = $this->detectPageId();
382 $publicDir = $this->getOptions()->getPublicDir();
383 $path = $this->pageId2Path($pageId);
384 $file = $path . DIRECTORY_SEPARATOR
. $this->pageId2Filename($pageId);
386 return $publicDir . $file;