wp/wp-includes/SimplePie/src/Sanitize.php
changeset 22 8c2e4d02f4ef
equal deleted inserted replaced
21:48c4eec2b7e6 22:8c2e4d02f4ef
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * SimplePie
       
     5  *
       
     6  * A PHP-Based RSS and Atom Feed Framework.
       
     7  * Takes the hard work out of managing a complete RSS/Atom solution.
       
     8  *
       
     9  * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
       
    10  * All rights reserved.
       
    11  *
       
    12  * Redistribution and use in source and binary forms, with or without modification, are
       
    13  * permitted provided that the following conditions are met:
       
    14  *
       
    15  * 	* Redistributions of source code must retain the above copyright notice, this list of
       
    16  * 	  conditions and the following disclaimer.
       
    17  *
       
    18  * 	* Redistributions in binary form must reproduce the above copyright notice, this list
       
    19  * 	  of conditions and the following disclaimer in the documentation and/or other materials
       
    20  * 	  provided with the distribution.
       
    21  *
       
    22  * 	* Neither the name of the SimplePie Team nor the names of its contributors may be used
       
    23  * 	  to endorse or promote products derived from this software without specific prior
       
    24  * 	  written permission.
       
    25  *
       
    26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
       
    27  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
       
    28  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
       
    29  * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
       
    30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
       
    31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
       
    33  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
       
    34  * POSSIBILITY OF SUCH DAMAGE.
       
    35  *
       
    36  * @package SimplePie
       
    37  * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
       
    38  * @author Ryan Parman
       
    39  * @author Sam Sneddon
       
    40  * @author Ryan McCue
       
    41  * @link http://simplepie.org/ SimplePie
       
    42  * @license http://www.opensource.org/licenses/bsd-license.php BSD License
       
    43  */
       
    44 
       
    45 namespace SimplePie;
       
    46 
       
    47 use InvalidArgumentException;
       
    48 use SimplePie\Cache\Base;
       
    49 use SimplePie\Cache\BaseDataCache;
       
    50 use SimplePie\Cache\CallableNameFilter;
       
    51 use SimplePie\Cache\DataCache;
       
    52 use SimplePie\Cache\NameFilter;
       
    53 
       
    54 /**
       
    55  * Used for data cleanup and post-processing
       
    56  *
       
    57  *
       
    58  * This class can be overloaded with {@see \SimplePie\SimplePie::set_sanitize_class()}
       
    59  *
       
    60  * @package SimplePie
       
    61  * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags
       
    62  */
       
    63 class Sanitize implements RegistryAware
       
    64 {
       
    65     // Private vars
       
    66     public $base;
       
    67 
       
    68     // Options
       
    69     public $remove_div = true;
       
    70     public $image_handler = '';
       
    71     public $strip_htmltags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'];
       
    72     public $encode_instead_of_strip = false;
       
    73     public $strip_attributes = ['bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'];
       
    74     public $rename_attributes = [];
       
    75     public $add_attributes = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']];
       
    76     public $strip_comments = false;
       
    77     public $output_encoding = 'UTF-8';
       
    78     public $enable_cache = true;
       
    79     public $cache_location = './cache';
       
    80     public $cache_name_function = 'md5';
       
    81 
       
    82     /**
       
    83      * @var NameFilter
       
    84      */
       
    85     private $cache_namefilter;
       
    86     public $timeout = 10;
       
    87     public $useragent = '';
       
    88     public $force_fsockopen = false;
       
    89     public $replace_url_attributes = null;
       
    90     public $registry;
       
    91 
       
    92     /**
       
    93      * @var DataCache|null
       
    94      */
       
    95     private $cache = null;
       
    96 
       
    97     /**
       
    98      * @var int Cache duration (in seconds)
       
    99      */
       
   100     private $cache_duration = 3600;
       
   101 
       
   102     /**
       
   103      * List of domains for which to force HTTPS.
       
   104      * @see \SimplePie\Sanitize::set_https_domains()
       
   105      * Array is a tree split at DNS levels. Example:
       
   106      * array('biz' => true, 'com' => array('example' => true), 'net' => array('example' => array('www' => true)))
       
   107      */
       
   108     public $https_domains = [];
       
   109 
       
   110     public function __construct()
       
   111     {
       
   112         // Set defaults
       
   113         $this->set_url_replacements(null);
       
   114     }
       
   115 
       
   116     public function remove_div($enable = true)
       
   117     {
       
   118         $this->remove_div = (bool) $enable;
       
   119     }
       
   120 
       
   121     public function set_image_handler($page = false)
       
   122     {
       
   123         if ($page) {
       
   124             $this->image_handler = (string) $page;
       
   125         } else {
       
   126             $this->image_handler = false;
       
   127         }
       
   128     }
       
   129 
       
   130     public function set_registry(\SimplePie\Registry $registry)/* : void */
       
   131     {
       
   132         $this->registry = $registry;
       
   133     }
       
   134 
       
   135     public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie\Cache', ?DataCache $cache = null)
       
   136     {
       
   137         if (isset($enable_cache)) {
       
   138             $this->enable_cache = (bool) $enable_cache;
       
   139         }
       
   140 
       
   141         if ($cache_location) {
       
   142             $this->cache_location = (string) $cache_location;
       
   143         }
       
   144 
       
   145         if (!is_string($cache_name_function) && !is_object($cache_name_function) && !$cache_name_function instanceof NameFilter) {
       
   146             throw new InvalidArgumentException(sprintf(
       
   147                 '%s(): Argument #3 ($cache_name_function) must be of type %s',
       
   148                 __METHOD__,
       
   149                 NameFilter::class
       
   150             ), 1);
       
   151         }
       
   152 
       
   153         // BC: $cache_name_function could be a callable as string
       
   154         if (is_string($cache_name_function)) {
       
   155             // trigger_error(sprintf('Providing $cache_name_function as string in "%s()" is deprecated since SimplePie 1.8.0, provide as "%s" instead.', __METHOD__, NameFilter::class), \E_USER_DEPRECATED);
       
   156             $this->cache_name_function = (string) $cache_name_function;
       
   157 
       
   158             $cache_name_function = new CallableNameFilter($cache_name_function);
       
   159         }
       
   160 
       
   161         $this->cache_namefilter = $cache_name_function;
       
   162 
       
   163         if ($cache !== null) {
       
   164             $this->cache = $cache;
       
   165         }
       
   166     }
       
   167 
       
   168     public function pass_file_data($file_class = 'SimplePie\File', $timeout = 10, $useragent = '', $force_fsockopen = false)
       
   169     {
       
   170         if ($timeout) {
       
   171             $this->timeout = (string) $timeout;
       
   172         }
       
   173 
       
   174         if ($useragent) {
       
   175             $this->useragent = (string) $useragent;
       
   176         }
       
   177 
       
   178         if ($force_fsockopen) {
       
   179             $this->force_fsockopen = (string) $force_fsockopen;
       
   180         }
       
   181     }
       
   182 
       
   183     public function strip_htmltags($tags = ['base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'])
       
   184     {
       
   185         if ($tags) {
       
   186             if (is_array($tags)) {
       
   187                 $this->strip_htmltags = $tags;
       
   188             } else {
       
   189                 $this->strip_htmltags = explode(',', $tags);
       
   190             }
       
   191         } else {
       
   192             $this->strip_htmltags = false;
       
   193         }
       
   194     }
       
   195 
       
   196     public function encode_instead_of_strip($encode = false)
       
   197     {
       
   198         $this->encode_instead_of_strip = (bool) $encode;
       
   199     }
       
   200 
       
   201     public function rename_attributes($attribs = [])
       
   202     {
       
   203         if ($attribs) {
       
   204             if (is_array($attribs)) {
       
   205                 $this->rename_attributes = $attribs;
       
   206             } else {
       
   207                 $this->rename_attributes = explode(',', $attribs);
       
   208             }
       
   209         } else {
       
   210             $this->rename_attributes = false;
       
   211         }
       
   212     }
       
   213 
       
   214     public function strip_attributes($attribs = ['bgsound', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'])
       
   215     {
       
   216         if ($attribs) {
       
   217             if (is_array($attribs)) {
       
   218                 $this->strip_attributes = $attribs;
       
   219             } else {
       
   220                 $this->strip_attributes = explode(',', $attribs);
       
   221             }
       
   222         } else {
       
   223             $this->strip_attributes = false;
       
   224         }
       
   225     }
       
   226 
       
   227     public function add_attributes($attribs = ['audio' => ['preload' => 'none'], 'iframe' => ['sandbox' => 'allow-scripts allow-same-origin'], 'video' => ['preload' => 'none']])
       
   228     {
       
   229         if ($attribs) {
       
   230             if (is_array($attribs)) {
       
   231                 $this->add_attributes = $attribs;
       
   232             } else {
       
   233                 $this->add_attributes = explode(',', $attribs);
       
   234             }
       
   235         } else {
       
   236             $this->add_attributes = false;
       
   237         }
       
   238     }
       
   239 
       
   240     public function strip_comments($strip = false)
       
   241     {
       
   242         $this->strip_comments = (bool) $strip;
       
   243     }
       
   244 
       
   245     public function set_output_encoding($encoding = 'UTF-8')
       
   246     {
       
   247         $this->output_encoding = (string) $encoding;
       
   248     }
       
   249 
       
   250     /**
       
   251      * Set element/attribute key/value pairs of HTML attributes
       
   252      * containing URLs that need to be resolved relative to the feed
       
   253      *
       
   254      * Defaults to |a|@href, |area|@href, |audio|@src, |blockquote|@cite,
       
   255      * |del|@cite, |form|@action, |img|@longdesc, |img|@src, |input|@src,
       
   256      * |ins|@cite, |q|@cite, |source|@src, |video|@src
       
   257      *
       
   258      * @since 1.0
       
   259      * @param array|null $element_attribute Element/attribute key/value pairs, null for default
       
   260      */
       
   261     public function set_url_replacements($element_attribute = null)
       
   262     {
       
   263         if ($element_attribute === null) {
       
   264             $element_attribute = [
       
   265                 'a' => 'href',
       
   266                 'area' => 'href',
       
   267                 'audio' => 'src',
       
   268                 'blockquote' => 'cite',
       
   269                 'del' => 'cite',
       
   270                 'form' => 'action',
       
   271                 'img' => [
       
   272                     'longdesc',
       
   273                     'src'
       
   274                 ],
       
   275                 'input' => 'src',
       
   276                 'ins' => 'cite',
       
   277                 'q' => 'cite',
       
   278                 'source' => 'src',
       
   279                 'video' => [
       
   280                     'poster',
       
   281                     'src'
       
   282                 ]
       
   283             ];
       
   284         }
       
   285         $this->replace_url_attributes = (array) $element_attribute;
       
   286     }
       
   287 
       
   288     /**
       
   289      * Set the list of domains for which to force HTTPS.
       
   290      * @see \SimplePie\Misc::https_url()
       
   291      * Example array('biz', 'example.com', 'example.org', 'www.example.net');
       
   292      */
       
   293     public function set_https_domains($domains)
       
   294     {
       
   295         $this->https_domains = [];
       
   296         foreach ($domains as $domain) {
       
   297             $domain = trim($domain, ". \t\n\r\0\x0B");
       
   298             $segments = array_reverse(explode('.', $domain));
       
   299             $node = &$this->https_domains;
       
   300             foreach ($segments as $segment) {//Build a tree
       
   301                 if ($node === true) {
       
   302                     break;
       
   303                 }
       
   304                 if (!isset($node[$segment])) {
       
   305                     $node[$segment] = [];
       
   306                 }
       
   307                 $node = &$node[$segment];
       
   308             }
       
   309             $node = true;
       
   310         }
       
   311     }
       
   312 
       
   313     /**
       
   314      * Check if the domain is in the list of forced HTTPS.
       
   315      */
       
   316     protected function is_https_domain($domain)
       
   317     {
       
   318         $domain = trim($domain, '. ');
       
   319         $segments = array_reverse(explode('.', $domain));
       
   320         $node = &$this->https_domains;
       
   321         foreach ($segments as $segment) {//Explore the tree
       
   322             if (isset($node[$segment])) {
       
   323                 $node = &$node[$segment];
       
   324             } else {
       
   325                 break;
       
   326             }
       
   327         }
       
   328         return $node === true;
       
   329     }
       
   330 
       
   331     /**
       
   332      * Force HTTPS for selected Web sites.
       
   333      */
       
   334     public function https_url($url)
       
   335     {
       
   336         return (strtolower(substr($url, 0, 7)) === 'http://') &&
       
   337             $this->is_https_domain(parse_url($url, PHP_URL_HOST)) ?
       
   338             substr_replace($url, 's', 4, 0) : //Add the 's' to HTTPS
       
   339             $url;
       
   340     }
       
   341 
       
   342     public function sanitize($data, $type, $base = '')
       
   343     {
       
   344         $data = trim($data);
       
   345         if ($data !== '' || $type & \SimplePie\SimplePie::CONSTRUCT_IRI) {
       
   346             if ($type & \SimplePie\SimplePie::CONSTRUCT_MAYBE_HTML) {
       
   347                 if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . \SimplePie\SimplePie::PCRE_HTML_ATTRIBUTE . '>)/', $data)) {
       
   348                     $type |= \SimplePie\SimplePie::CONSTRUCT_HTML;
       
   349                 } else {
       
   350                     $type |= \SimplePie\SimplePie::CONSTRUCT_TEXT;
       
   351                 }
       
   352             }
       
   353 
       
   354             if ($type & \SimplePie\SimplePie::CONSTRUCT_BASE64) {
       
   355                 $data = base64_decode($data);
       
   356             }
       
   357 
       
   358             if ($type & (\SimplePie\SimplePie::CONSTRUCT_HTML | \SimplePie\SimplePie::CONSTRUCT_XHTML)) {
       
   359                 if (!class_exists('DOMDocument')) {
       
   360                     throw new \SimplePie\Exception('DOMDocument not found, unable to use sanitizer');
       
   361                 }
       
   362                 $document = new \DOMDocument();
       
   363                 $document->encoding = 'UTF-8';
       
   364 
       
   365                 $data = $this->preprocess($data, $type);
       
   366 
       
   367                 set_error_handler(['SimplePie\Misc', 'silence_errors']);
       
   368                 $document->loadHTML($data);
       
   369                 restore_error_handler();
       
   370 
       
   371                 $xpath = new \DOMXPath($document);
       
   372 
       
   373                 // Strip comments
       
   374                 if ($this->strip_comments) {
       
   375                     $comments = $xpath->query('//comment()');
       
   376 
       
   377                     foreach ($comments as $comment) {
       
   378                         $comment->parentNode->removeChild($comment);
       
   379                     }
       
   380                 }
       
   381 
       
   382                 // Strip out HTML tags and attributes that might cause various security problems.
       
   383                 // Based on recommendations by Mark Pilgrim at:
       
   384                 // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely
       
   385                 if ($this->strip_htmltags) {
       
   386                     foreach ($this->strip_htmltags as $tag) {
       
   387                         $this->strip_tag($tag, $document, $xpath, $type);
       
   388                     }
       
   389                 }
       
   390 
       
   391                 if ($this->rename_attributes) {
       
   392                     foreach ($this->rename_attributes as $attrib) {
       
   393                         $this->rename_attr($attrib, $xpath);
       
   394                     }
       
   395                 }
       
   396 
       
   397                 if ($this->strip_attributes) {
       
   398                     foreach ($this->strip_attributes as $attrib) {
       
   399                         $this->strip_attr($attrib, $xpath);
       
   400                     }
       
   401                 }
       
   402 
       
   403                 if ($this->add_attributes) {
       
   404                     foreach ($this->add_attributes as $tag => $valuePairs) {
       
   405                         $this->add_attr($tag, $valuePairs, $document);
       
   406                     }
       
   407                 }
       
   408 
       
   409                 // Replace relative URLs
       
   410                 $this->base = $base;
       
   411                 foreach ($this->replace_url_attributes as $element => $attributes) {
       
   412                     $this->replace_urls($document, $element, $attributes);
       
   413                 }
       
   414 
       
   415                 // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
       
   416                 if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) {
       
   417                     $images = $document->getElementsByTagName('img');
       
   418 
       
   419                     foreach ($images as $img) {
       
   420                         if ($img->hasAttribute('src')) {
       
   421                             $image_url = $this->cache_namefilter->filter($img->getAttribute('src'));
       
   422                             $cache = $this->get_cache($image_url);
       
   423 
       
   424                             if ($cache->get_data($image_url, false)) {
       
   425                                 $img->setAttribute('src', $this->image_handler . $image_url);
       
   426                             } else {
       
   427                                 $file = $this->registry->create(File::class, [$img->getAttribute('src'), $this->timeout, 5, ['X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']], $this->useragent, $this->force_fsockopen]);
       
   428                                 $headers = $file->headers;
       
   429 
       
   430                                 if ($file->success && ($file->method & \SimplePie\SimplePie::FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) {
       
   431                                     if ($cache->set_data($image_url, ['headers' => $file->headers, 'body' => $file->body], $this->cache_duration)) {
       
   432                                         $img->setAttribute('src', $this->image_handler . $image_url);
       
   433                                     } else {
       
   434                                         trigger_error("$this->cache_location is not writable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING);
       
   435                                     }
       
   436                                 }
       
   437                             }
       
   438                         }
       
   439                     }
       
   440                 }
       
   441 
       
   442                 // Get content node
       
   443                 $div = $document->getElementsByTagName('body')->item(0)->firstChild;
       
   444                 // Finally, convert to a HTML string
       
   445                 $data = trim($document->saveHTML($div));
       
   446 
       
   447                 if ($this->remove_div) {
       
   448                     $data = preg_replace('/^<div' . \SimplePie\SimplePie::PCRE_XML_ATTRIBUTE . '>/', '', $data);
       
   449                     $data = preg_replace('/<\/div>$/', '', $data);
       
   450                 } else {
       
   451                     $data = preg_replace('/^<div' . \SimplePie\SimplePie::PCRE_XML_ATTRIBUTE . '>/', '<div>', $data);
       
   452                 }
       
   453 
       
   454                 $data = str_replace('</source>', '', $data);
       
   455             }
       
   456 
       
   457             if ($type & \SimplePie\SimplePie::CONSTRUCT_IRI) {
       
   458                 $absolute = $this->registry->call(Misc::class, 'absolutize_url', [$data, $base]);
       
   459                 if ($absolute !== false) {
       
   460                     $data = $absolute;
       
   461                 }
       
   462             }
       
   463 
       
   464             if ($type & (\SimplePie\SimplePie::CONSTRUCT_TEXT | \SimplePie\SimplePie::CONSTRUCT_IRI)) {
       
   465                 $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
       
   466             }
       
   467 
       
   468             if ($this->output_encoding !== 'UTF-8') {
       
   469                 $data = $this->registry->call(Misc::class, 'change_encoding', [$data, 'UTF-8', $this->output_encoding]);
       
   470             }
       
   471         }
       
   472         return $data;
       
   473     }
       
   474 
       
   475     protected function preprocess($html, $type)
       
   476     {
       
   477         $ret = '';
       
   478         $html = preg_replace('%</?(?:html|body)[^>]*?'.'>%is', '', $html);
       
   479         if ($type & ~\SimplePie\SimplePie::CONSTRUCT_XHTML) {
       
   480             // Atom XHTML constructs are wrapped with a div by default
       
   481             // Note: No protection if $html contains a stray </div>!
       
   482             $html = '<div>' . $html . '</div>';
       
   483             $ret .= '<!DOCTYPE html>';
       
   484             $content_type = 'text/html';
       
   485         } else {
       
   486             $ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
       
   487             $content_type = 'application/xhtml+xml';
       
   488         }
       
   489 
       
   490         $ret .= '<html><head>';
       
   491         $ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />';
       
   492         $ret .= '</head><body>' . $html . '</body></html>';
       
   493         return $ret;
       
   494     }
       
   495 
       
   496     public function replace_urls($document, $tag, $attributes)
       
   497     {
       
   498         if (!is_array($attributes)) {
       
   499             $attributes = [$attributes];
       
   500         }
       
   501 
       
   502         if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) {
       
   503             $elements = $document->getElementsByTagName($tag);
       
   504             foreach ($elements as $element) {
       
   505                 foreach ($attributes as $attribute) {
       
   506                     if ($element->hasAttribute($attribute)) {
       
   507                         $value = $this->registry->call(Misc::class, 'absolutize_url', [$element->getAttribute($attribute), $this->base]);
       
   508                         if ($value !== false) {
       
   509                             $value = $this->https_url($value);
       
   510                             $element->setAttribute($attribute, $value);
       
   511                         }
       
   512                     }
       
   513                 }
       
   514             }
       
   515         }
       
   516     }
       
   517 
       
   518     public function do_strip_htmltags($match)
       
   519     {
       
   520         if ($this->encode_instead_of_strip) {
       
   521             if (isset($match[4]) && !in_array(strtolower($match[1]), ['script', 'style'])) {
       
   522                 $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8');
       
   523                 $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8');
       
   524                 return "&lt;$match[1]$match[2]&gt;$match[3]&lt;/$match[1]&gt;";
       
   525             } else {
       
   526                 return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8');
       
   527             }
       
   528         } elseif (isset($match[4]) && !in_array(strtolower($match[1]), ['script', 'style'])) {
       
   529             return $match[4];
       
   530         } else {
       
   531             return '';
       
   532         }
       
   533     }
       
   534 
       
   535     protected function strip_tag($tag, $document, $xpath, $type)
       
   536     {
       
   537         $elements = $xpath->query('body//' . $tag);
       
   538         if ($this->encode_instead_of_strip) {
       
   539             foreach ($elements as $element) {
       
   540                 $fragment = $document->createDocumentFragment();
       
   541 
       
   542                 // For elements which aren't script or style, include the tag itself
       
   543                 if (!in_array($tag, ['script', 'style'])) {
       
   544                     $text = '<' . $tag;
       
   545                     if ($element->hasAttributes()) {
       
   546                         $attrs = [];
       
   547                         foreach ($element->attributes as $name => $attr) {
       
   548                             $value = $attr->value;
       
   549 
       
   550                             // In XHTML, empty values should never exist, so we repeat the value
       
   551                             if (empty($value) && ($type & \SimplePie\SimplePie::CONSTRUCT_XHTML)) {
       
   552                                 $value = $name;
       
   553                             }
       
   554                             // For HTML, empty is fine
       
   555                             elseif (empty($value) && ($type & \SimplePie\SimplePie::CONSTRUCT_HTML)) {
       
   556                                 $attrs[] = $name;
       
   557                                 continue;
       
   558                             }
       
   559 
       
   560                             // Standard attribute text
       
   561                             $attrs[] = $name . '="' . $attr->value . '"';
       
   562                         }
       
   563                         $text .= ' ' . implode(' ', $attrs);
       
   564                     }
       
   565                     $text .= '>';
       
   566                     $fragment->appendChild(new \DOMText($text));
       
   567                 }
       
   568 
       
   569                 $number = $element->childNodes->length;
       
   570                 for ($i = $number; $i > 0; $i--) {
       
   571                     $child = $element->childNodes->item(0);
       
   572                     $fragment->appendChild($child);
       
   573                 }
       
   574 
       
   575                 if (!in_array($tag, ['script', 'style'])) {
       
   576                     $fragment->appendChild(new \DOMText('</' . $tag . '>'));
       
   577                 }
       
   578 
       
   579                 $element->parentNode->replaceChild($fragment, $element);
       
   580             }
       
   581 
       
   582             return;
       
   583         } elseif (in_array($tag, ['script', 'style'])) {
       
   584             foreach ($elements as $element) {
       
   585                 $element->parentNode->removeChild($element);
       
   586             }
       
   587 
       
   588             return;
       
   589         } else {
       
   590             foreach ($elements as $element) {
       
   591                 $fragment = $document->createDocumentFragment();
       
   592                 $number = $element->childNodes->length;
       
   593                 for ($i = $number; $i > 0; $i--) {
       
   594                     $child = $element->childNodes->item(0);
       
   595                     $fragment->appendChild($child);
       
   596                 }
       
   597 
       
   598                 $element->parentNode->replaceChild($fragment, $element);
       
   599             }
       
   600         }
       
   601     }
       
   602 
       
   603     protected function strip_attr($attrib, $xpath)
       
   604     {
       
   605         $elements = $xpath->query('//*[@' . $attrib . ']');
       
   606 
       
   607         foreach ($elements as $element) {
       
   608             $element->removeAttribute($attrib);
       
   609         }
       
   610     }
       
   611 
       
   612     protected function rename_attr($attrib, $xpath)
       
   613     {
       
   614         $elements = $xpath->query('//*[@' . $attrib . ']');
       
   615 
       
   616         foreach ($elements as $element) {
       
   617             $element->setAttribute('data-sanitized-' . $attrib, $element->getAttribute($attrib));
       
   618             $element->removeAttribute($attrib);
       
   619         }
       
   620     }
       
   621 
       
   622     protected function add_attr($tag, $valuePairs, $document)
       
   623     {
       
   624         $elements = $document->getElementsByTagName($tag);
       
   625         foreach ($elements as $element) {
       
   626             foreach ($valuePairs as $attrib => $value) {
       
   627                 $element->setAttribute($attrib, $value);
       
   628             }
       
   629         }
       
   630     }
       
   631 
       
   632     /**
       
   633      * Get a DataCache
       
   634      *
       
   635      * @param string $image_url Only needed for BC, can be removed in SimplePie 2.0.0
       
   636      *
       
   637      * @return DataCache
       
   638      */
       
   639     private function get_cache($image_url = '')
       
   640     {
       
   641         if ($this->cache === null) {
       
   642             // @trigger_error(sprintf('Not providing as PSR-16 cache implementation is deprecated since SimplePie 1.8.0, please use "SimplePie\SimplePie::set_cache()".'), \E_USER_DEPRECATED);
       
   643             $cache = $this->registry->call(Cache::class, 'get_handler', [
       
   644                 $this->cache_location,
       
   645                 $image_url,
       
   646                 Base::TYPE_IMAGE
       
   647             ]);
       
   648 
       
   649             return new BaseDataCache($cache);
       
   650         }
       
   651 
       
   652         return $this->cache;
       
   653     }
       
   654 }
       
   655 
       
   656 class_alias('SimplePie\Sanitize', 'SimplePie_Sanitize');