File indexing completed on 2024-05-12 06:02:04

0001 <?php
0002 
0003 class HTMLPurifier_ConfigSchema_InterchangeBuilder
0004 {
0005 
0006     /**
0007      * Used for processing DEFAULT, nothing else.
0008      * @type HTMLPurifier_VarParser
0009      */
0010     protected $varParser;
0011 
0012     /**
0013      * @param HTMLPurifier_VarParser $varParser
0014      */
0015     public function __construct($varParser = null)
0016     {
0017         $this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
0018     }
0019 
0020     /**
0021      * @param string $dir
0022      * @return HTMLPurifier_ConfigSchema_Interchange
0023      */
0024     public static function buildFromDirectory($dir = null)
0025     {
0026         $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
0027         $interchange = new HTMLPurifier_ConfigSchema_Interchange();
0028         return $builder->buildDir($interchange, $dir);
0029     }
0030 
0031     /**
0032      * @param HTMLPurifier_ConfigSchema_Interchange $interchange
0033      * @param string $dir
0034      * @return HTMLPurifier_ConfigSchema_Interchange
0035      */
0036     public function buildDir($interchange, $dir = null)
0037     {
0038         if (!$dir) {
0039             $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema';
0040         }
0041         if (file_exists($dir . '/info.ini')) {
0042             $info = parse_ini_file($dir . '/info.ini');
0043             $interchange->name = $info['name'];
0044         }
0045 
0046         $files = array();
0047         $dh = opendir($dir);
0048         while (false !== ($file = readdir($dh))) {
0049             if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
0050                 continue;
0051             }
0052             $files[] = $file;
0053         }
0054         closedir($dh);
0055 
0056         sort($files);
0057         foreach ($files as $file) {
0058             $this->buildFile($interchange, $dir . '/' . $file);
0059         }
0060         return $interchange;
0061     }
0062 
0063     /**
0064      * @param HTMLPurifier_ConfigSchema_Interchange $interchange
0065      * @param string $file
0066      */
0067     public function buildFile($interchange, $file)
0068     {
0069         $parser = new HTMLPurifier_StringHashParser();
0070         $this->build(
0071             $interchange,
0072             new HTMLPurifier_StringHash($parser->parseFile($file))
0073         );
0074     }
0075 
0076     /**
0077      * Builds an interchange object based on a hash.
0078      * @param HTMLPurifier_ConfigSchema_Interchange $interchange HTMLPurifier_ConfigSchema_Interchange object to build
0079      * @param HTMLPurifier_StringHash $hash source data
0080      * @throws HTMLPurifier_ConfigSchema_Exception
0081      */
0082     public function build($interchange, $hash)
0083     {
0084         if (!$hash instanceof HTMLPurifier_StringHash) {
0085             $hash = new HTMLPurifier_StringHash($hash);
0086         }
0087         if (!isset($hash['ID'])) {
0088             throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
0089         }
0090         if (strpos($hash['ID'], '.') === false) {
0091             if (count($hash) == 2 && isset($hash['DESCRIPTION'])) {
0092                 $hash->offsetGet('DESCRIPTION'); // prevent complaining
0093             } else {
0094                 throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace');
0095             }
0096         } else {
0097             $this->buildDirective($interchange, $hash);
0098         }
0099         $this->_findUnused($hash);
0100     }
0101 
0102     /**
0103      * @param HTMLPurifier_ConfigSchema_Interchange $interchange
0104      * @param HTMLPurifier_StringHash $hash
0105      * @throws HTMLPurifier_ConfigSchema_Exception
0106      */
0107     public function buildDirective($interchange, $hash)
0108     {
0109         $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
0110 
0111         // These are required elements:
0112         $directive->id = $this->id($hash->offsetGet('ID'));
0113         $id = $directive->id->toString(); // convenience
0114 
0115         if (isset($hash['TYPE'])) {
0116             $type = explode('/', $hash->offsetGet('TYPE'));
0117             if (isset($type[1])) {
0118                 $directive->typeAllowsNull = true;
0119             }
0120             $directive->type = $type[0];
0121         } else {
0122             throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined");
0123         }
0124 
0125         if (isset($hash['DEFAULT'])) {
0126             try {
0127                 $directive->default = $this->varParser->parse(
0128                     $hash->offsetGet('DEFAULT'),
0129                     $directive->type,
0130                     $directive->typeAllowsNull
0131                 );
0132             } catch (HTMLPurifier_VarParserException $e) {
0133                 throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'");
0134             }
0135         }
0136 
0137         if (isset($hash['DESCRIPTION'])) {
0138             $directive->description = $hash->offsetGet('DESCRIPTION');
0139         }
0140 
0141         if (isset($hash['ALLOWED'])) {
0142             $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED')));
0143         }
0144 
0145         if (isset($hash['VALUE-ALIASES'])) {
0146             $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES'));
0147         }
0148 
0149         if (isset($hash['ALIASES'])) {
0150             $raw_aliases = trim($hash->offsetGet('ALIASES'));
0151             $aliases = preg_split('/\s*,\s*/', $raw_aliases);
0152             foreach ($aliases as $alias) {
0153                 $directive->aliases[] = $this->id($alias);
0154             }
0155         }
0156 
0157         if (isset($hash['VERSION'])) {
0158             $directive->version = $hash->offsetGet('VERSION');
0159         }
0160 
0161         if (isset($hash['DEPRECATED-USE'])) {
0162             $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE'));
0163         }
0164 
0165         if (isset($hash['DEPRECATED-VERSION'])) {
0166             $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION');
0167         }
0168 
0169         if (isset($hash['EXTERNAL'])) {
0170             $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL')));
0171         }
0172 
0173         $interchange->addDirective($directive);
0174     }
0175 
0176     /**
0177      * Evaluates an array PHP code string without array() wrapper
0178      * @param string $contents
0179      */
0180     protected function evalArray($contents)
0181     {
0182         return eval('return array(' . $contents . ');');
0183     }
0184 
0185     /**
0186      * Converts an array list into a lookup array.
0187      * @param array $array
0188      * @return array
0189      */
0190     protected function lookup($array)
0191     {
0192         $ret = array();
0193         foreach ($array as $val) {
0194             $ret[$val] = true;
0195         }
0196         return $ret;
0197     }
0198 
0199     /**
0200      * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id
0201      * object based on a string Id.
0202      * @param string $id
0203      * @return HTMLPurifier_ConfigSchema_Interchange_Id
0204      */
0205     protected function id($id)
0206     {
0207         return HTMLPurifier_ConfigSchema_Interchange_Id::make($id);
0208     }
0209 
0210     /**
0211      * Triggers errors for any unused keys passed in the hash; such keys
0212      * may indicate typos, missing values, etc.
0213      * @param HTMLPurifier_StringHash $hash Hash to check.
0214      */
0215     protected function _findUnused($hash)
0216     {
0217         $accessed = $hash->getAccessed();
0218         foreach ($hash as $k => $v) {
0219             if (!isset($accessed[$k])) {
0220                 trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE);
0221             }
0222         }
0223     }
0224 }
0225 
0226 // vim: et sw=4 sts=4