File indexing completed on 2024-12-22 05:36:22

0001 <?php
0002 
0003 /**
0004  * Parses string representations into their corresponding native PHP
0005  * variable type. The base implementation does a simple type-check.
0006  */
0007 class HTMLPurifier_VarParser
0008 {
0009 
0010     const STRING = 1;
0011     const ISTRING = 2;
0012     const TEXT = 3;
0013     const ITEXT = 4;
0014     const INT = 5;
0015     const FLOAT = 6;
0016     const BOOL = 7;
0017     const LOOKUP = 8;
0018     const ALIST = 9;
0019     const HASH = 10;
0020     const MIXED = 11;
0021 
0022     /**
0023      * Lookup table of allowed types. Mainly for backwards compatibility, but
0024      * also convenient for transforming string type names to the integer constants.
0025      */
0026     public static $types = array(
0027         'string' => self::STRING,
0028         'istring' => self::ISTRING,
0029         'text' => self::TEXT,
0030         'itext' => self::ITEXT,
0031         'int' => self::INT,
0032         'float' => self::FLOAT,
0033         'bool' => self::BOOL,
0034         'lookup' => self::LOOKUP,
0035         'list' => self::ALIST,
0036         'hash' => self::HASH,
0037         'mixed' => self::MIXED
0038     );
0039 
0040     /**
0041      * Lookup table of types that are string, and can have aliases or
0042      * allowed value lists.
0043      */
0044     public static $stringTypes = array(
0045         self::STRING => true,
0046         self::ISTRING => true,
0047         self::TEXT => true,
0048         self::ITEXT => true,
0049     );
0050 
0051     /**
0052      * Validate a variable according to type.
0053      * It may return NULL as a valid type if $allow_null is true.
0054      *
0055      * @param mixed $var Variable to validate
0056      * @param int $type Type of variable, see HTMLPurifier_VarParser->types
0057      * @param bool $allow_null Whether or not to permit null as a value
0058      * @return string Validated and type-coerced variable
0059      * @throws HTMLPurifier_VarParserException
0060      */
0061     final public function parse($var, $type, $allow_null = false)
0062     {
0063         if (is_string($type)) {
0064             if (!isset(HTMLPurifier_VarParser::$types[$type])) {
0065                 throw new HTMLPurifier_VarParserException("Invalid type '$type'");
0066             } else {
0067                 $type = HTMLPurifier_VarParser::$types[$type];
0068             }
0069         }
0070         $var = $this->parseImplementation($var, $type, $allow_null);
0071         if ($allow_null && $var === null) {
0072             return null;
0073         }
0074         // These are basic checks, to make sure nothing horribly wrong
0075         // happened in our implementations.
0076         switch ($type) {
0077             case (self::STRING):
0078             case (self::ISTRING):
0079             case (self::TEXT):
0080             case (self::ITEXT):
0081                 if (!is_string($var)) {
0082                     break;
0083                 }
0084                 if ($type == self::ISTRING || $type == self::ITEXT) {
0085                     $var = strtolower($var);
0086                 }
0087                 return $var;
0088             case (self::INT):
0089                 if (!is_int($var)) {
0090                     break;
0091                 }
0092                 return $var;
0093             case (self::FLOAT):
0094                 if (!is_float($var)) {
0095                     break;
0096                 }
0097                 return $var;
0098             case (self::BOOL):
0099                 if (!is_bool($var)) {
0100                     break;
0101                 }
0102                 return $var;
0103             case (self::LOOKUP):
0104             case (self::ALIST):
0105             case (self::HASH):
0106                 if (!is_array($var)) {
0107                     break;
0108                 }
0109                 if ($type === self::LOOKUP) {
0110                     foreach ($var as $k) {
0111                         if ($k !== true) {
0112                             $this->error('Lookup table contains value other than true');
0113                         }
0114                     }
0115                 } elseif ($type === self::ALIST) {
0116                     $keys = array_keys($var);
0117                     if (array_keys($keys) !== $keys) {
0118                         $this->error('Indices for list are not uniform');
0119                     }
0120                 }
0121                 return $var;
0122             case (self::MIXED):
0123                 return $var;
0124             default:
0125                 $this->errorInconsistent(get_class($this), $type);
0126         }
0127         $this->errorGeneric($var, $type);
0128     }
0129 
0130     /**
0131      * Actually implements the parsing. Base implementation does not
0132      * do anything to $var. Subclasses should overload this!
0133      * @param mixed $var
0134      * @param int $type
0135      * @param bool $allow_null
0136      * @return string
0137      */
0138     protected function parseImplementation($var, $type, $allow_null)
0139     {
0140         return $var;
0141     }
0142 
0143     /**
0144      * Throws an exception.
0145      * @throws HTMLPurifier_VarParserException
0146      */
0147     protected function error($msg)
0148     {
0149         throw new HTMLPurifier_VarParserException($msg);
0150     }
0151 
0152     /**
0153      * Throws an inconsistency exception.
0154      * @note This should not ever be called. It would be called if we
0155      *       extend the allowed values of HTMLPurifier_VarParser without
0156      *       updating subclasses.
0157      * @param string $class
0158      * @param int $type
0159      * @throws HTMLPurifier_Exception
0160      */
0161     protected function errorInconsistent($class, $type)
0162     {
0163         throw new HTMLPurifier_Exception(
0164             "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) .
0165             " not implemented"
0166         );
0167     }
0168 
0169     /**
0170      * Generic error for if a type didn't work.
0171      * @param mixed $var
0172      * @param int $type
0173      */
0174     protected function errorGeneric($var, $type)
0175     {
0176         $vtype = gettype($var);
0177         $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype");
0178     }
0179 
0180     /**
0181      * @param int $type
0182      * @return string
0183      */
0184     public static function getTypeName($type)
0185     {
0186         static $lookup;
0187         if (!$lookup) {
0188             // Lazy load the alternative lookup table
0189             $lookup = array_flip(HTMLPurifier_VarParser::$types);
0190         }
0191         if (!isset($lookup[$type])) {
0192             return 'unknown';
0193         }
0194         return $lookup[$type];
0195     }
0196 }
0197 
0198 // vim: et sw=4 sts=4