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-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\Cache\Pattern; 11 12use Traversable; 13use Zend\Cache\Exception; 14use Zend\Cache\StorageFactory; 15use Zend\Cache\Storage\StorageInterface as Storage; 16use Zend\Stdlib\AbstractOptions; 17 18class PatternOptions extends AbstractOptions 19{ 20 /** 21 * Used by: 22 * - ClassCache 23 * - ObjectCache 24 * @var bool 25 */ 26 protected $cacheByDefault = true; 27 28 /** 29 * Used by: 30 * - CallbackCache 31 * - ClassCache 32 * - ObjectCache 33 * @var bool 34 */ 35 protected $cacheOutput = true; 36 37 /** 38 * Used by: 39 * - ClassCache 40 * @var null|string 41 */ 42 protected $class; 43 44 /** 45 * Used by: 46 * - ClassCache 47 * @var array 48 */ 49 protected $classCacheMethods = array(); 50 51 /** 52 * Used by: 53 * - ClassCache 54 * @var array 55 */ 56 protected $classNonCacheMethods = array(); 57 58 /** 59 * Used by: 60 * - CaptureCache 61 * @var false|int 62 */ 63 protected $umask = false; 64 65 /** 66 * Used by: 67 * - CaptureCache 68 * @var false|int 69 */ 70 protected $dirPermission = 0700; 71 72 /** 73 * Used by: 74 * - CaptureCache 75 * @var false|int 76 */ 77 protected $filePermission = 0600; 78 79 /** 80 * Used by: 81 * - CaptureCache 82 * @var bool 83 */ 84 protected $fileLocking = true; 85 86 /** 87 * Used by: 88 * - CaptureCache 89 * @var string 90 */ 91 protected $indexFilename = 'index.html'; 92 93 /** 94 * Used by: 95 * - ObjectCache 96 * @var null|object 97 */ 98 protected $object; 99 100 /** 101 * Used by: 102 * - ObjectCache 103 * @var bool 104 */ 105 protected $objectCacheMagicProperties = false; 106 107 /** 108 * Used by: 109 * - ObjectCache 110 * @var array 111 */ 112 protected $objectCacheMethods = array(); 113 114 /** 115 * Used by: 116 * - ObjectCache 117 * @var null|string 118 */ 119 protected $objectKey; 120 121 /** 122 * Used by: 123 * - ObjectCache 124 * @var array 125 */ 126 protected $objectNonCacheMethods = array('__tostring'); 127 128 /** 129 * Used by: 130 * - CaptureCache 131 * @var null|string 132 */ 133 protected $publicDir; 134 135 /** 136 * Used by: 137 * - CallbackCache 138 * - ClassCache 139 * - ObjectCache 140 * - OutputCache 141 * @var null|Storage 142 */ 143 protected $storage; 144 145 /** 146 * Constructor 147 * 148 * @param array|Traversable|null $options 149 * @return PatternOptions 150 * @throws Exception\InvalidArgumentException 151 */ 152 public function __construct($options = null) 153 { 154 // disable file/directory permissions by default on windows systems 155 if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { 156 $this->filePermission = false; 157 $this->dirPermission = false; 158 } 159 160 parent::__construct($options); 161 } 162 163 /** 164 * Set flag indicating whether or not to cache by default 165 * 166 * Used by: 167 * - ClassCache 168 * - ObjectCache 169 * 170 * @param bool $cacheByDefault 171 * @return PatternOptions 172 */ 173 public function setCacheByDefault($cacheByDefault) 174 { 175 $this->cacheByDefault = $cacheByDefault; 176 return $this; 177 } 178 179 /** 180 * Do we cache by default? 181 * 182 * Used by: 183 * - ClassCache 184 * - ObjectCache 185 * 186 * @return bool 187 */ 188 public function getCacheByDefault() 189 { 190 return $this->cacheByDefault; 191 } 192 193 /** 194 * Set whether or not to cache output 195 * 196 * Used by: 197 * - CallbackCache 198 * - ClassCache 199 * - ObjectCache 200 * 201 * @param bool $cacheOutput 202 * @return PatternOptions 203 */ 204 public function setCacheOutput($cacheOutput) 205 { 206 $this->cacheOutput = (bool) $cacheOutput; 207 return $this; 208 } 209 210 /** 211 * Will we cache output? 212 * 213 * Used by: 214 * - CallbackCache 215 * - ClassCache 216 * - ObjectCache 217 * 218 * @return bool 219 */ 220 public function getCacheOutput() 221 { 222 return $this->cacheOutput; 223 } 224 225 /** 226 * Set class name 227 * 228 * Used by: 229 * - ClassCache 230 * 231 * @param string $class 232 * @throws Exception\InvalidArgumentException 233 * @return PatternOptions 234 */ 235 public function setClass($class) 236 { 237 if (!is_string($class)) { 238 throw new Exception\InvalidArgumentException('Invalid classname provided; must be a string'); 239 } 240 $this->class = $class; 241 return $this; 242 } 243 244 /** 245 * Get class name 246 * 247 * Used by: 248 * - ClassCache 249 * 250 * @return null|string 251 */ 252 public function getClass() 253 { 254 return $this->class; 255 } 256 257 /** 258 * Set list of method return values to cache 259 * 260 * Used by: 261 * - ClassCache 262 * 263 * @param array $classCacheMethods 264 * @return PatternOptions 265 */ 266 public function setClassCacheMethods(array $classCacheMethods) 267 { 268 $this->classCacheMethods = $this->recursiveStrtolower($classCacheMethods); 269 return $this; 270 } 271 272 /** 273 * Get list of methods from which to cache return values 274 * 275 * Used by: 276 * - ClassCache 277 * 278 * @return array 279 */ 280 public function getClassCacheMethods() 281 { 282 return $this->classCacheMethods; 283 } 284 285 /** 286 * Set list of method return values NOT to cache 287 * 288 * Used by: 289 * - ClassCache 290 * 291 * @param array $classNonCacheMethods 292 * @return PatternOptions 293 */ 294 public function setClassNonCacheMethods(array $classNonCacheMethods) 295 { 296 $this->classNonCacheMethods = $this->recursiveStrtolower($classNonCacheMethods); 297 return $this; 298 } 299 300 /** 301 * Get list of methods from which NOT to cache return values 302 * 303 * Used by: 304 * - ClassCache 305 * 306 * @return array 307 */ 308 public function getClassNonCacheMethods() 309 { 310 return $this->classNonCacheMethods; 311 } 312 313 /** 314 * Set directory permission 315 * 316 * @param false|int $dirPermission 317 * @throws Exception\InvalidArgumentException 318 * @return PatternOptions 319 */ 320 public function setDirPermission($dirPermission) 321 { 322 if ($dirPermission !== false) { 323 if (is_string($dirPermission)) { 324 $dirPermission = octdec($dirPermission); 325 } else { 326 $dirPermission = (int) $dirPermission; 327 } 328 329 // validate 330 if (($dirPermission & 0700) != 0700) { 331 throw new Exception\InvalidArgumentException( 332 'Invalid directory permission: need permission to execute, read and write by owner' 333 ); 334 } 335 } 336 337 $this->dirPermission = $dirPermission; 338 return $this; 339 } 340 341 /** 342 * Gets directory permission 343 * 344 * @return false|int 345 */ 346 public function getDirPermission() 347 { 348 return $this->dirPermission; 349 } 350 351 /** 352 * Set umask 353 * 354 * Used by: 355 * - CaptureCache 356 * 357 * @param false|int $umask 358 * @throws Exception\InvalidArgumentException 359 * @return PatternOptions 360 */ 361 public function setUmask($umask) 362 { 363 if ($umask !== false) { 364 if (is_string($umask)) { 365 $umask = octdec($umask); 366 } else { 367 $umask = (int) $umask; 368 } 369 370 // validate 371 if ($umask & 0700) { 372 throw new Exception\InvalidArgumentException( 373 'Invalid umask: need permission to execute, read and write by owner' 374 ); 375 } 376 377 // normalize 378 $umask = $umask & ~0002; 379 } 380 381 $this->umask = $umask; 382 return $this; 383 } 384 385 /** 386 * Get umask 387 * 388 * Used by: 389 * - CaptureCache 390 * 391 * @return false|int 392 */ 393 public function getUmask() 394 { 395 return $this->umask; 396 } 397 398 /** 399 * Set whether or not file locking should be used 400 * 401 * Used by: 402 * - CaptureCache 403 * 404 * @param bool $fileLocking 405 * @return PatternOptions 406 */ 407 public function setFileLocking($fileLocking) 408 { 409 $this->fileLocking = (bool) $fileLocking; 410 return $this; 411 } 412 413 /** 414 * Is file locking enabled? 415 * 416 * Used by: 417 * - CaptureCache 418 * 419 * @return bool 420 */ 421 public function getFileLocking() 422 { 423 return $this->fileLocking; 424 } 425 426 /** 427 * Set file permission 428 * 429 * @param false|int $filePermission 430 * @throws Exception\InvalidArgumentException 431 * @return PatternOptions 432 */ 433 public function setFilePermission($filePermission) 434 { 435 if ($filePermission !== false) { 436 if (is_string($filePermission)) { 437 $filePermission = octdec($filePermission); 438 } else { 439 $filePermission = (int) $filePermission; 440 } 441 442 // validate 443 if (($filePermission & 0600) != 0600) { 444 throw new Exception\InvalidArgumentException( 445 'Invalid file permission: need permission to read and write by owner' 446 ); 447 } elseif ($filePermission & 0111) { 448 throw new Exception\InvalidArgumentException( 449 "Invalid file permission: Files shoudn't be executable" 450 ); 451 } 452 } 453 454 $this->filePermission = $filePermission; 455 return $this; 456 } 457 458 /** 459 * Gets file permission 460 * 461 * @return false|int 462 */ 463 public function getFilePermission() 464 { 465 return $this->filePermission; 466 } 467 468 /** 469 * Set value for index filename 470 * 471 * @param string $indexFilename 472 * @return PatternOptions 473 */ 474 public function setIndexFilename($indexFilename) 475 { 476 $this->indexFilename = (string) $indexFilename; 477 return $this; 478 } 479 480 /** 481 * Get value for index filename 482 * 483 * @return string 484 */ 485 public function getIndexFilename() 486 { 487 return $this->indexFilename; 488 } 489 490 /** 491 * Set object to cache 492 * 493 * @param mixed $object 494 * @throws Exception\InvalidArgumentException 495 * @return PatternOptions 496 */ 497 public function setObject($object) 498 { 499 if (!is_object($object)) { 500 throw new Exception\InvalidArgumentException( 501 sprintf('%s expects an object; received "%s"', __METHOD__, gettype($object)) 502 ); 503 } 504 $this->object = $object; 505 return $this; 506 } 507 508 /** 509 * Get object to cache 510 * 511 * @return null|object 512 */ 513 public function getObject() 514 { 515 return $this->object; 516 } 517 518 /** 519 * Set flag indicating whether or not to cache magic properties 520 * 521 * Used by: 522 * - ObjectCache 523 * 524 * @param bool $objectCacheMagicProperties 525 * @return PatternOptions 526 */ 527 public function setObjectCacheMagicProperties($objectCacheMagicProperties) 528 { 529 $this->objectCacheMagicProperties = (bool) $objectCacheMagicProperties; 530 return $this; 531 } 532 533 /** 534 * Should we cache magic properties? 535 * 536 * Used by: 537 * - ObjectCache 538 * 539 * @return bool 540 */ 541 public function getObjectCacheMagicProperties() 542 { 543 return $this->objectCacheMagicProperties; 544 } 545 546 /** 547 * Set list of object methods for which to cache return values 548 * 549 * @param array $objectCacheMethods 550 * @return PatternOptions 551 * @throws Exception\InvalidArgumentException 552 */ 553 public function setObjectCacheMethods(array $objectCacheMethods) 554 { 555 $this->objectCacheMethods = $this->normalizeObjectMethods($objectCacheMethods); 556 return $this; 557 } 558 559 /** 560 * Get list of object methods for which to cache return values 561 * 562 * @return array 563 */ 564 public function getObjectCacheMethods() 565 { 566 return $this->objectCacheMethods; 567 } 568 569 /** 570 * Set the object key part. 571 * 572 * Used to generate a callback key in order to speed up key generation. 573 * 574 * Used by: 575 * - ObjectCache 576 * 577 * @param null|string $objectKey The object key or NULL to use the objects class name 578 * @return PatternOptions 579 */ 580 public function setObjectKey($objectKey) 581 { 582 if ($objectKey !== null) { 583 $this->objectKey = (string) $objectKey; 584 } else { 585 $this->objectKey = null; 586 } 587 return $this; 588 } 589 590 /** 591 * Get object key 592 * 593 * Used by: 594 * - ObjectCache 595 * 596 * @return string 597 */ 598 public function getObjectKey() 599 { 600 if ($this->objectKey === null) { 601 return get_class($this->getObject()); 602 } 603 return $this->objectKey; 604 } 605 606 /** 607 * Set list of object methods for which NOT to cache return values 608 * 609 * @param array $objectNonCacheMethods 610 * @return PatternOptions 611 * @throws Exception\InvalidArgumentException 612 */ 613 public function setObjectNonCacheMethods(array $objectNonCacheMethods) 614 { 615 $this->objectNonCacheMethods = $this->normalizeObjectMethods($objectNonCacheMethods); 616 return $this; 617 } 618 619 /** 620 * Get list of object methods for which NOT to cache return values 621 * 622 * @return array 623 */ 624 public function getObjectNonCacheMethods() 625 { 626 return $this->objectNonCacheMethods; 627 } 628 629 /** 630 * Set location of public directory 631 * 632 * Used by: 633 * - CaptureCache 634 * 635 * @param string $publicDir 636 * @throws Exception\InvalidArgumentException 637 * @return PatternOptions 638 */ 639 public function setPublicDir($publicDir) 640 { 641 $publicDir = (string) $publicDir; 642 643 if (!is_dir($publicDir)) { 644 throw new Exception\InvalidArgumentException( 645 "Public directory '{$publicDir}' not found or not a directory" 646 ); 647 } elseif (!is_writable($publicDir)) { 648 throw new Exception\InvalidArgumentException( 649 "Public directory '{$publicDir}' not writable" 650 ); 651 } elseif (!is_readable($publicDir)) { 652 throw new Exception\InvalidArgumentException( 653 "Public directory '{$publicDir}' not readable" 654 ); 655 } 656 657 $this->publicDir = rtrim(realpath($publicDir), DIRECTORY_SEPARATOR); 658 return $this; 659 } 660 661 /** 662 * Get location of public directory 663 * 664 * Used by: 665 * - CaptureCache 666 * 667 * @return null|string 668 */ 669 public function getPublicDir() 670 { 671 return $this->publicDir; 672 } 673 674 /** 675 * Set storage adapter 676 * 677 * Required for the following Pattern classes: 678 * - CallbackCache 679 * - ClassCache 680 * - ObjectCache 681 * - OutputCache 682 * 683 * @param string|array|Storage $storage 684 * @return PatternOptions 685 */ 686 public function setStorage($storage) 687 { 688 $this->storage = $this->storageFactory($storage); 689 return $this; 690 } 691 692 /** 693 * Get storage adapter 694 * 695 * Used by: 696 * - CallbackCache 697 * - ClassCache 698 * - ObjectCache 699 * - OutputCache 700 * 701 * @return null|Storage 702 */ 703 public function getStorage() 704 { 705 return $this->storage; 706 } 707 708 /** 709 * Recursively apply strtolower on all values of an array, and return as a 710 * list of unique values 711 * 712 * @param array $array 713 * @return array 714 */ 715 protected function recursiveStrtolower(array $array) 716 { 717 return array_values(array_unique(array_map('strtolower', $array))); 718 } 719 720 /** 721 * Normalize object methods 722 * 723 * Recursively casts values to lowercase, then determines if any are in a 724 * list of methods not handled, raising an exception if so. 725 * 726 * @param array $methods 727 * @return array 728 * @throws Exception\InvalidArgumentException 729 */ 730 protected function normalizeObjectMethods(array $methods) 731 { 732 $methods = $this->recursiveStrtolower($methods); 733 $intersect = array_intersect(array('__set', '__get', '__unset', '__isset'), $methods); 734 if (!empty($intersect)) { 735 throw new Exception\InvalidArgumentException( 736 "Magic properties are handled by option 'cache_magic_properties'" 737 ); 738 } 739 return $methods; 740 } 741 742 /** 743 * Create a storage object from a given specification 744 * 745 * @param array|string|Storage $storage 746 * @throws Exception\InvalidArgumentException 747 * @return Storage 748 */ 749 protected function storageFactory($storage) 750 { 751 if (is_array($storage)) { 752 $storage = StorageFactory::factory($storage); 753 } elseif (is_string($storage)) { 754 $storage = StorageFactory::adapterFactory($storage); 755 } elseif (!($storage instanceof Storage)) { 756 throw new Exception\InvalidArgumentException( 757 'The storage must be an instanceof Zend\Cache\Storage\StorageInterface ' 758 . 'or an array passed to Zend\Cache\Storage::factory ' 759 . 'or simply the name of the storage adapter' 760 ); 761 } 762 763 return $storage; 764 } 765} 766