InterchangeBuilder.php 5.9 KB

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