PageRenderTime 66ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/View/Resolver/TemplatePathStack.php

https://github.com/MontmereLimited/zf2
PHP | 354 lines | 227 code | 23 blank | 104 comment | 17 complexity | 04591edf5532a48ac3528a9befc13be3 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_View
  17. * @subpackage Resolver
  18. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. namespace Zend\View\Resolver;
  22. use SplFileInfo,
  23. Zend\Stdlib\SplStack,
  24. Zend\View\Exception,
  25. Zend\View\Renderer,
  26. Zend\View\Resolver;
  27. /**
  28. * Resolves view scripts based on a stack of paths
  29. *
  30. * @category Zend
  31. * @package Zend_View
  32. * @subpackage Resolver
  33. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  34. * @license http://framework.zend.com/license/new-bsd New BSD License
  35. */
  36. class TemplatePathStack implements Resolver
  37. {
  38. const FAILURE_NO_PATHS = 'TemplatePathStack_Failure_No_Paths';
  39. const FAILURE_NOT_FOUND = 'TemplatePathStack_Failure_Not_Found';
  40. /**
  41. * Default suffix to use
  42. *
  43. * Appends this suffix if the template requested does not use it.
  44. *
  45. * @var string
  46. */
  47. protected $defaultSuffix = 'phtml';
  48. /**
  49. * @var SplStack
  50. */
  51. protected $paths;
  52. /**
  53. * Reason for last lookup failure
  54. *
  55. * @var false|string
  56. */
  57. protected $lastLookupFailure = false;
  58. /**
  59. * Flag indicating whether or not LFI protection for rendering view scripts is enabled
  60. * @var bool
  61. */
  62. protected $lfiProtectionOn = true;
  63. /**@+
  64. * Flags used to determine if a stream wrapper should be used for enabling short tags
  65. * @var bool
  66. */
  67. protected $useViewStream = false;
  68. protected $useStreamWrapper = false;
  69. /**@-*/
  70. /**
  71. * Constructor
  72. *
  73. * @param null|array|Traversable $options
  74. * @return void
  75. */
  76. public function __construct($options = null)
  77. {
  78. $this->useViewStream = (bool) ini_get('short_open_tag');
  79. if ($this->useViewStream) {
  80. if (!in_array('zend.view', stream_get_wrappers())) {
  81. stream_wrapper_register('zend.view', 'Zend\View\Stream');
  82. }
  83. }
  84. $this->paths = new SplStack;
  85. if (null !== $options) {
  86. $this->setOptions($options);
  87. }
  88. }
  89. /**
  90. * Configure object
  91. *
  92. * @param array|Traversable $options
  93. * @return void
  94. * @throws Exception\InvalidArgumentException
  95. */
  96. public function setOptions($options)
  97. {
  98. if (!is_array($options) && !$options instanceof \Traversable) {
  99. throw new Exception\InvalidArgumentException(sprintf(
  100. 'Expected array or Traversable object; received "%s"',
  101. (is_object($options) ? get_class($options) : gettype($options))
  102. ));
  103. }
  104. foreach ($options as $key => $value) {
  105. switch (strtolower($key)) {
  106. case 'lfi_protection':
  107. $this->setLfiProtection($value);
  108. break;
  109. case 'script_paths':
  110. $this->addPaths($value);
  111. break;
  112. case 'use_stream_wrapper':
  113. $this->setUseStreamWrapper($value);
  114. break;
  115. default:
  116. break;
  117. }
  118. }
  119. }
  120. /**
  121. * Set default file suffix
  122. *
  123. * @param string $defaultSuffix
  124. * @return TemplatePathStack
  125. */
  126. public function setDefaultSuffix($defaultSuffix)
  127. {
  128. $this->defaultSuffix = (string) $defaultSuffix;
  129. $this->defaultSuffix = ltrim($this->defaultSuffix, '.');
  130. return $this;
  131. }
  132. /**
  133. * Get default file suffix
  134. *
  135. * @return string
  136. */
  137. public function getDefaultSuffix()
  138. {
  139. return $this->defaultSuffix;
  140. }
  141. /**
  142. * Add many paths to the stack at once
  143. *
  144. * @param array $paths
  145. * @return TemplatePathStack
  146. */
  147. public function addPaths(array $paths)
  148. {
  149. foreach ($paths as $path) {
  150. $this->addPath($path);
  151. }
  152. return $this;
  153. }
  154. /**
  155. * Rest the path stack to the paths provided
  156. *
  157. * @param SplStack|array $paths
  158. * @return TemplatePathStack
  159. * @throws Exception\InvalidArgumentException
  160. */
  161. public function setPaths($paths)
  162. {
  163. if ($paths instanceof SplStack) {
  164. $this->paths = $paths;
  165. } elseif (is_array($paths)) {
  166. $this->clearPaths();
  167. $this->addPaths($paths);
  168. } else {
  169. throw new Exception\InvalidArgumentException(
  170. "Invalid argument provided for \$paths, expecting either an array or SplStack object"
  171. );
  172. }
  173. return $this;
  174. }
  175. /**
  176. * Normalize a path for insertion in the stack
  177. *
  178. * @param string $path
  179. * @return string
  180. */
  181. public static function normalizePath($path)
  182. {
  183. $path = rtrim($path, '/');
  184. $path = rtrim($path, '\\');
  185. $path .= DIRECTORY_SEPARATOR;
  186. return $path;
  187. }
  188. /**
  189. * Add a single path to the stack
  190. *
  191. * @param string $path
  192. * @return TemplatePathStack
  193. * @throws Exception\InvalidArgumentException
  194. */
  195. public function addPath($path)
  196. {
  197. if (!is_string($path)) {
  198. throw new Exception\InvalidArgumentException(sprintf(
  199. 'Invalid path provided; must be a string, received %s',
  200. gettype($path)
  201. ));
  202. }
  203. $this->paths[] = static::normalizePath($path);
  204. return $this;
  205. }
  206. /**
  207. * Clear all paths
  208. *
  209. * @return void
  210. */
  211. public function clearPaths()
  212. {
  213. $this->paths = new SplStack;
  214. }
  215. /**
  216. * Returns stack of paths
  217. *
  218. * @return SplStack
  219. */
  220. public function getPaths()
  221. {
  222. return $this->paths;
  223. }
  224. /**
  225. * Set LFI protection flag
  226. *
  227. * @param bool $flag
  228. * @return \Zend\View\TemplatePathStack
  229. */
  230. public function setLfiProtection($flag)
  231. {
  232. $this->lfiProtectionOn = (bool) $flag;
  233. return $this;
  234. }
  235. /**
  236. * Return status of LFI protection flag
  237. *
  238. * @return bool
  239. */
  240. public function isLfiProtectionOn()
  241. {
  242. return $this->lfiProtectionOn;
  243. }
  244. /**
  245. * Set flag indicating if stream wrapper should be used if short_open_tag is off
  246. *
  247. * @param bool $flag
  248. * @return \Zend\View\View
  249. */
  250. public function setUseStreamWrapper($flag)
  251. {
  252. $this->useStreamWrapper = (bool) $flag;
  253. return $this;
  254. }
  255. /**
  256. * Should the stream wrapper be used if short_open_tag is off?
  257. *
  258. * Returns true if the use_stream_wrapper flag is set, and if short_open_tag
  259. * is disabled.
  260. *
  261. * @return bool
  262. */
  263. public function useStreamWrapper()
  264. {
  265. return ($this->useViewStream && $this->useStreamWrapper);
  266. }
  267. /**
  268. * Retrieve the filesystem path to a view script
  269. *
  270. * @param string $name
  271. * @param null|Renderer $renderer
  272. * @return string
  273. * @throws Exception\RuntimeException
  274. */
  275. public function resolve($name, Renderer $renderer = null)
  276. {
  277. $this->lastLookupFailure = false;
  278. if ($this->isLfiProtectionOn() && preg_match('#\.\.[\\\/]#', $name)) {
  279. throw new Exception\DomainException(
  280. 'Requested scripts may not include parent directory traversal ("../", "..\\" notation)'
  281. );
  282. }
  283. if (!count($this->paths)) {
  284. $this->lastLookupFailure = static::FAILURE_NO_PATHS;
  285. return false;
  286. }
  287. // Ensure we have the expected file extension
  288. $defaultSuffix = $this->getDefaultSuffix();
  289. if (pathinfo($name, PATHINFO_EXTENSION) != $defaultSuffix) {;
  290. $name .= '.' . $defaultSuffix;
  291. }
  292. foreach ($this->paths as $path) {
  293. $file = new SplFileInfo($path . $name);
  294. if ($file->isReadable()) {
  295. // Found! Return it.
  296. if (($filePath = $file->getRealPath()) === false && substr($path, 0, 7) === 'phar://') {
  297. // Do not try to expand phar paths (realpath + phars == fail)
  298. $filePath = $path . $name;
  299. if (!file_exists($filePath)) {
  300. break;
  301. }
  302. }
  303. if ($this->useStreamWrapper()) {
  304. // If using a stream wrapper, prepend the spec to the path
  305. $filePath = 'zend.view://' . $filePath;
  306. }
  307. return $filePath;
  308. }
  309. }
  310. $this->lastLookupFailure = static::FAILURE_NOT_FOUND;
  311. return false;
  312. }
  313. /**
  314. * Get the last lookup failure message, if any
  315. *
  316. * @return false|string
  317. */
  318. public function getLastLookupFailure()
  319. {
  320. return $this->lastLookupFailure;
  321. }
  322. }