Completed
Pull Request — 3.6 (#7850)
by Jono
07:19
created

SimplePage   D

Complexity

Total Complexity 95

Size/Duplication

Total Lines 649
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 0
loc 649
rs 4.2737
c 0
b 0
f 0
wmc 95
lcom 1
cbo 11

49 Methods

Rating   Name   Duplication   Size   Complexity  
A _extractResponse() 0 9 1
A _noResponse() 0 9 1
A getRequest() 0 3 1
A getRaw() 0 3 1
A getText() 0 6 2
A getHeaders() 0 6 2
A getMethod() 0 3 1
A getUrl() 0 3 1
A getBaseUrl() 0 3 1
A getRequestData() 0 3 1
A getTransportError() 0 3 1
A getMimeType() 0 6 2
A getResponseCode() 0 6 2
A getAuthentication() 0 6 2
A getRealm() 0 6 2
A getFrameFocus() 0 3 1
A setFrameFocusByIndex() 0 3 1
A setFrameFocus() 0 3 1
A clearFrameFocus() 0 2 1
B acceptTag() 0 14 6
A acceptLabelStart() 0 4 1
A acceptLabelEnd() 0 11 3
A _isFormElement() 0 3 1
A acceptFormStart() 0 3 1
A acceptFormEnd() 0 5 2
A acceptFramesetStart() 0 6 2
A acceptFramesetEnd() 0 5 2
A acceptFrame() 0 7 3
A _isLoadingFrames() 0 6 2
A _linkIsAbsolute() 0 4 2
A _addLink() 0 3 1
A acceptPageEnd() 0 12 4
A hasFrames() 0 3 1
A getFrameset() 0 12 4
A getFrames() 0 4 1
A getUrls() 0 8 2
A getUrlsByLabel() 0 9 3
A getUrlById() 0 8 3
A _getUrlFromLink() 0 7 2
A expandUrl() 0 7 3
A _setBase() 0 4 1
A _setTitle() 0 3 1
A getTitle() 0 6 2
A getFormBySubmit() 0 9 3
A getFormByImage() 0 9 3
A getFormById() 0 9 3
A setField() 0 9 3
A getField() 0 9 3
A SimplePage() 0 16 2

How to fix   Complexity   

Complex Class

Complex classes like SimplePage often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SimplePage, and based on these observations, apply Extract Interface, too.

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