Source for file form.php

Documentation is available at form.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Platform
  4.  * @subpackage  Form
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM'or die;
  11.  
  12. jimport('joomla.filesystem.path');
  13. jimport('joomla.utilities.arrayhelper');
  14.  
  15. /**
  16.  * Form Class for the Joomla Platform.
  17.  *
  18.  * This class implements a robust API for constructing, populating, filtering, and validating forms.
  19.  * It uses XML definitions to construct form fields and a variety of field and rule classes to
  20.  * render and validate the form.
  21.  *
  22.  * @package     Joomla.Platform
  23.  * @subpackage  Form
  24.  * @link        http://www.w3.org/TR/html4/interact/forms.html
  25.  * @link        http://www.w3.org/TR/html5/forms.html
  26.  * @since       11.1
  27.  */
  28. class JForm
  29. {
  30.     /**
  31.      * The JRegistry data store for form fields during display.
  32.      * @var    object 
  33.      * @since  11.1
  34.      */
  35.     protected $data;
  36.  
  37.     /**
  38.      * The form object errors array.
  39.      * @var    array 
  40.      * @since  11.1
  41.      */
  42.     protected $errors = array();
  43.  
  44.     /**
  45.      * The name of the form instance.
  46.      * @var    string 
  47.      * @since  11.1
  48.      */
  49.     protected $name;
  50.  
  51.     /**
  52.      * The form object options for use in rendering and validation.
  53.      * @var    array 
  54.      * @since  11.1
  55.      */
  56.     protected $options = array();
  57.  
  58.     /**
  59.      * The form XML definition.
  60.      * @var    SimpleXMLElement 
  61.      * @since  11.1
  62.      */
  63.     protected $xml;
  64.  
  65.     /**
  66.      * Form instances.
  67.      * @var    array 
  68.      * @since  11.1
  69.      */
  70.     protected static $forms array();
  71.  
  72.     /**
  73.      * Alows extensions to implement repeating elements
  74.      * @var    mixed 
  75.      * @since  3.2
  76.      */
  77.     public $repeat = false;
  78.  
  79.     /**
  80.      * Method to instantiate the form object.
  81.      *
  82.      * @param   string  $name     The name of the form.
  83.      * @param   array   $options  An array of form options.
  84.      *
  85.      * @since   11.1
  86.      */
  87.     public function __construct($namearray $options array())
  88.     {
  89.         // Set the name for the form.
  90.         $this->name = $name;
  91.  
  92.         // Initialise the JRegistry data.
  93.         $this->data = new JRegistry;
  94.  
  95.         // Set the options if specified.
  96.         $this->options['control'= isset($options['control']$options['control'false;
  97.     }
  98.  
  99.     /**
  100.      * Method to bind data to the form.
  101.      *
  102.      * @param   mixed  $data  An array or object of data to bind to the form.
  103.      *
  104.      * @return  boolean  True on success.
  105.      *
  106.      * @since   11.1
  107.      */
  108.     public function bind($data)
  109.     {
  110.         // Make sure there is a valid JForm XML document.
  111.         if (!($this->xml instanceof SimpleXMLElement))
  112.         {
  113.             return false;
  114.         }
  115.  
  116.         // The data must be an object or array.
  117.         if (!is_object($data&& !is_array($data))
  118.         {
  119.             return false;
  120.         }
  121.  
  122.         // Convert the input to an array.
  123.         if (is_object($data))
  124.         {
  125.             if ($data instanceof JRegistry)
  126.             {
  127.                 // Handle a JRegistry.
  128.                 $data $data->toArray();
  129.             }
  130.             elseif ($data instanceof JObject)
  131.             {
  132.                 // Handle a JObject.
  133.                 $data $data->getProperties();
  134.             }
  135.             else
  136.             {
  137.                 // Handle other types of objects.
  138.                 $data = (array) $data;
  139.             }
  140.         }
  141.  
  142.         // Process the input data.
  143.         foreach ($data as $k => $v)
  144.         {
  145.             if ($this->findField($k))
  146.             {
  147.                 // If the field exists set the value.
  148.                 $this->data->set($k$v);
  149.             }
  150.             elseif (is_object($v|| JArrayHelper::isAssociative($v))
  151.             {
  152.                 // If the value is an object or an associative array hand it off to the recursive bind level method.
  153.                 $this->bindLevel($k$v);
  154.             }
  155.         }
  156.  
  157.         return true;
  158.     }
  159.  
  160.     /**
  161.      * Method to bind data to the form for the group level.
  162.      *
  163.      * @param   string  $group  The dot-separated form group path on which to bind the data.
  164.      * @param   mixed   $data   An array or object of data to bind to the form for the group level.
  165.      *
  166.      * @return  void 
  167.      *
  168.      * @since   11.1
  169.      */
  170.     protected function bindLevel($group$data)
  171.     {
  172.         // Ensure the input data is an array.
  173.         settype($data'array');
  174.  
  175.         // Process the input data.
  176.         foreach ($data as $k => $v)
  177.         {
  178.             if ($this->findField($k$group))
  179.             {
  180.                 // If the field exists set the value.
  181.                 $this->data->set($group '.' $k$v);
  182.             }
  183.             elseif (is_object($v|| JArrayHelper::isAssociative($v))
  184.             {
  185.                 // If the value is an object or an associative array, hand it off to the recursive bind level method
  186.                 $this->bindLevel($group '.' $k$v);
  187.             }
  188.         }
  189.     }
  190.  
  191.     /**
  192.      * Method to filter the form data.
  193.      *
  194.      * @param   array   $data   An array of field values to filter.
  195.      * @param   string  $group  The dot-separated form group path on which to filter the fields.
  196.      *
  197.      * @return  mixed  Array or false.
  198.      *
  199.      * @since   11.1
  200.      */
  201.     public function filter($data$group null)
  202.     {
  203.         // Make sure there is a valid JForm XML document.
  204.         if (!($this->xml instanceof SimpleXMLElement))
  205.         {
  206.             return false;
  207.         }
  208.  
  209.         $input new JRegistry($data);
  210.         $output new JRegistry;
  211.  
  212.         // Get the fields for which to filter the data.
  213.         $fields $this->findFieldsByGroup($group);
  214.  
  215.         if (!$fields)
  216.         {
  217.             // PANIC!
  218.             return false;
  219.         }
  220.  
  221.         // Filter the fields.
  222.         foreach ($fields as $field)
  223.         {
  224.             $name = (string) $field['name'];
  225.  
  226.             // Get the field groups for the element.
  227.             $attrs $field->xpath('ancestor::fields[@name]/@name');
  228.             $groups array_map('strval'$attrs $attrs array());
  229.             $group implode('.'$groups);
  230.  
  231.             // Get the field value from the data input.
  232.             if ($group)
  233.             {
  234.                 // Filter the value if it exists.
  235.                 if ($input->exists($group '.' $name))
  236.                 {
  237.                     $output->set($group '.' $name$this->filterField($field$input->get($group '.' $name(string) $field['default'])));
  238.                 }
  239.             }
  240.             else
  241.             {
  242.                 // Filter the value if it exists.
  243.                 if ($input->exists($name))
  244.                 {
  245.                     $output->set($name$this->filterField($field$input->get($name(string) $field['default'])));
  246.                 }
  247.             }
  248.         }
  249.  
  250.         return $output->toArray();
  251.     }
  252.  
  253.     /**
  254.      * Return all errors, if any.
  255.      *
  256.      * @return  array  Array of error messages or RuntimeException objects.
  257.      *
  258.      * @since   11.1
  259.      */
  260.     public function getErrors()
  261.     {
  262.         return $this->errors;
  263.     }
  264.  
  265.     /**
  266.      * Method to get a form field represented as a JFormField object.
  267.      *
  268.      * @param   string  $name   The name of the form field.
  269.      * @param   string  $group  The optional dot-separated form group path on which to find the field.
  270.      * @param   mixed   $value  The optional value to use as the default for the field.
  271.      *
  272.      * @return  mixed  The JFormField object for the field or boolean false on error.
  273.      *
  274.      * @since   11.1
  275.      */
  276.     public function getField($name$group null$value null)
  277.     {
  278.         // Make sure there is a valid JForm XML document.
  279.         if (!($this->xml instanceof SimpleXMLElement))
  280.         {
  281.             return false;
  282.         }
  283.  
  284.         // Attempt to find the field by name and group.
  285.         $element $this->findField($name$group);
  286.  
  287.         // If the field element was not found return false.
  288.         if (!$element)
  289.         {
  290.             return false;
  291.         }
  292.  
  293.         return $this->loadField($element$group$value);
  294.     }
  295.  
  296.     /**
  297.      * Method to get an attribute value from a field XML element.  If the attribute doesn't exist or
  298.      * is null then the optional default value will be used.
  299.      *
  300.      * @param   string  $name       The name of the form field for which to get the attribute value.
  301.      * @param   string  $attribute  The name of the attribute for which to get a value.
  302.      * @param   mixed   $default    The optional default value to use if no attribute value exists.
  303.      * @param   string  $group      The optional dot-separated form group path on which to find the field.
  304.      *
  305.      * @return  mixed  The attribute value for the field.
  306.      *
  307.      * @since   11.1
  308.      * @throws  UnexpectedValueException
  309.      */
  310.     public function getFieldAttribute($name$attribute$default null$group null)
  311.     {
  312.         // Make sure there is a valid JForm XML document.
  313.         if (!($this->xml instanceof SimpleXMLElement))
  314.         {
  315.             throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement'get_class($this)));
  316.         }
  317.  
  318.         // Find the form field element from the definition.
  319.         $element $this->findField($name$group);
  320.  
  321.         // If the element exists and the attribute exists for the field return the attribute value.
  322.         if (($element instanceof SimpleXMLElement&& ((string) $element[$attribute]))
  323.         {
  324.             return (string) $element[$attribute];
  325.         }
  326.  
  327.         // Otherwise return the given default value.
  328.         else
  329.         {
  330.             return $default;
  331.         }
  332.     }
  333.  
  334.     /**
  335.      * Method to get an array of JFormField objects in a given fieldset by name.  If no name is
  336.      * given then all fields are returned.
  337.      *
  338.      * @param   string  $set  The optional name of the fieldset.
  339.      *
  340.      * @return  array  The array of JFormField objects in the fieldset.
  341.      *
  342.      * @since   11.1
  343.      */
  344.     public function getFieldset($set null)
  345.     {
  346.         $fields array();
  347.  
  348.         // Get all of the field elements in the fieldset.
  349.         if ($set)
  350.         {
  351.             $elements $this->findFieldsByFieldset($set);
  352.         }
  353.  
  354.         // Get all fields.
  355.         else
  356.         {
  357.             $elements $this->findFieldsByGroup();
  358.         }
  359.  
  360.         // If no field elements were found return empty.
  361.         if (empty($elements))
  362.         {
  363.             return $fields;
  364.         }
  365.  
  366.         // Build the result array from the found field elements.
  367.         foreach ($elements as $element)
  368.         {
  369.             // Get the field groups for the element.
  370.             $attrs $element->xpath('ancestor::fields[@name]/@name');
  371.             $groups array_map('strval'$attrs $attrs array());
  372.             $group implode('.'$groups);
  373.  
  374.             // If the field is successfully loaded add it to the result array.
  375.             if ($field $this->loadField($element$group))
  376.             {
  377.                 $fields[$field->id$field;
  378.             }
  379.         }
  380.  
  381.         return $fields;
  382.     }
  383.  
  384.     /**
  385.      * Method to get an array of fieldset objects optionally filtered over a given field group.
  386.      *
  387.      * @param   string  $group  The dot-separated form group path on which to filter the fieldsets.
  388.      *
  389.      * @return  array  The array of fieldset objects.
  390.      *
  391.      * @since   11.1
  392.      */
  393.     public function getFieldsets($group null)
  394.     {
  395.         $fieldsets array();
  396.         $sets array();
  397.  
  398.         // Make sure there is a valid JForm XML document.
  399.         if (!($this->xml instanceof SimpleXMLElement))
  400.         {
  401.             return $fieldsets;
  402.         }
  403.  
  404.         if ($group)
  405.         {
  406.             // Get the fields elements for a given group.
  407.             $elements &$this->findGroup($group);
  408.  
  409.             foreach ($elements as &$element)
  410.             {
  411.                 // Get an array of <fieldset /> elements and fieldset attributes within the fields element.
  412.                 if ($tmp $element->xpath('descendant::fieldset[@name] | descendant::field[@fieldset]/@fieldset'))
  413.                 {
  414.                     $sets array_merge($sets(array) $tmp);
  415.                 }
  416.             }
  417.         }
  418.         else
  419.         {
  420.             // Get an array of <fieldset /> elements and fieldset attributes.
  421.             $sets $this->xml->xpath('//fieldset[@name] | //field[@fieldset]/@fieldset');
  422.         }
  423.  
  424.         // If no fieldsets are found return empty.
  425.         if (empty($sets))
  426.         {
  427.             return $fieldsets;
  428.         }
  429.  
  430.         // Process each found fieldset.
  431.         foreach ($sets as $set)
  432.         {
  433.             // Are we dealing with a fieldset element?
  434.             if ((string) $set['name'])
  435.             {
  436.                 // Only create it if it doesn't already exist.
  437.                 if (empty($fieldsets[(string) $set['name']]))
  438.                 {
  439.                     // Build the fieldset object.
  440.                     $fieldset = (object) array('name' => '''label' => '''description' => '');
  441.  
  442.                     foreach ($set->attributes(as $name => $value)
  443.                     {
  444.                         $fieldset->$name = (string) $value;
  445.                     }
  446.  
  447.                     // Add the fieldset object to the list.
  448.                     $fieldsets[$fieldset->name$fieldset;
  449.                 }
  450.             }
  451.  
  452.             // Must be dealing with a fieldset attribute.
  453.             else
  454.             {
  455.                 // Only create it if it doesn't already exist.
  456.                 if (empty($fieldsets[(string) $set]))
  457.                 {
  458.                     // Attempt to get the fieldset element for data (throughout the entire form document).
  459.                     $tmp $this->xml->xpath('//fieldset[@name="' . (string) $set '"]');
  460.  
  461.                     // If no element was found, build a very simple fieldset object.
  462.                     if (empty($tmp))
  463.                     {
  464.                         $fieldset = (object) array('name' => (string) $set'label' => '''description' => '');
  465.                     }
  466.  
  467.                     // Build the fieldset object from the element.
  468.                     else
  469.                     {
  470.                         $fieldset = (object) array('name' => '''label' => '''description' => '');
  471.  
  472.                         foreach ($tmp[0]->attributes(as $name => $value)
  473.                         {
  474.                             $fieldset->$name = (string) $value;
  475.                         }
  476.                     }
  477.  
  478.                     // Add the fieldset object to the list.
  479.                     $fieldsets[$fieldset->name$fieldset;
  480.                 }
  481.             }
  482.         }
  483.  
  484.         return $fieldsets;
  485.     }
  486.  
  487.     /**
  488.      * Method to get the form control. This string serves as a container for all form fields. For
  489.      * example, if there is a field named 'foo' and a field named 'bar' and the form control is
  490.      * empty the fields will be rendered like: <input name="foo" /> and <input name="bar" />.  If
  491.      * the form control is set to 'joomla' however, the fields would be rendered like:
  492.      * <input name="joomla[foo]" /> and <input name="joomla[bar]" />.
  493.      *
  494.      * @return  string  The form control string.
  495.      *
  496.      * @since   11.1
  497.      */
  498.     public function getFormControl()
  499.     {
  500.         return (string) $this->options['control'];
  501.     }
  502.  
  503.     /**
  504.      * Method to get an array of JFormField objects in a given field group by name.
  505.      *
  506.      * @param   string   $group   The dot-separated form group path for which to get the form fields.
  507.      * @param   boolean  $nested  True to also include fields in nested groups that are inside of the
  508.      *                             group for which to find fields.
  509.      *
  510.      * @return  array    The array of JFormField objects in the field group.
  511.      *
  512.      * @since   11.1
  513.      */
  514.     public function getGroup($group$nested false)
  515.     {
  516.         $fields array();
  517.  
  518.         // Get all of the field elements in the field group.
  519.         $elements $this->findFieldsByGroup($group$nested);
  520.  
  521.         // If no field elements were found return empty.
  522.         if (empty($elements))
  523.         {
  524.             return $fields;
  525.         }
  526.  
  527.         // Build the result array from the found field elements.
  528.         foreach ($elements as $element)
  529.         {
  530.             // Get the field groups for the element.
  531.             $attrs    $element->xpath('ancestor::fields[@name]/@name');
  532.             $groups    array_map('strval'$attrs $attrs array());
  533.             $group    implode('.'$groups);
  534.  
  535.             // If the field is successfully loaded add it to the result array.
  536.             if ($field $this->loadField($element$group))
  537.             {
  538.                 $fields[$field->id$field;
  539.             }
  540.         }
  541.  
  542.         return $fields;
  543.     }
  544.  
  545.     /**
  546.      * Method to get a form field markup for the field input.
  547.      *
  548.      * @param   string  $name   The name of the form field.
  549.      * @param   string  $group  The optional dot-separated form group path on which to find the field.
  550.      * @param   mixed   $value  The optional value to use as the default for the field.
  551.      *
  552.      * @return  string  The form field markup.
  553.      *
  554.      * @since   11.1
  555.      */
  556.     public function getInput($name$group null$value null)
  557.     {
  558.         // Attempt to get the form field.
  559.         if ($field $this->getField($name$group$value))
  560.         {
  561.             return $field->input;
  562.         }
  563.  
  564.         return '';
  565.     }
  566.  
  567.     /**
  568.      * Method to get the label for a field input.
  569.      *
  570.      * @param   string  $name   The name of the form field.
  571.      * @param   string  $group  The optional dot-separated form group path on which to find the field.
  572.      *
  573.      * @return  string  The form field label.
  574.      *
  575.      * @since   11.1
  576.      */
  577.     public function getLabel($name$group null)
  578.     {
  579.         // Attempt to get the form field.
  580.         if ($field $this->getField($name$group))
  581.         {
  582.             return $field->label;
  583.         }
  584.  
  585.         return '';
  586.     }
  587.  
  588.     /**
  589.      * Method to get the form name.
  590.      *
  591.      * @return  string  The name of the form.
  592.      *
  593.      * @since   11.1
  594.      */
  595.     public function getName()
  596.     {
  597.         return $this->name;
  598.     }
  599.  
  600.     /**
  601.      * Method to get the value of a field.
  602.      *
  603.      * @param   string  $name     The name of the field for which to get the value.
  604.      * @param   string  $group    The optional dot-separated form group path on which to get the value.
  605.      * @param   mixed   $default  The optional default value of the field value is empty.
  606.      *
  607.      * @return  mixed  The value of the field or the default value if empty.
  608.      *
  609.      * @since   11.1
  610.      */
  611.     public function getValue($name$group null$default null)
  612.     {
  613.         // If a group is set use it.
  614.         if ($group)
  615.         {
  616.             $return $this->data->get($group '.' $name$default);
  617.         }
  618.         else
  619.         {
  620.             $return $this->data->get($name$default);
  621.         }
  622.  
  623.         return $return;
  624.     }
  625.  
  626.     /**
  627.      * Method to get a control group with label and input.
  628.      *
  629.      * @param   string  $name     The name of the field for which to get the value.
  630.      * @param   string  $group    The optional dot-separated form group path on which to get the value.
  631.      * @param   mixed   $default  The optional default value of the field value is empty.
  632.      *
  633.      * @return  string  A string containing the html for the control goup
  634.      *
  635.      * @since   3.2
  636.      */
  637.     public function getControlGroup($name$group null$default null)
  638.     {
  639.         $field $this->getField($name$group$default);
  640.         if ($field)
  641.         {
  642.             return $field->getControlGroup();
  643.         }
  644.         return '';
  645.     }
  646.  
  647.     /**
  648.      * Method to get all control groups with label and input of a fieldset.
  649.      *
  650.      * @param   string  $name  The name of the fieldset for which to get the values.
  651.      *
  652.      * @return  string  A string containing the html for the control goups
  653.      *
  654.      * @since   3.2
  655.      */
  656.     public function getControlGroups($name)
  657.     {
  658.         $fields $this->getFieldset($name);
  659.  
  660.         $html array();
  661.         foreach ($fields as $field)
  662.         {
  663.             $html[$field->getControlGroup();
  664.         }
  665.  
  666.         return implode(''$html);
  667.     }
  668.  
  669.     /**
  670.      * Method to load the form description from an XML string or object.
  671.      *
  672.      * The replace option works per field.  If a field being loaded already exists in the current
  673.      * form definition then the behavior or load will vary depending upon the replace flag.  If it
  674.      * is set to true, then the existing field will be replaced in its exact location by the new
  675.      * field being loaded.  If it is false, then the new field being loaded will be ignored and the
  676.      * method will move on to the next field to load.
  677.      *
  678.      * @param   string  $data     The name of an XML string or object.
  679.      * @param   string  $replace  Flag to toggle whether form fields should be replaced if a field
  680.      *                             already exists with the same group/name.
  681.      * @param   string  $xpath    An optional xpath to search for the fields.
  682.      *
  683.      * @return  boolean  True on success, false otherwise.
  684.      *
  685.      * @since   11.1
  686.      */
  687.     public function load($data$replace true$xpath false)
  688.     {
  689.         // If the data to load isn't already an XML element or string return false.
  690.         if ((!($data instanceof SimpleXMLElement)) && (!is_string($data)))
  691.         {
  692.             return false;
  693.         }
  694.  
  695.         // Attempt to load the XML if a string.
  696.         if (is_string($data))
  697.         {
  698.             try
  699.             {
  700.                 $data new SimpleXMLElement($data);
  701.             }
  702.             catch (Exception $e)
  703.             {
  704.                 return false;
  705.             }
  706.  
  707.             // Make sure the XML loaded correctly.
  708.             if (!$data)
  709.             {
  710.                 return false;
  711.             }
  712.         }
  713.  
  714.         // If we have no XML definition at this point let's make sure we get one.
  715.         if (empty($this->xml))
  716.         {
  717.             // If no XPath query is set to search for fields, and we have a <form />, set it and return.
  718.             if (!$xpath && ($data->getName(== 'form'))
  719.             {
  720.                 $this->xml = $data;
  721.  
  722.                 // Synchronize any paths found in the load.
  723.                 $this->syncPaths();
  724.  
  725.                 return true;
  726.             }
  727.  
  728.             // Create a root element for the form.
  729.             else
  730.             {
  731.                 $this->xml = new SimpleXMLElement('<form></form>');
  732.             }
  733.         }
  734.  
  735.         // Get the XML elements to load.
  736.         $elements array();
  737.  
  738.         if ($xpath)
  739.         {
  740.             $elements $data->xpath($xpath);
  741.         }
  742.         elseif ($data->getName(== 'form')
  743.         {
  744.             $elements $data->children();
  745.         }
  746.  
  747.         // If there is nothing to load return true.
  748.         if (empty($elements))
  749.         {
  750.             return true;
  751.         }
  752.  
  753.         // Load the found form elements.
  754.         foreach ($elements as $element)
  755.         {
  756.             // Get an array of fields with the correct name.
  757.             $fields $element->xpath('descendant-or-self::field');
  758.  
  759.             foreach ($fields as $field)
  760.             {
  761.                 // Get the group names as strings for ancestor fields elements.
  762.                 $attrs $field->xpath('ancestor::fields[@name]/@name');
  763.                 $groups array_map('strval'$attrs $attrs array());
  764.  
  765.                 // Check to see if the field exists in the current form.
  766.                 if ($current $this->findField((string) $field['name']implode('.'$groups)))
  767.                 {
  768.                     // If set to replace found fields, replace the data and remove the field so we don't add it twice.
  769.                     if ($replace)
  770.                     {
  771.                         $olddom dom_import_simplexml($current);
  772.                         $loadeddom dom_import_simplexml($field);
  773.                         $addeddom $olddom->ownerDocument->importNode($loadeddom);
  774.                         $olddom->parentNode->replaceChild($addeddom$olddom);
  775.                         $loadeddom->parentNode->removeChild($loadeddom);
  776.                     }
  777.                     else
  778.                     {
  779.                         unset($field);
  780.                     }
  781.                 }
  782.             }
  783.  
  784.             // Merge the new field data into the existing XML document.
  785.             self::addNode($this->xml$element);
  786.         }
  787.  
  788.         // Synchronize any paths found in the load.
  789.         $this->syncPaths();
  790.  
  791.         return true;
  792.     }
  793.  
  794.     /**
  795.      * Method to load the form description from an XML file.
  796.      *
  797.      * The reset option works on a group basis. If the XML file references
  798.      * groups that have already been created they will be replaced with the
  799.      * fields in the new XML file unless the $reset parameter has been set
  800.      * to false.
  801.      *
  802.      * @param   string  $file   The filesystem path of an XML file.
  803.      * @param   string  $reset  Flag to toggle whether form fields should be replaced if a field
  804.      *                           already exists with the same group/name.
  805.      * @param   string  $xpath  An optional xpath to search for the fields.
  806.      *
  807.      * @return  boolean  True on success, false otherwise.
  808.      *
  809.      * @since   11.1
  810.      */
  811.     public function loadFile($file$reset true$xpath false)
  812.     {
  813.         // Check to see if the path is an absolute path.
  814.         if (!is_file($file))
  815.         {
  816.             // Not an absolute path so let's attempt to find one using JPath.
  817.             $file JPath::find(self::addFormPath()strtolower($file'.xml');
  818.  
  819.             // If unable to find the file return false.
  820.             if (!$file)
  821.             {
  822.                 return false;
  823.             }
  824.         }
  825.  
  826.         // Attempt to load the XML file.
  827.         $xml simplexml_load_file($file);
  828.  
  829.         return $this->load($xml$reset$xpath);
  830.     }
  831.  
  832.     /**
  833.      * Method to remove a field from the form definition.
  834.      *
  835.      * @param   string  $name   The name of the form field for which remove.
  836.      * @param   string  $group  The optional dot-separated form group path on which to find the field.
  837.      *
  838.      * @return  boolean  True on success.
  839.      *
  840.      * @since   11.1
  841.      * @throws  UnexpectedValueException
  842.      */
  843.     public function removeField($name$group null)
  844.     {
  845.         // Make sure there is a valid JForm XML document.
  846.         if (!($this->xml instanceof SimpleXMLElement))
  847.         {
  848.             throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement'get_class($this)));
  849.         }
  850.  
  851.         // Find the form field element from the definition.
  852.         $element $this->findField($name$group);
  853.  
  854.         // If the element exists remove it from the form definition.
  855.         if ($element instanceof SimpleXMLElement)
  856.         {
  857.             $dom dom_import_simplexml($element);
  858.             $dom->parentNode->removeChild($dom);
  859.         }
  860.  
  861.         return true;
  862.     }
  863.  
  864.     /**
  865.      * Method to remove a group from the form definition.
  866.      *
  867.      * @param   string  $group  The dot-separated form group path for the group to remove.
  868.      *
  869.      * @return  boolean  True on success.
  870.      *
  871.      * @since   11.1
  872.      * @throws  UnexpectedValueException
  873.      */
  874.     public function removeGroup($group)
  875.     {
  876.         // Make sure there is a valid JForm XML document.
  877.         if (!($this->xml instanceof SimpleXMLElement))
  878.         {
  879.             throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement'get_class($this)));
  880.         }
  881.  
  882.         // Get the fields elements for a given group.
  883.         $elements &$this->findGroup($group);
  884.  
  885.         foreach ($elements as &$element)
  886.         {
  887.             $dom dom_import_simplexml($element);
  888.             $dom->parentNode->removeChild($dom);
  889.         }
  890.  
  891.         return true;
  892.     }
  893.  
  894.     /**
  895.      * Method to reset the form data store and optionally the form XML definition.
  896.      *
  897.      * @param   boolean  $xml  True to also reset the XML form definition.
  898.      *
  899.      * @return  boolean  True on success.
  900.      *
  901.      * @since   11.1
  902.      */
  903.     public function reset($xml false)
  904.     {
  905.         unset($this->data);
  906.         $this->data = new JRegistry;
  907.  
  908.         if ($xml)
  909.         {
  910.             unset($this->xml);
  911.             $this->xml = new SimpleXMLElement('<form></form>');
  912.         }
  913.  
  914.         return true;
  915.     }
  916.  
  917.     /**
  918.      * Method to set a field XML element to the form definition.  If the replace flag is set then
  919.      * the field will be set whether it already exists or not.  If it isn't set, then the field
  920.      * will not be replaced if it already exists.
  921.      *
  922.      * @param   SimpleXMLElement  $element  The XML element object representation of the form field.
  923.      * @param   string            $group    The optional dot-separated form group path on which to set the field.
  924.      * @param   boolean           $replace  True to replace an existing field if one already exists.
  925.      *
  926.      * @return  boolean  True on success.
  927.      *
  928.      * @since   11.1
  929.      * @throws  UnexpectedValueException
  930.      */
  931.     public function setField(SimpleXMLElement $element$group null$replace true)
  932.     {
  933.         // Make sure there is a valid JForm XML document.
  934.         if (!($this->xml instanceof SimpleXMLElement))
  935.         {
  936.             throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement'get_class($this)));
  937.         }
  938.  
  939.         // Find the form field element from the definition.
  940.         $old $this->findField((string) $element['name']$group);
  941.  
  942.         // If an existing field is found and replace flag is false do nothing and return true.
  943.         if (!$replace && !empty($old))
  944.         {
  945.             return true;
  946.         }
  947.  
  948.         // If an existing field is found and replace flag is true remove the old field.
  949.         if ($replace && !empty($old&& ($old instanceof SimpleXMLElement))
  950.         {
  951.             $dom dom_import_simplexml($old);
  952.             $dom->parentNode->removeChild($dom);
  953.         }
  954.  
  955.         // If no existing field is found find a group element and add the field as a child of it.
  956.         if ($group)
  957.         {
  958.             // Get the fields elements for a given group.
  959.             $fields &$this->findGroup($group);
  960.  
  961.             // If an appropriate fields element was found for the group, add the element.
  962.             if (isset($fields[0]&& ($fields[0instanceof SimpleXMLElement))
  963.             {
  964.                 self::addNode($fields[0]$element);
  965.             }
  966.         }
  967.         else
  968.         {
  969.             // Set the new field to the form.
  970.             self::addNode($this->xml$element);
  971.         }
  972.  
  973.         // Synchronize any paths found in the load.
  974.         $this->syncPaths();
  975.  
  976.         return true;
  977.     }
  978.  
  979.     /**
  980.      * Method to set an attribute value for a field XML element.
  981.      *
  982.      * @param   string  $name       The name of the form field for which to set the attribute value.
  983.      * @param   string  $attribute  The name of the attribute for which to set a value.
  984.      * @param   mixed   $value      The value to set for the attribute.
  985.      * @param   string  $group      The optional dot-separated form group path on which to find the field.
  986.      *
  987.      * @return  boolean  True on success.
  988.      *
  989.      * @since   11.1
  990.      * @throws  UnexpectedValueException
  991.      */
  992.     public function setFieldAttribute($name$attribute$value$group null)
  993.     {
  994.         // Make sure there is a valid JForm XML document.
  995.         if (!($this->xml instanceof SimpleXMLElement))
  996.         {
  997.             throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement'get_class($this)));
  998.         }
  999.  
  1000.         // Find the form field element from the definition.
  1001.         $element $this->findField($name$group);
  1002.  
  1003.         // If the element doesn't exist return false.
  1004.         if (!($element instanceof SimpleXMLElement))
  1005.         {
  1006.             return false;
  1007.         }
  1008.  
  1009.         // Otherwise set the attribute and return true.
  1010.         else
  1011.         {
  1012.             $element[$attribute$value;
  1013.  
  1014.             // Synchronize any paths found in the load.
  1015.             $this->syncPaths();
  1016.  
  1017.             return true;
  1018.         }
  1019.     }
  1020.  
  1021.     /**
  1022.      * Method to set some field XML elements to the form definition.  If the replace flag is set then
  1023.      * the fields will be set whether they already exists or not.  If it isn't set, then the fields
  1024.      * will not be replaced if they already exist.
  1025.      *
  1026.      * @param   array    &$elements  The array of XML element object representations of the form fields.
  1027.      * @param   string   $group      The optional dot-separated form group path on which to set the fields.
  1028.      * @param   boolean  $replace    True to replace existing fields if they already exist.
  1029.      *
  1030.      * @return  boolean  True on success.
  1031.      *
  1032.      * @since   11.1
  1033.      * @throws  UnexpectedValueException
  1034.      */
  1035.     public function setFields(&$elements$group null$replace true)
  1036.     {
  1037.         // Make sure there is a valid JForm XML document.
  1038.         if (!($this->xml instanceof SimpleXMLElement))
  1039.         {
  1040.             throw new UnexpectedValueException(sprintf('%s::getFieldAttribute `xml` is not an instance of SimpleXMLElement'get_class($this)));
  1041.         }
  1042.  
  1043.         // Make sure the elements to set are valid.
  1044.         foreach ($elements as $element)
  1045.         {
  1046.             if (!($element instanceof SimpleXMLElement))
  1047.             {
  1048.                 throw new UnexpectedValueException(sprintf('$element not SimpleXMLElement in %s::setFields'get_class($this)));
  1049.             }
  1050.         }
  1051.  
  1052.         // Set the fields.
  1053.         $return true;
  1054.  
  1055.         foreach ($elements as $element)
  1056.         {
  1057.             if (!$this->setField($element$group$replace))
  1058.             {
  1059.                 $return false;
  1060.             }
  1061.         }
  1062.  
  1063.         // Synchronize any paths found in the load.
  1064.         $this->syncPaths();
  1065.  
  1066.         return $return;
  1067.     }
  1068.  
  1069.     /**
  1070.      * Method to set the value of a field. If the field does not exist in the form then the method
  1071.      * will return false.
  1072.      *
  1073.      * @param   string  $name   The name of the field for which to set the value.
  1074.      * @param   string  $group  The optional dot-separated form group path on which to find the field.
  1075.      * @param   mixed   $value  The value to set for the field.
  1076.      *
  1077.      * @return  boolean  True on success.
  1078.      *
  1079.      * @since   11.1
  1080.      */
  1081.     public function setValue($name$group null$value null)
  1082.     {
  1083.         // If the field does not exist return false.
  1084.         if (!$this->findField($name$group))
  1085.         {
  1086.             return false;
  1087.         }
  1088.  
  1089.         // If a group is set use it.
  1090.         if ($group)
  1091.         {
  1092.             $this->data->set($group '.' $name$value);
  1093.         }
  1094.         else
  1095.         {
  1096.             $this->data->set($name$value);
  1097.         }
  1098.  
  1099.         return true;
  1100.     }
  1101.  
  1102.     /**
  1103.      * Method to validate form data.
  1104.      *
  1105.      * Validation warnings will be pushed into JForm::errors and should be
  1106.      * retrieved with JForm::getErrors() when validate returns boolean false.
  1107.      *
  1108.      * @param   array   $data   An array of field values to validate.
  1109.      * @param   string  $group  The optional dot-separated form group path on which to filter the
  1110.      *                           fields to be validated.
  1111.      *
  1112.      * @return  mixed  True on sucess.
  1113.      *
  1114.      * @since   11.1
  1115.      */
  1116.     public function validate($data$group null)
  1117.     {
  1118.         // Make sure there is a valid JForm XML document.
  1119.         if (!($this->xml instanceof SimpleXMLElement))
  1120.         {
  1121.             return false;
  1122.         }
  1123.  
  1124.         $return true;
  1125.  
  1126.         // Create an input registry object from the data to validate.
  1127.         $input new JRegistry($data);
  1128.  
  1129.         // Get the fields for which to validate the data.
  1130.         $fields $this->findFieldsByGroup($group);
  1131.  
  1132.         if (!$fields)
  1133.         {
  1134.             // PANIC!
  1135.             return false;
  1136.         }
  1137.  
  1138.         // Validate the fields.
  1139.         foreach ($fields as $field)
  1140.         {
  1141.             $value null;
  1142.             $name = (string) $field['name'];
  1143.  
  1144.             // Get the group names as strings for ancestor fields elements.
  1145.             $attrs $field->xpath('ancestor::fields[@name]/@name');
  1146.             $groups array_map('strval'$attrs $attrs array());
  1147.             $group implode('.'$groups);
  1148.  
  1149.             // Get the value from the input data.
  1150.             if ($group)
  1151.             {
  1152.                 $value $input->get($group '.' $name);
  1153.             }
  1154.             else
  1155.             {
  1156.                 $value $input->get($name);
  1157.             }
  1158.  
  1159.             // Validate the field.
  1160.             $valid $this->validateField($field$group$value$input);
  1161.  
  1162.             // Check for an error.
  1163.             if ($valid instanceof Exception)
  1164.             {
  1165.                 array_push($this->errors$valid);
  1166.                 $return false;
  1167.             }
  1168.         }
  1169.  
  1170.         return $return;
  1171.     }
  1172.  
  1173.     /**
  1174.      * Method to apply an input filter to a value based on field data.
  1175.      *
  1176.      * @param   string  $element  The XML element object representation of the form field.
  1177.      * @param   mixed   $value    The value to filter for the field.
  1178.      *
  1179.      * @return  mixed   The filtered value.
  1180.      *
  1181.      * @since   11.1
  1182.      */
  1183.     protected function filterField($element$value)
  1184.     {
  1185.         // Make sure there is a valid SimpleXMLElement.
  1186.         if (!($element instanceof SimpleXMLElement))
  1187.         {
  1188.             return false;
  1189.         }
  1190.  
  1191.         // Get the field filter type.
  1192.         $filter = (string) $element['filter'];
  1193.  
  1194.         // Process the input value based on the filter.
  1195.         $return null;
  1196.  
  1197.         switch (strtoupper($filter))
  1198.         {
  1199.             // Access Control Rules.
  1200.             case 'RULES':
  1201.                 $return array();
  1202.  
  1203.                 foreach ((array) $value as $action => $ids)
  1204.                 {
  1205.                     // Build the rules array.
  1206.                     $return[$actionarray();
  1207.  
  1208.                     foreach ($ids as $id => $p)
  1209.                     {
  1210.                         if ($p !== '')
  1211.                         {
  1212.                             $return[$action][$id($p == '1' || $p == 'true'true false;
  1213.                         }
  1214.                     }
  1215.                 }
  1216.                 break;
  1217.  
  1218.             // Do nothing, thus leaving the return value as null.
  1219.             case 'UNSET':
  1220.                 break;
  1221.  
  1222.             // No Filter.
  1223.             case 'RAW':
  1224.                 $return $value;
  1225.                 break;
  1226.  
  1227.             // Filter the input as an array of integers.
  1228.             case 'INT_ARRAY':
  1229.                 // Make sure the input is an array.
  1230.                 if (is_object($value))
  1231.                 {
  1232.                     $value get_object_vars($value);
  1233.                 }
  1234.  
  1235.                 $value is_array($value$value array($value);
  1236.  
  1237.                 JArrayHelper::toInteger($value);
  1238.                 $return $value;
  1239.                 break;
  1240.  
  1241.             // Filter safe HTML.
  1242.             case 'SAFEHTML':
  1243.                 $return JFilterInput::getInstance(nullnull11)->clean($value'string');
  1244.                 break;
  1245.  
  1246.             // Convert a date to UTC based on the server timezone offset.
  1247.             case 'SERVER_UTC':
  1248.                 if ((int) $value 0)
  1249.                 {
  1250.                     // Get the server timezone setting.
  1251.                     $offset JFactory::getConfig()->get('offset');
  1252.  
  1253.                     // Return an SQL formatted datetime string in UTC.
  1254.                     $return JFactory::getDate($value$offset)->toSql();
  1255.                 }
  1256.                 else
  1257.                 {
  1258.                     $return '';
  1259.                 }
  1260.                 break;
  1261.  
  1262.             // Convert a date to UTC based on the user timezone offset.
  1263.             case 'USER_UTC':
  1264.                 if ((int) $value 0)
  1265.                 {
  1266.                     // Get the user timezone setting defaulting to the server timezone setting.
  1267.                     $offset JFactory::getUser()->getParam('timezone'JFactory::getConfig()->get('offset'));
  1268.  
  1269.                     // Return a MySQL formatted datetime string in UTC.
  1270.                     $return JFactory::getDate($value$offset)->toSql();
  1271.                 }
  1272.                 else
  1273.                 {
  1274.                     $return '';
  1275.                 }
  1276.                 break;
  1277.  
  1278.             // Ensures a protocol is present in the saved field. Only use when
  1279.             // the only permitted protocols requre '://'. See JFormRuleUrl for list of these.
  1280.  
  1281.             case 'URL':
  1282.                 if (empty($value))
  1283.                 {
  1284.                     return false;
  1285.                 }
  1286.  
  1287.                 $value JFilterInput::getInstance()->clean($value'html');
  1288.                 $value trim($value);
  1289.  
  1290.                 // Check for a protocol
  1291.                 $protocol parse_url($valuePHP_URL_SCHEME);
  1292.  
  1293.                 // If there is no protocol and the relative option is not specified,
  1294.                 // we assume that it is an external URL and prepend http://.
  1295.                 if (($element['type'== 'url' && !$protocol &&  !$element['relative'])
  1296.                     || (!$element['type'== 'url' && !$protocol))
  1297.                 {
  1298.                     $protocol 'http';
  1299.  
  1300.                     // If it looks like an internal link, then add the root.
  1301.                     if (substr($value0== 'index.php')
  1302.                     {
  1303.                         $value JUri::root($value;
  1304.                     }
  1305.  
  1306.                     // Otherwise we treat it is an external link.
  1307.                     // Put the url back together.
  1308.                     $value $protocol '://' $value;
  1309.                 }
  1310.  
  1311.                 // If relative URLS are allowed we assume that URLs without protocols are internal.
  1312.                 elseif (!$protocol && $element['relative'])
  1313.                 {
  1314.                     $host JUri::getInstance('SERVER')->gethost();
  1315.  
  1316.                     // If it starts with the host string, just prepend the protocol.
  1317.                     if (substr($value0== $host)
  1318.                     {
  1319.                         $value 'http://' $value;
  1320.                     }
  1321.  
  1322.                     // Otherwise prepend the root.
  1323.                     else
  1324.                     {
  1325.                         $value JUri::root($value;
  1326.                     }
  1327.                 }
  1328.  
  1329.                 $value JStringPunycode::urlToPunycode($value);
  1330.                 $return $value;
  1331.                 break;
  1332.  
  1333.             case 'TEL':
  1334.                 $value trim($value);
  1335.  
  1336.                 // Does it match the NANP pattern?
  1337.                 if (preg_match('/^(?:\+?1[-. ]?)?\(?([2-9][0-8][0-9])\)?[-. ]?([2-9][0-9]{2})[-. ]?([0-9]{4})$/'$value== 1)
  1338.                 {
  1339.                     $number = (string) preg_replace('/[^\d]/'''$value);
  1340.  
  1341.                     if (substr($number01== 1)
  1342.                     {
  1343.                         $number substr($number1);
  1344.                     }
  1345.  
  1346.                     if (substr($number02== '+1')
  1347.                     {
  1348.                         $number substr($number2);
  1349.                     }
  1350.  
  1351.                     $result '1.' $number;
  1352.                 }
  1353.  
  1354.                 // If not, does it match ITU-T?
  1355.                 elseif (preg_match('/^\+(?:[0-9] ?){6,14}[0-9]$/'$value== 1)
  1356.                 {
  1357.                     $countrycode substr($value0strpos($value' '));
  1358.                     $countrycode = (string) preg_replace('/[^\d]/'''$countrycode);
  1359.                     $number strstr($value' ');
  1360.                     $number = (string) preg_replace('/[^\d]/'''$number);
  1361.                     $result $countrycode '.' $number;
  1362.                 }
  1363.  
  1364.                 // If not, does it match EPP?
  1365.                 elseif (preg_match('/^\+[0-9]{1,3}\.[0-9]{4,14}(?:x.+)?$/'$value== 1)
  1366.                 {
  1367.                     if (strstr($value'x'))
  1368.                     {
  1369.                         $xpos strpos($value'x');
  1370.                         $value substr($value0$xpos);
  1371.                     }
  1372.  
  1373.                     $result str_replace('+'''$value);
  1374.                 }
  1375.  
  1376.                 // Maybe it is already ccc.nnnnnnn?
  1377.                 elseif (preg_match('/[0-9]{1,3}\.[0-9]{4,14}$/'$value== 1)
  1378.                 {
  1379.                     $result $value;
  1380.                 }
  1381.  
  1382.                 // If not, can we make it a string of digits?
  1383.                 else
  1384.                 {
  1385.                     $value = (string) preg_replace('/[^\d]/'''$value);
  1386.  
  1387.                     if ($value != null && strlen($value<= 15)
  1388.                     {
  1389.                         $length strlen($value);
  1390.  
  1391.                         // If it is fewer than 13 digits assume it is a local number
  1392.                         if ($length <= 12)
  1393.                         {
  1394.                             $result '.' $value;
  1395.                         }
  1396.                         else
  1397.                         {
  1398.                             // If it has 13 or more digits let's make a country code.
  1399.                             $cclen $length 12;
  1400.                             $result substr($value0$cclen'.' substr($value$cclen);
  1401.                         }
  1402.                     }
  1403.  
  1404.                     // If not let's not save anything.
  1405.                     else
  1406.                     {
  1407.                         $result '';
  1408.                     }
  1409.                 }
  1410.  
  1411.                 $return $result;
  1412.  
  1413.                 break;
  1414.             default:
  1415.                 // Check for a callback filter.
  1416.                 if (strpos($filter'::'!== false && is_callable(explode('::'$filter)))
  1417.                 {
  1418.                     $return call_user_func(explode('::'$filter)$value);
  1419.                 }
  1420.  
  1421.                 // Filter using a callback function if specified.
  1422.                 elseif (function_exists($filter))
  1423.                 {
  1424.                     $return call_user_func($filter$value);
  1425.                 }
  1426.  
  1427.                 // Filter using JFilterInput. All HTML code is filtered by default.
  1428.                 else
  1429.                 {
  1430.                     $return JFilterInput::getInstance()->clean($value$filter);
  1431.                 }
  1432.                 break;
  1433.         }
  1434.  
  1435.         return $return;
  1436.     }
  1437.  
  1438.     /**
  1439.      * Method to get a form field represented as an XML element object.
  1440.      *
  1441.      * @param   string  $name   The name of the form field.
  1442.      * @param   string  $group  The optional dot-separated form group path on which to find the field.
  1443.      *
  1444.      * @return  mixed  The XML element object for the field or boolean false on error.
  1445.      *
  1446.      * @since   11.1
  1447.      */
  1448.     protected function findField($name$group null)
  1449.     {
  1450.         $element false;
  1451.         $fields array();
  1452.  
  1453.         // Make sure there is a valid JForm XML document.
  1454.         if (!($this->xml instanceof SimpleXMLElement))
  1455.         {
  1456.             return false;
  1457.         }
  1458.  
  1459.         // Let's get the appropriate field element based on the method arguments.
  1460.         if ($group)
  1461.         {
  1462.             // Get the fields elements for a given group.
  1463.             $elements &$this->findGroup($group);
  1464.  
  1465.             // Get all of the field elements with the correct name for the fields elements.
  1466.             foreach ($elements as $element)
  1467.             {
  1468.                 // If there are matching field elements add them to the fields array.
  1469.                 if ($tmp $element->xpath('descendant::field[@name="' $name '"]'))
  1470.                 {
  1471.                     $fields array_merge($fields$tmp);
  1472.                 }
  1473.             }
  1474.  
  1475.             // Make sure something was found.
  1476.             if (!$fields)
  1477.             {
  1478.                 return false;
  1479.             }
  1480.  
  1481.             // Use the first correct match in the given group.
  1482.             $groupNames explode('.'$group);
  1483.  
  1484.             foreach ($fields as &$field)
  1485.             {
  1486.                 // Get the group names as strings for ancestor fields elements.
  1487.                 $attrs $field->xpath('ancestor::fields[@name]/@name');
  1488.                 $names array_map('strval'$attrs $attrs array());
  1489.  
  1490.                 // If the field is in the exact group use it and break out of the loop.
  1491.                 if ($names == (array) $groupNames)
  1492.                 {
  1493.                     $element &$field;
  1494.                     break;
  1495.                 }
  1496.             }
  1497.         }
  1498.         else
  1499.         {
  1500.             // Get an array of fields with the correct name.
  1501.             $fields $this->xml->xpath('//field[@name="' $name '"]');
  1502.  
  1503.             // Make sure something was found.
  1504.             if (!$fields)
  1505.             {
  1506.                 return false;
  1507.             }
  1508.  
  1509.             // Search through the fields for the right one.
  1510.             foreach ($fields as &$field)
  1511.             {
  1512.                 // If we find an ancestor fields element with a group name then it isn't what we want.
  1513.                 if ($field->xpath('ancestor::fields[@name]'))
  1514.                 {
  1515.                     continue;
  1516.                 }
  1517.  
  1518.                 // Found it!
  1519.                 else
  1520.                 {
  1521.                     $element &$field;
  1522.                     break;
  1523.                 }
  1524.             }
  1525.         }
  1526.  
  1527.         return $element;
  1528.     }
  1529.  
  1530.     /**
  1531.      * Method to get an array of <field /> elements from the form XML document which are
  1532.      * in a specified fieldset by name.
  1533.      *
  1534.      * @param   string  $name  The name of the fieldset.
  1535.      *
  1536.      * @return  mixed  Boolean false on error or array of SimpleXMLElement objects.
  1537.      *
  1538.      * @since   11.1
  1539.      */
  1540.     protected function &findFieldsByFieldset($name)
  1541.     {
  1542.         $false false;
  1543.  
  1544.         // Make sure there is a valid JForm XML document.
  1545.         if (!($this->xml instanceof SimpleXMLElement))
  1546.         {
  1547.             return $false;
  1548.         }
  1549.  
  1550.         /*
  1551.          * Get an array of <field /> elements that are underneath a <fieldset /> element
  1552.          * with the appropriate name attribute, and also any <field /> elements with
  1553.          * the appropriate fieldset attribute. To allow repeatable elements only immediate
  1554.          * field descendants of the fieldset are selected.
  1555.          */
  1556.         $fields $this->xml->xpath('//fieldset[@name="' $name '"]/field | //field[@fieldset="' $name '"]');
  1557.  
  1558.         return $fields;
  1559.     }
  1560.  
  1561.     /**
  1562.      * Method to get an array of <field /> elements from the form XML document which are
  1563.      * in a control group by name.
  1564.      *
  1565.      * @param   mixed    $group   The optional dot-separated form group path on which to find the fields.
  1566.      *                             Null will return all fields. False will return fields not in a group.
  1567.      * @param   boolean  $nested  True to also include fields in nested groups that are inside of the
  1568.      *                             group for which to find fields.
  1569.      *
  1570.      * @return  mixed  Boolean false on error or array of SimpleXMLElement objects.
  1571.      *
  1572.      * @since   11.1
  1573.      */
  1574.     protected function &findFieldsByGroup($group null$nested false)
  1575.     {
  1576.         $false false;
  1577.         $fields array();
  1578.  
  1579.         // Make sure there is a valid JForm XML document.
  1580.         if (!($this->xml instanceof SimpleXMLElement))
  1581.         {
  1582.             return $false;
  1583.         }
  1584.  
  1585.         // Get only fields in a specific group?
  1586.         if ($group)
  1587.         {
  1588.             // Get the fields elements for a given group.
  1589.             $elements &$this->findGroup($group);
  1590.  
  1591.             // Get all of the field elements for the fields elements.
  1592.             foreach ($elements as $element)
  1593.             {
  1594.                 // If there are field elements add them to the return result.
  1595.                 if ($tmp $element->xpath('descendant::field'))
  1596.                 {
  1597.                     // If we also want fields in nested groups then just merge the arrays.
  1598.                     if ($nested)
  1599.                     {
  1600.                         $fields array_merge($fields$tmp);
  1601.                     }
  1602.  
  1603.                     // If we want to exclude nested groups then we need to check each field.
  1604.                     else
  1605.                     {
  1606.                         $groupNames explode('.'$group);
  1607.  
  1608.                         foreach ($tmp as $field)
  1609.                         {
  1610.                             // Get the names of the groups that the field is in.
  1611.                             $attrs $field->xpath('ancestor::fields[@name]/@name');
  1612.                             $names array_map('strval'$attrs $attrs array());
  1613.  
  1614.                             // If the field is in the specific group then add it to the return list.
  1615.                             if ($names == (array) $groupNames)
  1616.                             {
  1617.                                 $fields array_merge($fieldsarray($field));
  1618.                             }
  1619.                         }
  1620.                     }
  1621.                 }
  1622.             }
  1623.         }
  1624.         elseif ($group === false)
  1625.         {
  1626.             // Get only field elements not in a group.
  1627.             $fields $this->xml->xpath('descendant::fields[not(@name)]/field | descendant::fields[not(@name)]/fieldset/field ');
  1628.         }
  1629.         else
  1630.         {
  1631.             // Get an array of all the <field /> elements.
  1632.             $fields $this->xml->xpath('//field');
  1633.         }
  1634.  
  1635.         return $fields;
  1636.     }
  1637.  
  1638.     /**
  1639.      * Method to get a form field group represented as an XML element object.
  1640.      *
  1641.      * @param   string  $group  The dot-separated form group path on which to find the group.
  1642.      *
  1643.      * @return  mixed  An array of XML element objects for the group or boolean false on error.
  1644.      *
  1645.      * @since   11.1
  1646.      */
  1647.     protected function &findGroup($group)
  1648.     {
  1649.         $false false;
  1650.         $groups array();
  1651.         $tmp array();
  1652.  
  1653.         // Make sure there is a valid JForm XML document.
  1654.         if (!($this->xml instanceof SimpleXMLElement))
  1655.         {
  1656.             return $false;
  1657.         }
  1658.  
  1659.         // Make sure there is actually a group to find.
  1660.         $group explode('.'$group);
  1661.  
  1662.         if (!empty($group))
  1663.         {
  1664.             // Get any fields elements with the correct group name.
  1665.             $elements $this->xml->xpath('//fields[@name="' . (string) $group[0'"]');
  1666.  
  1667.             // Check to make sure that there are no parent groups for each element.
  1668.             foreach ($elements as $element)
  1669.             {
  1670.                 if (!$element->xpath('ancestor::fields[@name]'))
  1671.                 {
  1672.                     $tmp[$element;
  1673.                 }
  1674.             }
  1675.  
  1676.             // Iterate through the nested groups to find any matching form field groups.
  1677.             for ($i 1$n count($group)$i $n$i++)
  1678.             {
  1679.                 // Initialise some loop variables.
  1680.                 $validNames array_slice($group0$i 1);
  1681.                 $current $tmp;
  1682.                 $tmp array();
  1683.  
  1684.                 // Check to make sure that there are no parent groups for each element.
  1685.                 foreach ($current as $element)
  1686.                 {
  1687.                     // Get any fields elements with the correct group name.
  1688.                     $children $element->xpath('descendant::fields[@name="' . (string) $group[$i'"]');
  1689.  
  1690.                     // For the found fields elements validate that they are in the correct groups.
  1691.                     foreach ($children as $fields)
  1692.                     {
  1693.                         // Get the group names as strings for ancestor fields elements.
  1694.                         $attrs $fields->xpath('ancestor-or-self::fields[@name]/@name');
  1695.                         $names array_map('strval'$attrs $attrs array());
  1696.  
  1697.                         // If the group names for the fields element match the valid names at this
  1698.                         // level add the fields element.
  1699.                         if ($validNames == $names)
  1700.                         {
  1701.                             $tmp[$fields;
  1702.                         }
  1703.                     }
  1704.                 }
  1705.             }
  1706.  
  1707.             // Only include valid XML objects.
  1708.             foreach ($tmp as $element)
  1709.             {
  1710.                 if ($element instanceof SimpleXMLElement)
  1711.                 {
  1712.                     $groups[$element;
  1713.                 }
  1714.             }
  1715.         }
  1716.  
  1717.         return $groups;
  1718.     }
  1719.  
  1720.     /**
  1721.      * Method to load, setup and return a JFormField object based on field data.
  1722.      *
  1723.      * @param   string  $element  The XML element object representation of the form field.
  1724.      * @param   string  $group    The optional dot-separated form group path on which to find the field.
  1725.      * @param   mixed   $value    The optional value to use as the default for the field.
  1726.      *
  1727.      * @return  mixed  The JFormField object for the field or boolean false on error.
  1728.      *
  1729.      * @since   11.1
  1730.      */
  1731.     protected function loadField($element$group null$value null)
  1732.     {
  1733.         // Make sure there is a valid SimpleXMLElement.
  1734.         if (!($element instanceof SimpleXMLElement))
  1735.         {
  1736.             return false;
  1737.         }
  1738.  
  1739.         // Get the field type.
  1740.         $type $element['type'? (string) $element['type''text';
  1741.  
  1742.         // Load the JFormField object for the field.
  1743.         $field $this->loadFieldType($type);
  1744.  
  1745.         // If the object could not be loaded, get a text field object.
  1746.         if ($field === false)
  1747.         {
  1748.             $field $this->loadFieldType('text');
  1749.         }
  1750.  
  1751.         /*
  1752.          * Get the value for the form field if not set.
  1753.          * Default to the translated version of the 'default' attribute
  1754.          * if 'translate_default' attribute if set to 'true' or '1'
  1755.          * else the value of the 'default' attribute for the field.
  1756.          */
  1757.         if ($value === null)
  1758.         {
  1759.             $default = (string) $element['default'];
  1760.  
  1761.             if (($translate $element['translate_default']&& ((string) $translate == 'true' || (string) $translate == '1'))
  1762.             {
  1763.                 $lang JFactory::getLanguage();
  1764.  
  1765.                 if ($lang->hasKey($default))
  1766.                 {
  1767.                     $debug $lang->setDebug(false);
  1768.                     $default JText::_($default);
  1769.                     $lang->setDebug($debug);
  1770.                 }
  1771.                 else
  1772.                 {
  1773.                     $default JText::_($default);
  1774.                 }
  1775.             }
  1776.  
  1777.             $value $this->getValue((string) $element['name']$group$default);
  1778.         }
  1779.  
  1780.         // Setup the JFormField object.
  1781.         $field->setForm($this);
  1782.  
  1783.         if ($field->setup($element$value$group))
  1784.         {
  1785.             return $field;
  1786.         }
  1787.         else
  1788.         {
  1789.             return false;
  1790.         }
  1791.     }
  1792.  
  1793.     /**
  1794.      * Proxy for {@link JFormHelper::loadFieldType()}.
  1795.      *
  1796.      * @param   string   $type  The field type.
  1797.      * @param   boolean  $new   Flag to toggle whether we should get a new instance of the object.
  1798.      *
  1799.      * @return  mixed  JFormField object on success, false otherwise.
  1800.      *
  1801.      * @since   11.1
  1802.      */
  1803.     protected function loadFieldType($type$new true)
  1804.     {
  1805.         return JFormHelper::loadFieldType($type$new);
  1806.     }
  1807.  
  1808.     /**
  1809.      * Proxy for JFormHelper::loadRuleType().
  1810.      *
  1811.      * @param   string   $type  The rule type.
  1812.      * @param   boolean  $new   Flag to toggle whether we should get a new instance of the object.
  1813.      *
  1814.      * @return  mixed  JFormRule object on success, false otherwise.
  1815.      *
  1816.      * @see     JFormHelper::loadRuleType()
  1817.      * @since   11.1
  1818.      */
  1819.     protected function loadRuleType($type$new true)
  1820.     {
  1821.         return JFormHelper::loadRuleType($type$new);
  1822.     }
  1823.  
  1824.     /**
  1825.      * Method to synchronize any field, form or rule paths contained in the XML document.
  1826.      *
  1827.      * @return  boolean  True on success.
  1828.      *
  1829.      * @since   11.1
  1830.      * @todo    Maybe we should receive all addXXXpaths attributes at once?
  1831.      */
  1832.     protected function syncPaths()
  1833.     {
  1834.         // Make sure there is a valid JForm XML document.
  1835.         if (!($this->xml instanceof SimpleXMLElement))
  1836.         {
  1837.             return false;
  1838.         }
  1839.  
  1840.         // Get any addfieldpath attributes from the form definition.
  1841.         $paths $this->xml->xpath('//*[@addfieldpath]/@addfieldpath');
  1842.         $paths array_map('strval'$paths $paths array());
  1843.  
  1844.         // Add the field paths.
  1845.         foreach ($paths as $path)
  1846.         {
  1847.             $path JPATH_ROOT '/' ltrim($path'/\\');
  1848.             self::addFieldPath($path);
  1849.         }
  1850.  
  1851.         // Get any addformpath attributes from the form definition.
  1852.         $paths $this->xml->xpath('//*[@addformpath]/@addformpath');
  1853.         $paths array_map('strval'$paths $paths array());
  1854.  
  1855.         // Add the form paths.
  1856.         foreach ($paths as $path)
  1857.         {
  1858.             $path JPATH_ROOT '/' ltrim($path'/\\');
  1859.             self::addFormPath($path);
  1860.         }
  1861.  
  1862.         // Get any addrulepath attributes from the form definition.
  1863.         $paths $this->xml->xpath('//*[@addrulepath]/@addrulepath');
  1864.         $paths array_map('strval'$paths $paths array());
  1865.  
  1866.         // Add the rule paths.
  1867.         foreach ($paths as $path)
  1868.         {
  1869.             $path JPATH_ROOT '/' ltrim($path'/\\');
  1870.             self::addRulePath($path);
  1871.         }
  1872.  
  1873.         return true;
  1874.     }
  1875.  
  1876.     /**
  1877.      * Method to validate a JFormField object based on field data.
  1878.      *
  1879.      * @param   SimpleXMLElement  $element  The XML element object representation of the form field.
  1880.      * @param   string            $group    The optional dot-separated form group path on which to find the field.
  1881.      * @param   mixed             $value    The optional value to use as the default for the field.
  1882.      * @param   JRegistry         $input    An optional JRegistry object with the entire data set to validate
  1883.      *                                       against the entire form.
  1884.      *
  1885.      * @return  mixed  Boolean true if field value is valid, Exception on failure.
  1886.      *
  1887.      * @since   11.1
  1888.      * @throws  InvalidArgumentException
  1889.      * @throws  UnexpectedValueException
  1890.      */
  1891.     protected function validateField(SimpleXMLElement $element$group null$value nullJRegistry $input null)
  1892.     {
  1893.         $valid true;
  1894.  
  1895.         // Check if the field is required.
  1896.         $required ((string) $element['required'== 'true' || (string) $element['required'== 'required');
  1897.  
  1898.         if ($required)
  1899.         {
  1900.             // If the field is required and the value is empty return an error message.
  1901.             if (($value === ''|| ($value === null))
  1902.             {
  1903.                 if ($element['label'])
  1904.                 {
  1905.                     $message JText::_($element['label']);
  1906.                 }
  1907.                 else
  1908.                 {
  1909.                     $message JText::_($element['name']);
  1910.                 }
  1911.  
  1912.                 $message JText::sprintf('JLIB_FORM_VALIDATE_FIELD_REQUIRED'$message);
  1913.  
  1914.                 return new RuntimeException($message);
  1915.             }
  1916.         }
  1917.  
  1918.         // Get the field validation rule.
  1919.         if ($type = (string) $element['validate'])
  1920.         {
  1921.             // Load the JFormRule object for the field.
  1922.             $rule $this->loadRuleType($type);
  1923.  
  1924.             // If the object could not be loaded return an error message.
  1925.             if ($rule === false)
  1926.             {
  1927.                 throw new UnexpectedValueException(sprintf('%s::validateField() rule `%s` missing.'get_class($this)$type));
  1928.             }
  1929.  
  1930.             // Run the field validation rule test.
  1931.             $valid $rule->test($element$value$group$input$this);
  1932.  
  1933.             // Check for an error in the validation test.
  1934.             if ($valid instanceof Exception)
  1935.             {
  1936.                 return $valid;
  1937.             }
  1938.         }
  1939.  
  1940.         // Check if the field is valid.
  1941.         if ($valid === false)
  1942.         {
  1943.             // Does the field have a defined error message?
  1944.             $message = (string) $element['message'];
  1945.  
  1946.             if ($message)
  1947.             {
  1948.                 $message JText::_($element['message']);
  1949.  
  1950.                 return new UnexpectedValueException($message);
  1951.             }
  1952.             else
  1953.             {
  1954.                 $message JText::_($element['label']);
  1955.                 $message JText::sprintf('JLIB_FORM_VALIDATE_FIELD_INVALID'$message);
  1956.  
  1957.                 return new UnexpectedValueException($message);
  1958.             }
  1959.         }
  1960.  
  1961.         return true;
  1962.     }
  1963.  
  1964.     /**
  1965.      * Proxy for {@link JFormHelper::addFieldPath()}.
  1966.      *
  1967.      * @param   mixed  $new  A path or array of paths to add.
  1968.      *
  1969.      * @return  array  The list of paths that have been added.
  1970.      *
  1971.      * @since   11.1
  1972.      */
  1973.     public static function addFieldPath($new null)
  1974.     {
  1975.         return JFormHelper::addFieldPath($new);
  1976.     }
  1977.  
  1978.     /**
  1979.      * Proxy for JFormHelper::addFormPath().
  1980.      *
  1981.      * @param   mixed  $new  A path or array of paths to add.
  1982.      *
  1983.      * @return  array  The list of paths that have been added.
  1984.      *
  1985.      * @see     JFormHelper::addFormPath()
  1986.      * @since   11.1
  1987.      */
  1988.     public static function addFormPath($new null)
  1989.     {
  1990.         return JFormHelper::addFormPath($new);
  1991.     }
  1992.  
  1993.     /**
  1994.      * Proxy for JFormHelper::addRulePath().
  1995.      *
  1996.      * @param   mixed  $new  A path or array of paths to add.
  1997.      *
  1998.      * @return  array  The list of paths that have been added.
  1999.      *
  2000.      * @see     JFormHelper::addRulePath()
  2001.      * @since   11.1
  2002.      */
  2003.     public static function addRulePath($new null)
  2004.     {
  2005.         return JFormHelper::addRulePath($new);
  2006.     }
  2007.  
  2008.     /**
  2009.      * Method to get an instance of a form.
  2010.      *
  2011.      * @param   string  $name     The name of the form.
  2012.      * @param   string  $data     The name of an XML file or string to load as the form definition.
  2013.      * @param   array   $options  An array of form options.
  2014.      * @param   string  $replace  Flag to toggle whether form fields should be replaced if a field
  2015.      *                             already exists with the same group/name.
  2016.      * @param   string  $xpath    An optional xpath to search for the fields.
  2017.      *
  2018.      * @return  object  JForm instance.
  2019.      *
  2020.      * @since   11.1
  2021.      * @throws  InvalidArgumentException if no data provided.
  2022.      * @throws  RuntimeException if the form could not be loaded.
  2023.      */
  2024.     public static function getInstance($name$data null$options array()$replace true$xpath false)
  2025.     {
  2026.         // Reference to array with form instances
  2027.         $forms &self::$forms;
  2028.  
  2029.         // Only instantiate the form if it does not already exist.
  2030.         if (!isset($forms[$name]))
  2031.         {
  2032.             $data trim($data);
  2033.  
  2034.             if (empty($data))
  2035.             {
  2036.                 throw new InvalidArgumentException(sprintf('JForm::getInstance(name, *%s*)'gettype($data)));
  2037.             }
  2038.  
  2039.             // Instantiate the form.
  2040.             $forms[$namenew JForm($name$options);
  2041.  
  2042.             // Load the data.
  2043.             if (substr(trim($data)01== '<')
  2044.             {
  2045.                 if ($forms[$name]->load($data$replace$xpath== false)
  2046.                 {
  2047.                     throw new RuntimeException('JForm::getInstance could not load form');
  2048.                 }
  2049.             }
  2050.             else
  2051.             {
  2052.                 if ($forms[$name]->loadFile($data$replace$xpath== false)
  2053.                 {
  2054.                     throw new RuntimeException('JForm::getInstance could not load file');
  2055.                 }
  2056.             }
  2057.         }
  2058.  
  2059.         return $forms[$name];
  2060.     }
  2061.  
  2062.     /**
  2063.      * Adds a new child SimpleXMLElement node to the source.
  2064.      *
  2065.      * @param   SimpleXMLElement  $source  The source element on which to append.
  2066.      * @param   SimpleXMLElement  $new     The new element to append.
  2067.      *
  2068.      * @return  void 
  2069.      *
  2070.      * @since   11.1
  2071.      */
  2072.     protected static function addNode(SimpleXMLElement $sourceSimpleXMLElement $new)
  2073.     {
  2074.         // Add the new child node.
  2075.         $node $source->addChild($new->getName()trim($new));
  2076.  
  2077.         // Add the attributes of the child node.
  2078.         foreach ($new->attributes(as $name => $value)
  2079.         {
  2080.             $node->addAttribute($name$value);
  2081.         }
  2082.  
  2083.         // Add any children of the new node.
  2084.         foreach ($new->children(as $child)
  2085.         {
  2086.             self::addNode($node$child);
  2087.         }
  2088.     }
  2089.  
  2090.     /**
  2091.      * Update the attributes of a child node
  2092.      *
  2093.      * @param   SimpleXMLElement  $source  The source element on which to append the attributes
  2094.      * @param   SimpleXMLElement  $new     The new element to append
  2095.      *
  2096.      * @return  void 
  2097.      *
  2098.      * @since   11.1
  2099.      */
  2100.     protected static function mergeNode(SimpleXMLElement $sourceSimpleXMLElement $new)
  2101.     {
  2102.         // Update the attributes of the child node.
  2103.         foreach ($new->attributes(as $name => $value)
  2104.         {
  2105.             if (isset($source[$name]))
  2106.             {
  2107.                 $source[$name= (string) $value;
  2108.             }
  2109.             else
  2110.             {
  2111.                 $source->addAttribute($name$value);
  2112.             }
  2113.         }
  2114.     }
  2115.  
  2116.     /**
  2117.      * Merges new elements into a source <fields> element.
  2118.      *
  2119.      * @param   SimpleXMLElement  $source  The source element.
  2120.      * @param   SimpleXMLElement  $new     The new element to merge.
  2121.      *
  2122.      * @return  void 
  2123.      *
  2124.      * @since   11.1
  2125.      */
  2126.     protected static function mergeNodes(SimpleXMLElement $sourceSimpleXMLElement $new)
  2127.     {
  2128.         // The assumption is that the inputs are at the same relative level.
  2129.         // So we just have to scan the children and deal with them.
  2130.  
  2131.         // Update the attributes of the child node.
  2132.         foreach ($new->attributes(as $name => $value)
  2133.         {
  2134.             if (isset($source[$name]))
  2135.             {
  2136.                 $source[$name= (string) $value;
  2137.             }
  2138.             else
  2139.             {
  2140.                 $source->addAttribute($name$value);
  2141.             }
  2142.         }
  2143.  
  2144.         foreach ($new->children(as $child)
  2145.         {
  2146.             $type $child->getName();
  2147.             $name $child['name'];
  2148.  
  2149.             // Does this node exist?
  2150.             $fields $source->xpath($type '[@name="' $name '"]');
  2151.  
  2152.             if (empty($fields))
  2153.             {
  2154.                 // This node does not exist, so add it.
  2155.                 self::addNode($source$child);
  2156.             }
  2157.             else
  2158.             {
  2159.                 // This node does exist.
  2160.                 switch ($type)
  2161.                 {
  2162.                     case 'field':
  2163.                         self::mergeNode($fields[0]$child);
  2164.                         break;
  2165.  
  2166.                     default:
  2167.                         self::mergeNodes($fields[0]$child);
  2168.                         break;
  2169.                 }
  2170.             }
  2171.         }
  2172.     }
  2173.  
  2174.     /**
  2175.      * Returns the value of an attribute of the form itself
  2176.      *
  2177.      * @param   string  $name     Name of the attribute to get
  2178.      * @param   mixed   $default  Optional value to return if attribute not found
  2179.      *
  2180.      * @return  mixed             Value of the attribute / default
  2181.      *
  2182.      * @since   3.2
  2183.      */
  2184.     public function getAttribute($name$default null)
  2185.     {
  2186.         if ($this->xml instanceof SimpleXMLElement)
  2187.         {
  2188.             $attributes $this->xml->attributes();
  2189.  
  2190.             // Ensure that the attribute exists
  2191.             if (property_exists($attributes$name))
  2192.             {
  2193.                 $value $attributes->$name;
  2194.  
  2195.                 if ($value !== null)
  2196.                 {
  2197.                     return (string) $value;
  2198.                 }
  2199.             }
  2200.         }
  2201.  
  2202.         return $default;
  2203.     }
  2204.  
  2205.     /**
  2206.      * Getter for the form data
  2207.      *
  2208.      * @return   JRegistry  Object with the data
  2209.      *
  2210.      * @since    3.2
  2211.      */
  2212.     public function getData()
  2213.     {
  2214.         return $this->data;
  2215.     }
  2216.  
  2217.     /**
  2218.      * Method to get the XML form object
  2219.      *
  2220.      * @return  SimpleXMLElement  The form XML object
  2221.      *
  2222.      * @since   3.2
  2223.      */
  2224.     public function getXml()
  2225.     {
  2226.         return $this->xml;
  2227.     }
  2228. }

Documentation generated on Tue, 19 Nov 2013 15:03:34 +0100 by phpDocumentor 1.4.3