page.php 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. <?php
  2. /**
  3. * Base include file for SimpleTest
  4. * @package SimpleTest
  5. * @subpackage WebTester
  6. * @version $Id: page.php 1786 2008-04-26 17:32:20Z pp11 $
  7. */
  8. /**#@+
  9. * include other SimpleTest class files
  10. */
  11. require_once(dirname(__FILE__) . '/http.php');
  12. require_once(dirname(__FILE__) . '/parser.php');
  13. require_once(dirname(__FILE__) . '/tag.php');
  14. require_once(dirname(__FILE__) . '/form.php');
  15. require_once(dirname(__FILE__) . '/selector.php');
  16. /**#@-*/
  17. /**
  18. * Creates tags and widgets given HTML tag
  19. * attributes.
  20. * @package SimpleTest
  21. * @subpackage WebTester
  22. */
  23. class SimpleTagBuilder {
  24. /**
  25. * Factory for the tag objects. Creates the
  26. * appropriate tag object for the incoming tag name
  27. * and attributes.
  28. * @param string $name HTML tag name.
  29. * @param hash $attributes Element attributes.
  30. * @return SimpleTag Tag object.
  31. * @access public
  32. */
  33. function createTag($name, $attributes) {
  34. static $map = array(
  35. 'a' => 'SimpleAnchorTag',
  36. 'title' => 'SimpleTitleTag',
  37. 'base' => 'SimpleBaseTag',
  38. 'button' => 'SimpleButtonTag',
  39. 'textarea' => 'SimpleTextAreaTag',
  40. 'option' => 'SimpleOptionTag',
  41. 'label' => 'SimpleLabelTag',
  42. 'form' => 'SimpleFormTag',
  43. 'frame' => 'SimpleFrameTag');
  44. $attributes = $this->keysToLowerCase($attributes);
  45. if (array_key_exists($name, $map)) {
  46. $tag_class = $map[$name];
  47. return new $tag_class($attributes);
  48. } elseif ($name == 'select') {
  49. return $this->createSelectionTag($attributes);
  50. } elseif ($name == 'input') {
  51. return $this->createInputTag($attributes);
  52. }
  53. return new SimpleTag($name, $attributes);
  54. }
  55. /**
  56. * Factory for selection fields.
  57. * @param hash $attributes Element attributes.
  58. * @return SimpleTag Tag object.
  59. * @access protected
  60. */
  61. protected function createSelectionTag($attributes) {
  62. if (isset($attributes['multiple'])) {
  63. return new MultipleSelectionTag($attributes);
  64. }
  65. return new SimpleSelectionTag($attributes);
  66. }
  67. /**
  68. * Factory for input tags.
  69. * @param hash $attributes Element attributes.
  70. * @return SimpleTag Tag object.
  71. * @access protected
  72. */
  73. protected function createInputTag($attributes) {
  74. if (! isset($attributes['type'])) {
  75. return new SimpleTextTag($attributes);
  76. }
  77. $type = strtolower(trim($attributes['type']));
  78. $map = array(
  79. 'submit' => 'SimpleSubmitTag',
  80. 'image' => 'SimpleImageSubmitTag',
  81. 'checkbox' => 'SimpleCheckboxTag',
  82. 'radio' => 'SimpleRadioButtonTag',
  83. 'text' => 'SimpleTextTag',
  84. 'hidden' => 'SimpleTextTag',
  85. 'password' => 'SimpleTextTag',
  86. 'file' => 'SimpleUploadTag');
  87. if (array_key_exists($type, $map)) {
  88. $tag_class = $map[$type];
  89. return new $tag_class($attributes);
  90. }
  91. return false;
  92. }
  93. /**
  94. * Make the keys lower case for case insensitive look-ups.
  95. * @param hash $map Hash to convert.
  96. * @return hash Unchanged values, but keys lower case.
  97. * @access private
  98. */
  99. protected function keysToLowerCase($map) {
  100. $lower = array();
  101. foreach ($map as $key => $value) {
  102. $lower[strtolower($key)] = $value;
  103. }
  104. return $lower;
  105. }
  106. }
  107. /**
  108. * SAX event handler. Maintains a list of
  109. * open tags and dispatches them as they close.
  110. * @package SimpleTest
  111. * @subpackage WebTester
  112. */
  113. class SimplePageBuilder extends SimpleSaxListener {
  114. private $tags;
  115. private $page;
  116. private $private_content_tag;
  117. /**
  118. * Sets the builder up empty.
  119. * @access public
  120. */
  121. function __construct() {
  122. parent::__construct();
  123. }
  124. /**
  125. * Frees up any references so as to allow the PHP garbage
  126. * collection from unset() to work.
  127. * @access public
  128. */
  129. function free() {
  130. unset($this->tags);
  131. unset($this->page);
  132. unset($this->private_content_tags);
  133. }
  134. /**
  135. * Reads the raw content and send events
  136. * into the page to be built.
  137. * @param $response SimpleHttpResponse Fetched response.
  138. * @return SimplePage Newly parsed page.
  139. * @access public
  140. */
  141. function parse($response) {
  142. $this->tags = array();
  143. $this->page = $this->createPage($response);
  144. $parser = $this->createParser($this);
  145. $parser->parse($response->getContent());
  146. $this->page->acceptPageEnd();
  147. return $this->page;
  148. }
  149. /**
  150. * Creates an empty page.
  151. * @return SimplePage New unparsed page.
  152. * @access protected
  153. */
  154. protected function createPage($response) {
  155. return new SimplePage($response);
  156. }
  157. /**
  158. * Creates the parser used with the builder.
  159. * @param $listener SimpleSaxListener Target of parser.
  160. * @return SimpleSaxParser Parser to generate
  161. * events for the builder.
  162. * @access protected
  163. */
  164. protected function createParser(&$listener) {
  165. return new SimpleHtmlSaxParser($listener);
  166. }
  167. /**
  168. * Start of element event. Opens a new tag.
  169. * @param string $name Element name.
  170. * @param hash $attributes Attributes without content
  171. * are marked as true.
  172. * @return boolean False on parse error.
  173. * @access public
  174. */
  175. function startElement($name, $attributes) {
  176. $factory = new SimpleTagBuilder();
  177. $tag = $factory->createTag($name, $attributes);
  178. if (! $tag) {
  179. return true;
  180. }
  181. if ($tag->getTagName() == 'label') {
  182. $this->page->acceptLabelStart($tag);
  183. $this->openTag($tag);
  184. return true;
  185. }
  186. if ($tag->getTagName() == 'form') {
  187. $this->page->acceptFormStart($tag);
  188. return true;
  189. }
  190. if ($tag->getTagName() == 'frameset') {
  191. $this->page->acceptFramesetStart($tag);
  192. return true;
  193. }
  194. if ($tag->getTagName() == 'frame') {
  195. $this->page->acceptFrame($tag);
  196. return true;
  197. }
  198. if ($tag->isPrivateContent() && ! isset($this->private_content_tag)) {
  199. $this->private_content_tag = &$tag;
  200. }
  201. if ($tag->expectEndTag()) {
  202. $this->openTag($tag);
  203. return true;
  204. }
  205. $this->page->acceptTag($tag);
  206. return true;
  207. }
  208. /**
  209. * End of element event.
  210. * @param string $name Element name.
  211. * @return boolean False on parse error.
  212. * @access public
  213. */
  214. function endElement($name) {
  215. if ($name == 'label') {
  216. $this->page->acceptLabelEnd();
  217. return true;
  218. }
  219. if ($name == 'form') {
  220. $this->page->acceptFormEnd();
  221. return true;
  222. }
  223. if ($name == 'frameset') {
  224. $this->page->acceptFramesetEnd();
  225. return true;
  226. }
  227. if ($this->hasNamedTagOnOpenTagStack($name)) {
  228. $tag = array_pop($this->tags[$name]);
  229. if ($tag->isPrivateContent() && $this->private_content_tag->getTagName() == $name) {
  230. unset($this->private_content_tag);
  231. }
  232. $this->addContentTagToOpenTags($tag);
  233. $this->page->acceptTag($tag);
  234. return true;
  235. }
  236. return true;
  237. }
  238. /**
  239. * Test to see if there are any open tags awaiting
  240. * closure that match the tag name.
  241. * @param string $name Element name.
  242. * @return boolean True if any are still open.
  243. * @access private
  244. */
  245. protected function hasNamedTagOnOpenTagStack($name) {
  246. return isset($this->tags[$name]) && (count($this->tags[$name]) > 0);
  247. }
  248. /**
  249. * Unparsed, but relevant data. The data is added
  250. * to every open tag.
  251. * @param string $text May include unparsed tags.
  252. * @return boolean False on parse error.
  253. * @access public
  254. */
  255. function addContent($text) {
  256. if (isset($this->private_content_tag)) {
  257. $this->private_content_tag->addContent($text);
  258. } else {
  259. $this->addContentToAllOpenTags($text);
  260. }
  261. return true;
  262. }
  263. /**
  264. * Any content fills all currently open tags unless it
  265. * is part of an option tag.
  266. * @param string $text May include unparsed tags.
  267. * @access private
  268. */
  269. protected function addContentToAllOpenTags($text) {
  270. foreach (array_keys($this->tags) as $name) {
  271. for ($i = 0, $count = count($this->tags[$name]); $i < $count; $i++) {
  272. $this->tags[$name][$i]->addContent($text);
  273. }
  274. }
  275. }
  276. /**
  277. * Parsed data in tag form. The parsed tag is added
  278. * to every open tag. Used for adding options to select
  279. * fields only.
  280. * @param SimpleTag $tag Option tags only.
  281. * @access private
  282. */
  283. protected function addContentTagToOpenTags(&$tag) {
  284. if ($tag->getTagName() != 'option') {
  285. return;
  286. }
  287. foreach (array_keys($this->tags) as $name) {
  288. for ($i = 0, $count = count($this->tags[$name]); $i < $count; $i++) {
  289. $this->tags[$name][$i]->addTag($tag);
  290. }
  291. }
  292. }
  293. /**
  294. * Opens a tag for receiving content. Multiple tags
  295. * will be receiving input at the same time.
  296. * @param SimpleTag $tag New content tag.
  297. * @access private
  298. */
  299. protected function openTag($tag) {
  300. $name = $tag->getTagName();
  301. if (! in_array($name, array_keys($this->tags))) {
  302. $this->tags[$name] = array();
  303. }
  304. $this->tags[$name][] = $tag;
  305. }
  306. }
  307. /**
  308. * A wrapper for a web page.
  309. * @package SimpleTest
  310. * @subpackage WebTester
  311. */
  312. class SimplePage {
  313. private $links;
  314. private $title;
  315. private $last_widget;
  316. private $label;
  317. private $left_over_labels;
  318. private $open_forms;
  319. private $complete_forms;
  320. private $frameset;
  321. private $frames;
  322. private $frameset_nesting_level;
  323. private $transport_error;
  324. private $raw;
  325. private $text;
  326. private $sent;
  327. private $headers;
  328. private $method;
  329. private $url;
  330. private $base = false;
  331. private $request_data;
  332. /**
  333. * Parses a page ready to access it's contents.
  334. * @param SimpleHttpResponse $response Result of HTTP fetch.
  335. * @access public
  336. */
  337. function __construct($response = false) {
  338. $this->links = array();
  339. $this->title = false;
  340. $this->left_over_labels = array();
  341. $this->open_forms = array();
  342. $this->complete_forms = array();
  343. $this->frameset = false;
  344. $this->frames = array();
  345. $this->frameset_nesting_level = 0;
  346. $this->text = false;
  347. if ($response) {
  348. $this->extractResponse($response);
  349. } else {
  350. $this->noResponse();
  351. }
  352. }
  353. /**
  354. * Extracts all of the response information.
  355. * @param SimpleHttpResponse $response Response being parsed.
  356. * @access private
  357. */
  358. protected function extractResponse($response) {
  359. $this->transport_error = $response->getError();
  360. $this->raw = $response->getContent();
  361. $this->sent = $response->getSent();
  362. $this->headers = $response->getHeaders();
  363. $this->method = $response->getMethod();
  364. $this->url = $response->getUrl();
  365. $this->request_data = $response->getRequestData();
  366. }
  367. /**
  368. * Sets up a missing response.
  369. * @access private
  370. */
  371. protected function noResponse() {
  372. $this->transport_error = 'No page fetched yet';
  373. $this->raw = false;
  374. $this->sent = false;
  375. $this->headers = false;
  376. $this->method = 'GET';
  377. $this->url = false;
  378. $this->request_data = false;
  379. }
  380. /**
  381. * Original request as bytes sent down the wire.
  382. * @return mixed Sent content.
  383. * @access public
  384. */
  385. function getRequest() {
  386. return $this->sent;
  387. }
  388. /**
  389. * Accessor for raw text of page.
  390. * @return string Raw unparsed content.
  391. * @access public
  392. */
  393. function getRaw() {
  394. return $this->raw;
  395. }
  396. /**
  397. * Accessor for plain text of page as a text browser
  398. * would see it.
  399. * @return string Plain text of page.
  400. * @access public
  401. */
  402. function getText() {
  403. if (! $this->text) {
  404. $this->text = SimpleHtmlSaxParser::normalise($this->raw);
  405. }
  406. return $this->text;
  407. }
  408. /**
  409. * Accessor for raw headers of page.
  410. * @return string Header block as text.
  411. * @access public
  412. */
  413. function getHeaders() {
  414. if ($this->headers) {
  415. return $this->headers->getRaw();
  416. }
  417. return false;
  418. }
  419. /**
  420. * Original request method.
  421. * @return string GET, POST or HEAD.
  422. * @access public
  423. */
  424. function getMethod() {
  425. return $this->method;
  426. }
  427. /**
  428. * Original resource name.
  429. * @return SimpleUrl Current url.
  430. * @access public
  431. */
  432. function getUrl() {
  433. return $this->url;
  434. }
  435. /**
  436. * Base URL if set via BASE tag page url otherwise
  437. * @return SimpleUrl Base url.
  438. * @access public
  439. */
  440. function getBaseUrl() {
  441. return $this->base;
  442. }
  443. /**
  444. * Original request data.
  445. * @return mixed Sent content.
  446. * @access public
  447. */
  448. function getRequestData() {
  449. return $this->request_data;
  450. }
  451. /**
  452. * Accessor for last error.
  453. * @return string Error from last response.
  454. * @access public
  455. */
  456. function getTransportError() {
  457. return $this->transport_error;
  458. }
  459. /**
  460. * Accessor for current MIME type.
  461. * @return string MIME type as string; e.g. 'text/html'
  462. * @access public
  463. */
  464. function getMimeType() {
  465. if ($this->headers) {
  466. return $this->headers->getMimeType();
  467. }
  468. return false;
  469. }
  470. /**
  471. * Accessor for HTTP response code.
  472. * @return integer HTTP response code received.
  473. * @access public
  474. */
  475. function getResponseCode() {
  476. if ($this->headers) {
  477. return $this->headers->getResponseCode();
  478. }
  479. return false;
  480. }
  481. /**
  482. * Accessor for last Authentication type. Only valid
  483. * straight after a challenge (401).
  484. * @return string Description of challenge type.
  485. * @access public
  486. */
  487. function getAuthentication() {
  488. if ($this->headers) {
  489. return $this->headers->getAuthentication();
  490. }
  491. return false;
  492. }
  493. /**
  494. * Accessor for last Authentication realm. Only valid
  495. * straight after a challenge (401).
  496. * @return string Name of security realm.
  497. * @access public
  498. */
  499. function getRealm() {
  500. if ($this->headers) {
  501. return $this->headers->getRealm();
  502. }
  503. return false;
  504. }
  505. /**
  506. * Accessor for current frame focus. Will be
  507. * false as no frames.
  508. * @return array Always empty.
  509. * @access public
  510. */
  511. function getFrameFocus() {
  512. return array();
  513. }
  514. /**
  515. * Sets the focus by index. The integer index starts from 1.
  516. * @param integer $choice Chosen frame.
  517. * @return boolean Always false.
  518. * @access public
  519. */
  520. function setFrameFocusByIndex($choice) {
  521. return false;
  522. }
  523. /**
  524. * Sets the focus by name. Always fails for a leaf page.
  525. * @param string $name Chosen frame.
  526. * @return boolean False as no frames.
  527. * @access public
  528. */
  529. function setFrameFocus($name) {
  530. return false;
  531. }
  532. /**
  533. * Clears the frame focus. Does nothing for a leaf page.
  534. * @access public
  535. */
  536. function clearFrameFocus() {
  537. }
  538. /**
  539. * Adds a tag to the page.
  540. * @param SimpleTag $tag Tag to accept.
  541. * @access public
  542. */
  543. function acceptTag($tag) {
  544. if ($tag->getTagName() == "a") {
  545. $this->addLink($tag);
  546. } elseif ($tag->getTagName() == "base") {
  547. $this->setBase($tag);
  548. } elseif ($tag->getTagName() == "title") {
  549. $this->setTitle($tag);
  550. } elseif ($this->isFormElement($tag->getTagName())) {
  551. for ($i = 0; $i < count($this->open_forms); $i++) {
  552. $this->open_forms[$i]->addWidget($tag);
  553. }
  554. $this->last_widget = &$tag;
  555. }
  556. }
  557. /**
  558. * Opens a label for a described widget.
  559. * @param SimpleFormTag $tag Tag to accept.
  560. * @access public
  561. */
  562. function acceptLabelStart($tag) {
  563. $this->label = $tag;
  564. unset($this->last_widget);
  565. }
  566. /**
  567. * Closes the most recently opened label.
  568. * @access public
  569. */
  570. function acceptLabelEnd() {
  571. if (isset($this->label)) {
  572. if (isset($this->last_widget)) {
  573. $this->last_widget->setLabel($this->label->getText());
  574. unset($this->last_widget);
  575. } else {
  576. $this->left_over_labels[] = SimpleTestCompatibility::copy($this->label);
  577. }
  578. unset($this->label);
  579. }
  580. }
  581. /**
  582. * Tests to see if a tag is a possible form
  583. * element.
  584. * @param string $name HTML element name.
  585. * @return boolean True if form element.
  586. * @access private
  587. */
  588. protected function isFormElement($name) {
  589. return in_array($name, array('input', 'button', 'textarea', 'select'));
  590. }
  591. /**
  592. * Opens a form. New widgets go here.
  593. * @param SimpleFormTag $tag Tag to accept.
  594. * @access public
  595. */
  596. function acceptFormStart($tag) {
  597. $this->open_forms[] = new SimpleForm($tag, $this);
  598. }
  599. /**
  600. * Closes the most recently opened form.
  601. * @access public
  602. */
  603. function acceptFormEnd() {
  604. if (count($this->open_forms)) {
  605. $this->complete_forms[] = array_pop($this->open_forms);
  606. }
  607. }
  608. /**
  609. * Opens a frameset. A frameset may contain nested
  610. * frameset tags.
  611. * @param SimpleFramesetTag $tag Tag to accept.
  612. * @access public
  613. */
  614. function acceptFramesetStart($tag) {
  615. if (! $this->isLoadingFrames()) {
  616. $this->frameset = $tag;
  617. }
  618. $this->frameset_nesting_level++;
  619. }
  620. /**
  621. * Closes the most recently opened frameset.
  622. * @access public
  623. */
  624. function acceptFramesetEnd() {
  625. if ($this->isLoadingFrames()) {
  626. $this->frameset_nesting_level--;
  627. }
  628. }
  629. /**
  630. * Takes a single frame tag and stashes it in
  631. * the current frame set.
  632. * @param SimpleFrameTag $tag Tag to accept.
  633. * @access public
  634. */
  635. function acceptFrame($tag) {
  636. if ($this->isLoadingFrames()) {
  637. if ($tag->getAttribute('src')) {
  638. $this->frames[] = $tag;
  639. }
  640. }
  641. }
  642. /**
  643. * Test to see if in the middle of reading
  644. * a frameset.
  645. * @return boolean True if inframeset.
  646. * @access private
  647. */
  648. protected function isLoadingFrames() {
  649. if (! $this->frameset) {
  650. return false;
  651. }
  652. return ($this->frameset_nesting_level > 0);
  653. }
  654. /**
  655. * Test to see if link is an absolute one.
  656. * @param string $url Url to test.
  657. * @return boolean True if absolute.
  658. * @access protected
  659. */
  660. protected function linkIsAbsolute($url) {
  661. $parsed = new SimpleUrl($url);
  662. return (boolean)($parsed->getScheme() && $parsed->getHost());
  663. }
  664. /**
  665. * Adds a link to the page.
  666. * @param SimpleAnchorTag $tag Link to accept.
  667. * @access protected
  668. */
  669. protected function addLink($tag) {
  670. $this->links[] = $tag;
  671. }
  672. /**
  673. * Marker for end of complete page. Any work in
  674. * progress can now be closed.
  675. * @access public
  676. */
  677. function acceptPageEnd() {
  678. while (count($this->open_forms)) {
  679. $this->complete_forms[] = array_pop($this->open_forms);
  680. }
  681. foreach ($this->left_over_labels as $label) {
  682. for ($i = 0, $count = count($this->complete_forms); $i < $count; $i++) {
  683. $this->complete_forms[$i]->attachLabelBySelector(
  684. new SimpleById($label->getFor()),
  685. $label->getText());
  686. }
  687. }
  688. }
  689. /**
  690. * Test for the presence of a frameset.
  691. * @return boolean True if frameset.
  692. * @access public
  693. */
  694. function hasFrames() {
  695. return (boolean)$this->frameset;
  696. }
  697. /**
  698. * Accessor for frame name and source URL for every frame that
  699. * will need to be loaded. Immediate children only.
  700. * @return boolean/array False if no frameset or
  701. * otherwise a hash of frame URLs.
  702. * The key is either a numerical
  703. * base one index or the name attribute.
  704. * @access public
  705. */
  706. function getFrameset() {
  707. if (! $this->frameset) {
  708. return false;
  709. }
  710. $urls = array();
  711. for ($i = 0; $i < count($this->frames); $i++) {
  712. $name = $this->frames[$i]->getAttribute('name');
  713. $url = new SimpleUrl($this->frames[$i]->getAttribute('src'));
  714. $urls[$name ? $name : $i + 1] = $this->expandUrl($url);
  715. }
  716. return $urls;
  717. }
  718. /**
  719. * Fetches a list of loaded frames.
  720. * @return array/string Just the URL for a single page.
  721. * @access public
  722. */
  723. function getFrames() {
  724. $url = $this->expandUrl($this->getUrl());
  725. return $url->asString();
  726. }
  727. /**
  728. * Accessor for a list of all links.
  729. * @return array List of urls with scheme of
  730. * http or https and hostname.
  731. * @access public
  732. */
  733. function getUrls() {
  734. $all = array();
  735. foreach ($this->links as $link) {
  736. $url = $this->getUrlFromLink($link);
  737. $all[] = $url->asString();
  738. }
  739. return $all;
  740. }
  741. /**
  742. * Accessor for URLs by the link label. Label will match
  743. * regardess of whitespace issues and case.
  744. * @param string $label Text of link.
  745. * @return array List of links with that label.
  746. * @access public
  747. */
  748. function getUrlsByLabel($label) {
  749. $matches = array();
  750. foreach ($this->links as $link) {
  751. if ($link->getText() == $label) {
  752. $matches[] = $this->getUrlFromLink($link);
  753. }
  754. }
  755. return $matches;
  756. }
  757. /**
  758. * Accessor for a URL by the id attribute.
  759. * @param string $id Id attribute of link.
  760. * @return SimpleUrl URL with that id of false if none.
  761. * @access public
  762. */
  763. function getUrlById($id) {
  764. foreach ($this->links as $link) {
  765. if ($link->getAttribute('id') === (string)$id) {
  766. return $this->getUrlFromLink($link);
  767. }
  768. }
  769. return false;
  770. }
  771. /**
  772. * Converts a link tag into a target URL.
  773. * @param SimpleAnchor $link Parsed link.
  774. * @return SimpleUrl URL with frame target if any.
  775. * @access private
  776. */
  777. protected function getUrlFromLink($link) {
  778. $url = $this->expandUrl($link->getHref());
  779. if ($link->getAttribute('target')) {
  780. $url->setTarget($link->getAttribute('target'));
  781. }
  782. return $url;
  783. }
  784. /**
  785. * Expands expandomatic URLs into fully qualified
  786. * URLs.
  787. * @param SimpleUrl $url Relative URL.
  788. * @return SimpleUrl Absolute URL.
  789. * @access public
  790. */
  791. function expandUrl($url) {
  792. if (! is_object($url)) {
  793. $url = new SimpleUrl($url);
  794. }
  795. $location = $this->getBaseUrl() ? $this->getBaseUrl() : new SimpleUrl();
  796. return $url->makeAbsolute($location->makeAbsolute($this->getUrl()));
  797. }
  798. /**
  799. * Sets the base url for the page.
  800. * @param SimpleTag $tag Base URL for page.
  801. * @access protected
  802. */
  803. protected function setBase($tag) {
  804. $url = $tag->getAttribute('href');
  805. $this->base = new SimpleUrl($url);
  806. }
  807. /**
  808. * Sets the title tag contents.
  809. * @param SimpleTitleTag $tag Title of page.
  810. * @access protected
  811. */
  812. protected function setTitle($tag) {
  813. $this->title = $tag;
  814. }
  815. /**
  816. * Accessor for parsed title.
  817. * @return string Title or false if no title is present.
  818. * @access public
  819. */
  820. function getTitle() {
  821. if ($this->title) {
  822. return $this->title->getText();
  823. }
  824. return false;
  825. }
  826. /**
  827. * Finds a held form by button label. Will only
  828. * search correctly built forms.
  829. * @param SimpleSelector $selector Button finder.
  830. * @return SimpleForm Form object containing
  831. * the button.
  832. * @access public
  833. */
  834. function &getFormBySubmit($selector) {
  835. for ($i = 0; $i < count($this->complete_forms); $i++) {
  836. if ($this->complete_forms[$i]->hasSubmit($selector)) {
  837. return $this->complete_forms[$i];
  838. }
  839. }
  840. $null = null;
  841. return $null;
  842. }
  843. /**
  844. * Finds a held form by image using a selector.
  845. * Will only search correctly built forms.
  846. * @param SimpleSelector $selector Image finder.
  847. * @return SimpleForm Form object containing
  848. * the image.
  849. * @access public
  850. */
  851. function getFormByImage($selector) {
  852. for ($i = 0; $i < count($this->complete_forms); $i++) {
  853. if ($this->complete_forms[$i]->hasImage($selector)) {
  854. return $this->complete_forms[$i];
  855. }
  856. }
  857. return null;
  858. }
  859. /**
  860. * Finds a held form by the form ID. A way of
  861. * identifying a specific form when we have control
  862. * of the HTML code.
  863. * @param string $id Form label.
  864. * @return SimpleForm Form object containing the matching ID.
  865. * @access public
  866. */
  867. function getFormById($id) {
  868. for ($i = 0; $i < count($this->complete_forms); $i++) {
  869. if ($this->complete_forms[$i]->getId() == $id) {
  870. return $this->complete_forms[$i];
  871. }
  872. }
  873. return null;
  874. }
  875. /**
  876. * Sets a field on each form in which the field is
  877. * available.
  878. * @param SimpleSelector $selector Field finder.
  879. * @param string $value Value to set field to.
  880. * @return boolean True if value is valid.
  881. * @access public
  882. */
  883. function setField($selector, $value, $position=false) {
  884. $is_set = false;
  885. for ($i = 0; $i < count($this->complete_forms); $i++) {
  886. if ($this->complete_forms[$i]->setField($selector, $value, $position)) {
  887. $is_set = true;
  888. }
  889. }
  890. return $is_set;
  891. }
  892. /**
  893. * Accessor for a form element value within a page.
  894. * @param SimpleSelector $selector Field finder.
  895. * @return string/boolean A string if the field is
  896. * present, false if unchecked
  897. * and null if missing.
  898. * @access public
  899. */
  900. function getField($selector) {
  901. for ($i = 0; $i < count($this->complete_forms); $i++) {
  902. $value = $this->complete_forms[$i]->getValue($selector);
  903. if (isset($value)) {
  904. return $value;
  905. }
  906. }
  907. return null;
  908. }
  909. }
  910. ?>