PageRenderTime 62ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Filter/StripTags.php

https://github.com/jverkoey/snaapilookup
PHP | 284 lines | 127 code | 23 blank | 134 comment | 22 complexity | 06f35114798148a21212fdbc3dec93e2 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_Filter
  17. * @copyright Copyright (c) 2005-2008 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 8064 2008-02-16 10:58:39Z 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-2008 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. * @var boolean
  43. */
  44. public $commentsAllowed;
  45. /**
  46. * Array of allowed tags and allowed attributes for each allowed tag
  47. *
  48. * Tags are stored in the array keys, and the array values are themselves
  49. * arrays of the attributes allowed for the corresponding tag.
  50. *
  51. * @var array
  52. */
  53. protected $_tagsAllowed = array();
  54. /**
  55. * Array of allowed attributes for all allowed tags
  56. *
  57. * Attributes stored here are allowed for all of the allowed tags.
  58. *
  59. * @var array
  60. */
  61. protected $_attributesAllowed = array();
  62. /**
  63. * Sets the filter options
  64. *
  65. * @param array|string $tagsAllowed
  66. * @param array|string $attributesAllowed
  67. * @param boolean $allowComments
  68. * @return void
  69. */
  70. public function __construct($tagsAllowed = null, $attributesAllowed = null, $commentsAllowed = false)
  71. {
  72. $this->setTagsAllowed($tagsAllowed);
  73. $this->setAttributesAllowed($attributesAllowed);
  74. $this->commentsAllowed = (boolean) $commentsAllowed;
  75. }
  76. /**
  77. * Returns the tagsAllowed option
  78. *
  79. * @return array
  80. */
  81. public function getTagsAllowed()
  82. {
  83. return $this->_tagsAllowed;
  84. }
  85. /**
  86. * Sets the tagsAllowed option
  87. *
  88. * @param array|string $tagsAllowed
  89. * @return Zend_Filter_StripTags Provides a fluent interface
  90. */
  91. public function setTagsAllowed($tagsAllowed)
  92. {
  93. if (!is_array($tagsAllowed)) {
  94. $tagsAllowed = array($tagsAllowed);
  95. }
  96. foreach ($tagsAllowed as $index => $element) {
  97. // If the tag was provided without attributes
  98. if (is_int($index) && is_string($element)) {
  99. // Canonicalize the tag name
  100. $tagName = strtolower($element);
  101. // Store the tag as allowed with no attributes
  102. $this->_tagsAllowed[$tagName] = array();
  103. }
  104. // Otherwise, if a tag was provided with attributes
  105. else if (is_string($index) && (is_array($element) || is_string($element))) {
  106. // Canonicalize the tag name
  107. $tagName = strtolower($index);
  108. // Canonicalize the attributes
  109. if (is_string($element)) {
  110. $element = array($element);
  111. }
  112. // Store the tag as allowed with the provided attributes
  113. $this->_tagsAllowed[$tagName] = array();
  114. foreach ($element as $attribute) {
  115. if (is_string($attribute)) {
  116. // Canonicalize the attribute name
  117. $attributeName = strtolower($attribute);
  118. $this->_tagsAllowed[$tagName][$attributeName] = null;
  119. }
  120. }
  121. }
  122. }
  123. return $this;
  124. }
  125. /**
  126. * Returns the attributesAllowed option
  127. *
  128. * @return array
  129. */
  130. public function getAttributesAllowed()
  131. {
  132. return $this->_attributesAllowed;
  133. }
  134. /**
  135. * Sets the attributesAllowed option
  136. *
  137. * @param array|string $attributesAllowed
  138. * @return Zend_Filter_StripTags Provides a fluent interface
  139. */
  140. public function setAttributesAllowed($attributesAllowed)
  141. {
  142. if (!is_array($attributesAllowed)) {
  143. $attributesAllowed = array($attributesAllowed);
  144. }
  145. // Store each attribute as allowed
  146. foreach ($attributesAllowed as $attribute) {
  147. if (is_string($attribute)) {
  148. // Canonicalize the attribute name
  149. $attributeName = strtolower($attribute);
  150. $this->_attributesAllowed[$attributeName] = null;
  151. }
  152. }
  153. return $this;
  154. }
  155. /**
  156. * Defined by Zend_Filter_Interface
  157. *
  158. * @todo improve docblock descriptions
  159. *
  160. * @param string $value
  161. * @return string
  162. */
  163. public function filter($value)
  164. {
  165. $valueCopy = (string) $value;
  166. // If comments are allowed, then replace them with unique identifiers
  167. if ($this->commentsAllowed) {
  168. preg_match_all('/<\!--.*?--\s*>/s' , (string) $valueCopy, $matches);
  169. $comments = array_unique($matches[0]);
  170. foreach ($comments as $k => $v) {
  171. $valueCopy = str_replace($v, self::UNIQUE_ID_PREFIX . $k, $valueCopy);
  172. }
  173. }
  174. // Initialize accumulator for filtered data
  175. $dataFiltered = '';
  176. // Parse the input data iteratively as regular pre-tag text followed by a
  177. // tag; either may be empty strings
  178. preg_match_all('/([^<]*)(<?[^>]*>?)/', (string) $valueCopy, $matches);
  179. // Iterate over each set of matches
  180. foreach ($matches[1] as $index => $preTag) {
  181. // If the pre-tag text is non-empty, strip any ">" characters from it
  182. if (strlen($preTag)) {
  183. $preTag = str_replace('>', '', $preTag);
  184. }
  185. // If a tag exists in this match, then filter the tag
  186. $tag = $matches[2][$index];
  187. if (strlen($tag)) {
  188. $tagFiltered = $this->_filterTag($tag);
  189. } else {
  190. $tagFiltered = '';
  191. }
  192. // Add the filtered pre-tag text and filtered tag to the data buffer
  193. $dataFiltered .= $preTag . $tagFiltered;
  194. }
  195. // If comments are allowed, then replace the unique identifiers with the corresponding comments
  196. if ($this->commentsAllowed) {
  197. foreach ($comments as $k => $v) {
  198. $dataFiltered = str_replace(self::UNIQUE_ID_PREFIX . $k, $v, $dataFiltered);
  199. }
  200. }
  201. // Return the filtered data
  202. return $dataFiltered;
  203. }
  204. /**
  205. * Filters a single tag against the current option settings
  206. *
  207. * @param string $tag
  208. * @return string
  209. */
  210. protected function _filterTag($tag)
  211. {
  212. // Parse the tag into:
  213. // 1. a starting delimiter (mandatory)
  214. // 2. a tag name (if available)
  215. // 3. a string of attributes (if available)
  216. // 4. an ending delimiter (if available)
  217. $isMatch = preg_match('~(</?)(\w*)((/(?!>)|[^/>])*)(/?>)~', $tag, $matches);
  218. // If the tag does not match, then strip the tag entirely
  219. if (!$isMatch) {
  220. return '';
  221. }
  222. // Save the matches to more meaningfully named variables
  223. $tagStart = $matches[1];
  224. $tagName = strtolower($matches[2]);
  225. $tagAttributes = $matches[3];
  226. $tagEnd = $matches[5];
  227. // If the tag is not an allowed tag, then remove the tag entirely
  228. if (!isset($this->_tagsAllowed[$tagName])) {
  229. return '';
  230. }
  231. // Trim the attribute string of whitespace at the ends
  232. $tagAttributes = trim($tagAttributes);
  233. // If there are non-whitespace characters in the attribute string
  234. if (strlen($tagAttributes)) {
  235. // Parse iteratively for well-formed attributes
  236. preg_match_all('/(\w+)=([\'"])((.(?!=\2))+)\2/s', $tagAttributes, $matches);
  237. // Initialize valid attribute accumulator
  238. $tagAttributes = '';
  239. // Iterate over each matched attribute
  240. foreach ($matches[1] as $index => $attributeName) {
  241. $attributeName = strtolower($attributeName);
  242. $attributeDelimiter = $matches[2][$index];
  243. $attributeValue = $matches[3][$index];
  244. // If the attribute is not allowed, then remove it entirely
  245. if (!array_key_exists($attributeName, $this->_tagsAllowed[$tagName])
  246. && !array_key_exists($attributeName, $this->_attributesAllowed)) {
  247. continue;
  248. }
  249. // Add the attribute to the accumulator
  250. $tagAttributes .= " $attributeName=" . $attributeDelimiter
  251. . $attributeValue . $attributeDelimiter;
  252. }
  253. }
  254. // Reconstruct tags ending with "/>" as backwards-compatible XHTML tag
  255. if (strpos($tagEnd, '/') !== false) {
  256. $tagEnd = " $tagEnd";
  257. }
  258. // Return the filtered tag
  259. return $tagStart . $tagName . $tagAttributes . $tagEnd;
  260. }
  261. }