File indexing completed on 2024-06-09 05:50:18
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Filter 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 0023 /** 0024 * @see Zend_Filter_Interface 0025 */ 0026 // require_once 'Zend/Filter/Interface.php'; 0027 0028 0029 /** 0030 * @category Zend 0031 * @package Zend_Filter 0032 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0033 * @license http://framework.zend.com/license/new-bsd New BSD License 0034 */ 0035 class Zend_Filter_StripTags implements Zend_Filter_Interface 0036 { 0037 /** 0038 * Unique ID prefix used for allowing comments 0039 */ 0040 const UNIQUE_ID_PREFIX = '__Zend_Filter_StripTags__'; 0041 0042 /** 0043 * Whether comments are allowed 0044 * 0045 * If false (the default), then comments are removed from the input string. 0046 * 0047 * This setting is now deprecated, and ignored internally. 0048 * 0049 * @deprecated 0050 * @var boolean 0051 */ 0052 public $commentsAllowed = false; 0053 0054 /** 0055 * Array of allowed tags and allowed attributes for each allowed tag 0056 * 0057 * Tags are stored in the array keys, and the array values are themselves 0058 * arrays of the attributes allowed for the corresponding tag. 0059 * 0060 * @var array 0061 */ 0062 protected $_tagsAllowed = array(); 0063 0064 /** 0065 * Array of allowed attributes for all allowed tags 0066 * 0067 * Attributes stored here are allowed for all of the allowed tags. 0068 * 0069 * @var array 0070 */ 0071 protected $_attributesAllowed = array(); 0072 0073 /** 0074 * Sets the filter options 0075 * Allowed options are 0076 * 'allowTags' => Tags which are allowed 0077 * 'allowAttribs' => Attributes which are allowed 0078 * 'allowComments' => Are comments allowed ? 0079 * 0080 * @param string|array|Zend_Config $options 0081 * @return void 0082 */ 0083 public function __construct($options = null) 0084 { 0085 if ($options instanceof Zend_Config) { 0086 $options = $options->toArray(); 0087 } else if ((!is_array($options)) || (is_array($options) && !array_key_exists('allowTags', $options) && 0088 !array_key_exists('allowAttribs', $options) && !array_key_exists('allowComments', $options))) { 0089 $options = func_get_args(); 0090 $temp['allowTags'] = array_shift($options); 0091 if (!empty($options)) { 0092 $temp['allowAttribs'] = array_shift($options); 0093 } 0094 0095 if (!empty($options)) { 0096 $temp['allowComments'] = array_shift($options); 0097 } 0098 0099 $options = $temp; 0100 } 0101 0102 if (array_key_exists('allowTags', $options)) { 0103 $this->setTagsAllowed($options['allowTags']); 0104 } 0105 0106 if (array_key_exists('allowAttribs', $options)) { 0107 $this->setAttributesAllowed($options['allowAttribs']); 0108 } 0109 0110 if (array_key_exists('allowComments', $options)) { 0111 $this->setCommentsAllowed($options['allowComments']); 0112 } 0113 } 0114 0115 /** 0116 * Returns the commentsAllowed option 0117 * 0118 * This setting is now deprecated and ignored internally. 0119 * 0120 * @deprecated 0121 * @return bool 0122 */ 0123 public function getCommentsAllowed() 0124 { 0125 return $this->commentsAllowed; 0126 } 0127 0128 /** 0129 * Sets the commentsAllowed option 0130 * 0131 * This setting is now deprecated and ignored internally. 0132 * 0133 * @deprecated 0134 * @param boolean $commentsAllowed 0135 * @return Zend_Filter_StripTags Provides a fluent interface 0136 */ 0137 public function setCommentsAllowed($commentsAllowed) 0138 { 0139 $this->commentsAllowed = (boolean) $commentsAllowed; 0140 return $this; 0141 } 0142 0143 /** 0144 * Returns the tagsAllowed option 0145 * 0146 * @return array 0147 */ 0148 public function getTagsAllowed() 0149 { 0150 return $this->_tagsAllowed; 0151 } 0152 0153 /** 0154 * Sets the tagsAllowed option 0155 * 0156 * @param array|string $tagsAllowed 0157 * @return Zend_Filter_StripTags Provides a fluent interface 0158 */ 0159 public function setTagsAllowed($tagsAllowed) 0160 { 0161 if (!is_array($tagsAllowed)) { 0162 $tagsAllowed = array($tagsAllowed); 0163 } 0164 0165 foreach ($tagsAllowed as $index => $element) { 0166 // If the tag was provided without attributes 0167 if (is_int($index) && is_string($element)) { 0168 // Canonicalize the tag name 0169 $tagName = strtolower($element); 0170 // Store the tag as allowed with no attributes 0171 $this->_tagsAllowed[$tagName] = array(); 0172 } 0173 // Otherwise, if a tag was provided with attributes 0174 else if (is_string($index) && (is_array($element) || is_string($element))) { 0175 // Canonicalize the tag name 0176 $tagName = strtolower($index); 0177 // Canonicalize the attributes 0178 if (is_string($element)) { 0179 $element = array($element); 0180 } 0181 // Store the tag as allowed with the provided attributes 0182 $this->_tagsAllowed[$tagName] = array(); 0183 foreach ($element as $attribute) { 0184 if (is_string($attribute)) { 0185 // Canonicalize the attribute name 0186 $attributeName = strtolower($attribute); 0187 $this->_tagsAllowed[$tagName][$attributeName] = null; 0188 } 0189 } 0190 } 0191 } 0192 0193 return $this; 0194 } 0195 0196 /** 0197 * Returns the attributesAllowed option 0198 * 0199 * @return array 0200 */ 0201 public function getAttributesAllowed() 0202 { 0203 return $this->_attributesAllowed; 0204 } 0205 0206 /** 0207 * Sets the attributesAllowed option 0208 * 0209 * @param array|string $attributesAllowed 0210 * @return Zend_Filter_StripTags Provides a fluent interface 0211 */ 0212 public function setAttributesAllowed($attributesAllowed) 0213 { 0214 if (!is_array($attributesAllowed)) { 0215 $attributesAllowed = array($attributesAllowed); 0216 } 0217 0218 // Store each attribute as allowed 0219 foreach ($attributesAllowed as $attribute) { 0220 if (is_string($attribute)) { 0221 // Canonicalize the attribute name 0222 $attributeName = strtolower($attribute); 0223 $this->_attributesAllowed[$attributeName] = null; 0224 } 0225 } 0226 0227 return $this; 0228 } 0229 0230 /** 0231 * Defined by Zend_Filter_Interface 0232 * 0233 * @todo improve docblock descriptions 0234 * 0235 * @param string $value 0236 * @return string 0237 */ 0238 public function filter($value) 0239 { 0240 $value = (string) $value; 0241 0242 // Strip HTML comments first 0243 while (strpos($value, '<!--') !== false) { 0244 $pos = strrpos($value, '<!--'); 0245 $start = substr($value, 0, $pos); 0246 $value = substr($value, $pos); 0247 0248 // If there is no comment closing tag, strip whole text 0249 if (!preg_match('/--\s*>/s', $value)) { 0250 $value = ''; 0251 } else { 0252 $value = preg_replace('/<(?:!(?:--[\s\S]*?--\s*)?(>))/s', '', $value); 0253 } 0254 0255 $value = $start . $value; 0256 } 0257 0258 // Initialize accumulator for filtered data 0259 $dataFiltered = ''; 0260 // Parse the input data iteratively as regular pre-tag text followed by a 0261 // tag; either may be empty strings 0262 preg_match_all('/([^<]*)(<?[^>]*>?)/', (string) $value, $matches); 0263 0264 // Iterate over each set of matches 0265 foreach ($matches[1] as $index => $preTag) { 0266 // If the pre-tag text is non-empty, strip any ">" characters from it 0267 if (strlen($preTag)) { 0268 $preTag = str_replace('>', '', $preTag); 0269 } 0270 // If a tag exists in this match, then filter the tag 0271 $tag = $matches[2][$index]; 0272 if (strlen($tag)) { 0273 $tagFiltered = $this->_filterTag($tag); 0274 } else { 0275 $tagFiltered = ''; 0276 } 0277 // Add the filtered pre-tag text and filtered tag to the data buffer 0278 $dataFiltered .= $preTag . $tagFiltered; 0279 } 0280 0281 // Return the filtered data 0282 return $dataFiltered; 0283 } 0284 0285 /** 0286 * Filters a single tag against the current option settings 0287 * 0288 * @param string $tag 0289 * @return string 0290 */ 0291 protected function _filterTag($tag) 0292 { 0293 // Parse the tag into: 0294 // 1. a starting delimiter (mandatory) 0295 // 2. a tag name (if available) 0296 // 3. a string of attributes (if available) 0297 // 4. an ending delimiter (if available) 0298 $isMatch = preg_match('~(</?)(\w*)((/(?!>)|[^/>])*)(/?>)~', $tag, $matches); 0299 0300 // If the tag does not match, then strip the tag entirely 0301 if (!$isMatch) { 0302 return ''; 0303 } 0304 0305 // Save the matches to more meaningfully named variables 0306 $tagStart = $matches[1]; 0307 $tagName = strtolower($matches[2]); 0308 $tagAttributes = $matches[3]; 0309 $tagEnd = $matches[5]; 0310 0311 // If the tag is not an allowed tag, then remove the tag entirely 0312 if (!isset($this->_tagsAllowed[$tagName])) { 0313 return ''; 0314 } 0315 0316 // Trim the attribute string of whitespace at the ends 0317 $tagAttributes = trim($tagAttributes); 0318 0319 // If there are non-whitespace characters in the attribute string 0320 if (strlen($tagAttributes)) { 0321 // Parse iteratively for well-formed attributes 0322 preg_match_all('/([\w-]+)\s*=\s*(?:(")(.*?)"|(\')(.*?)\')/s', $tagAttributes, $matches); 0323 0324 // Initialize valid attribute accumulator 0325 $tagAttributes = ''; 0326 0327 // Iterate over each matched attribute 0328 foreach ($matches[1] as $index => $attributeName) { 0329 $attributeName = strtolower($attributeName); 0330 $attributeDelimiter = empty($matches[2][$index]) ? $matches[4][$index] : $matches[2][$index]; 0331 $attributeValue = empty($matches[3][$index]) ? $matches[5][$index] : $matches[3][$index]; 0332 0333 // If the attribute is not allowed, then remove it entirely 0334 if (!array_key_exists($attributeName, $this->_tagsAllowed[$tagName]) 0335 && !array_key_exists($attributeName, $this->_attributesAllowed)) { 0336 continue; 0337 } 0338 // Add the attribute to the accumulator 0339 $tagAttributes .= " $attributeName=" . $attributeDelimiter 0340 . $attributeValue . $attributeDelimiter; 0341 } 0342 } 0343 0344 // Reconstruct tags ending with "/>" as backwards-compatible XHTML tag 0345 if (strpos($tagEnd, '/') !== false) { 0346 $tagEnd = " $tagEnd"; 0347 } 0348 0349 // Return the filtered tag 0350 return $tagStart . $tagName . $tagAttributes . $tagEnd; 0351 } 0352 }