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 ?>