useViewStream = (bool) ini_get('short_open_tag'); if ($this->useViewStream) { if (!in_array('zend.view', stream_get_wrappers())) { stream_wrapper_register('zend.view', 'Zend\View\Stream'); } } $this->paths = new SplStack; if (null !== $options) { $this->setOptions($options); } } /** * Configure object * * @param array|Traversable $options * @return void * @throws Exception\InvalidArgumentException */ public function setOptions($options) { if (!is_array($options) && !$options instanceof Traversable) { throw new Exception\InvalidArgumentException(sprintf( 'Expected array or Traversable object; received "%s"', (is_object($options) ? get_class($options) : gettype($options)) )); } foreach ($options as $key => $value) { switch (strtolower($key)) { case 'lfi_protection': $this->setLfiProtection($value); break; case 'script_paths': $this->addPaths($value); break; case 'use_stream_wrapper': $this->setUseStreamWrapper($value); break; case 'default_suffix': $this->setDefaultSuffix($value); break; default: break; } } } /** * Set default file suffix * * @param string $defaultSuffix * @return TemplatePathStack */ public function setDefaultSuffix($defaultSuffix) { $this->defaultSuffix = (string) $defaultSuffix; $this->defaultSuffix = ltrim($this->defaultSuffix, '.'); return $this; } /** * Get default file suffix * * @return string */ public function getDefaultSuffix() { return $this->defaultSuffix; } /** * Add many paths to the stack at once * * @param array $paths * @return TemplatePathStack */ public function addPaths(array $paths) { foreach ($paths as $path) { $this->addPath($path); } return $this; } /** * Rest the path stack to the paths provided * * @param SplStack|array $paths * @return TemplatePathStack * @throws Exception\InvalidArgumentException */ public function setPaths($paths) { if ($paths instanceof SplStack) { $this->paths = $paths; } elseif (is_array($paths)) { $this->clearPaths(); $this->addPaths($paths); } else { throw new Exception\InvalidArgumentException( "Invalid argument provided for \$paths, expecting either an array or SplStack object" ); } return $this; } /** * Normalize a path for insertion in the stack * * @param string $path * @return string */ public static function normalizePath($path) { $path = rtrim($path, '/'); $path = rtrim($path, '\\'); $path .= DIRECTORY_SEPARATOR; return $path; } /** * Add a single path to the stack * * @param string $path * @return TemplatePathStack * @throws Exception\InvalidArgumentException */ public function addPath($path) { if (!is_string($path)) { throw new Exception\InvalidArgumentException(sprintf( 'Invalid path provided; must be a string, received %s', gettype($path) )); } $this->paths[] = static::normalizePath($path); return $this; } /** * Clear all paths * * @return void */ public function clearPaths() { $this->paths = new SplStack; } /** * Returns stack of paths * * @return SplStack */ public function getPaths() { return $this->paths; } /** * Set LFI protection flag * * @param bool $flag * @return TemplatePathStack */ public function setLfiProtection($flag) { $this->lfiProtectionOn = (bool) $flag; return $this; } /** * Return status of LFI protection flag * * @return bool */ public function isLfiProtectionOn() { return $this->lfiProtectionOn; } /** * Set flag indicating if stream wrapper should be used if short_open_tag is off * * @param bool $flag * @return TemplatePathStack */ public function setUseStreamWrapper($flag) { $this->useStreamWrapper = (bool) $flag; return $this; } /** * Should the stream wrapper be used if short_open_tag is off? * * Returns true if the use_stream_wrapper flag is set, and if short_open_tag * is disabled. * * @return bool */ public function useStreamWrapper() { return ($this->useViewStream && $this->useStreamWrapper); } /** * Retrieve the filesystem path to a view script * * @param string $name * @param null|Renderer $renderer * @return string * @throws Exception\DomainException */ public function resolve($name, Renderer $renderer = null) { $this->lastLookupFailure = false; if ($this->isLfiProtectionOn() && preg_match('#\.\.[\\\/]#', $name)) { throw new Exception\DomainException( 'Requested scripts may not include parent directory traversal ("../", "..\\" notation)' ); } if (!count($this->paths)) { $this->lastLookupFailure = static::FAILURE_NO_PATHS; return false; } // Ensure we have the expected file extension $defaultSuffix = $this->getDefaultSuffix(); if (pathinfo($name, PATHINFO_EXTENSION) == '') { $name .= '.' . $defaultSuffix; } foreach ($this->paths as $path) { $file = new SplFileInfo($path . $name); if ($file->isReadable()) { // Found! Return it. if (($filePath = $file->getRealPath()) === false && substr($path, 0, 7) === 'phar://') { // Do not try to expand phar paths (realpath + phars == fail) $filePath = $path . $name; if (!file_exists($filePath)) { break; } } if ($this->useStreamWrapper()) { // If using a stream wrapper, prepend the spec to the path $filePath = 'zend.view://' . $filePath; } return $filePath; } } $this->lastLookupFailure = static::FAILURE_NOT_FOUND; return false; } /** * Get the last lookup failure message, if any * * @return false|string */ public function getLastLookupFailure() { return $this->lastLookupFailure; } }