page.php 28 KB

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