PageRenderTime 66ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Components/DomCrawler/Form.php

https://github.com/come/symfony
PHP | 353 lines | 174 code | 40 blank | 139 comment | 44 complexity | 111453ded2ada0e604fea13a4d2e96bf MD5 | raw file
Possible License(s): ISC
  1. <?php
  2. namespace Symfony\Components\DomCrawler;
  3. /*
  4. * This file is part of the Symfony package.
  5. *
  6. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. /**
  12. * Form represents an HTML form.
  13. *
  14. * @package Symfony
  15. * @subpackage Components_DomCrawler
  16. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  17. */
  18. class Form implements \ArrayAccess
  19. {
  20. protected $document;
  21. protected $button;
  22. protected $node;
  23. protected $fields;
  24. protected $method;
  25. protected $host;
  26. protected $path;
  27. /**
  28. * Constructor.
  29. *
  30. * @param \DOMNode $node A \DOMNode instance
  31. * @param string $method The method to use for the link (if null, it defaults to the method defined by the form)
  32. * @param string $host The base URI to use for absolute links (like http://localhost)
  33. * @param string $path The base path for relative links (/ by default)
  34. *
  35. * @throws \LogicException if the node is not a button inside a form tag
  36. */
  37. public function __construct(\DOMNode $node, $method = null, $host = null, $path = '/')
  38. {
  39. $this->button = $node;
  40. if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image')))) {
  41. do {
  42. // use the ancestor form element
  43. if (null === $node = $node->parentNode) {
  44. throw new \LogicException('The selected node does not have a form ancestor.');
  45. }
  46. } while ('form' != $node->nodeName);
  47. } else {
  48. throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
  49. }
  50. $this->node = $node;
  51. $this->method = $method;
  52. $this->host = $host;
  53. $this->path = empty($path) ? '/' : $path;
  54. $this->initialize();
  55. }
  56. /**
  57. * Gets the form node associated with this form.
  58. *
  59. * @return \DOMNode A \DOMNode instance
  60. */
  61. public function getFormNode()
  62. {
  63. return $this->node;
  64. }
  65. /**
  66. * Sets the value of the fields.
  67. *
  68. * @param array $values An array of field values
  69. */
  70. public function setValues(array $values)
  71. {
  72. foreach ($values as $name => $value) {
  73. $this[$name] = $value;
  74. }
  75. return $this;
  76. }
  77. /**
  78. * Gets the field values.
  79. *
  80. * The returned array does not include file fields (@see getFiles).
  81. *
  82. * @return array An array of field values.
  83. */
  84. public function getValues()
  85. {
  86. $values = array();
  87. foreach ($this->fields as $name => $field) {
  88. if (!$field instanceof Field\FileFormField && $field->hasValue()) {
  89. $values[$name] = $field->getValue();
  90. }
  91. }
  92. return $values;
  93. }
  94. /**
  95. * Gets the file field values.
  96. *
  97. * @return array An array of file field values.
  98. */
  99. public function getFiles()
  100. {
  101. if (!in_array($this->getMethod(), array('post', 'put', 'delete'))) {
  102. return array();
  103. }
  104. $files = array();
  105. foreach ($this->fields as $name => $field) {
  106. if ($field instanceof Field\FileFormField) {
  107. $files[$name] = $field->getValue();
  108. }
  109. }
  110. return $files;
  111. }
  112. /**
  113. * Gets the field values as PHP.
  114. *
  115. * This method converts fields with th array notation
  116. * (like foo[bar] to arrays) like PHP does.
  117. *
  118. * @return array An array of field values.
  119. */
  120. public function getPhpValues()
  121. {
  122. $qs = http_build_query($this->getValues());
  123. parse_str($qs, $values);
  124. return $values;
  125. }
  126. /**
  127. * Gets the file field values as PHP.
  128. *
  129. * This method converts fields with th array notation
  130. * (like foo[bar] to arrays) like PHP does.
  131. *
  132. * @return array An array of field values.
  133. */
  134. public function getPhpFiles()
  135. {
  136. $qs = http_build_query($this->getFiles());
  137. parse_str($qs, $values);
  138. return $values;
  139. }
  140. /**
  141. * Gets the URI of the form.
  142. *
  143. * The returned URI is not the same as the form "action" attribute.
  144. * This method merges the value if the method is GET to mimics
  145. * browser behavior.
  146. *
  147. * @param Boolean $absolute Whether to return an absolute URI or not (this only works if a base URI has been provided)
  148. *
  149. * @return string The URI
  150. */
  151. public function getUri($absolute = true)
  152. {
  153. $uri = $this->node->getAttribute('action');
  154. $urlHaveScheme = 'http' === substr($uri, 0, 4);
  155. if (!in_array($this->getMethod(), array('post', 'put', 'delete')) && $queryString = http_build_query($this->getValues(), null, '&')) {
  156. $sep = false === strpos($uri, '?') ? '?' : '&';
  157. $uri .= $sep.$queryString;
  158. }
  159. if ($uri && '/' !== $uri[0] && !$urlHaveScheme) {
  160. $uri = $this->path.$uri;
  161. }
  162. if ($absolute && null !== $this->host && !$urlHaveScheme) {
  163. return $this->host.$uri;
  164. }
  165. return $uri;
  166. }
  167. /**
  168. * Gets the form method.
  169. *
  170. * If no method is defined in the form, GET is returned.
  171. *
  172. * @return string The method
  173. */
  174. public function getMethod()
  175. {
  176. if (null !== $this->method) {
  177. return $this->method;
  178. }
  179. return $this->node->getAttribute('method') ? strtolower($this->node->getAttribute('method')) : 'get';
  180. }
  181. /**
  182. * Returns true if the named field exists.
  183. *
  184. * @param string $name The field name
  185. *
  186. * @return Boolean true if the field exists, false otherwise
  187. */
  188. public function hasField($name)
  189. {
  190. return isset($this->fields[$name]);
  191. }
  192. /**
  193. * Gets a named field.
  194. *
  195. * @param string $name The field name
  196. *
  197. * @return Field\FormField The field instance
  198. *
  199. * @throws \InvalidArgumentException When field is not present in this form
  200. */
  201. public function getField($name)
  202. {
  203. if (!$this->hasField($name)) {
  204. throw new \InvalidArgumentException(sprintf('The form has no "%s" field', $name));
  205. }
  206. return $this->fields[$name];
  207. }
  208. /**
  209. * Sets a named field.
  210. *
  211. * @param string $name The field name
  212. *
  213. * @return Field\FormField The field instance
  214. */
  215. public function setField(Field\FormField $field)
  216. {
  217. $this->fields[$field->getName()] = $field;
  218. }
  219. /**
  220. * Gets all fields.
  221. *
  222. * @return array An array of fields
  223. */
  224. public function getFields()
  225. {
  226. return $this->fields;
  227. }
  228. protected function initialize()
  229. {
  230. $this->fields = array();
  231. $document = new \DOMDocument('1.0', 'UTF-8');
  232. $node = $document->importNode($this->node, true);
  233. $button = $document->importNode($this->button, true);
  234. $root = $document->appendChild($document->createElement('_root'));
  235. $root->appendChild($node);
  236. $root->appendChild($button);
  237. $xpath = new \DOMXPath($document);
  238. foreach ($xpath->query('descendant::input | descendant::textarea | descendant::select', $root) as $node) {
  239. if ($node->hasAttribute('disabled') || !$node->hasAttribute('name')) {
  240. continue;
  241. }
  242. $nodeName = $node->nodeName;
  243. if ($node === $button) {
  244. $this->setField(new Field\InputFormField($node));
  245. } elseif ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type')) {
  246. $this->setField(new Field\ChoiceFormField($node));
  247. } elseif ('input' == $nodeName && 'radio' == $node->getAttribute('type')) {
  248. if ($this->hasField($node->getAttribute('name'))) {
  249. $this->getField($node->getAttribute('name'))->addChoice($node);
  250. } else {
  251. $this->setField(new Field\ChoiceFormField($node));
  252. }
  253. } elseif ('input' == $nodeName && 'file' == $node->getAttribute('type')) {
  254. $this->setField(new Field\FileFormField($node));
  255. } elseif ('input' == $nodeName && !in_array($node->getAttribute('type'), array('submit', 'button', 'image'))) {
  256. $this->setField(new Field\InputFormField($node));
  257. } elseif ('textarea' == $nodeName) {
  258. $this->setField(new Field\TextareaFormField($node));
  259. }
  260. }
  261. }
  262. /**
  263. * Returns true if the named field exists.
  264. *
  265. * @param string $name The field name
  266. *
  267. * @return Boolean true if the field exists, false otherwise
  268. */
  269. public function offsetExists($name)
  270. {
  271. return $this->hasField($name);
  272. }
  273. /**
  274. * Gets the value of a field.
  275. *
  276. * @param string $name The field name
  277. *
  278. * @return Symfony\Components\DomCrawler\Field The associated Field instance
  279. *
  280. * @throws \InvalidArgumentException if the field does not exist
  281. */
  282. public function offsetGet($name)
  283. {
  284. if (!$this->hasField($name)) {
  285. throw new \InvalidArgumentException(sprintf('The form field "%s" does not exist', $name));
  286. }
  287. return $this->fields[$name];
  288. }
  289. /**
  290. * Sets the value of a field.
  291. *
  292. * @param string $name The field name
  293. * @param string|array $value The value of the field
  294. *
  295. * @throws \InvalidArgumentException if the field does not exist
  296. */
  297. public function offsetSet($name, $value)
  298. {
  299. if (!$this->hasField($name)) {
  300. throw new \InvalidArgumentException(sprintf('The form field "%s" does not exist', $name));
  301. }
  302. $this->fields[$name]->setValue($value);
  303. }
  304. /**
  305. * Unimplemented.
  306. *
  307. * @param string $name The field name
  308. */
  309. public function offsetUnset($name)
  310. {
  311. throw new \LogicException('The Form fields cannot be removed.');
  312. }
  313. }