source: branches/version-2_13_0/data/module/Net/URL2.php @ 23126

Revision 23126, 26.8 KB checked in by m_uehara, 11 years ago (diff)

#2348 r23116 - r23125 をマージ

Line 
1<?php
2/**
3 * Net_URL2, a class representing a URL as per RFC 3986.
4 *
5 * PHP version 5
6 *
7 * LICENSE:
8 *
9 * Copyright (c) 2007-2009, Peytz & Co. A/S
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 *   * Redistributions of source code must retain the above copyright
17 *     notice, this list of conditions and the following disclaimer.
18 *   * Redistributions in binary form must reproduce the above copyright
19 *     notice, this list of conditions and the following disclaimer in
20 *     the documentation and/or other materials provided with the distribution.
21 *   * Neither the name of the Net_URL2 nor the names of its contributors may
22 *     be used to endorse or promote products derived from this software
23 *     without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
26 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
33 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * @category  Networking
38 * @package   Net_URL2
39 * @author    Christian Schmidt <schmidt@php.net>
40 * @copyright 2007-2009 Peytz & Co. A/S
41 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
42 * @version   CVS: $Id: URL2.php 309223 2011-03-14 14:26:32Z till $
43 * @link      http://www.rfc-editor.org/rfc/rfc3986.txt
44 */
45
46/**
47 * Represents a URL as per RFC 3986.
48 *
49 * @category  Networking
50 * @package   Net_URL2
51 * @author    Christian Schmidt <schmidt@php.net>
52 * @copyright 2007-2009 Peytz & Co. A/S
53 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
54 * @version   Release: @package_version@
55 * @link      http://pear.php.net/package/Net_URL2
56 */
57class Net_URL2
58{
59    /**
60     * Do strict parsing in resolve() (see RFC 3986, section 5.2.2). Default
61     * is true.
62     */
63    const OPTION_STRICT = 'strict';
64
65    /**
66     * Represent arrays in query using PHP's [] notation. Default is true.
67     */
68    const OPTION_USE_BRACKETS = 'use_brackets';
69
70    /**
71     * URL-encode query variable keys. Default is true.
72     */
73    const OPTION_ENCODE_KEYS = 'encode_keys';
74
75    /**
76     * Query variable separators when parsing the query string. Every character
77     * is considered a separator. Default is "&".
78     */
79    const OPTION_SEPARATOR_INPUT = 'input_separator';
80
81    /**
82     * Query variable separator used when generating the query string. Default
83     * is "&".
84     */
85    const OPTION_SEPARATOR_OUTPUT = 'output_separator';
86
87    /**
88     * Default options corresponds to how PHP handles $_GET.
89     */
90    private $_options = array(
91        self::OPTION_STRICT           => true,
92        self::OPTION_USE_BRACKETS     => true,
93        self::OPTION_ENCODE_KEYS      => true,
94        self::OPTION_SEPARATOR_INPUT  => '&',
95        self::OPTION_SEPARATOR_OUTPUT => '&',
96        );
97
98    /**
99     * @var  string|bool
100     */
101    private $_scheme = false;
102
103    /**
104     * @var  string|bool
105     */
106    private $_userinfo = false;
107
108    /**
109     * @var  string|bool
110     */
111    private $_host = false;
112
113    /**
114     * @var  string|bool
115     */
116    private $_port = false;
117
118    /**
119     * @var  string
120     */
121    private $_path = '';
122
123    /**
124     * @var  string|bool
125     */
126    private $_query = false;
127
128    /**
129     * @var  string|bool
130     */
131    private $_fragment = false;
132
133    /**
134     * Constructor.
135     *
136     * @param string $url     an absolute or relative URL
137     * @param array  $options an array of OPTION_xxx constants
138     *
139     * @return $this
140     * @uses   self::parseUrl()
141     */
142    public function __construct($url, array $options = array())
143    {
144        foreach ($options as $optionName => $value) {
145            if (array_key_exists($optionName, $this->_options)) {
146                $this->_options[$optionName] = $value;
147            }
148        }
149
150        $this->parseUrl($url);
151    }
152
153    /**
154     * Magic Setter.
155     *
156     * This method will magically set the value of a private variable ($var)
157     * with the value passed as the args
158     *
159     * @param  string $var      The private variable to set.
160     * @param  mixed  $arg      An argument of any type.
161     * @return void
162     */
163    public function __set($var, $arg)
164    {
165        $method = 'set' . $var;
166        if (method_exists($this, $method)) {
167            $this->$method($arg);
168        }
169    }
170
171    /**
172     * Magic Getter.
173     *
174     * This is the magic get method to retrieve the private variable
175     * that was set by either __set() or it's setter...
176     *
177     * @param  string $var         The property name to retrieve.
178     * @return mixed  $this->$var  Either a boolean false if the
179     *                             property is not set or the value
180     *                             of the private property.
181     */
182    public function __get($var)
183    {
184        $method = 'get' . $var;
185        if (method_exists($this, $method)) {
186            return $this->$method();
187        }
188
189        return false;
190    }
191
192    /**
193     * Returns the scheme, e.g. "http" or "urn", or false if there is no
194     * scheme specified, i.e. if this is a relative URL.
195     *
196     * @return  string|bool
197     */
198    public function getScheme()
199    {
200        return $this->_scheme;
201    }
202
203    /**
204     * Sets the scheme, e.g. "http" or "urn". Specify false if there is no
205     * scheme specified, i.e. if this is a relative URL.
206     *
207     * @param string|bool $scheme e.g. "http" or "urn", or false if there is no
208     *                            scheme specified, i.e. if this is a relative
209     *                            URL
210     *
211     * @return $this
212     * @see    getScheme()
213     */
214    public function setScheme($scheme)
215    {
216        $this->_scheme = $scheme;
217        return $this;
218    }
219
220    /**
221     * Returns the user part of the userinfo part (the part preceding the first
222     *  ":"), or false if there is no userinfo part.
223     *
224     * @return  string|bool
225     */
226    public function getUser()
227    {
228        return $this->_userinfo !== false
229            ? preg_replace('@:.*$@', '', $this->_userinfo)
230            : false;
231    }
232
233    /**
234     * Returns the password part of the userinfo part (the part after the first
235     *  ":"), or false if there is no userinfo part (i.e. the URL does not
236     * contain "@" in front of the hostname) or the userinfo part does not
237     * contain ":".
238     *
239     * @return  string|bool
240     */
241    public function getPassword()
242    {
243        return $this->_userinfo !== false
244            ? substr(strstr($this->_userinfo, ':'), 1)
245            : false;
246    }
247
248    /**
249     * Returns the userinfo part, or false if there is none, i.e. if the
250     * authority part does not contain "@".
251     *
252     * @return  string|bool
253     */
254    public function getUserinfo()
255    {
256        return $this->_userinfo;
257    }
258
259    /**
260     * Sets the userinfo part. If two arguments are passed, they are combined
261     * in the userinfo part as username ":" password.
262     *
263     * @param string|bool $userinfo userinfo or username
264     * @param string|bool $password optional password, or false
265     *
266     * @return $this
267     */
268    public function setUserinfo($userinfo, $password = false)
269    {
270        $this->_userinfo = $userinfo;
271        if ($password !== false) {
272            $this->_userinfo .= ':' . $password;
273        }
274        return $this;
275    }
276
277    /**
278     * Returns the host part, or false if there is no authority part, e.g.
279     * relative URLs.
280     *
281     * @return  string|bool a hostname, an IP address, or false
282     */
283    public function getHost()
284    {
285        return $this->_host;
286    }
287
288    /**
289     * Sets the host part. Specify false if there is no authority part, e.g.
290     * relative URLs.
291     *
292     * @param string|bool $host a hostname, an IP address, or false
293     *
294     * @return $this
295     */
296    public function setHost($host)
297    {
298        $this->_host = $host;
299        return $this;
300    }
301
302    /**
303     * Returns the port number, or false if there is no port number specified,
304     * i.e. if the default port is to be used.
305     *
306     * @return  string|bool
307     */
308    public function getPort()
309    {
310        return $this->_port;
311    }
312
313    /**
314     * Sets the port number. Specify false if there is no port number specified,
315     * i.e. if the default port is to be used.
316     *
317     * @param string|bool $port a port number, or false
318     *
319     * @return $this
320     */
321    public function setPort($port)
322    {
323        $this->_port = $port;
324        return $this;
325    }
326
327    /**
328     * Returns the authority part, i.e. [ userinfo "@" ] host [ ":" port ], or
329     * false if there is no authority.
330     *
331     * @return string|bool
332     */
333    public function getAuthority()
334    {
335        if (!$this->_host) {
336            return false;
337        }
338
339        $authority = '';
340
341        if ($this->_userinfo !== false) {
342            $authority .= $this->_userinfo . '@';
343        }
344
345        $authority .= $this->_host;
346
347        if ($this->_port !== false) {
348            $authority .= ':' . $this->_port;
349        }
350
351        return $authority;
352    }
353
354    /**
355     * Sets the authority part, i.e. [ userinfo "@" ] host [ ":" port ]. Specify
356     * false if there is no authority.
357     *
358     * @param string|false $authority a hostname or an IP addresse, possibly
359     *                                with userinfo prefixed and port number
360     *                                appended, e.g. "foo:bar@example.org:81".
361     *
362     * @return $this
363     */
364    public function setAuthority($authority)
365    {
366        $this->_userinfo = false;
367        $this->_host     = false;
368        $this->_port     = false;
369        if (preg_match('@^(([^\@]*)\@)?([^:]+)(:(\d*))?$@', $authority, $reg)) {
370            if ($reg[1]) {
371                $this->_userinfo = $reg[2];
372            }
373
374            $this->_host = $reg[3];
375            if (isset($reg[5])) {
376                $this->_port = $reg[5];
377            }
378        }
379        return $this;
380    }
381
382    /**
383     * Returns the path part (possibly an empty string).
384     *
385     * @return string
386     */
387    public function getPath()
388    {
389        return $this->_path;
390    }
391
392    /**
393     * Sets the path part (possibly an empty string).
394     *
395     * @param string $path a path
396     *
397     * @return $this
398     */
399    public function setPath($path)
400    {
401        $this->_path = $path;
402        return $this;
403    }
404
405    /**
406     * Returns the query string (excluding the leading "?"), or false if "?"
407     * is not present in the URL.
408     *
409     * @return  string|bool
410     * @see     self::getQueryVariables()
411     */
412    public function getQuery()
413    {
414        return $this->_query;
415    }
416
417    /**
418     * Sets the query string (excluding the leading "?"). Specify false if "?"
419     * is not present in the URL.
420     *
421     * @param string|bool $query a query string, e.g. "foo=1&bar=2"
422     *
423     * @return $this
424     * @see    self::setQueryVariables()
425     */
426    public function setQuery($query)
427    {
428        $this->_query = $query;
429        return $this;
430    }
431
432    /**
433     * Returns the fragment name, or false if "#" is not present in the URL.
434     *
435     * @return  string|bool
436     */
437    public function getFragment()
438    {
439        return $this->_fragment;
440    }
441
442    /**
443     * Sets the fragment name. Specify false if "#" is not present in the URL.
444     *
445     * @param string|bool $fragment a fragment excluding the leading "#", or
446     *                              false
447     *
448     * @return $this
449     */
450    public function setFragment($fragment)
451    {
452        $this->_fragment = $fragment;
453        return $this;
454    }
455
456    /**
457     * Returns the query string like an array as the variables would appear in
458     * $_GET in a PHP script. If the URL does not contain a "?", an empty array
459     * is returned.
460     *
461     * @return  array
462     */
463    public function getQueryVariables()
464    {
465        $pattern = '/[' .
466                   preg_quote($this->getOption(self::OPTION_SEPARATOR_INPUT), '/') .
467                   ']/';
468        $parts   = preg_split($pattern, $this->_query, -1, PREG_SPLIT_NO_EMPTY);
469        $return  = array();
470
471        foreach ($parts as $part) {
472            if (strpos($part, '=') !== false) {
473                list($key, $value) = explode('=', $part, 2);
474            } else {
475                $key   = $part;
476                $value = null;
477            }
478
479            if ($this->getOption(self::OPTION_ENCODE_KEYS)) {
480                $key = rawurldecode($key);
481            }
482            $value = rawurldecode($value);
483
484            if ($this->getOption(self::OPTION_USE_BRACKETS) &&
485                preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) {
486
487                $key = $matches[1];
488                $idx = $matches[2];
489
490                // Ensure is an array
491                if (empty($return[$key]) || !is_array($return[$key])) {
492                    $return[$key] = array();
493                }
494
495                // Add data
496                if ($idx === '') {
497                    $return[$key][] = $value;
498                } else {
499                    $return[$key][$idx] = $value;
500                }
501            } elseif (!$this->getOption(self::OPTION_USE_BRACKETS)
502                      && !empty($return[$key])
503            ) {
504                $return[$key]   = (array) $return[$key];
505                $return[$key][] = $value;
506            } else {
507                $return[$key] = $value;
508            }
509        }
510
511        return $return;
512    }
513
514    /**
515     * Sets the query string to the specified variable in the query string.
516     *
517     * @param array $array (name => value) array
518     *
519     * @return $this
520     */
521    public function setQueryVariables(array $array)
522    {
523        if (!$array) {
524            $this->_query = false;
525        } else {
526            $this->_query = $this->buildQuery(
527                $array,
528                $this->getOption(self::OPTION_SEPARATOR_OUTPUT)
529            );
530        }
531        return $this;
532    }
533
534    /**
535     * Sets the specified variable in the query string.
536     *
537     * @param string $name  variable name
538     * @param mixed  $value variable value
539     *
540     * @return $this
541     */
542    public function setQueryVariable($name, $value)
543    {
544        $array = $this->getQueryVariables();
545        $array[$name] = $value;
546        $this->setQueryVariables($array);
547        return $this;
548    }
549
550    /**
551     * Removes the specifed variable from the query string.
552     *
553     * @param string $name a query string variable, e.g. "foo" in "?foo=1"
554     *
555     * @return void
556     */
557    public function unsetQueryVariable($name)
558    {
559        $array = $this->getQueryVariables();
560        unset($array[$name]);
561        $this->setQueryVariables($array);
562    }
563
564    /**
565     * Returns a string representation of this URL.
566     *
567     * @return  string
568     */
569    public function getURL()
570    {
571        // See RFC 3986, section 5.3
572        $url = "";
573
574        if ($this->_scheme !== false) {
575            $url .= $this->_scheme . ':';
576        }
577
578        $authority = $this->getAuthority();
579        if ($authority !== false) {
580            $url .= '//' . $authority;
581        }
582        $url .= $this->_path;
583
584        if ($this->_query !== false) {
585            $url .= '?' . $this->_query;
586        }
587
588        if ($this->_fragment !== false) {
589            $url .= '#' . $this->_fragment;
590        }
591   
592        return $url;
593    }
594
595    /**
596     * Returns a string representation of this URL.
597     *
598     * @return  string
599     * @see toString()
600     */
601    public function __toString()
602    {
603        return $this->getURL();
604    }
605
606    /**
607     * Returns a normalized string representation of this URL. This is useful
608     * for comparison of URLs.
609     *
610     * @return  string
611     */
612    public function getNormalizedURL()
613    {
614        $url = clone $this;
615        $url->normalize();
616        return $url->getUrl();
617    }
618
619    /**
620     * Returns a normalized Net_URL2 instance.
621     *
622     * @return  Net_URL2
623     */
624    public function normalize()
625    {
626        // See RFC 3886, section 6
627
628        // Schemes are case-insensitive
629        if ($this->_scheme) {
630            $this->_scheme = strtolower($this->_scheme);
631        }
632
633        // Hostnames are case-insensitive
634        if ($this->_host) {
635            $this->_host = strtolower($this->_host);
636        }
637
638        // Remove default port number for known schemes (RFC 3986, section 6.2.3)
639        if ($this->_port &&
640            $this->_scheme &&
641            $this->_port == getservbyname($this->_scheme, 'tcp')) {
642
643            $this->_port = false;
644        }
645
646        // Normalize case of %XX percentage-encodings (RFC 3986, section 6.2.2.1)
647        foreach (array('_userinfo', '_host', '_path') as $part) {
648            if ($this->$part) {
649                $this->$part = preg_replace('/%[0-9a-f]{2}/ie',
650                                            'strtoupper("\0")',
651                                            $this->$part);
652            }
653        }
654
655        // Path segment normalization (RFC 3986, section 6.2.2.3)
656        $this->_path = self::removeDotSegments($this->_path);
657
658        // Scheme based normalization (RFC 3986, section 6.2.3)
659        if ($this->_host && !$this->_path) {
660            $this->_path = '/';
661        }
662    }
663
664    /**
665     * Returns whether this instance represents an absolute URL.
666     *
667     * @return  bool
668     */
669    public function isAbsolute()
670    {
671        return (bool) $this->_scheme;
672    }
673
674    /**
675     * Returns an Net_URL2 instance representing an absolute URL relative to
676     * this URL.
677     *
678     * @param Net_URL2|string $reference relative URL
679     *
680     * @return Net_URL2
681     */
682    public function resolve($reference)
683    {
684        if (!$reference instanceof Net_URL2) {
685            $reference = new self($reference);
686        }
687        if (!$this->isAbsolute()) {
688            throw new Exception('Base-URL must be absolute');
689        }
690
691        // A non-strict parser may ignore a scheme in the reference if it is
692        // identical to the base URI's scheme.
693        if (!$this->getOption(self::OPTION_STRICT) && $reference->_scheme == $this->_scheme) {
694            $reference->_scheme = false;
695        }
696
697        $target = new self('');
698        if ($reference->_scheme !== false) {
699            $target->_scheme = $reference->_scheme;
700            $target->setAuthority($reference->getAuthority());
701            $target->_path  = self::removeDotSegments($reference->_path);
702            $target->_query = $reference->_query;
703        } else {
704            $authority = $reference->getAuthority();
705            if ($authority !== false) {
706                $target->setAuthority($authority);
707                $target->_path  = self::removeDotSegments($reference->_path);
708                $target->_query = $reference->_query;
709            } else {
710                if ($reference->_path == '') {
711                    $target->_path = $this->_path;
712                    if ($reference->_query !== false) {
713                        $target->_query = $reference->_query;
714                    } else {
715                        $target->_query = $this->_query;
716                    }
717                } else {
718                    if (substr($reference->_path, 0, 1) == '/') {
719                        $target->_path = self::removeDotSegments($reference->_path);
720                    } else {
721                        // Merge paths (RFC 3986, section 5.2.3)
722                        if ($this->_host !== false && $this->_path == '') {
723                            $target->_path = '/' . $this->_path;
724                        } else {
725                            $i = strrpos($this->_path, '/');
726                            if ($i !== false) {
727                                $target->_path = substr($this->_path, 0, $i + 1);
728                            }
729                            $target->_path .= $reference->_path;
730                        }
731                        $target->_path = self::removeDotSegments($target->_path);
732                    }
733                    $target->_query = $reference->_query;
734                }
735                $target->setAuthority($this->getAuthority());
736            }
737            $target->_scheme = $this->_scheme;
738        }
739
740        $target->_fragment = $reference->_fragment;
741
742        return $target;
743    }
744
745    /**
746     * Removes dots as described in RFC 3986, section 5.2.4, e.g.
747     * "/foo/../bar/baz" => "/bar/baz"
748     *
749     * @param string $path a path
750     *
751     * @return string a path
752     */
753    public static function removeDotSegments($path)
754    {
755        $output = '';
756
757        // Make sure not to be trapped in an infinite loop due to a bug in this
758        // method
759        $j = 0;
760        while ($path && $j++ < 100) {
761            if (substr($path, 0, 2) == './') {
762                // Step 2.A
763                $path = substr($path, 2);
764            } elseif (substr($path, 0, 3) == '../') {
765                // Step 2.A
766                $path = substr($path, 3);
767            } elseif (substr($path, 0, 3) == '/./' || $path == '/.') {
768                // Step 2.B
769                $path = '/' . substr($path, 3);
770            } elseif (substr($path, 0, 4) == '/../' || $path == '/..') {
771                // Step 2.C
772                $path   = '/' . substr($path, 4);
773                $i      = strrpos($output, '/');
774                $output = $i === false ? '' : substr($output, 0, $i);
775            } elseif ($path == '.' || $path == '..') {
776                // Step 2.D
777                $path = '';
778            } else {
779                // Step 2.E
780                $i = strpos($path, '/');
781                if ($i === 0) {
782                    $i = strpos($path, '/', 1);
783                }
784                if ($i === false) {
785                    $i = strlen($path);
786                }
787                $output .= substr($path, 0, $i);
788                $path = substr($path, $i);
789            }
790        }
791
792        return $output;
793    }
794
795    /**
796     * Percent-encodes all non-alphanumeric characters except these: _ . - ~
797     * Similar to PHP's rawurlencode(), except that it also encodes ~ in PHP
798     * 5.2.x and earlier.
799     *
800     * @param  $raw the string to encode
801     * @return string
802     */
803    public static function urlencode($string)
804    {
805        $encoded = rawurlencode($string);
806
807        // This is only necessary in PHP < 5.3.
808        $encoded = str_replace('%7E', '~', $encoded);
809        return $encoded;
810    }
811
812    /**
813     * Returns a Net_URL2 instance representing the canonical URL of the
814     * currently executing PHP script.
815     *
816     * @return  string
817     */
818    public static function getCanonical()
819    {
820        if (!isset($_SERVER['REQUEST_METHOD'])) {
821            // ALERT - no current URL
822            throw new Exception('Script was not called through a webserver');
823        }
824
825        // Begin with a relative URL
826        $url = new self($_SERVER['PHP_SELF']);
827        $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
828        $url->_host   = $_SERVER['SERVER_NAME'];
829        $port = $_SERVER['SERVER_PORT'];
830        if ($url->_scheme == 'http' && $port != 80 ||
831            $url->_scheme == 'https' && $port != 443) {
832
833            $url->_port = $port;
834        }
835        return $url;
836    }
837
838    /**
839     * Returns the URL used to retrieve the current request.
840     *
841     * @return  string
842     */
843    public static function getRequestedURL()
844    {
845        return self::getRequested()->getUrl();
846    }
847
848    /**
849     * Returns a Net_URL2 instance representing the URL used to retrieve the
850     * current request.
851     *
852     * @return  Net_URL2
853     */
854    public static function getRequested()
855    {
856        if (!isset($_SERVER['REQUEST_METHOD'])) {
857            // ALERT - no current URL
858            throw new Exception('Script was not called through a webserver');
859        }
860
861        // Begin with a relative URL
862        $url = new self($_SERVER['REQUEST_URI']);
863        $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
864        // Set host and possibly port
865        $url->setAuthority($_SERVER['HTTP_HOST']);
866        return $url;
867    }
868
869    /**
870     * Returns the value of the specified option.
871     *
872     * @param string $optionName The name of the option to retrieve
873     *
874     * @return  mixed
875     */
876    public function getOption($optionName)
877    {
878        return isset($this->_options[$optionName])
879            ? $this->_options[$optionName] : false;
880    }
881
882    /**
883     * A simple version of http_build_query in userland. The encoded string is
884     * percentage encoded according to RFC 3986.
885     *
886     * @param array  $data      An array, which has to be converted into
887     *                          QUERY_STRING. Anything is possible.
888     * @param string $seperator See {@link self::OPTION_SEPARATOR_OUTPUT}
889     * @param string $key       For stacked values (arrays in an array).
890     *
891     * @return string
892     */
893    protected function buildQuery(array $data, $separator, $key = null)
894    {
895        $query = array();
896        foreach ($data as $name => $value) {
897            if ($this->getOption(self::OPTION_ENCODE_KEYS) === true) {
898                $name = rawurlencode($name);
899            }
900            if ($key !== null) {
901                if ($this->getOption(self::OPTION_USE_BRACKETS) === true) {
902                    $name = $key . '[' . $name . ']';
903                } else {
904                    $name = $key;
905                }
906            }
907            if (is_array($value)) {
908                $query[] = $this->buildQuery($value, $separator, $name);
909            } else {
910                $query[] = $name . '=' . rawurlencode($value);
911            }
912        }
913        return implode($separator, $query);
914    }
915
916    /**
917     * This method uses a funky regex to parse the url into the designated parts.
918     *
919     * @param string $url
920     *
921     * @return void
922     * @uses   self::$_scheme, self::setAuthority(), self::$_path, self::$_query,
923     *         self::$_fragment
924     * @see    self::__construct()
925     */
926    protected function parseUrl($url)
927    {
928        // The regular expression is copied verbatim from RFC 3986, appendix B.
929        // The expression does not validate the URL but matches any string.
930        preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!',
931                   $url,
932                   $matches);
933
934        // "path" is always present (possibly as an empty string); the rest
935        // are optional.
936        $this->_scheme   = !empty($matches[1]) ? $matches[2] : false;
937        $this->setAuthority(!empty($matches[3]) ? $matches[4] : false);
938        $this->_path     = $matches[5];
939        $this->_query    = !empty($matches[6]) ? $matches[7] : false;
940        $this->_fragment = !empty($matches[8]) ? $matches[9] : false;
941    }
942}
Note: See TracBrowser for help on using the repository browser.