00001 <?php
00002 
00003 
00004 
00005 
00006 
00007 
00008 class Url {
00009         const TEMPORARY = false;
00010         const PERMANENT = true;
00011         
00012         const ABSOLUTE = 'absolute';
00013         const RELATIVE = 'relative';
00014         
00015         const ENCODE_PARAMS = 'encode';
00016         const NO_ENCODE_PARAMS = 'encodenot';
00017         
00018 
00019 
00020 
00021         const EQUALS_FULL = 0;
00022 
00023 
00024 
00025         const EQUALS_IGNORE_QUERY = 1;
00026         
00027         
00028         private $data = array();
00029         
00030 
00031 
00032 
00033 
00034 
00035         public function __construct($url = '', $fallback_host = '') {
00036                 $this->parse($url, $fallback_host);
00037         }
00038         
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046         protected function do_parse_url($url, $fallback_host = '') {
00047                 
00048                 if (strpos($url, '://') === false) {
00049                         if (substr($url, 0, 1) == '/') {
00050                                 $url = $fallback_host . $url;
00051                         }
00052                         $url = 'http://' . $url; 
00053                 }
00054                 $ret = false;
00055                 try {
00056                         $ret = @parse_url($url);
00057                 }
00058                 catch (Exception $ex) {
00059                         
00060                         $ret = array();                 
00061                 }
00062                 if ($ret === false) {
00063                         $ret = array();
00064                 }
00065                 return $ret;
00066         }
00067 
00068 
00069 
00070 
00071         protected function parse($url, $fallback_host = '') {
00072                 $url = trim($url);
00073                 $data = array();
00074                 if (!empty($url)) {
00075                         $data = $this->do_parse_url($url, $fallback_host);
00076                 }
00077                 
00078                 $this->set_scheme(Arr::get_item($data, 'scheme', 'http'));
00079                 $this->set_host(Arr::get_item($data, 'host', ''));
00080                 $this->set_port(Arr::get_item($data, 'port', ''));
00081                 $this->set_path(Arr::get_item($data, 'path', ''));
00082                 $this->set_fragment(Arr::get_item($data, 'fragment', ''));
00083                 $this->set_query(Arr::get_item($data, 'query', ''));
00084         }
00085         
00086 
00087 
00088 
00089 
00090 
00091 
00092         protected function parse_query($query) {
00093                 $ret = array();
00094                 
00095                 
00096                 $sep = ini_get('arg_separator.input');
00097                 $l = String::length($sep);
00098                 if ($l > 1) {
00099                         
00100                         $all_seps = $sep;
00101                         $sep = String::substr($all_seps, 0, 1);                 
00102                         for ($i = 1; $i < $l; $i++) {
00103                                 $query = str_replace(String::substr($all_seps,$i, 1), $sep, $query);
00104                         }
00105                 }
00106                 
00107                 
00108                 $arrItems = explode($sep, $query);
00109                 foreach($arrItems as $query_item) {
00110                         $arr = explode('=', $query_item, 2);
00111                         $pname = String::convert(urldecode($arr[0]));
00112                         $pvalue = (count($arr) > 1) ? String::convert(urldecode($arr[1])) : '';
00113                         if (!empty($pname)) {
00114                                 if (substr($pname, -2) == '[]') {
00115                                         $ret[$pname][] = $pvalue;
00116                                 }
00117                                 else {
00118                                         $ret[$pname] = $pvalue;
00119                                 }
00120                         }
00121                 }
00122                 
00123                 return $ret;
00124                 
00125                 
00126                 
00127                 
00128                 
00129                 
00130                 
00131                 
00132                 
00133                 
00134                 
00135                 
00136                 
00137                 
00138                 
00139                 
00140                 
00141                 
00142         }
00143         
00144 
00145 
00146 
00147 
00148 
00149         public function __sleep() {
00150                 $this->url = $this->build();
00151                 return array('url');
00152         }
00153         
00154         public function __wakeup() {
00155                 $this->parse($this->url); 
00156         }
00157         
00158 
00159 
00160 
00161 
00162 
00163 
00164 
00165         public function equals($other, $mode = self::EQUALS_FULL) {
00166                 $check_against = ($other instanceof Url) ? $other : Url::create($other);
00167                 $ret = true;
00168                 $ret = $ret && $this->get_path() == $check_against->get_path();
00169                 $ret = $ret && $this->get_host() == $check_against->get_host();
00170                 $ret = $ret && $this->get_port() == $check_against->get_port();
00171                 $ret = $ret && $this->get_scheme() == $check_against->get_scheme();
00172                 if ($mode == self::EQUALS_FULL) {
00173                         $ret = $ret && $this->get_query() == $check_against->get_query();
00174                 }
00175                 return $ret;
00176         }
00177         
00178 
00179 
00180 
00181         public function is_empty() {
00182                 return $this->data['host'] === '';
00183         }
00184         
00185 
00186 
00187 
00188 
00189 
00190         public static function current() {
00191                 static $_url = false;
00192                 if ($_url === false) {
00193                         $_url = new Url(RequestInfo::current()->url_invoked());
00194                 }
00195                 return clone($_url);
00196         }       
00197         
00198 
00199 
00200 
00201 
00202 
00203 
00204         public static function create($url) {
00205                 return new Url($url);
00206         }
00207         
00208 
00209 
00210 
00211 
00212 
00213 
00214 
00215         public static function create_with_fallback_host($url, $host) {
00216                 return new Url($url, $host);
00217         }
00218                 
00219 
00220 
00221 
00222 
00223 
00224 
00225 
00226         public function replace_query_parameter($name, $value) {
00227                 if ($value === '' || $value === false) {
00228                         unset($this->data['query'][$name]);
00229                 }
00230                 else {
00231                         $this->data['query'][$name] = $value;
00232                 }
00233                 return $this;
00234         }
00235         
00236 
00237 
00238 
00239 
00240 
00241 
00242         public function replace_query_parameters($arr_params) {
00243                 foreach($arr_params as $key => $value) {
00244                         $this->replace_query_parameter($key, $value);
00245                 }
00246                 return $this;
00247         }
00248         
00249 
00250 
00251 
00252 
00253 
00254 
00255         public function set_path($path) {
00256                 $this->data['path'] = ltrim($path, '/');
00257                 return $this;
00258         }
00259         
00260 
00261 
00262 
00263         public function get_path() {
00264                 return $this->data['path'];                             
00265         }
00266         
00267 
00268 
00269 
00270 
00271 
00272 
00273         public function set_query($query) {
00274                 $this->data['query'] = $this->parse_query($query);
00275                 return $this;
00276         }
00277         
00278 
00279 
00280 
00281         public function get_query($encode = Url::ENCODE_PARAMS) {
00282                 $sep = html_entity_decode(ini_get('arg_separator.output'), ENT_QUOTES, GyroLocale::get_charset());
00283                 $ret = '';
00284                 foreach($this->get_query_params($encode) as $key => $value) {
00285                         $this->query_reduce($ret, $sep, $key, $value);
00286                 }
00287                 return $ret;
00288         }
00289         
00290 
00291 
00292 
00293 
00294 
00295 
00296 
00297 
00298         protected function query_reduce(&$current, $sep, $key, $value) {
00299                 if ($key) {
00300                         if (is_array($value)) {
00301                                 foreach($value as $v) {
00302                                         $this->query_reduce($current, $sep, $key, $v);
00303                                 }
00304                         }
00305                         else {
00306                                 if ($current) {
00307                                         $current .= $sep;
00308                                 }
00309                                 $current .= $key . '=' . $value;
00310                         }
00311                 }
00312         }
00313 
00314 
00315 
00316 
00317         public function get_query_param($name, $default = false, $encode = Url::NO_ENCODE_PARAMS) {
00318                 $ret = Arr::get_item($this->data['query'], $name, $default);
00319                 if ($encode == Url::ENCODE_PARAMS) {
00320                         if (is_array($ret)) {
00321                                 array_walk_recursive($ret, array($this, 'callback_urlencode'));
00322                         }
00323                         else {
00324                                 $this->callback_urlencode($ret);
00325                         }
00326                 }
00327                 return $ret;            
00328         }
00329         
00330 
00331 
00332 
00333         protected function callback_urlencode(&$value, $key = false) {
00334                 $value = urlencode($value);
00335         }
00336 
00337 
00338 
00339 
00340         public function get_query_params($encode = Url::NO_ENCODE_PARAMS) {
00341                 $ret = Arr::get_item($this->data, 'query', array());
00342                 if ($encode == Url::ENCODE_PARAMS) {
00343                         array_walk_recursive($ret, array($this, 'callback_urlencode'));
00344                 }
00345                 return $ret;
00346         }
00347 
00348 
00349 
00350 
00351         public function get_scheme() {
00352                 return $this->data['scheme'];
00353         }
00354 
00355 
00356 
00357 
00358         public function set_scheme($scheme) {
00359                 $this->data['scheme'] = $scheme;
00360                 return $this;
00361         }
00362         
00363 
00364 
00365 
00366         public function get_host() {
00367                 
00368                 return $this->data['host']; 
00369         }
00370         
00371 
00372 
00373 
00374 
00375 
00376         public function set_host($host) {
00377                 $this->data['host'] = String::to_lower($host);
00378                 return $this;
00379         }
00380         
00381 
00382 
00383 
00384 
00385 
00386 
00387 
00388 
00389 
00390 
00391         public function set_host_array($arr_host) {
00392                 $arr_temp = $this->parse_host();
00393                 if (isset($arr_host['subdomain'])) {
00394                         $arr_temp['subdomain'] = $arr_host['subdomain'];
00395                 }
00396                 if (isset($arr_host['domain'])) {
00397                         $arr_temp['domain'] = $arr_host['domain'];
00398                 }
00399                 else {
00400                         if (isset($arr_host['tld'])) {
00401                                 $arr_temp['tld'] = $arr_host['tld'];
00402                         }
00403                         if (isset($arr_host['sld'])) {
00404                                 $arr_temp['sld'] = $arr_host['sld'];
00405                         }
00406                         unset($arr_temp['domain']);
00407                 }
00408                 
00409                 
00410                 $arr_build = array();
00411                 if (!empty($arr_temp['subdomain'])) {
00412                         $arr_build[] = $arr_temp['subdomain'];
00413                 }
00414                 if (!empty($arr_temp['domain'])) {
00415                         $arr_build[] = $arr_temp['domain'];
00416                 }
00417                 else {
00418                         if (!empty($arr_temp['sld'])) {
00419                                 $arr_build[] = $arr_temp['sld'];
00420                         }
00421                         if (!empty($arr_temp['tld'])) {
00422                                 $arr_build[] = $arr_temp['tld'];
00423                         }
00424                 }
00425 
00426                 return $this->set_host(implode('.', $arr_build));               
00427         }
00428         
00429 
00430 
00431 
00432 
00433 
00434 
00435 
00436 
00437 
00438 
00439 
00440 
00441 
00442         public function parse_host() {
00443                 $host = $this->get_host();
00444                 $ret = array(
00445                         'tld' => '',
00446                         'sld' => '',
00447                         'domain' => '',
00448                         'subdomain' => '',
00449                         'data' => explode('.', $host)
00450                 );
00451                 $l_host = strlen($host);  
00452                 if ($l_host > 0) {
00453                         require_once(dirname(__FILE__) . '/data/tld.lst.php');
00454                         $tlds = get_tlds();
00455                         
00456                         
00457                         foreach($tlds as $tld) {
00458                                 $l_tld_check = strlen($tld) + 1; 
00459                                 
00460                                 
00461                                 if ($l_tld_check >= $l_host) {
00462                                         
00463                                         continue;
00464                                 } 
00465                                 
00466                                 
00467                                 if (substr($host, -$l_tld_check, $l_tld_check) === '.' . $tld) {
00468                                         $ret['tld'] = $tld;
00469                                         $tmp = explode('.', $tld);
00470                                         $count_data = count($ret['data']);
00471                                         $count_tld = count($tmp);
00472                                         $index_domain = $count_data - $count_tld - 1; 
00473                                         if ($index_domain >= 0) {
00474                                                 $ret['sld'] = $ret['data'][$index_domain];
00475                                                 $arr_subdomain = array();
00476                                                 for($i = 0; $i < $index_domain; $i++) {
00477                                                         $arr_subdomain[] = $ret['data'][$i];
00478                                                 }
00479                                                 $ret['subdomain'] = implode('.', $arr_subdomain);
00480                                                 $ret['domain'] = $ret['sld'] . '.' . $ret['tld'];
00481                                         }
00482                                         break;
00483                                 }
00484                         } 
00485                 }
00486                 unset($tlds); 
00487                 return $ret;
00488         }
00489         
00490         public function set_port($port) {
00491                 $this->data['port'] = ($port) ? intval($port) : $port;
00492                 return $this;
00493         }
00494         
00495         public function get_port() {
00496                 return $this->data['port'];
00497         }
00498         
00499 
00500 
00501 
00502         public function get_fragment() {
00503                 return $this->data['fragment'];
00504         }
00505 
00506 
00507 
00508 
00509 
00510 
00511         public function set_fragment($fragment) {
00512                 $this->data['fragment'] = $fragment;
00513                 return $this;
00514         }
00515         
00516         
00517 
00518 
00519 
00520         public function is_valid() {
00521                 $ret = !$this->is_empty();
00522                 
00523                 $src_host = $this->get_host();
00524                 if ($ret && !Validation::is_ip($src_host)) {
00525                         $ret = $ret && (preg_match('|^([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+$|i', $src_host) != 0);
00526                         if ($ret) {
00527                                 $host = $this->parse_host();
00528                                 $ret = $ret && !empty($host['tld']);
00529                                 $ret = $ret && !empty($host['domain']); 
00530                         }
00531                 }
00532                                 
00533                 return $ret; 
00534         }
00535         
00536 
00537 
00538 
00539 
00540 
00541 
00542 
00543 
00544         public function build($mode = Url::ABSOLUTE, $encoding = Url::ENCODE_PARAMS) {
00545                 $out = '';
00546                 
00547                 if ($mode == Url::ABSOLUTE) {
00548                         $out .= $this->get_scheme();
00549                         $out .= '://';
00550                 
00551                         $host = $this->get_host();
00552                         if (empty($host)) {
00553                                 throw new Exception('Url: No Host specified!');
00554                         }
00555                         $out .= $host;
00556                         $port = $this->get_port();
00557                         if ($port) {
00558                                 $out .= ':' . $port; 
00559                         }
00560                 }
00561                 
00562                 $out .= '/';
00563                 $out .= $this->get_path();
00564                                  
00565                 $query = $this->get_query($encoding);
00566                 if (!empty($query)) {
00567                         $out .= '?' . $query;
00568                 }
00569                 
00570                 $anchor = $this->get_fragment();
00571                 if (!empty($anchor)) {
00572                         $out .= '#' . $anchor;
00573                 }
00574                 
00575                 return $out;
00576         }
00577         
00578 
00579 
00580 
00581 
00582 
00583 
00584         public function output() {
00585                 $out = $this->build(true);
00586                 print $out;
00587                 return $this;
00588         }
00589                 
00590 
00591 
00592 
00593 
00594 
00595 
00596         public function __toString() {
00597                 return $this->build();
00598         }                       
00599                 
00600 
00601 
00602 
00603 
00604 
00605         function clean() {
00606                 $ret = Arr::get_item($this->data, 'path', '');
00607                 $this->data['path'] = String::plain_ascii($ret);
00608                 return $this;   
00609         }
00610         
00611 
00612 
00613 
00614         
00615 
00616 
00617 
00618 
00619 
00620         public function redirect($permanent = self::TEMPORARY) {
00621                 if (headers_sent() == false) {
00622                         $address = 'Location: ' . $this->build();
00623                         if ($permanent == self::PERMANENT) {
00624                                 Common::send_status_code(301); 
00625                         }
00626                         else {
00627                                 Common::send_status_code(302); 
00628                         }
00629                         session_write_close(); 
00630                         header($address);
00631                         exit;
00632                 }
00633                 else {
00634                         throw new Exception('Url: Redirect to ' . $this->build() . ' not possible, headers already sent'); 
00635                 }
00636         }
00637         
00638 
00639 
00640 
00641 
00642 
00643         public function clear_query() {
00644                 $this->data['query'] = array();
00645                 return $this;
00646         }
00647         
00648 
00649 
00650 
00651 
00652 
00653         public function is_ancestor_of($path_to_check) {
00654                 $path_to_check = trim($this->clean_path_for_comparison($path_to_check), '/');
00655                 $current = trim($this->get_path(), '/');
00656         
00657                 $ret = false; 
00658                 if (!empty($current) && !empty($path_to_check) && strpos($current . '/', $path_to_check . '/') === 0) {
00659                         $ret = true;
00660                 }
00661                 else if (empty($current) && empty($path_to_check)) {
00662                         $ret = true;
00663                 }               
00664                 
00665                 return $ret;
00666         }
00667 
00668 
00669 
00670 
00671 
00672 
00673 
00674         public function is_same_as($path_to_check) {
00675                 $path_to_check = $this->clean_path_for_comparison($path_to_check);
00676                 $current = ltrim($this->get_path(), '/');
00677 
00678                 return $current == $path_to_check;
00679         }
00680 
00681         protected function clean_path_for_comparison($path_to_check) {
00682                 foreach(array('?', '#') as $remove) {
00683                         $pos = strpos($path_to_check, $remove);
00684                         if ($pos !== false) {
00685                                 $path_to_check = substr($path_to_check, 0, $pos);
00686                         }
00687                 }
00688 
00689                 $path_to_check = ltrim($path_to_check, "/");
00690                 return $path_to_check;
00691         }
00692         
00693         public static function validate_current() {
00694                 if (!empty($_POST)) {
00695                         return;
00696                 }
00697                 
00698                 $url = Url::current();
00699                 $path = trim($url->get_path());
00700                 
00701                 if ($path == Config::get_value(Config::URL_BASEDIR)) {
00702                         return;
00703                 } 
00704 
00705 
00706                 
00707                 
00708                 $pathclean = str_replace('%20', '', $path);
00709                 $pathclean = str_replace('//', '/', $pathclean);
00710                 
00711                 $dirs = explode('/', $pathclean);
00712                 $dirsclean = array();
00713                 for ($i = 0; $i < sizeof($dirs); $i++) {
00714                         if ('.' === $dirs[$i]) {
00715                                 continue;
00716                         }
00717                         if ('..' === $dirs[$i] && $i > 0 && '..' != $dirsclean[sizeof($dirsclean) - 1]) {
00718                                 array_pop($dirsclean);
00719                                 continue;
00720                         }
00721                         array_push($dirsclean, $dirs[$i]);
00722                 }
00723                 $pathclean = implode('/', $dirsclean);
00724                 $url->set_path($pathclean);
00725                 if ($url->build(Url::ABSOLUTE, Url::ENCODE_PARAMS) != RequestInfo::current()->url_invoked(RequestInfo::ABSOLUTE)) {
00726                         $url->redirect(self::PERMANENT);
00727                         exit();
00728                 } 
00729                 
00730                 $pos = String::strpos($path, '&'); 
00731                 if ($pos !== false) {
00732                         $path = String::left($path, $pos) . '?' . String::substr($path, $pos + 1);
00733                         $url->set_path($path);
00734                         $url->redirect(self::PERMANENT);
00735                         exit();
00736                 }
00737         }
00738 }
00739 ?>