Form.php 9.7 KB

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