PageRenderTime 71ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 2ms

/yii/framework/vendors/htmlpurifier/HTMLPurifier.standalone.php

https://bitbucket.org/ddonthula/zurmouser
PHP | 14492 lines | 7496 code | 2503 blank | 4493 comment | 1442 complexity | 7d678df995783a3e3f130996946c003d MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, GPL-2.0, GPL-3.0, BSD-3-Clause, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * @file
  4. * This file was auto-generated by generate-includes.php and includes all of
  5. * the core files required by HTML Purifier. Use this if performance is a
  6. * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
  7. * FILE, changes will be overwritten the next time the script is run.
  8. *
  9. * @version 4.4.0
  10. *
  11. * @warning
  12. * You must *not* include any other HTML Purifier files before this file,
  13. * because 'require' not 'require_once' is used.
  14. *
  15. * @warning
  16. * This file requires that the include path contains the HTML Purifier
  17. * library directory; this is not auto-set.
  18. */
  19. /*! @mainpage
  20. *
  21. * HTML Purifier is an HTML filter that will take an arbitrary snippet of
  22. * HTML and rigorously test, validate and filter it into a version that
  23. * is safe for output onto webpages. It achieves this by:
  24. *
  25. * -# Lexing (parsing into tokens) the document,
  26. * -# Executing various strategies on the tokens:
  27. * -# Removing all elements not in the whitelist,
  28. * -# Making the tokens well-formed,
  29. * -# Fixing the nesting of the nodes, and
  30. * -# Validating attributes of the nodes; and
  31. * -# Generating HTML from the purified tokens.
  32. *
  33. * However, most users will only need to interface with the HTMLPurifier
  34. * and HTMLPurifier_Config.
  35. */
  36. /*
  37. HTML Purifier 4.4.0 - Standards Compliant HTML Filtering
  38. Copyright (C) 2006-2008 Edward Z. Yang
  39. This library is free software; you can redistribute it and/or
  40. modify it under the terms of the GNU Lesser General Public
  41. License as published by the Free Software Foundation; either
  42. version 2.1 of the License, or (at your option) any later version.
  43. This library is distributed in the hope that it will be useful,
  44. but WITHOUT ANY WARRANTY; without even the implied warranty of
  45. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  46. Lesser General Public License for more details.
  47. You should have received a copy of the GNU Lesser General Public
  48. License along with this library; if not, write to the Free Software
  49. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  50. */
  51. /**
  52. * Facade that coordinates HTML Purifier's subsystems in order to purify HTML.
  53. *
  54. * @note There are several points in which configuration can be specified
  55. * for HTML Purifier. The precedence of these (from lowest to
  56. * highest) is as follows:
  57. * -# Instance: new HTMLPurifier($config)
  58. * -# Invocation: purify($html, $config)
  59. * These configurations are entirely independent of each other and
  60. * are *not* merged (this behavior may change in the future).
  61. *
  62. * @todo We need an easier way to inject strategies using the configuration
  63. * object.
  64. */
  65. class HTMLPurifier
  66. {
  67. /** Version of HTML Purifier */
  68. public $version = '4.4.0';
  69. /** Constant with version of HTML Purifier */
  70. const VERSION = '4.4.0';
  71. /** Global configuration object */
  72. public $config;
  73. /** Array of extra HTMLPurifier_Filter objects to run on HTML, for backwards compatibility */
  74. private $filters = array();
  75. /** Single instance of HTML Purifier */
  76. private static $instance;
  77. protected $strategy, $generator;
  78. /**
  79. * Resultant HTMLPurifier_Context of last run purification. Is an array
  80. * of contexts if the last called method was purifyArray().
  81. */
  82. public $context;
  83. /**
  84. * Initializes the purifier.
  85. * @param $config Optional HTMLPurifier_Config object for all instances of
  86. * the purifier, if omitted, a default configuration is
  87. * supplied (which can be overridden on a per-use basis).
  88. * The parameter can also be any type that
  89. * HTMLPurifier_Config::create() supports.
  90. */
  91. public function __construct($config = null) {
  92. $this->config = HTMLPurifier_Config::create($config);
  93. $this->strategy = new HTMLPurifier_Strategy_Core();
  94. }
  95. /**
  96. * Adds a filter to process the output. First come first serve
  97. * @param $filter HTMLPurifier_Filter object
  98. */
  99. public function addFilter($filter) {
  100. trigger_error('HTMLPurifier->addFilter() is deprecated, use configuration directives in the Filter namespace or Filter.Custom', E_USER_WARNING);
  101. $this->filters[] = $filter;
  102. }
  103. /**
  104. * Filters an HTML snippet/document to be XSS-free and standards-compliant.
  105. *
  106. * @param $html String of HTML to purify
  107. * @param $config HTMLPurifier_Config object for this operation, if omitted,
  108. * defaults to the config object specified during this
  109. * object's construction. The parameter can also be any type
  110. * that HTMLPurifier_Config::create() supports.
  111. * @return Purified HTML
  112. */
  113. public function purify($html, $config = null) {
  114. // :TODO: make the config merge in, instead of replace
  115. $config = $config ? HTMLPurifier_Config::create($config) : $this->config;
  116. // implementation is partially environment dependant, partially
  117. // configuration dependant
  118. $lexer = HTMLPurifier_Lexer::create($config);
  119. $context = new HTMLPurifier_Context();
  120. // setup HTML generator
  121. $this->generator = new HTMLPurifier_Generator($config, $context);
  122. $context->register('Generator', $this->generator);
  123. // set up global context variables
  124. if ($config->get('Core.CollectErrors')) {
  125. // may get moved out if other facilities use it
  126. $language_factory = HTMLPurifier_LanguageFactory::instance();
  127. $language = $language_factory->create($config, $context);
  128. $context->register('Locale', $language);
  129. $error_collector = new HTMLPurifier_ErrorCollector($context);
  130. $context->register('ErrorCollector', $error_collector);
  131. }
  132. // setup id_accumulator context, necessary due to the fact that
  133. // AttrValidator can be called from many places
  134. $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
  135. $context->register('IDAccumulator', $id_accumulator);
  136. $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);
  137. // setup filters
  138. $filter_flags = $config->getBatch('Filter');
  139. $custom_filters = $filter_flags['Custom'];
  140. unset($filter_flags['Custom']);
  141. $filters = array();
  142. foreach ($filter_flags as $filter => $flag) {
  143. if (!$flag) continue;
  144. if (strpos($filter, '.') !== false) continue;
  145. $class = "HTMLPurifier_Filter_$filter";
  146. $filters[] = new $class;
  147. }
  148. foreach ($custom_filters as $filter) {
  149. // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
  150. $filters[] = $filter;
  151. }
  152. $filters = array_merge($filters, $this->filters);
  153. // maybe prepare(), but later
  154. for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
  155. $html = $filters[$i]->preFilter($html, $config, $context);
  156. }
  157. // purified HTML
  158. $html =
  159. $this->generator->generateFromTokens(
  160. // list of tokens
  161. $this->strategy->execute(
  162. // list of un-purified tokens
  163. $lexer->tokenizeHTML(
  164. // un-purified HTML
  165. $html, $config, $context
  166. ),
  167. $config, $context
  168. )
  169. );
  170. for ($i = $filter_size - 1; $i >= 0; $i--) {
  171. $html = $filters[$i]->postFilter($html, $config, $context);
  172. }
  173. $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
  174. $this->context =& $context;
  175. return $html;
  176. }
  177. /**
  178. * Filters an array of HTML snippets
  179. * @param $config Optional HTMLPurifier_Config object for this operation.
  180. * See HTMLPurifier::purify() for more details.
  181. * @return Array of purified HTML
  182. */
  183. public function purifyArray($array_of_html, $config = null) {
  184. $context_array = array();
  185. foreach ($array_of_html as $key => $html) {
  186. $array_of_html[$key] = $this->purify($html, $config);
  187. $context_array[$key] = $this->context;
  188. }
  189. $this->context = $context_array;
  190. return $array_of_html;
  191. }
  192. /**
  193. * Singleton for enforcing just one HTML Purifier in your system
  194. * @param $prototype Optional prototype HTMLPurifier instance to
  195. * overload singleton with, or HTMLPurifier_Config
  196. * instance to configure the generated version with.
  197. */
  198. public static function instance($prototype = null) {
  199. if (!self::$instance || $prototype) {
  200. if ($prototype instanceof HTMLPurifier) {
  201. self::$instance = $prototype;
  202. } elseif ($prototype) {
  203. self::$instance = new HTMLPurifier($prototype);
  204. } else {
  205. self::$instance = new HTMLPurifier();
  206. }
  207. }
  208. return self::$instance;
  209. }
  210. /**
  211. * @note Backwards compatibility, see instance()
  212. */
  213. public static function getInstance($prototype = null) {
  214. return HTMLPurifier::instance($prototype);
  215. }
  216. }
  217. /**
  218. * Defines common attribute collections that modules reference
  219. */
  220. class HTMLPurifier_AttrCollections
  221. {
  222. /**
  223. * Associative array of attribute collections, indexed by name
  224. */
  225. public $info = array();
  226. /**
  227. * Performs all expansions on internal data for use by other inclusions
  228. * It also collects all attribute collection extensions from
  229. * modules
  230. * @param $attr_types HTMLPurifier_AttrTypes instance
  231. * @param $modules Hash array of HTMLPurifier_HTMLModule members
  232. */
  233. public function __construct($attr_types, $modules) {
  234. // load extensions from the modules
  235. foreach ($modules as $module) {
  236. foreach ($module->attr_collections as $coll_i => $coll) {
  237. if (!isset($this->info[$coll_i])) {
  238. $this->info[$coll_i] = array();
  239. }
  240. foreach ($coll as $attr_i => $attr) {
  241. if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
  242. // merge in includes
  243. $this->info[$coll_i][$attr_i] = array_merge(
  244. $this->info[$coll_i][$attr_i], $attr);
  245. continue;
  246. }
  247. $this->info[$coll_i][$attr_i] = $attr;
  248. }
  249. }
  250. }
  251. // perform internal expansions and inclusions
  252. foreach ($this->info as $name => $attr) {
  253. // merge attribute collections that include others
  254. $this->performInclusions($this->info[$name]);
  255. // replace string identifiers with actual attribute objects
  256. $this->expandIdentifiers($this->info[$name], $attr_types);
  257. }
  258. }
  259. /**
  260. * Takes a reference to an attribute associative array and performs
  261. * all inclusions specified by the zero index.
  262. * @param &$attr Reference to attribute array
  263. */
  264. public function performInclusions(&$attr) {
  265. if (!isset($attr[0])) return;
  266. $merge = $attr[0];
  267. $seen = array(); // recursion guard
  268. // loop through all the inclusions
  269. for ($i = 0; isset($merge[$i]); $i++) {
  270. if (isset($seen[$merge[$i]])) continue;
  271. $seen[$merge[$i]] = true;
  272. // foreach attribute of the inclusion, copy it over
  273. if (!isset($this->info[$merge[$i]])) continue;
  274. foreach ($this->info[$merge[$i]] as $key => $value) {
  275. if (isset($attr[$key])) continue; // also catches more inclusions
  276. $attr[$key] = $value;
  277. }
  278. if (isset($this->info[$merge[$i]][0])) {
  279. // recursion
  280. $merge = array_merge($merge, $this->info[$merge[$i]][0]);
  281. }
  282. }
  283. unset($attr[0]);
  284. }
  285. /**
  286. * Expands all string identifiers in an attribute array by replacing
  287. * them with the appropriate values inside HTMLPurifier_AttrTypes
  288. * @param &$attr Reference to attribute array
  289. * @param $attr_types HTMLPurifier_AttrTypes instance
  290. */
  291. public function expandIdentifiers(&$attr, $attr_types) {
  292. // because foreach will process new elements we add, make sure we
  293. // skip duplicates
  294. $processed = array();
  295. foreach ($attr as $def_i => $def) {
  296. // skip inclusions
  297. if ($def_i === 0) continue;
  298. if (isset($processed[$def_i])) continue;
  299. // determine whether or not attribute is required
  300. if ($required = (strpos($def_i, '*') !== false)) {
  301. // rename the definition
  302. unset($attr[$def_i]);
  303. $def_i = trim($def_i, '*');
  304. $attr[$def_i] = $def;
  305. }
  306. $processed[$def_i] = true;
  307. // if we've already got a literal object, move on
  308. if (is_object($def)) {
  309. // preserve previous required
  310. $attr[$def_i]->required = ($required || $attr[$def_i]->required);
  311. continue;
  312. }
  313. if ($def === false) {
  314. unset($attr[$def_i]);
  315. continue;
  316. }
  317. if ($t = $attr_types->get($def)) {
  318. $attr[$def_i] = $t;
  319. $attr[$def_i]->required = $required;
  320. } else {
  321. unset($attr[$def_i]);
  322. }
  323. }
  324. }
  325. }
  326. /**
  327. * Base class for all validating attribute definitions.
  328. *
  329. * This family of classes forms the core for not only HTML attribute validation,
  330. * but also any sort of string that needs to be validated or cleaned (which
  331. * means CSS properties and composite definitions are defined here too).
  332. * Besides defining (through code) what precisely makes the string valid,
  333. * subclasses are also responsible for cleaning the code if possible.
  334. */
  335. abstract class HTMLPurifier_AttrDef
  336. {
  337. /**
  338. * Tells us whether or not an HTML attribute is minimized. Has no
  339. * meaning in other contexts.
  340. */
  341. public $minimized = false;
  342. /**
  343. * Tells us whether or not an HTML attribute is required. Has no
  344. * meaning in other contexts
  345. */
  346. public $required = false;
  347. /**
  348. * Validates and cleans passed string according to a definition.
  349. *
  350. * @param $string String to be validated and cleaned.
  351. * @param $config Mandatory HTMLPurifier_Config object.
  352. * @param $context Mandatory HTMLPurifier_AttrContext object.
  353. */
  354. abstract public function validate($string, $config, $context);
  355. /**
  356. * Convenience method that parses a string as if it were CDATA.
  357. *
  358. * This method process a string in the manner specified at
  359. * <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
  360. * leading and trailing whitespace, ignoring line feeds, and replacing
  361. * carriage returns and tabs with spaces. While most useful for HTML
  362. * attributes specified as CDATA, it can also be applied to most CSS
  363. * values.
  364. *
  365. * @note This method is not entirely standards compliant, as trim() removes
  366. * more types of whitespace than specified in the spec. In practice,
  367. * this is rarely a problem, as those extra characters usually have
  368. * already been removed by HTMLPurifier_Encoder.
  369. *
  370. * @warning This processing is inconsistent with XML's whitespace handling
  371. * as specified by section 3.3.3 and referenced XHTML 1.0 section
  372. * 4.7. However, note that we are NOT necessarily
  373. * parsing XML, thus, this behavior may still be correct. We
  374. * assume that newlines have been normalized.
  375. */
  376. public function parseCDATA($string) {
  377. $string = trim($string);
  378. $string = str_replace(array("\n", "\t", "\r"), ' ', $string);
  379. return $string;
  380. }
  381. /**
  382. * Factory method for creating this class from a string.
  383. * @param $string String construction info
  384. * @return Created AttrDef object corresponding to $string
  385. */
  386. public function make($string) {
  387. // default implementation, return a flyweight of this object.
  388. // If $string has an effect on the returned object (i.e. you
  389. // need to overload this method), it is best
  390. // to clone or instantiate new copies. (Instantiation is safer.)
  391. return $this;
  392. }
  393. /**
  394. * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
  395. * properly. THIS IS A HACK!
  396. */
  397. protected function mungeRgb($string) {
  398. return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
  399. }
  400. /**
  401. * Parses a possibly escaped CSS string and returns the "pure"
  402. * version of it.
  403. */
  404. protected function expandCSSEscape($string) {
  405. // flexibly parse it
  406. $ret = '';
  407. for ($i = 0, $c = strlen($string); $i < $c; $i++) {
  408. if ($string[$i] === '\\') {
  409. $i++;
  410. if ($i >= $c) {
  411. $ret .= '\\';
  412. break;
  413. }
  414. if (ctype_xdigit($string[$i])) {
  415. $code = $string[$i];
  416. for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
  417. if (!ctype_xdigit($string[$i])) break;
  418. $code .= $string[$i];
  419. }
  420. // We have to be extremely careful when adding
  421. // new characters, to make sure we're not breaking
  422. // the encoding.
  423. $char = HTMLPurifier_Encoder::unichr(hexdec($code));
  424. if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue;
  425. $ret .= $char;
  426. if ($i < $c && trim($string[$i]) !== '') $i--;
  427. continue;
  428. }
  429. if ($string[$i] === "\n") continue;
  430. }
  431. $ret .= $string[$i];
  432. }
  433. return $ret;
  434. }
  435. }
  436. /**
  437. * Processes an entire attribute array for corrections needing multiple values.
  438. *
  439. * Occasionally, a certain attribute will need to be removed and popped onto
  440. * another value. Instead of creating a complex return syntax for
  441. * HTMLPurifier_AttrDef, we just pass the whole attribute array to a
  442. * specialized object and have that do the special work. That is the
  443. * family of HTMLPurifier_AttrTransform.
  444. *
  445. * An attribute transformation can be assigned to run before or after
  446. * HTMLPurifier_AttrDef validation. See HTMLPurifier_HTMLDefinition for
  447. * more details.
  448. */
  449. abstract class HTMLPurifier_AttrTransform
  450. {
  451. /**
  452. * Abstract: makes changes to the attributes dependent on multiple values.
  453. *
  454. * @param $attr Assoc array of attributes, usually from
  455. * HTMLPurifier_Token_Tag::$attr
  456. * @param $config Mandatory HTMLPurifier_Config object.
  457. * @param $context Mandatory HTMLPurifier_Context object
  458. * @returns Processed attribute array.
  459. */
  460. abstract public function transform($attr, $config, $context);
  461. /**
  462. * Prepends CSS properties to the style attribute, creating the
  463. * attribute if it doesn't exist.
  464. * @param $attr Attribute array to process (passed by reference)
  465. * @param $css CSS to prepend
  466. */
  467. public function prependCSS(&$attr, $css) {
  468. $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
  469. $attr['style'] = $css . $attr['style'];
  470. }
  471. /**
  472. * Retrieves and removes an attribute
  473. * @param $attr Attribute array to process (passed by reference)
  474. * @param $key Key of attribute to confiscate
  475. */
  476. public function confiscateAttr(&$attr, $key) {
  477. if (!isset($attr[$key])) return null;
  478. $value = $attr[$key];
  479. unset($attr[$key]);
  480. return $value;
  481. }
  482. }
  483. /**
  484. * Provides lookup array of attribute types to HTMLPurifier_AttrDef objects
  485. */
  486. class HTMLPurifier_AttrTypes
  487. {
  488. /**
  489. * Lookup array of attribute string identifiers to concrete implementations
  490. */
  491. protected $info = array();
  492. /**
  493. * Constructs the info array, supplying default implementations for attribute
  494. * types.
  495. */
  496. public function __construct() {
  497. // XXX This is kind of poor, since we don't actually /clone/
  498. // instances; instead, we use the supplied make() attribute. So,
  499. // the underlying class must know how to deal with arguments.
  500. // With the old implementation of Enum, that ignored its
  501. // arguments when handling a make dispatch, the IAlign
  502. // definition wouldn't work.
  503. // pseudo-types, must be instantiated via shorthand
  504. $this->info['Enum'] = new HTMLPurifier_AttrDef_Enum();
  505. $this->info['Bool'] = new HTMLPurifier_AttrDef_HTML_Bool();
  506. $this->info['CDATA'] = new HTMLPurifier_AttrDef_Text();
  507. $this->info['ID'] = new HTMLPurifier_AttrDef_HTML_ID();
  508. $this->info['Length'] = new HTMLPurifier_AttrDef_HTML_Length();
  509. $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength();
  510. $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens();
  511. $this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels();
  512. $this->info['Text'] = new HTMLPurifier_AttrDef_Text();
  513. $this->info['URI'] = new HTMLPurifier_AttrDef_URI();
  514. $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
  515. $this->info['Color'] = new HTMLPurifier_AttrDef_HTML_Color();
  516. $this->info['IAlign'] = self::makeEnum('top,middle,bottom,left,right');
  517. $this->info['LAlign'] = self::makeEnum('top,bottom,left,right');
  518. $this->info['FrameTarget'] = new HTMLPurifier_AttrDef_HTML_FrameTarget();
  519. // unimplemented aliases
  520. $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
  521. $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text();
  522. $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
  523. $this->info['Character'] = new HTMLPurifier_AttrDef_Text();
  524. // "proprietary" types
  525. $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class();
  526. // number is really a positive integer (one or more digits)
  527. // FIXME: ^^ not always, see start and value of list items
  528. $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true);
  529. }
  530. private static function makeEnum($in) {
  531. return new HTMLPurifier_AttrDef_Clone(new HTMLPurifier_AttrDef_Enum(explode(',', $in)));
  532. }
  533. /**
  534. * Retrieves a type
  535. * @param $type String type name
  536. * @return Object AttrDef for type
  537. */
  538. public function get($type) {
  539. // determine if there is any extra info tacked on
  540. if (strpos($type, '#') !== false) list($type, $string) = explode('#', $type, 2);
  541. else $string = '';
  542. if (!isset($this->info[$type])) {
  543. trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR);
  544. return;
  545. }
  546. return $this->info[$type]->make($string);
  547. }
  548. /**
  549. * Sets a new implementation for a type
  550. * @param $type String type name
  551. * @param $impl Object AttrDef for type
  552. */
  553. public function set($type, $impl) {
  554. $this->info[$type] = $impl;
  555. }
  556. }
  557. /**
  558. * Validates the attributes of a token. Doesn't manage required attributes
  559. * very well. The only reason we factored this out was because RemoveForeignElements
  560. * also needed it besides ValidateAttributes.
  561. */
  562. class HTMLPurifier_AttrValidator
  563. {
  564. /**
  565. * Validates the attributes of a token, returning a modified token
  566. * that has valid tokens
  567. * @param $token Reference to token to validate. We require a reference
  568. * because the operation this class performs on the token are
  569. * not atomic, so the context CurrentToken to be updated
  570. * throughout
  571. * @param $config Instance of HTMLPurifier_Config
  572. * @param $context Instance of HTMLPurifier_Context
  573. */
  574. public function validateToken(&$token, &$config, $context) {
  575. $definition = $config->getHTMLDefinition();
  576. $e =& $context->get('ErrorCollector', true);
  577. // initialize IDAccumulator if necessary
  578. $ok =& $context->get('IDAccumulator', true);
  579. if (!$ok) {
  580. $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
  581. $context->register('IDAccumulator', $id_accumulator);
  582. }
  583. // initialize CurrentToken if necessary
  584. $current_token =& $context->get('CurrentToken', true);
  585. if (!$current_token) $context->register('CurrentToken', $token);
  586. if (
  587. !$token instanceof HTMLPurifier_Token_Start &&
  588. !$token instanceof HTMLPurifier_Token_Empty
  589. ) return $token;
  590. // create alias to global definition array, see also $defs
  591. // DEFINITION CALL
  592. $d_defs = $definition->info_global_attr;
  593. // don't update token until the very end, to ensure an atomic update
  594. $attr = $token->attr;
  595. // do global transformations (pre)
  596. // nothing currently utilizes this
  597. foreach ($definition->info_attr_transform_pre as $transform) {
  598. $attr = $transform->transform($o = $attr, $config, $context);
  599. if ($e) {
  600. if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  601. }
  602. }
  603. // do local transformations only applicable to this element (pre)
  604. // ex. <p align="right"> to <p style="text-align:right;">
  605. foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
  606. $attr = $transform->transform($o = $attr, $config, $context);
  607. if ($e) {
  608. if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  609. }
  610. }
  611. // create alias to this element's attribute definition array, see
  612. // also $d_defs (global attribute definition array)
  613. // DEFINITION CALL
  614. $defs = $definition->info[$token->name]->attr;
  615. $attr_key = false;
  616. $context->register('CurrentAttr', $attr_key);
  617. // iterate through all the attribute keypairs
  618. // Watch out for name collisions: $key has previously been used
  619. foreach ($attr as $attr_key => $value) {
  620. // call the definition
  621. if ( isset($defs[$attr_key]) ) {
  622. // there is a local definition defined
  623. if ($defs[$attr_key] === false) {
  624. // We've explicitly been told not to allow this element.
  625. // This is usually when there's a global definition
  626. // that must be overridden.
  627. // Theoretically speaking, we could have a
  628. // AttrDef_DenyAll, but this is faster!
  629. $result = false;
  630. } else {
  631. // validate according to the element's definition
  632. $result = $defs[$attr_key]->validate(
  633. $value, $config, $context
  634. );
  635. }
  636. } elseif ( isset($d_defs[$attr_key]) ) {
  637. // there is a global definition defined, validate according
  638. // to the global definition
  639. $result = $d_defs[$attr_key]->validate(
  640. $value, $config, $context
  641. );
  642. } else {
  643. // system never heard of the attribute? DELETE!
  644. $result = false;
  645. }
  646. // put the results into effect
  647. if ($result === false || $result === null) {
  648. // this is a generic error message that should replaced
  649. // with more specific ones when possible
  650. if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed');
  651. // remove the attribute
  652. unset($attr[$attr_key]);
  653. } elseif (is_string($result)) {
  654. // generally, if a substitution is happening, there
  655. // was some sort of implicit correction going on. We'll
  656. // delegate it to the attribute classes to say exactly what.
  657. // simple substitution
  658. $attr[$attr_key] = $result;
  659. } else {
  660. // nothing happens
  661. }
  662. // we'd also want slightly more complicated substitution
  663. // involving an array as the return value,
  664. // although we're not sure how colliding attributes would
  665. // resolve (certain ones would be completely overriden,
  666. // others would prepend themselves).
  667. }
  668. $context->destroy('CurrentAttr');
  669. // post transforms
  670. // global (error reporting untested)
  671. foreach ($definition->info_attr_transform_post as $transform) {
  672. $attr = $transform->transform($o = $attr, $config, $context);
  673. if ($e) {
  674. if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  675. }
  676. }
  677. // local (error reporting untested)
  678. foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
  679. $attr = $transform->transform($o = $attr, $config, $context);
  680. if ($e) {
  681. if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  682. }
  683. }
  684. $token->attr = $attr;
  685. // destroy CurrentToken if we made it ourselves
  686. if (!$current_token) $context->destroy('CurrentToken');
  687. }
  688. }
  689. // constants are slow, so we use as few as possible
  690. if (!defined('HTMLPURIFIER_PREFIX')) {
  691. define('HTMLPURIFIER_PREFIX', dirname(__FILE__) . '/standalone');
  692. set_include_path(HTMLPURIFIER_PREFIX . PATH_SEPARATOR . get_include_path());
  693. }
  694. // accomodations for versions earlier than 5.0.2
  695. // borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
  696. if (!defined('PHP_EOL')) {
  697. switch (strtoupper(substr(PHP_OS, 0, 3))) {
  698. case 'WIN':
  699. define('PHP_EOL', "\r\n");
  700. break;
  701. case 'DAR':
  702. define('PHP_EOL', "\r");
  703. break;
  704. default:
  705. define('PHP_EOL', "\n");
  706. }
  707. }
  708. /**
  709. * Bootstrap class that contains meta-functionality for HTML Purifier such as
  710. * the autoload function.
  711. *
  712. * @note
  713. * This class may be used without any other files from HTML Purifier.
  714. */
  715. class HTMLPurifier_Bootstrap
  716. {
  717. /**
  718. * Autoload function for HTML Purifier
  719. * @param $class Class to load
  720. */
  721. public static function autoload($class) {
  722. $file = HTMLPurifier_Bootstrap::getPath($class);
  723. if (!$file) return false;
  724. // Technically speaking, it should be ok and more efficient to
  725. // just do 'require', but Antonio Parraga reports that with
  726. // Zend extensions such as Zend debugger and APC, this invariant
  727. // may be broken. Since we have efficient alternatives, pay
  728. // the cost here and avoid the bug.
  729. require_once HTMLPURIFIER_PREFIX . '/' . $file;
  730. return true;
  731. }
  732. /**
  733. * Returns the path for a specific class.
  734. */
  735. public static function getPath($class) {
  736. if (strncmp('HTMLPurifier', $class, 12) !== 0) return false;
  737. // Custom implementations
  738. if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
  739. $code = str_replace('_', '-', substr($class, 22));
  740. $file = 'HTMLPurifier/Language/classes/' . $code . '.php';
  741. } else {
  742. $file = str_replace('_', '/', $class) . '.php';
  743. }
  744. if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false;
  745. return $file;
  746. }
  747. /**
  748. * "Pre-registers" our autoloader on the SPL stack.
  749. */
  750. public static function registerAutoload() {
  751. $autoload = array('HTMLPurifier_Bootstrap', 'autoload');
  752. if ( ($funcs = spl_autoload_functions()) === false ) {
  753. spl_autoload_register($autoload);
  754. } elseif (function_exists('spl_autoload_unregister')) {
  755. $buggy = version_compare(PHP_VERSION, '5.2.11', '<');
  756. $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
  757. version_compare(PHP_VERSION, '5.1.0', '>=');
  758. foreach ($funcs as $func) {
  759. if ($buggy && is_array($func)) {
  760. // :TRICKY: There are some compatibility issues and some
  761. // places where we need to error out
  762. $reflector = new ReflectionMethod($func[0], $func[1]);
  763. if (!$reflector->isStatic()) {
  764. throw new Exception('
  765. HTML Purifier autoloader registrar is not compatible
  766. with non-static object methods due to PHP Bug #44144;
  767. Please do not use HTMLPurifier.autoload.php (or any
  768. file that includes this file); instead, place the code:
  769. spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
  770. after your own autoloaders.
  771. ');
  772. }
  773. // Suprisingly, spl_autoload_register supports the
  774. // Class::staticMethod callback format, although call_user_func doesn't
  775. if ($compat) $func = implode('::', $func);
  776. }
  777. spl_autoload_unregister($func);
  778. }
  779. spl_autoload_register($autoload);
  780. foreach ($funcs as $func) spl_autoload_register($func);
  781. }
  782. }
  783. }
  784. /**
  785. * Super-class for definition datatype objects, implements serialization
  786. * functions for the class.
  787. */
  788. abstract class HTMLPurifier_Definition
  789. {
  790. /**
  791. * Has setup() been called yet?
  792. */
  793. public $setup = false;
  794. /**
  795. * If true, write out the final definition object to the cache after
  796. * setup. This will be true only if all invocations to get a raw
  797. * definition object are also optimized. This does not cause file
  798. * system thrashing because on subsequent calls the cached object
  799. * is used and any writes to the raw definition object are short
  800. * circuited. See enduser-customize.html for the high-level
  801. * picture.
  802. */
  803. public $optimized = null;
  804. /**
  805. * What type of definition is it?
  806. */
  807. public $type;
  808. /**
  809. * Sets up the definition object into the final form, something
  810. * not done by the constructor
  811. * @param $config HTMLPurifier_Config instance
  812. */
  813. abstract protected function doSetup($config);
  814. /**
  815. * Setup function that aborts if already setup
  816. * @param $config HTMLPurifier_Config instance
  817. */
  818. public function setup($config) {
  819. if ($this->setup) return;
  820. $this->setup = true;
  821. $this->doSetup($config);
  822. }
  823. }
  824. /**
  825. * Defines allowed CSS attributes and what their values are.
  826. * @see HTMLPurifier_HTMLDefinition
  827. */
  828. class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
  829. {
  830. public $type = 'CSS';
  831. /**
  832. * Assoc array of attribute name to definition object.
  833. */
  834. public $info = array();
  835. /**
  836. * Constructs the info array. The meat of this class.
  837. */
  838. protected function doSetup($config) {
  839. $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
  840. array('left', 'right', 'center', 'justify'), false);
  841. $border_style =
  842. $this->info['border-bottom-style'] =
  843. $this->info['border-right-style'] =
  844. $this->info['border-left-style'] =
  845. $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
  846. array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
  847. 'groove', 'ridge', 'inset', 'outset'), false);
  848. $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
  849. $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
  850. array('none', 'left', 'right', 'both'), false);
  851. $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
  852. array('none', 'left', 'right'), false);
  853. $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
  854. array('normal', 'italic', 'oblique'), false);
  855. $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
  856. array('normal', 'small-caps'), false);
  857. $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
  858. array(
  859. new HTMLPurifier_AttrDef_Enum(array('none')),
  860. new HTMLPurifier_AttrDef_CSS_URI()
  861. )
  862. );
  863. $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
  864. array('inside', 'outside'), false);
  865. $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
  866. array('disc', 'circle', 'square', 'decimal', 'lower-roman',
  867. 'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
  868. $this->info['list-style-image'] = $uri_or_none;
  869. $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
  870. $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
  871. array('capitalize', 'uppercase', 'lowercase', 'none'), false);
  872. $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
  873. $this->info['background-image'] = $uri_or_none;
  874. $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
  875. array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
  876. );
  877. $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
  878. array('scroll', 'fixed')
  879. );
  880. $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
  881. $border_color =
  882. $this->info['border-top-color'] =
  883. $this->info['border-bottom-color'] =
  884. $this->info['border-left-color'] =
  885. $this->info['border-right-color'] =
  886. $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  887. new HTMLPurifier_AttrDef_Enum(array('transparent')),
  888. new HTMLPurifier_AttrDef_CSS_Color()
  889. ));
  890. $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
  891. $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
  892. $border_width =
  893. $this->info['border-top-width'] =
  894. $this->info['border-bottom-width'] =
  895. $this->info['border-left-width'] =
  896. $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  897. new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
  898. new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
  899. ));
  900. $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
  901. $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  902. new HTMLPurifier_AttrDef_Enum(array('normal')),
  903. new HTMLPurifier_AttrDef_CSS_Length()
  904. ));
  905. $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  906. new HTMLPurifier_AttrDef_Enum(array('normal')),
  907. new HTMLPurifier_AttrDef_CSS_Length()
  908. ));
  909. $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  910. new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small',
  911. 'small', 'medium', 'large', 'x-large', 'xx-large',
  912. 'larger', 'smaller')),
  913. new HTMLPurifier_AttrDef_CSS_Percentage(),
  914. new HTMLPurifier_AttrDef_CSS_Length()
  915. ));
  916. $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  917. new HTMLPurifier_AttrDef_Enum(array('normal')),
  918. new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
  919. new HTMLPurifier_AttrDef_CSS_Length('0'),
  920. new HTMLPurifier_AttrDef_CSS_Percentage(true)
  921. ));
  922. $margin =
  923. $this->info['margin-top'] =
  924. $this->info['margin-bottom'] =
  925. $this->info['margin-left'] =
  926. $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  927. new HTMLPurifier_AttrDef_CSS_Length(),
  928. new HTMLPurifier_AttrDef_CSS_Percentage(),
  929. new HTMLPurifier_AttrDef_Enum(array('auto'))
  930. ));
  931. $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
  932. // non-negative
  933. $padding =
  934. $this->info['padding-top'] =
  935. $this->info['padding-bottom'] =
  936. $this->info['padding-left'] =
  937. $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  938. new HTMLPurifier_AttrDef_CSS_Length('0'),
  939. new HTMLPurifier_AttrDef_CSS_Percentage(true)
  940. ));
  941. $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
  942. $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  943. new HTMLPurifier_AttrDef_CSS_Length(),
  944. new HTMLPurifier_AttrDef_CSS_Percentage()
  945. ));
  946. $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array(
  947. new HTMLPurifier_AttrDef_CSS_Length('0'),
  948. new HTMLPurifier_AttrDef_CSS_Percentage(true),
  949. new HTMLPurifier_AttrDef_Enum(array('auto'))
  950. ));
  951. $max = $config->get('CSS.MaxImgLength');
  952. $this->info['width'] =
  953. $this->info['height'] =
  954. $max === null ?
  955. $trusted_wh :
  956. new HTMLPurifier_AttrDef_Switch('img',
  957. // For img tags:
  958. new HTMLPurifier_AttrDef_CSS_Composite(array(
  959. new HTMLPurifier_AttrDef_CSS_Length('0', $max),
  960. new HTMLPurifier_AttrDef_Enum(array('auto'))
  961. )),
  962. // For everyone else:
  963. $trusted_wh
  964. );
  965. $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
  966. $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
  967. // this could use specialized code
  968. $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
  969. array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300',
  970. '400', '500', '600', '700', '800', '900'), false);
  971. // MUST be called after other font properties, as it references
  972. // a CSSDefinition object
  973. $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
  974. // same here
  975. $this->info['border'] =
  976. $this->info['border-bottom'] =
  977. $this->info['border-top'] =
  978. $this->info['border-left'] =
  979. $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
  980. $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
  981. 'collapse', 'separate'));
  982. $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array(
  983. 'top', 'bottom'));
  984. $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
  985. 'auto', 'fixed'));
  986. $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  987. new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
  988. 'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
  989. new HTMLPurifier_AttrDef_CSS_Length(),
  990. new HTMLPurifier_AttrDef_CSS_Percentage()
  991. ));
  992. $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
  993. // partial support
  994. $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
  995. if ($config->get('CSS.Proprietary')) {
  996. $this->doSetupProprietary($config);
  997. }
  998. if ($config->get('CSS.AllowTricky')) {
  999. $this->doSetupTricky($config);
  1000. }
  1001. if ($config->get('CSS.Trusted')) {
  1002. $this->doSetupTrusted($config);
  1003. }
  1004. $allow_important = $config->get('CSS.AllowImportant');
  1005. // wrap all attr-defs with decorator that handles !important
  1006. foreach ($this->info as $k => $v) {
  1007. $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
  1008. }
  1009. $this->setupConfigStuff($config);
  1010. }
  1011. protected function doSetupProprietary($config) {
  1012. // Internet Explorer only scrollbar colors
  1013. $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
  1014. $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
  1015. $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
  1016. $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
  1017. $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
  1018. $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
  1019. // technically not proprietary, but CSS3, and no one supports it
  1020. $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
  1021. $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
  1022. $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
  1023. // only opacity, for now
  1024. $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
  1025. }
  1026. protected function doSetupTricky($config) {
  1027. $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array(
  1028. 'inline', 'block', 'list-item', 'run-in', 'compact',
  1029. 'marker', 'table', 'inline-table', 'table-row-group',
  1030. 'table-header-group', 'table-footer-group', 'table-row',
  1031. 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none'
  1032. ));
  1033. $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array(
  1034. 'visible', 'hidden', 'collapse'
  1035. ));
  1036. $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
  1037. }
  1038. protected function doSetupTrusted($config) {
  1039. $this->info['position'] = new HTMLPurifier_AttrDef_Enum(array(
  1040. 'static', 'relative', 'absolute', 'fixed'
  1041. ));
  1042. $this->info['top'] =
  1043. $this->info['left'] =
  1044. $this->info['right'] =
  1045. $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  1046. new HTMLPurifier_AttrDef_CSS_Length(),
  1047. new HTMLPurifier_AttrDef_CSS_Percentage(),
  1048. new HTMLPurifier_AttrDef_Enum(array('auto')),
  1049. ));
  1050. $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
  1051. new HTMLPurifier_AttrDef_Integer(),
  1052. new HTMLPurifier_AttrDef_Enum(array('auto')),
  1053. ));
  1054. }
  1055. /**
  1056. * Performs extra config-based processing. Based off of
  1057. * HTMLPurifier_HTMLDefinition.
  1058. * @todo Refactor duplicate elements into common class (probably using
  1059. * composition, not inheritance).
  1060. */
  1061. protected function setupConfigStuff($config) {
  1062. // setup allowed elements
  1063. $support = "(for information on implementing this, see the ".
  1064. "support forums) ";
  1065. $allowed_properties = $config->get('CSS.AllowedProperties');
  1066. if ($allowed_properties !== null) {
  1067. foreach ($this->info as $name => $d) {
  1068. if(!isset($allowed_properties[$name])) unset($this->info[$name]);
  1069. unset($allowed_properties[$name]);
  1070. }
  1071. // emit errors
  1072. foreach ($allowed_properties as $name => $d) {
  1073. // :TODO: Is this htmlspecialchars() call really necessary?
  1074. $name = htmlspecialchars($name);
  1075. trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
  1076. }
  1077. }
  1078. $forbidden_properties = $config->get('CSS.ForbiddenProperties');
  1079. if ($forbidden_properties !== null) {
  1080. foreach ($this->info as $name => $d) {
  1081. if (isset($forbidden_properties[$name])) {
  1082. unset($this->info[$name]);
  1083. }
  1084. }
  1085. }

Large files files are truncated, but you can click here to view the full file