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

/lib/Zend/Filter/StripTags.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 352 lines | 154 code | 41 blank | 157 comment | 34 complexity | ff0f6a07f43455725399dd58798c1ac7 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  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_Filter
  17. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: StripTags.php 22806 2010-08-08 08:31:28Z thomas $
  20. */
  21. /**
  22. * @see Zend_Filter_Interface
  23. */
  24. #require_once 'Zend/Filter/Interface.php';
  25. /**
  26. * @category Zend
  27. * @package Zend_Filter
  28. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  29. * @license http://framework.zend.com/license/new-bsd New BSD License
  30. */
  31. class Zend_Filter_StripTags implements Zend_Filter_Interface
  32. {
  33. /**
  34. * Unique ID prefix used for allowing comments
  35. */
  36. const UNIQUE_ID_PREFIX = '__Zend_Filter_StripTags__';
  37. /**
  38. * Whether comments are allowed
  39. *
  40. * If false (the default), then comments are removed from the input string.
  41. *
  42. * This setting is now deprecated, and ignored internally.
  43. *
  44. * @deprecated
  45. * @var boolean
  46. */
  47. public $commentsAllowed = false;
  48. /**
  49. * Array of allowed tags and allowed attributes for each allowed tag
  50. *
  51. * Tags are stored in the array keys, and the array values are themselves
  52. * arrays of the attributes allowed for the corresponding tag.
  53. *
  54. * @var array
  55. */
  56. protected $_tagsAllowed = array();
  57. /**
  58. * Array of allowed attributes for all allowed tags
  59. *
  60. * Attributes stored here are allowed for all of the allowed tags.
  61. *
  62. * @var array
  63. */
  64. protected $_attributesAllowed = array();
  65. /**
  66. * Sets the filter options
  67. * Allowed options are
  68. * 'allowTags' => Tags which are allowed
  69. * 'allowAttribs' => Attributes which are allowed
  70. * 'allowComments' => Are comments allowed ?
  71. *
  72. * @param string|array|Zend_Config $options
  73. * @return void
  74. */
  75. public function __construct($options = null)
  76. {
  77. if ($options instanceof Zend_Config) {
  78. $options = $options->toArray();
  79. } else if ((!is_array($options)) || (is_array($options) && !array_key_exists('allowTags', $options) &&
  80. !array_key_exists('allowAttribs', $options) && !array_key_exists('allowComments', $options))) {
  81. $options = func_get_args();
  82. $temp['allowTags'] = array_shift($options);
  83. if (!empty($options)) {
  84. $temp['allowAttribs'] = array_shift($options);
  85. }
  86. if (!empty($options)) {
  87. $temp['allowComments'] = array_shift($options);
  88. }
  89. $options = $temp;
  90. }
  91. if (array_key_exists('allowTags', $options)) {
  92. $this->setTagsAllowed($options['allowTags']);
  93. }
  94. if (array_key_exists('allowAttribs', $options)) {
  95. $this->setAttributesAllowed($options['allowAttribs']);
  96. }
  97. if (array_key_exists('allowComments', $options)) {
  98. $this->setCommentsAllowed($options['allowComments']);
  99. }
  100. }
  101. /**
  102. * Returns the commentsAllowed option
  103. *
  104. * This setting is now deprecated and ignored internally.
  105. *
  106. * @deprecated
  107. * @return bool
  108. */
  109. public function getCommentsAllowed()
  110. {
  111. return $this->commentsAllowed;
  112. }
  113. /**
  114. * Sets the commentsAllowed option
  115. *
  116. * This setting is now deprecated and ignored internally.
  117. *
  118. * @deprecated
  119. * @param boolean $commentsAllowed
  120. * @return Zend_Filter_StripTags Provides a fluent interface
  121. */
  122. public function setCommentsAllowed($commentsAllowed)
  123. {
  124. $this->commentsAllowed = (boolean) $commentsAllowed;
  125. return $this;
  126. }
  127. /**
  128. * Returns the tagsAllowed option
  129. *
  130. * @return array
  131. */
  132. public function getTagsAllowed()
  133. {
  134. return $this->_tagsAllowed;
  135. }
  136. /**
  137. * Sets the tagsAllowed option
  138. *
  139. * @param array|string $tagsAllowed
  140. * @return Zend_Filter_StripTags Provides a fluent interface
  141. */
  142. public function setTagsAllowed($tagsAllowed)
  143. {
  144. if (!is_array($tagsAllowed)) {
  145. $tagsAllowed = array($tagsAllowed);
  146. }
  147. foreach ($tagsAllowed as $index => $element) {
  148. // If the tag was provided without attributes
  149. if (is_int($index) && is_string($element)) {
  150. // Canonicalize the tag name
  151. $tagName = strtolower($element);
  152. // Store the tag as allowed with no attributes
  153. $this->_tagsAllowed[$tagName] = array();
  154. }
  155. // Otherwise, if a tag was provided with attributes
  156. else if (is_string($index) && (is_array($element) || is_string($element))) {
  157. // Canonicalize the tag name
  158. $tagName = strtolower($index);
  159. // Canonicalize the attributes
  160. if (is_string($element)) {
  161. $element = array($element);
  162. }
  163. // Store the tag as allowed with the provided attributes
  164. $this->_tagsAllowed[$tagName] = array();
  165. foreach ($element as $attribute) {
  166. if (is_string($attribute)) {
  167. // Canonicalize the attribute name
  168. $attributeName = strtolower($attribute);
  169. $this->_tagsAllowed[$tagName][$attributeName] = null;
  170. }
  171. }
  172. }
  173. }
  174. return $this;
  175. }
  176. /**
  177. * Returns the attributesAllowed option
  178. *
  179. * @return array
  180. */
  181. public function getAttributesAllowed()
  182. {
  183. return $this->_attributesAllowed;
  184. }
  185. /**
  186. * Sets the attributesAllowed option
  187. *
  188. * @param array|string $attributesAllowed
  189. * @return Zend_Filter_StripTags Provides a fluent interface
  190. */
  191. public function setAttributesAllowed($attributesAllowed)
  192. {
  193. if (!is_array($attributesAllowed)) {
  194. $attributesAllowed = array($attributesAllowed);
  195. }
  196. // Store each attribute as allowed
  197. foreach ($attributesAllowed as $attribute) {
  198. if (is_string($attribute)) {
  199. // Canonicalize the attribute name
  200. $attributeName = strtolower($attribute);
  201. $this->_attributesAllowed[$attributeName] = null;
  202. }
  203. }
  204. return $this;
  205. }
  206. /**
  207. * Defined by Zend_Filter_Interface
  208. *
  209. * @todo improve docblock descriptions
  210. *
  211. * @param string $value
  212. * @return string
  213. */
  214. public function filter($value)
  215. {
  216. $value = (string) $value;
  217. // Strip HTML comments first
  218. while (strpos($value, '<!--') !== false) {
  219. $pos = strrpos($value, '<!--');
  220. $start = substr($value, 0, $pos);
  221. $value = substr($value, $pos);
  222. // If there is no comment closing tag, strip whole text
  223. if (!preg_match('/--\s*>/s', $value)) {
  224. $value = '';
  225. } else {
  226. $value = preg_replace('/<(?:!(?:--[\s\S]*?--\s*)?(>))/s', '', $value);
  227. }
  228. $value = $start . $value;
  229. }
  230. // Initialize accumulator for filtered data
  231. $dataFiltered = '';
  232. // Parse the input data iteratively as regular pre-tag text followed by a
  233. // tag; either may be empty strings
  234. preg_match_all('/([^<]*)(<?[^>]*>?)/', (string) $value, $matches);
  235. // Iterate over each set of matches
  236. foreach ($matches[1] as $index => $preTag) {
  237. // If the pre-tag text is non-empty, strip any ">" characters from it
  238. if (strlen($preTag)) {
  239. $preTag = str_replace('>', '', $preTag);
  240. }
  241. // If a tag exists in this match, then filter the tag
  242. $tag = $matches[2][$index];
  243. if (strlen($tag)) {
  244. $tagFiltered = $this->_filterTag($tag);
  245. } else {
  246. $tagFiltered = '';
  247. }
  248. // Add the filtered pre-tag text and filtered tag to the data buffer
  249. $dataFiltered .= $preTag . $tagFiltered;
  250. }
  251. // Return the filtered data
  252. return $dataFiltered;
  253. }
  254. /**
  255. * Filters a single tag against the current option settings
  256. *
  257. * @param string $tag
  258. * @return string
  259. */
  260. protected function _filterTag($tag)
  261. {
  262. // Parse the tag into:
  263. // 1. a starting delimiter (mandatory)
  264. // 2. a tag name (if available)
  265. // 3. a string of attributes (if available)
  266. // 4. an ending delimiter (if available)
  267. $isMatch = preg_match('~(</?)(\w*)((/(?!>)|[^/>])*)(/?>)~', $tag, $matches);
  268. // If the tag does not match, then strip the tag entirely
  269. if (!$isMatch) {
  270. return '';
  271. }
  272. // Save the matches to more meaningfully named variables
  273. $tagStart = $matches[1];
  274. $tagName = strtolower($matches[2]);
  275. $tagAttributes = $matches[3];
  276. $tagEnd = $matches[5];
  277. // If the tag is not an allowed tag, then remove the tag entirely
  278. if (!isset($this->_tagsAllowed[$tagName])) {
  279. return '';
  280. }
  281. // Trim the attribute string of whitespace at the ends
  282. $tagAttributes = trim($tagAttributes);
  283. // If there are non-whitespace characters in the attribute string
  284. if (strlen($tagAttributes)) {
  285. // Parse iteratively for well-formed attributes
  286. preg_match_all('/(\w+)\s*=\s*(?:(")(.*?)"|(\')(.*?)\')/s', $tagAttributes, $matches);
  287. // Initialize valid attribute accumulator
  288. $tagAttributes = '';
  289. // Iterate over each matched attribute
  290. foreach ($matches[1] as $index => $attributeName) {
  291. $attributeName = strtolower($attributeName);
  292. $attributeDelimiter = empty($matches[2][$index]) ? $matches[4][$index] : $matches[2][$index];
  293. $attributeValue = empty($matches[3][$index]) ? $matches[5][$index] : $matches[3][$index];
  294. // If the attribute is not allowed, then remove it entirely
  295. if (!array_key_exists($attributeName, $this->_tagsAllowed[$tagName])
  296. && !array_key_exists($attributeName, $this->_attributesAllowed)) {
  297. continue;
  298. }
  299. // Add the attribute to the accumulator
  300. $tagAttributes .= " $attributeName=" . $attributeDelimiter
  301. . $attributeValue . $attributeDelimiter;
  302. }
  303. }
  304. // Reconstruct tags ending with "/>" as backwards-compatible XHTML tag
  305. if (strpos($tagEnd, '/') !== false) {
  306. $tagEnd = " $tagEnd";
  307. }
  308. // Return the filtered tag
  309. return $tagStart . $tagName . $tagAttributes . $tagEnd;
  310. }
  311. }