PageRenderTime 52ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/DomCrawler/Form.php

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