/ lib / htmlpurifier / ConfigSchema / InterchangeBuilder.php
InterchangeBuilder.php
  1  <?php
  2  
  3  class HTMLPurifier_ConfigSchema_InterchangeBuilder
  4  {
  5  
  6      /**
  7       * Used for processing DEFAULT, nothing else.
  8       * @type HTMLPurifier_VarParser
  9       */
 10      protected $varParser;
 11  
 12      /**
 13       * @param HTMLPurifier_VarParser $varParser
 14       */
 15      public function __construct($varParser = null)
 16      {
 17          $this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
 18      }
 19  
 20      /**
 21       * @param string $dir
 22       * @return HTMLPurifier_ConfigSchema_Interchange
 23       */
 24      public static function buildFromDirectory($dir = null)
 25      {
 26          $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
 27          $interchange = new HTMLPurifier_ConfigSchema_Interchange();
 28          return $builder->buildDir($interchange, $dir);
 29      }
 30  
 31      /**
 32       * @param HTMLPurifier_ConfigSchema_Interchange $interchange
 33       * @param string $dir
 34       * @return HTMLPurifier_ConfigSchema_Interchange
 35       */
 36      public function buildDir($interchange, $dir = null)
 37      {
 38          if (!$dir) {
 39              $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema';
 40          }
 41          if (file_exists($dir . '/info.ini')) {
 42              $info = parse_ini_file($dir . '/info.ini');
 43              $interchange->name = $info['name'];
 44          }
 45  
 46          $files = array();
 47          $dh = opendir($dir);
 48          while (false !== ($file = readdir($dh))) {
 49              if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
 50                  continue;
 51              }
 52              $files[] = $file;
 53          }
 54          closedir($dh);
 55  
 56          sort($files);
 57          foreach ($files as $file) {
 58              $this->buildFile($interchange, $dir . '/' . $file);
 59          }
 60          return $interchange;
 61      }
 62  
 63      /**
 64       * @param HTMLPurifier_ConfigSchema_Interchange $interchange
 65       * @param string $file
 66       */
 67      public function buildFile($interchange, $file)
 68      {
 69          $parser = new HTMLPurifier_StringHashParser();
 70          $this->build(
 71              $interchange,
 72              new HTMLPurifier_StringHash($parser->parseFile($file))
 73          );
 74      }
 75  
 76      /**
 77       * Builds an interchange object based on a hash.
 78       * @param HTMLPurifier_ConfigSchema_Interchange $interchange HTMLPurifier_ConfigSchema_Interchange object to build
 79       * @param HTMLPurifier_StringHash $hash source data
 80       * @throws HTMLPurifier_ConfigSchema_Exception
 81       */
 82      public function build($interchange, $hash)
 83      {
 84          if (!$hash instanceof HTMLPurifier_StringHash) {
 85              $hash = new HTMLPurifier_StringHash($hash);
 86          }
 87          if (!isset($hash['ID'])) {
 88              throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
 89          }
 90          if (strpos($hash['ID'], '.') === false) {
 91              if (count($hash) == 2 && isset($hash['DESCRIPTION'])) {
 92                  $hash->offsetGet('DESCRIPTION'); // prevent complaining
 93              } else {
 94                  throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace');
 95              }
 96          } else {
 97              $this->buildDirective($interchange, $hash);
 98          }
 99          $this->_findUnused($hash);
100      }
101  
102      /**
103       * @param HTMLPurifier_ConfigSchema_Interchange $interchange
104       * @param HTMLPurifier_StringHash $hash
105       * @throws HTMLPurifier_ConfigSchema_Exception
106       */
107      public function buildDirective($interchange, $hash)
108      {
109          $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
110  
111          // These are required elements:
112          $directive->id = $this->id($hash->offsetGet('ID'));
113          $id = $directive->id->toString(); // convenience
114  
115          if (isset($hash['TYPE'])) {
116              $type = explode('/', $hash->offsetGet('TYPE'));
117              if (isset($type[1])) {
118                  $directive->typeAllowsNull = true;
119              }
120              $directive->type = $type[0];
121          } else {
122              throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined");
123          }
124  
125          if (isset($hash['DEFAULT'])) {
126              try {
127                  $directive->default = $this->varParser->parse(
128                      $hash->offsetGet('DEFAULT'),
129                      $directive->type,
130                      $directive->typeAllowsNull
131                  );
132              } catch (HTMLPurifier_VarParserException $e) {
133                  throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'");
134              }
135          }
136  
137          if (isset($hash['DESCRIPTION'])) {
138              $directive->description = $hash->offsetGet('DESCRIPTION');
139          }
140  
141          if (isset($hash['ALLOWED'])) {
142              $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED')));
143          }
144  
145          if (isset($hash['VALUE-ALIASES'])) {
146              $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES'));
147          }
148  
149          if (isset($hash['ALIASES'])) {
150              $raw_aliases = trim($hash->offsetGet('ALIASES'));
151              $aliases = preg_split('/\s*,\s*/', $raw_aliases);
152              foreach ($aliases as $alias) {
153                  $directive->aliases[] = $this->id($alias);
154              }
155          }
156  
157          if (isset($hash['VERSION'])) {
158              $directive->version = $hash->offsetGet('VERSION');
159          }
160  
161          if (isset($hash['DEPRECATED-USE'])) {
162              $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE'));
163          }
164  
165          if (isset($hash['DEPRECATED-VERSION'])) {
166              $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION');
167          }
168  
169          if (isset($hash['EXTERNAL'])) {
170              $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL')));
171          }
172  
173          $interchange->addDirective($directive);
174      }
175  
176      /**
177       * Evaluates an array PHP code string without array() wrapper
178       * @param string $contents
179       */
180      protected function evalArray($contents)
181      {
182          return eval('return array(' . $contents . ');');
183      }
184  
185      /**
186       * Converts an array list into a lookup array.
187       * @param array $array
188       * @return array
189       */
190      protected function lookup($array)
191      {
192          $ret = array();
193          foreach ($array as $val) {
194              $ret[$val] = true;
195          }
196          return $ret;
197      }
198  
199      /**
200       * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id
201       * object based on a string Id.
202       * @param string $id
203       * @return HTMLPurifier_ConfigSchema_Interchange_Id
204       */
205      protected function id($id)
206      {
207          return HTMLPurifier_ConfigSchema_Interchange_Id::make($id);
208      }
209  
210      /**
211       * Triggers errors for any unused keys passed in the hash; such keys
212       * may indicate typos, missing values, etc.
213       * @param HTMLPurifier_StringHash $hash Hash to check.
214       */
215      protected function _findUnused($hash)
216      {
217          $accessed = $hash->getAccessed();
218          foreach ($hash as $k => $v) {
219              if (!isset($accessed[$k])) {
220                  trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE);
221              }
222          }
223      }
224  }
225  
226  // vim: et sw=4 sts=4