VarParser.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <?php
  2. /**
  3. * Parses string representations into their corresponding native PHP
  4. * variable type. The base implementation does a simple type-check.
  5. */
  6. class HTMLPurifier_VarParser
  7. {
  8. const C_STRING = 1;
  9. const ISTRING = 2;
  10. const TEXT = 3;
  11. const ITEXT = 4;
  12. const C_INT = 5;
  13. const C_FLOAT = 6;
  14. const C_BOOL = 7;
  15. const LOOKUP = 8;
  16. const ALIST = 9;
  17. const HASH = 10;
  18. const C_MIXED = 11;
  19. /**
  20. * Lookup table of allowed types. Mainly for backwards compatibility, but
  21. * also convenient for transforming string type names to the integer constants.
  22. */
  23. public static $types = array(
  24. 'string' => self::C_STRING,
  25. 'istring' => self::ISTRING,
  26. 'text' => self::TEXT,
  27. 'itext' => self::ITEXT,
  28. 'int' => self::C_INT,
  29. 'float' => self::C_FLOAT,
  30. 'bool' => self::C_BOOL,
  31. 'lookup' => self::LOOKUP,
  32. 'list' => self::ALIST,
  33. 'hash' => self::HASH,
  34. 'mixed' => self::C_MIXED
  35. );
  36. /**
  37. * Lookup table of types that are string, and can have aliases or
  38. * allowed value lists.
  39. */
  40. public static $stringTypes = array(
  41. self::C_STRING => true,
  42. self::ISTRING => true,
  43. self::TEXT => true,
  44. self::ITEXT => true,
  45. );
  46. /**
  47. * Validate a variable according to type.
  48. * It may return NULL as a valid type if $allow_null is true.
  49. *
  50. * @param mixed $var Variable to validate
  51. * @param int $type Type of variable, see HTMLPurifier_VarParser->types
  52. * @param bool $allow_null Whether or not to permit null as a value
  53. * @return string Validated and type-coerced variable
  54. * @throws HTMLPurifier_VarParserException
  55. */
  56. final public function parse($var, $type, $allow_null = false)
  57. {
  58. if (is_string($type)) {
  59. if (!isset(HTMLPurifier_VarParser::$types[$type])) {
  60. throw new HTMLPurifier_VarParserException("Invalid type '$type'");
  61. } else {
  62. $type = HTMLPurifier_VarParser::$types[$type];
  63. }
  64. }
  65. $var = $this->parseImplementation($var, $type, $allow_null);
  66. if ($allow_null && $var === null) {
  67. return null;
  68. }
  69. // These are basic checks, to make sure nothing horribly wrong
  70. // happened in our implementations.
  71. switch ($type) {
  72. case (self::C_STRING):
  73. case (self::ISTRING):
  74. case (self::TEXT):
  75. case (self::ITEXT):
  76. if (!is_string($var)) {
  77. break;
  78. }
  79. if ($type == self::ISTRING || $type == self::ITEXT) {
  80. $var = strtolower($var);
  81. }
  82. return $var;
  83. case (self::C_INT):
  84. if (!is_int($var)) {
  85. break;
  86. }
  87. return $var;
  88. case (self::C_FLOAT):
  89. if (!is_float($var)) {
  90. break;
  91. }
  92. return $var;
  93. case (self::C_BOOL):
  94. if (!is_bool($var)) {
  95. break;
  96. }
  97. return $var;
  98. case (self::LOOKUP):
  99. case (self::ALIST):
  100. case (self::HASH):
  101. if (!is_array($var)) {
  102. break;
  103. }
  104. if ($type === self::LOOKUP) {
  105. foreach ($var as $k) {
  106. if ($k !== true) {
  107. $this->error('Lookup table contains value other than true');
  108. }
  109. }
  110. } elseif ($type === self::ALIST) {
  111. $keys = array_keys($var);
  112. if (array_keys($keys) !== $keys) {
  113. $this->error('Indices for list are not uniform');
  114. }
  115. }
  116. return $var;
  117. case (self::C_MIXED):
  118. return $var;
  119. default:
  120. $this->errorInconsistent(get_class($this), $type);
  121. }
  122. $this->errorGeneric($var, $type);
  123. }
  124. /**
  125. * Actually implements the parsing. Base implementation does not
  126. * do anything to $var. Subclasses should overload this!
  127. * @param mixed $var
  128. * @param int $type
  129. * @param bool $allow_null
  130. * @return string
  131. */
  132. protected function parseImplementation($var, $type, $allow_null)
  133. {
  134. return $var;
  135. }
  136. /**
  137. * Throws an exception.
  138. * @throws HTMLPurifier_VarParserException
  139. */
  140. protected function error($msg)
  141. {
  142. throw new HTMLPurifier_VarParserException($msg);
  143. }
  144. /**
  145. * Throws an inconsistency exception.
  146. * @note This should not ever be called. It would be called if we
  147. * extend the allowed values of HTMLPurifier_VarParser without
  148. * updating subclasses.
  149. * @param string $class
  150. * @param int $type
  151. * @throws HTMLPurifier_Exception
  152. */
  153. protected function errorInconsistent($class, $type)
  154. {
  155. throw new HTMLPurifier_Exception(
  156. "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) .
  157. " not implemented"
  158. );
  159. }
  160. /**
  161. * Generic error for if a type didn't work.
  162. * @param mixed $var
  163. * @param int $type
  164. */
  165. protected function errorGeneric($var, $type)
  166. {
  167. $vtype = gettype($var);
  168. $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype");
  169. }
  170. /**
  171. * @param int $type
  172. * @return string
  173. */
  174. public static function getTypeName($type)
  175. {
  176. static $lookup;
  177. if (!$lookup) {
  178. // Lazy load the alternative lookup table
  179. $lookup = array_flip(HTMLPurifier_VarParser::$types);
  180. }
  181. if (!isset($lookup[$type])) {
  182. return 'unknown';
  183. }
  184. return $lookup[$type];
  185. }
  186. }
  187. // vim: et sw=4 sts=4