Source for file HTMLModuleManager.php

Documentation is available at HTMLModuleManager.php

  1. <?php
  2.  
  3. {
  4.     
  5.     /**
  6.      * Instance of HTMLPurifier_DoctypeRegistry
  7.      */
  8.     public $doctypes;
  9.     
  10.     /**
  11.      * Instance of current doctype
  12.      */
  13.     public $doctype;
  14.     
  15.     /**
  16.      * Instance of HTMLPurifier_AttrTypes
  17.      */
  18.     public $attrTypes;
  19.     
  20.     /**
  21.      * Active instances of modules for the specified doctype are
  22.      * indexed, by name, in this array.
  23.      */
  24.     public $modules = array();
  25.     
  26.     /**
  27.      * Array of recognized HTMLPurifier_Module instances, indexed by
  28.      * module's class name. This array is usually lazy loaded, but a
  29.      * user can overload a module by pre-emptively registering it.
  30.      */
  31.     public $registeredModules = array();
  32.     
  33.     /**
  34.      * List of extra modules that were added by the user using addModule().
  35.      * These get unconditionally merged into the current doctype, whatever
  36.      * it may be.
  37.      */
  38.     public $userModules = array();
  39.     
  40.     /**
  41.      * Associative array of element name to list of modules that have
  42.      * definitions for the element; this array is dynamically filled.
  43.      */
  44.     public $elementLookup = array();
  45.     
  46.     /** List of prefixes we should use for registering small names */
  47.     public $prefixes = array('HTMLPurifier_HTMLModule_');
  48.     
  49.     public $contentSets;     /**< Instance of HTMLPurifier_ContentSets */
  50.     public $attrCollections/**< Instance of HTMLPurifier_AttrCollections */
  51.     
  52.     /** If set to true, unsafe elements and attributes will be allowed */
  53.     public $trusted = false;
  54.     
  55.     public function __construct({
  56.         
  57.         // editable internal objects
  58.         $this->attrTypes = new HTMLPurifier_AttrTypes();
  59.         $this->doctypes  = new HTMLPurifier_DoctypeRegistry();
  60.         
  61.         // setup basic modules
  62.         $common array(
  63.             'CommonAttributes''Text''Hypertext''List',
  64.             'Presentation''Edit''Bdo''Tables''Image',
  65.             'StyleAttribute''Scripting''Object'
  66.         );
  67.         $transitional array('Legacy''Target');
  68.         $xml array('XMLCommonAttributes');
  69.         $non_xml array('NonXMLCommonAttributes');
  70.         
  71.         // setup basic doctypes
  72.         $this->doctypes->register(
  73.             'HTML 4.01 Transitional'false,
  74.             array_merge($common$transitional$non_xml),
  75.             array('Tidy_Transitional''Tidy_Proprietary'),
  76.             array(),
  77.             '-//W3C//DTD HTML 4.01 Transitional//EN',
  78.             'http://www.w3.org/TR/html4/loose.dtd'
  79.         );
  80.         
  81.         $this->doctypes->register(
  82.             'HTML 4.01 Strict'false,
  83.             array_merge($common$non_xml),
  84.             array('Tidy_Strict''Tidy_Proprietary'),
  85.             array(),
  86.             '-//W3C//DTD HTML 4.01//EN',
  87.             'http://www.w3.org/TR/html4/strict.dtd'
  88.         );
  89.         
  90.         $this->doctypes->register(
  91.             'XHTML 1.0 Transitional'true,
  92.             array_merge($common$transitional$xml$non_xml),
  93.             array('Tidy_Transitional''Tidy_XHTML''Tidy_Proprietary'),
  94.             array(),
  95.             '-//W3C//DTD XHTML 1.0 Transitional//EN',
  96.             'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
  97.         );
  98.         
  99.         $this->doctypes->register(
  100.             'XHTML 1.0 Strict'true,
  101.             array_merge($common$xml$non_xml),
  102.             array('Tidy_Strict''Tidy_XHTML''Tidy_Strict''Tidy_Proprietary'),
  103.             array(),
  104.             '-//W3C//DTD XHTML 1.0 Strict//EN',
  105.             'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
  106.         );
  107.         
  108.         $this->doctypes->register(
  109.             'XHTML 1.1'true,
  110.             array_merge($common$xmlarray('Ruby')),
  111.             array('Tidy_Strict''Tidy_XHTML''Tidy_Proprietary''Tidy_Strict')// Tidy_XHTML1_1
  112.             array(),
  113.             '-//W3C//DTD XHTML 1.1//EN',
  114.             'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
  115.         );
  116.         
  117.     }
  118.     
  119.     /**
  120.      * Registers a module to the recognized module list, useful for
  121.      * overloading pre-existing modules.
  122.      * @param $module Mixed: string module name, with or without
  123.      *                 HTMLPurifier_HTMLModule prefix, or instance of
  124.      *                 subclass of HTMLPurifier_HTMLModule.
  125.      * @param $overload Boolean whether or not to overload previous modules.
  126.      *                   If this is not set, and you do overload a module,
  127.      *                   HTML Purifier will complain with a warning.
  128.      * @note This function will not call autoload, you must instantiate
  129.      *        (and thus invoke) autoload outside the method.
  130.      * @note If a string is passed as a module name, different variants
  131.      *        will be tested in this order:
  132.      *           - Check for HTMLPurifier_HTMLModule_$name
  133.      *           - Check all prefixes with $name in order they were added
  134.      *           - Check for literal object name
  135.      *           - Throw fatal error
  136.      *        If your object name collides with an internal class, specify
  137.      *        your module manually. All modules must have been included
  138.      *        externally: registerModule will not perform inclusions for you!
  139.      */
  140.     public function registerModule($module$overload false{
  141.         if (is_string($module)) {
  142.             // attempt to load the module
  143.             $original_module $module;
  144.             $ok false;
  145.             foreach ($this->prefixes as $prefix{
  146.                 $module $prefix $original_module;
  147.                 if (class_exists($module)) {
  148.                     $ok true;
  149.                     break;
  150.                 }
  151.             }
  152.             if (!$ok{
  153.                 $module $original_module;
  154.                 if (!class_exists($module)) {
  155.                     trigger_error($original_module ' module does not exist',
  156.                         E_USER_ERROR);
  157.                     return;
  158.                 }
  159.             }
  160.             $module new $module();
  161.         }
  162.         if (empty($module->name)) {
  163.             trigger_error('Module instance of ' get_class($module' must have name');
  164.             return;
  165.         }
  166.         if (!$overload && isset($this->registeredModules[$module->name])) {
  167.             trigger_error('Overloading ' $module->name ' without explicit overload parameter'E_USER_WARNING);
  168.         }
  169.         $this->registeredModules[$module->name$module;
  170.     }
  171.     
  172.     /**
  173.      * Adds a module to the current doctype by first registering it,
  174.      * and then tacking it on to the active doctype
  175.      */
  176.     public function addModule($module{
  177.         $this->registerModule($module);
  178.         if (is_object($module)) $module $module->name;
  179.         $this->userModules[$module;
  180.     }
  181.     
  182.     /**
  183.      * Adds a class prefix that registerModule() will use to resolve a
  184.      * string name to a concrete class
  185.      */
  186.     public function addPrefix($prefix{
  187.         $this->prefixes[$prefix;
  188.     }
  189.     
  190.     /**
  191.      * Performs processing on modules, after being called you may
  192.      * use getElement() and getElements()
  193.      * @param $config Instance of HTMLPurifier_Config
  194.      */
  195.     public function setup($config{
  196.         
  197.         $this->trusted = $config->get('HTML''Trusted');
  198.         
  199.         // generate
  200.         $this->doctype = $this->doctypes->make($config);
  201.         $modules $this->doctype->modules;
  202.         
  203.         // take out the default modules that aren't allowed
  204.         $lookup $config->get('HTML''AllowedModules');
  205.         $special_cases $config->get('HTML''CoreModules');
  206.         
  207.         if (is_array($lookup)) {
  208.             foreach ($modules as $k => $m{
  209.                 if (isset($special_cases[$m])) continue;
  210.                 if (!isset($lookup[$m])) unset($modules[$k]);
  211.             }
  212.         }
  213.         
  214.         // merge in custom modules
  215.         $modules array_merge($modules$this->userModules);
  216.         
  217.         // add proprietary module (this gets special treatment because
  218.         // it is completely removed from doctypes, etc.)
  219.         if ($config->get('HTML''Proprietary')) {
  220.             $modules['Proprietary';
  221.         }
  222.         
  223.         // add SafeObject/Safeembed modules
  224.         if ($config->get('HTML''SafeObject')) {
  225.             $modules['SafeObject';
  226.         }
  227.         if ($config->get('HTML''SafeEmbed')) {
  228.             $modules['SafeEmbed';
  229.         }
  230.         
  231.         foreach ($modules as $module{
  232.             $this->processModule($module);
  233.             $this->modules[$module]->setup($config);
  234.         }
  235.         
  236.         foreach ($this->doctype->tidyModules as $module{
  237.             $this->processModule($module);
  238.             $this->modules[$module]->setup($config);
  239.         }
  240.         
  241.         // prepare any injectors
  242.         foreach ($this->modules as $module{
  243.             $n array();
  244.             foreach ($module->info_injector as $i => $injector{
  245.                 if (!is_object($injector)) {
  246.                     $class "HTMLPurifier_Injector_$injector";
  247.                     $injector new $class;
  248.                 }
  249.                 $n[$injector->name$injector;
  250.             }
  251.             $module->info_injector $n;
  252.         }
  253.         
  254.         // setup lookup table based on all valid modules
  255.         foreach ($this->modules as $module{
  256.             foreach ($module->info as $name => $def{
  257.                 if (!isset($this->elementLookup[$name])) {
  258.                     $this->elementLookup[$namearray();
  259.                 }
  260.                 $this->elementLookup[$name][$module->name;
  261.             }
  262.         }
  263.         
  264.         // note the different choice
  265.         $this->contentSets = new HTMLPurifier_ContentSets(
  266.             // content set assembly deals with all possible modules,
  267.             // not just ones deemed to be "safe"
  268.             $this->modules
  269.         );
  270.         $this->attrCollections = new HTMLPurifier_AttrCollections(
  271.             $this->attrTypes,
  272.             // there is no way to directly disable a global attribute,
  273.             // but using AllowedAttributes or simply not including
  274.             // the module in your custom doctype should be sufficient
  275.             $this->modules
  276.         );
  277.     }
  278.     
  279.     /**
  280.      * Takes a module and adds it to the active module collection,
  281.      * registering it if necessary.
  282.      */
  283.     public function processModule($module{
  284.         if (!isset($this->registeredModules[$module]|| is_object($module)) {
  285.             $this->registerModule($module);
  286.         }
  287.         $this->modules[$module$this->registeredModules[$module];
  288.     }
  289.     
  290.     /**
  291.      * Retrieves merged element definitions.
  292.      * @return Array of HTMLPurifier_ElementDef
  293.      */
  294.     public function getElements({
  295.         
  296.         $elements array();
  297.         foreach ($this->modules as $module{
  298.             if (!$this->trusted && !$module->safecontinue;
  299.             foreach ($module->info as $name => $v{
  300.                 if (isset($elements[$name])) continue;
  301.                 $elements[$name$this->getElement($name);
  302.             }
  303.         }
  304.         
  305.         // remove dud elements, this happens when an element that
  306.         // appeared to be safe actually wasn't
  307.         foreach ($elements as $n => $v{
  308.             if ($v === falseunset($elements[$n]);
  309.         }
  310.         
  311.         return $elements;
  312.         
  313.     }
  314.     
  315.     /**
  316.      * Retrieves a single merged element definition
  317.      * @param $name Name of element
  318.      * @param $trusted Boolean trusted overriding parameter: set to true
  319.      *                  if you want the full version of an element
  320.      * @return Merged HTMLPurifier_ElementDef
  321.      * @note You may notice that modules are getting iterated over twice (once
  322.      *        in getElements() and once here). This
  323.      *        is because
  324.      */
  325.     public function getElement($name$trusted null{
  326.         
  327.         if (!isset($this->elementLookup[$name])) {
  328.             return false;
  329.         }
  330.         
  331.         // setup global state variables
  332.         $def false;
  333.         if ($trusted === null$trusted $this->trusted;
  334.         
  335.         // iterate through each module that has registered itself to this
  336.         // element
  337.         foreach($this->elementLookup[$nameas $module_name{
  338.             
  339.             $module $this->modules[$module_name];
  340.             
  341.             // refuse to create/merge from a module that is deemed unsafe--
  342.             // pretend the module doesn't exist--when trusted mode is not on.
  343.             if (!$trusted && !$module->safe{
  344.                 continue;
  345.             }
  346.             
  347.             // clone is used because, ideally speaking, the original
  348.             // definition should not be modified. Usually, this will
  349.             // make no difference, but for consistency's sake
  350.             $new_def clone $module->info[$name];
  351.             
  352.             if (!$def && $new_def->standalone{
  353.                 $def $new_def;
  354.             elseif ($def{
  355.                 // This will occur even if $new_def is standalone. In practice,
  356.                 // this will usually result in a full replacement.
  357.                 $def->mergeIn($new_def);
  358.             else {
  359.                 // :TODO:
  360.                 // non-standalone definitions that don't have a standalone
  361.                 // to merge into could be deferred to the end
  362.                 continue;
  363.             }
  364.             
  365.             // attribute value expansions
  366.             $this->attrCollections->performInclusions($def->attr);
  367.             $this->attrCollections->expandIdentifiers($def->attr$this->attrTypes);
  368.             
  369.             // descendants_are_inline, for ChildDef_Chameleon
  370.             if (is_string($def->content_model&&
  371.                 strpos($def->content_model'Inline'!== false{
  372.                 if ($name != 'del' && $name != 'ins'{
  373.                     // this is for you, ins/del
  374.                     $def->descendants_are_inline true;
  375.                 }
  376.             }
  377.             
  378.             $this->contentSets->generateChildDef($def$module);
  379.         }
  380.             
  381.         // add information on required attributes
  382.         foreach ($def->attr as $attr_name => $attr_def{
  383.             if ($attr_def->required{
  384.                 $def->required_attr[$attr_name;
  385.             }
  386.         }
  387.         
  388.         return $def;
  389.         
  390.     }
  391.     
  392. }

Documentation generated on Thu, 19 Jun 2008 18:49:31 -0400 by phpDocumentor 1.4.2