X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=vendor%2Fsymfony%2Fdom-crawler%2FForm.php;fp=vendor%2Fsymfony%2Fdom-crawler%2FForm.php;h=bad1b34935d04d400975bfe85c359a371e86191f;hp=0000000000000000000000000000000000000000;hb=a2bd1bf0c2c1f1a17d188f4dc0726a45494cefae;hpb=57c063afa3f66b07c4bbddc2d6129a96d90f0aad diff --git a/vendor/symfony/dom-crawler/Form.php b/vendor/symfony/dom-crawler/Form.php new file mode 100644 index 000000000..bad1b3493 --- /dev/null +++ b/vendor/symfony/dom-crawler/Form.php @@ -0,0 +1,473 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DomCrawler; + +use Symfony\Component\DomCrawler\Field\ChoiceFormField; +use Symfony\Component\DomCrawler\Field\FormField; + +/** + * Form represents an HTML form. + * + * @author Fabien Potencier + */ +class Form extends Link implements \ArrayAccess +{ + /** + * @var \DOMElement + */ + private $button; + + /** + * @var FormFieldRegistry + */ + private $fields; + + /** + * @var string + */ + private $baseHref; + + /** + * Constructor. + * + * @param \DOMElement $node A \DOMElement instance + * @param string $currentUri The URI of the page where the form is embedded + * @param string $method The method to use for the link (if null, it defaults to the method defined by the form) + * @param string $baseHref The URI of the used for relative links, but not for empty action + * + * @throws \LogicException if the node is not a button inside a form tag + */ + public function __construct(\DOMElement $node, $currentUri, $method = null, $baseHref = null) + { + parent::__construct($node, $currentUri, $method); + $this->baseHref = $baseHref; + + $this->initialize(); + } + + /** + * Gets the form node associated with this form. + * + * @return \DOMElement A \DOMElement instance + */ + public function getFormNode() + { + return $this->node; + } + + /** + * Sets the value of the fields. + * + * @param array $values An array of field values + * + * @return $this + */ + public function setValues(array $values) + { + foreach ($values as $name => $value) { + $this->fields->set($name, $value); + } + + return $this; + } + + /** + * Gets the field values. + * + * The returned array does not include file fields (@see getFiles). + * + * @return array An array of field values + */ + public function getValues() + { + $values = array(); + foreach ($this->fields->all() as $name => $field) { + if ($field->isDisabled()) { + continue; + } + + if (!$field instanceof Field\FileFormField && $field->hasValue()) { + $values[$name] = $field->getValue(); + } + } + + return $values; + } + + /** + * Gets the file field values. + * + * @return array An array of file field values + */ + public function getFiles() + { + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { + return array(); + } + + $files = array(); + + foreach ($this->fields->all() as $name => $field) { + if ($field->isDisabled()) { + continue; + } + + if ($field instanceof Field\FileFormField) { + $files[$name] = $field->getValue(); + } + } + + return $files; + } + + /** + * Gets the field values as PHP. + * + * This method converts fields with the array notation + * (like foo[bar] to arrays) like PHP does. + * + * @return array An array of field values + */ + public function getPhpValues() + { + $values = array(); + foreach ($this->getValues() as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + + /** + * Gets the file field values as PHP. + * + * This method converts fields with the array notation + * (like foo[bar] to arrays) like PHP does. + * The returned array is consistent with the array for field values + * (@see getPhpValues), rather than uploaded files found in $_FILES. + * For a compound file field foo[bar] it will create foo[bar][name], + * instead of foo[name][bar] which would be found in $_FILES. + * + * @return array An array of file field values + */ + public function getPhpFiles() + { + $values = array(); + foreach ($this->getFiles() as $name => $value) { + $qs = http_build_query(array($name => $value), '', '&'); + if (!empty($qs)) { + parse_str($qs, $expandedValue); + $varName = substr($name, 0, strlen(key($expandedValue))); + $values = array_replace_recursive($values, array($varName => current($expandedValue))); + } + } + + return $values; + } + + /** + * Gets the URI of the form. + * + * The returned URI is not the same as the form "action" attribute. + * This method merges the value if the method is GET to mimics + * browser behavior. + * + * @return string The URI + */ + public function getUri() + { + $uri = parent::getUri(); + + if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) { + $query = parse_url($uri, PHP_URL_QUERY); + $currentParameters = array(); + if ($query) { + parse_str($query, $currentParameters); + } + + $queryString = http_build_query(array_merge($currentParameters, $this->getValues()), null, '&'); + + $pos = strpos($uri, '?'); + $base = false === $pos ? $uri : substr($uri, 0, $pos); + $uri = rtrim($base.'?'.$queryString, '?'); + } + + return $uri; + } + + protected function getRawUri() + { + return $this->node->getAttribute('action'); + } + + /** + * Gets the form method. + * + * If no method is defined in the form, GET is returned. + * + * @return string The method + */ + public function getMethod() + { + if (null !== $this->method) { + return $this->method; + } + + return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET'; + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return bool true if the field exists, false otherwise + */ + public function has($name) + { + return $this->fields->has($name); + } + + /** + * Removes a field from the form. + * + * @param string $name The field name + */ + public function remove($name) + { + $this->fields->remove($name); + } + + /** + * Gets a named field. + * + * @param string $name The field name + * + * @return FormField The field instance + * + * @throws \InvalidArgumentException When field is not present in this form + */ + public function get($name) + { + return $this->fields->get($name); + } + + /** + * Sets a named field. + * + * @param FormField $field The field + */ + public function set(FormField $field) + { + $this->fields->add($field); + } + + /** + * Gets all fields. + * + * @return FormField[] + */ + public function all() + { + return $this->fields->all(); + } + + /** + * Returns true if the named field exists. + * + * @param string $name The field name + * + * @return bool true if the field exists, false otherwise + */ + public function offsetExists($name) + { + return $this->has($name); + } + + /** + * Gets the value of a field. + * + * @param string $name The field name + * + * @return FormField The associated Field instance + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetGet($name) + { + return $this->fields->get($name); + } + + /** + * Sets the value of a field. + * + * @param string $name The field name + * @param string|array $value The value of the field + * + * @throws \InvalidArgumentException if the field does not exist + */ + public function offsetSet($name, $value) + { + $this->fields->set($name, $value); + } + + /** + * Removes a field from the form. + * + * @param string $name The field name + */ + public function offsetUnset($name) + { + $this->fields->remove($name); + } + + /** + * Disables validation. + * + * @return self + */ + public function disableValidation() + { + foreach ($this->fields->all() as $field) { + if ($field instanceof Field\ChoiceFormField) { + $field->disableValidation(); + } + } + + return $this; + } + + /** + * Sets the node for the form. + * + * Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself. + * + * @param \DOMElement $node A \DOMElement instance + * + * @throws \LogicException If given node is not a button or input or does not have a form ancestor + */ + protected function setNode(\DOMElement $node) + { + $this->button = $node; + if ('button' === $node->nodeName || ('input' === $node->nodeName && in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image')))) { + if ($node->hasAttribute('form')) { + // if the node has the HTML5-compliant 'form' attribute, use it + $formId = $node->getAttribute('form'); + $form = $node->ownerDocument->getElementById($formId); + if (null === $form) { + throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); + } + $this->node = $form; + + return; + } + // we loop until we find a form ancestor + do { + if (null === $node = $node->parentNode) { + throw new \LogicException('The selected node does not have a form ancestor.'); + } + } while ('form' !== $node->nodeName); + } elseif ('form' !== $node->nodeName) { + throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName)); + } + + $this->node = $node; + } + + /** + * Adds form elements related to this form. + * + * Creates an internal copy of the submitted 'button' element and + * the form node or the entire document depending on whether we need + * to find non-descendant elements through HTML5 'form' attribute. + */ + private function initialize() + { + $this->fields = new FormFieldRegistry(); + + $xpath = new \DOMXPath($this->node->ownerDocument); + + // add submitted button if it has a valid name + if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) { + if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) { + $name = $this->button->getAttribute('name'); + $this->button->setAttribute('value', '0'); + + // temporarily change the name of the input node for the x coordinate + $this->button->setAttribute('name', $name.'.x'); + $this->set(new Field\InputFormField($this->button)); + + // temporarily change the name of the input node for the y coordinate + $this->button->setAttribute('name', $name.'.y'); + $this->set(new Field\InputFormField($this->button)); + + // restore the original name of the input node + $this->button->setAttribute('name', $name); + } else { + $this->set(new Field\InputFormField($this->button)); + } + } + + // find form elements corresponding to the current form + if ($this->node->hasAttribute('id')) { + // corresponding elements are either descendants or have a matching HTML5 form attribute + $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); + + $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId)); + foreach ($fieldNodes as $node) { + $this->addField($node); + } + } else { + // do the xpath query with $this->node as the context node, to only find descendant elements + // however, descendant elements with form attribute are not part of this form + $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node); + foreach ($fieldNodes as $node) { + $this->addField($node); + } + } + + if ($this->baseHref && '' !== $this->node->getAttribute('action')) { + $this->currentUri = $this->baseHref; + } + } + + private function addField(\DOMElement $node) + { + if (!$node->hasAttribute('name') || !$node->getAttribute('name')) { + return; + } + + $nodeName = $node->nodeName; + if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) { + $this->set(new Field\ChoiceFormField($node)); + } elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) { + // there may be other fields with the same name that are no choice + // fields already registered (see https://github.com/symfony/symfony/issues/11689) + if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) { + $this->get($node->getAttribute('name'))->addChoice($node); + } else { + $this->set(new Field\ChoiceFormField($node)); + } + } elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) { + $this->set(new Field\FileFormField($node)); + } elseif ('input' == $nodeName && !in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image'))) { + $this->set(new Field\InputFormField($node)); + } elseif ('textarea' == $nodeName) { + $this->set(new Field\TextareaFormField($node)); + } + } +}