/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Validator.php

https://github.com/ButuzGOL/Yii-blog-new · PHP · 223 lines · 140 code · 22 blank · 61 comment · 24 complexity · 8dfc82498a46b1a1a00a28c2f5f02197 MD5 · raw file

  1. <?php
  2. /**
  3. * Performs validations on HTMLPurifier_ConfigSchema_Interchange
  4. *
  5. * @note If you see '// handled by InterchangeBuilder', that means a
  6. * design decision in that class would prevent this validation from
  7. * ever being necessary. We have them anyway, however, for
  8. * redundancy.
  9. */
  10. class HTMLPurifier_ConfigSchema_Validator
  11. {
  12. /**
  13. * Easy to access global objects.
  14. */
  15. protected $interchange, $aliases;
  16. /**
  17. * Context-stack to provide easy to read error messages.
  18. */
  19. protected $context = array();
  20. /**
  21. * HTMLPurifier_VarParser to test default's type.
  22. */
  23. protected $parser;
  24. public function __construct() {
  25. $this->parser = new HTMLPurifier_VarParser();
  26. }
  27. /**
  28. * Validates a fully-formed interchange object. Throws an
  29. * HTMLPurifier_ConfigSchema_Exception if there's a problem.
  30. */
  31. public function validate($interchange) {
  32. $this->interchange = $interchange;
  33. $this->aliases = array();
  34. // PHP is a bit lax with integer <=> string conversions in
  35. // arrays, so we don't use the identical !== comparison
  36. foreach ($interchange->namespaces as $i => $namespace) {
  37. if ($i != $namespace->namespace) $this->error(false, "Integrity violation: key '$i' does not match internal id '{$namespace->namespace}'");
  38. $this->validateNamespace($namespace);
  39. }
  40. foreach ($interchange->directives as $i => $directive) {
  41. $id = $directive->id->toString();
  42. if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
  43. $this->validateDirective($directive);
  44. }
  45. return true;
  46. }
  47. /**
  48. * Validates a HTMLPurifier_ConfigSchema_Interchange_Namespace object.
  49. */
  50. public function validateNamespace($n) {
  51. $this->context[] = "namespace '{$n->namespace}'";
  52. $this->with($n, 'namespace')
  53. ->assertNotEmpty()
  54. ->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
  55. $this->with($n, 'description')
  56. ->assertNotEmpty()
  57. ->assertIsString(); // handled by InterchangeBuilder
  58. array_pop($this->context);
  59. }
  60. /**
  61. * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object.
  62. */
  63. public function validateId($id) {
  64. $id_string = $id->toString();
  65. $this->context[] = "id '$id_string'";
  66. if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
  67. // handled by InterchangeBuilder
  68. $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
  69. }
  70. if (!isset($this->interchange->namespaces[$id->namespace])) {
  71. $this->error('namespace', 'does not exist'); // assumes that the namespace was validated already
  72. }
  73. $this->with($id, 'directive')
  74. ->assertNotEmpty()
  75. ->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
  76. array_pop($this->context);
  77. }
  78. /**
  79. * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object.
  80. */
  81. public function validateDirective($d) {
  82. $id = $d->id->toString();
  83. $this->context[] = "directive '$id'";
  84. $this->validateId($d->id);
  85. $this->with($d, 'description')
  86. ->assertNotEmpty();
  87. // BEGIN - handled by InterchangeBuilder
  88. $this->with($d, 'type')
  89. ->assertNotEmpty();
  90. $this->with($d, 'typeAllowsNull')
  91. ->assertIsBool();
  92. try {
  93. // This also tests validity of $d->type
  94. $this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
  95. } catch (HTMLPurifier_VarParserException $e) {
  96. $this->error('default', 'had error: ' . $e->getMessage());
  97. }
  98. // END - handled by InterchangeBuilder
  99. if (!is_null($d->allowed) || !empty($d->valueAliases)) {
  100. // allowed and valueAliases require that we be dealing with
  101. // strings, so check for that early.
  102. $d_int = HTMLPurifier_VarParser::$types[$d->type];
  103. if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
  104. $this->error('type', 'must be a string type when used with allowed or value aliases');
  105. }
  106. }
  107. $this->validateDirectiveAllowed($d);
  108. $this->validateDirectiveValueAliases($d);
  109. $this->validateDirectiveAliases($d);
  110. array_pop($this->context);
  111. }
  112. /**
  113. * Extra validation if $allowed member variable of
  114. * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
  115. */
  116. public function validateDirectiveAllowed($d) {
  117. if (is_null($d->allowed)) return;
  118. $this->with($d, 'allowed')
  119. ->assertNotEmpty()
  120. ->assertIsLookup(); // handled by InterchangeBuilder
  121. if (is_string($d->default) && !isset($d->allowed[$d->default])) {
  122. $this->error('default', 'must be an allowed value');
  123. }
  124. $this->context[] = 'allowed';
  125. foreach ($d->allowed as $val => $x) {
  126. if (!is_string($val)) $this->error("value $val", 'must be a string');
  127. }
  128. array_pop($this->context);
  129. }
  130. /**
  131. * Extra validation if $valueAliases member variable of
  132. * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
  133. */
  134. public function validateDirectiveValueAliases($d) {
  135. if (is_null($d->valueAliases)) return;
  136. $this->with($d, 'valueAliases')
  137. ->assertIsArray(); // handled by InterchangeBuilder
  138. $this->context[] = 'valueAliases';
  139. foreach ($d->valueAliases as $alias => $real) {
  140. if (!is_string($alias)) $this->error("alias $alias", 'must be a string');
  141. if (!is_string($real)) $this->error("alias target $real from alias '$alias'", 'must be a string');
  142. if ($alias === $real) {
  143. $this->error("alias '$alias'", "must not be an alias to itself");
  144. }
  145. }
  146. if (!is_null($d->allowed)) {
  147. foreach ($d->valueAliases as $alias => $real) {
  148. if (isset($d->allowed[$alias])) {
  149. $this->error("alias '$alias'", 'must not be an allowed value');
  150. } elseif (!isset($d->allowed[$real])) {
  151. $this->error("alias '$alias'", 'must be an alias to an allowed value');
  152. }
  153. }
  154. }
  155. array_pop($this->context);
  156. }
  157. /**
  158. * Extra validation if $aliases member variable of
  159. * HTMLPurifier_ConfigSchema_Interchange_Directive is defined.
  160. */
  161. public function validateDirectiveAliases($d) {
  162. $this->with($d, 'aliases')
  163. ->assertIsArray(); // handled by InterchangeBuilder
  164. $this->context[] = 'aliases';
  165. foreach ($d->aliases as $alias) {
  166. $this->validateId($alias);
  167. $s = $alias->toString();
  168. if (isset($this->interchange->directives[$s])) {
  169. $this->error("alias '$s'", 'collides with another directive');
  170. }
  171. if (isset($this->aliases[$s])) {
  172. $other_directive = $this->aliases[$s];
  173. $this->error("alias '$s'", "collides with alias for directive '$other_directive'");
  174. }
  175. $this->aliases[$s] = $d->id->toString();
  176. }
  177. array_pop($this->context);
  178. }
  179. // protected helper functions
  180. /**
  181. * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom
  182. * for validating simple member variables of objects.
  183. */
  184. protected function with($obj, $member) {
  185. return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
  186. }
  187. /**
  188. * Emits an error, providing helpful context.
  189. */
  190. protected function error($target, $msg) {
  191. if ($target !== false) $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext();
  192. else $prefix = ucfirst($this->getFormattedContext());
  193. throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
  194. }
  195. /**
  196. * Returns a formatted context string.
  197. */
  198. protected function getFormattedContext() {
  199. return implode(' in ', array_reverse($this->context));
  200. }
  201. }