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