[ Index ] |
PHP Cross Reference of YOURLS |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 * YOURLS general functions 4 * 5 */ 6 7 /** 8 * Make an optimized regexp pattern from a string of characters 9 * 10 * @param string $string 11 * @return string 12 */ 13 function yourls_make_regexp_pattern( $string ) { 14 // Simple benchmarks show that regexp with smarter sequences (0-9, a-z, A-Z...) are not faster or slower than 0123456789 etc... 15 // add @ as an escaped character because @ is used as the regexp delimiter in yourls-loader.php 16 return preg_quote( $string, '@' ); 17 } 18 19 /** 20 * Get client IP Address. Returns a DB safe string. 21 * 22 * @return string 23 */ 24 function yourls_get_IP() { 25 $ip = ''; 26 27 // Precedence: if set, X-Forwarded-For > HTTP_X_FORWARDED_FOR > HTTP_CLIENT_IP > HTTP_VIA > REMOTE_ADDR 28 $headers = [ 'X-Forwarded-For', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_VIA', 'REMOTE_ADDR' ]; 29 foreach( $headers as $header ) { 30 if ( !empty( $_SERVER[ $header ] ) ) { 31 $ip = $_SERVER[ $header ]; 32 break; 33 } 34 } 35 36 // headers can contain multiple IPs (X-Forwarded-For = client, proxy1, proxy2). Take first one. 37 if ( strpos( $ip, ',' ) !== false ) 38 $ip = substr( $ip, 0, strpos( $ip, ',' ) ); 39 40 return (string)yourls_apply_filter( 'get_IP', yourls_sanitize_ip( $ip ) ); 41 } 42 43 /** 44 * Get next id a new link will have if no custom keyword provided 45 * 46 * @since 1.0 47 * @return int id of next link 48 */ 49 function yourls_get_next_decimal() { 50 return (int)yourls_apply_filter( 'get_next_decimal', (int)yourls_get_option( 'next_id' ) ); 51 } 52 53 /** 54 * Update id for next link with no custom keyword 55 * 56 * Note: this function relies upon yourls_update_option(), which will return either true or false 57 * depending upon if there has been an actual MySQL query updating the DB. 58 * In other words, this function may return false yet this would not mean it has functionally failed 59 * In other words I'm not sure if we really need this function to return something :face_with_eyes_looking_up: 60 * See issue 2621 for more on this. 61 * 62 * @since 1.0 63 * @param integer $int id for next link 64 * @return bool true or false depending on if there has been an actual MySQL query. See note above. 65 */ 66 function yourls_update_next_decimal( $int = 0 ) { 67 $int = ( $int == 0 ) ? yourls_get_next_decimal() + 1 : (int)$int ; 68 $update = yourls_update_option( 'next_id', $int ); 69 yourls_do_action( 'update_next_decimal', $int, $update ); 70 return $update; 71 } 72 73 /** 74 * Return XML output. 75 * 76 * @param array $array 77 * @return string 78 */ 79 function yourls_xml_encode( $array ) { 80 return (\Spatie\ArrayToXml\ArrayToXml::convert($array, '', true, 'UTF-8')); 81 } 82 83 /** 84 * Update click count on a short URL. Return 0/1 for error/success. 85 * 86 * @param string $keyword 87 * @param false|int $clicks 88 * @return int 0 or 1 for error/success 89 */ 90 function yourls_update_clicks( $keyword, $clicks = false ) { 91 // Allow plugins to short-circuit the whole function 92 $pre = yourls_apply_filter( 'shunt_update_clicks', false, $keyword, $clicks ); 93 if ( false !== $pre ) { 94 return $pre; 95 } 96 97 $keyword = yourls_sanitize_keyword( $keyword ); 98 $table = YOURLS_DB_TABLE_URL; 99 if ( $clicks !== false && is_int( $clicks ) && $clicks >= 0 ) { 100 $update = "UPDATE `$table` SET `clicks` = :clicks WHERE `keyword` = :keyword"; 101 $values = [ 'clicks' => $clicks, 'keyword' => $keyword ]; 102 } else { 103 $update = "UPDATE `$table` SET `clicks` = clicks + 1 WHERE `keyword` = :keyword"; 104 $values = [ 'keyword' => $keyword ]; 105 } 106 107 // Try and update click count. An error probably means a concurrency problem : just skip the update 108 try { 109 $result = yourls_get_db()->fetchAffected($update, $values); 110 } catch (Exception $e) { 111 $result = 0; 112 } 113 114 yourls_do_action( 'update_clicks', $keyword, $result, $clicks ); 115 116 return $result; 117 } 118 119 120 /** 121 * Return array of stats. (string)$filter is 'bottom', 'last', 'rand' or 'top'. (int)$limit is the number of links to return 122 * 123 * @param string $filter 'bottom', 'last', 'rand' or 'top' 124 * @param int $limit Number of links to return 125 * @param int $start Offset to start from 126 * @return array Array of links 127 */ 128 function yourls_get_stats($filter = 'top', $limit = 10, $start = 0) { 129 switch( $filter ) { 130 case 'bottom': 131 $sort_by = '`clicks`'; 132 $sort_order = 'asc'; 133 break; 134 case 'last': 135 $sort_by = '`timestamp`'; 136 $sort_order = 'desc'; 137 break; 138 case 'rand': 139 case 'random': 140 $sort_by = 'RAND()'; 141 $sort_order = ''; 142 break; 143 case 'top': 144 default: 145 $sort_by = '`clicks`'; 146 $sort_order = 'desc'; 147 break; 148 } 149 150 // Fetch links 151 $limit = intval( $limit ); 152 $start = intval( $start ); 153 if ( $limit > 0 ) { 154 155 $table_url = YOURLS_DB_TABLE_URL; 156 $results = yourls_get_db()->fetchObjects( "SELECT * FROM `$table_url` WHERE 1=1 ORDER BY $sort_by $sort_order LIMIT $start, $limit;" ); 157 158 $return = []; 159 $i = 1; 160 161 foreach ( (array)$results as $res ) { 162 $return['links']['link_'.$i++] = [ 163 'shorturl' => yourls_link($res->keyword), 164 'url' => $res->url, 165 'title' => $res->title, 166 'timestamp'=> $res->timestamp, 167 'ip' => $res->ip, 168 'clicks' => $res->clicks, 169 ]; 170 } 171 } 172 173 $return['stats'] = yourls_get_db_stats(); 174 175 $return['statusCode'] = '200'; 176 177 return yourls_apply_filter( 'get_stats', $return, $filter, $limit, $start ); 178 } 179 180 /** 181 * Get total number of URLs and sum of clicks. Input: optional "AND WHERE" clause. Returns array 182 * 183 * The $where parameter will contain additional SQL arguments: 184 * $where['sql'] will concatenate SQL clauses: $where['sql'] = ' AND something = :value AND otherthing < :othervalue'; 185 * $where['binds'] will hold the (name => value) placeholder pairs: $where['binds'] = array('value' => $value, 'othervalue' => $value2) 186 * 187 * @param array $where See comment above 188 * @return array 189 */ 190 function yourls_get_db_stats( $where = [ 'sql' => '', 'binds' => [] ] ) { 191 $table_url = YOURLS_DB_TABLE_URL; 192 193 $totals = yourls_get_db()->fetchObject( "SELECT COUNT(keyword) as count, SUM(clicks) as sum FROM `$table_url` WHERE 1=1 " . $where['sql'] , $where['binds'] ); 194 $return = [ 'total_links' => $totals->count, 'total_clicks' => $totals->sum ]; 195 196 return yourls_apply_filter( 'get_db_stats', $return, $where ); 197 } 198 199 /** 200 * Returns a sanitized a user agent string. Given what I found on http://www.user-agents.org/ it should be OK. 201 * 202 * @return string 203 */ 204 function yourls_get_user_agent() { 205 $ua = '-'; 206 207 if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) { 208 $ua = strip_tags( html_entity_decode( $_SERVER['HTTP_USER_AGENT'] )); 209 $ua = preg_replace('![^0-9a-zA-Z\':., /{}\(\)\[\]\+@&\!\?;_\-=~\*\#]!', '', $ua ); 210 } 211 212 return yourls_apply_filter( 'get_user_agent', substr( $ua, 0, 255 ) ); 213 } 214 215 /** 216 * Returns the sanitized referrer submitted by the browser. 217 * 218 * @return string HTTP Referrer or 'direct' if no referrer was provided 219 */ 220 function yourls_get_referrer() { 221 $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? yourls_sanitize_url_safe( $_SERVER['HTTP_REFERER'] ) : 'direct'; 222 223 return yourls_apply_filter( 'get_referrer', substr( $referrer, 0, 200 ) ); 224 } 225 226 /** 227 * Redirect to another page 228 * 229 * YOURLS redirection, either to internal or external URLs. If headers have not been sent, redirection 230 * is achieved with PHP's header(). If headers have been sent already and we're not in a command line 231 * client, redirection occurs with Javascript. 232 * 233 * Note: yourls_redirect() does not exit automatically, and should almost always be followed by a call to exit() 234 * to prevent the script from continuing. 235 * 236 * @since 1.4 237 * @param string $location URL to redirect to 238 * @param int $code HTTP status code to send 239 * @return int 1 for header redirection, 2 for js redirection, 3 otherwise (CLI) 240 */ 241 function yourls_redirect( $location, $code = 301 ) { 242 yourls_do_action( 'pre_redirect', $location, $code ); 243 $location = yourls_apply_filter( 'redirect_location', $location, $code ); 244 $code = yourls_apply_filter( 'redirect_code', $code, $location ); 245 246 // Redirect, either properly if possible, or via Javascript otherwise 247 if( !headers_sent() ) { 248 yourls_status_header( $code ); 249 header( "Location: $location" ); 250 return 1; 251 } 252 253 // Headers sent : redirect with JS if not in CLI 254 if( php_sapi_name() !== 'cli') { 255 yourls_redirect_javascript( $location ); 256 return 2; 257 } 258 259 // We're in CLI 260 return 3; 261 } 262 263 /** 264 * Redirect to an existing short URL 265 * 266 * Redirect client to an existing short URL (no check performed) and execute misc tasks: update 267 * clicks for short URL, update logs, and send an X-Robots-Tag header to control indexing of a page. 268 * 269 * @since 1.7.3 270 * @param string $url 271 * @param string $keyword 272 * @return void 273 */ 274 function yourls_redirect_shorturl($url, $keyword) { 275 yourls_do_action( 'redirect_shorturl', $url, $keyword ); 276 277 // Attempt to update click count in main table 278 yourls_update_clicks( $keyword ); 279 280 // Update detailed log for stats 281 yourls_log_redirect( $keyword ); 282 283 // Send an X-Robots-Tag header 284 yourls_robots_tag_header(); 285 286 yourls_redirect( $url, 301 ); 287 } 288 289 /** 290 * Send an X-Robots-Tag header. See #3486 291 * 292 * @since 1.9.2 293 * @return void 294 */ 295 function yourls_robots_tag_header() { 296 // Allow plugins to short-circuit the whole function 297 $pre = yourls_apply_filter( 'shunt_robots_tag_header', false ); 298 if ( false !== $pre ) { 299 return $pre; 300 } 301 302 // By default, we're sending a 'noindex' header 303 $tag = yourls_apply_filter( 'robots_tag_header', 'noindex' ); 304 $replace = yourls_apply_filter( 'robots_tag_header_replace', true ); 305 if ( !headers_sent() ) { 306 header( "X-Robots-Tag: $tag", $replace ); 307 } 308 } 309 310 311 /** 312 * Send headers to explicitly tell browser not to cache content or redirection 313 * 314 * @since 1.7.10 315 * @return void 316 */ 317 function yourls_no_cache_headers() { 318 if( !headers_sent() ) { 319 header( 'Expires: Thu, 23 Mar 1972 07:00:00 GMT' ); 320 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' ); 321 header( 'Cache-Control: no-cache, must-revalidate, max-age=0' ); 322 header( 'Pragma: no-cache' ); 323 } 324 } 325 326 /** 327 * Send header to prevent display within a frame from another site (avoid clickjacking) 328 * 329 * This header makes it impossible for an external site to display YOURLS admin within a frame, 330 * which allows for clickjacking. 331 * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options 332 * This said, the whole function is shuntable : legit uses of iframes should be still possible. 333 * 334 * @since 1.8.1 335 * @return void|mixed 336 */ 337 function yourls_no_frame_header() { 338 // Allow plugins to short-circuit the whole function 339 $pre = yourls_apply_filter( 'shunt_no_frame_header', false ); 340 if ( false !== $pre ) { 341 return $pre; 342 } 343 344 if( !headers_sent() ) { 345 header( 'X-Frame-Options: SAMEORIGIN' ); 346 } 347 } 348 349 /** 350 * Send a filterable content type header 351 * 352 * @since 1.7 353 * @param string $type content type ('text/html', 'application/json', ...) 354 * @return bool whether header was sent 355 */ 356 function yourls_content_type_header( $type ) { 357 yourls_do_action( 'content_type_header', $type ); 358 if( !headers_sent() ) { 359 $charset = yourls_apply_filter( 'content_type_header_charset', 'utf-8' ); 360 header( "Content-Type: $type; charset=$charset" ); 361 return true; 362 } 363 return false; 364 } 365 366 /** 367 * Set HTTP status header 368 * 369 * @since 1.4 370 * @param int $code status header code 371 * @return bool whether header was sent 372 */ 373 function yourls_status_header( $code = 200 ) { 374 yourls_do_action( 'status_header', $code ); 375 376 if( headers_sent() ) 377 return false; 378 379 $protocol = $_SERVER['SERVER_PROTOCOL']; 380 if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol ) 381 $protocol = 'HTTP/1.0'; 382 383 $code = intval( $code ); 384 $desc = yourls_get_HTTP_status( $code ); 385 386 @header ("$protocol $code $desc"); // This causes problems on IIS and some FastCGI setups 387 388 return true; 389 } 390 391 /** 392 * Redirect to another page using Javascript. 393 * Set optional (bool)$dontwait to false to force manual redirection (make sure a message has been read by user) 394 * 395 * @param string $location 396 * @param bool $dontwait 397 * @return void 398 */ 399 function yourls_redirect_javascript( $location, $dontwait = true ) { 400 yourls_do_action( 'pre_redirect_javascript', $location, $dontwait ); 401 $location = yourls_apply_filter( 'redirect_javascript', $location, $dontwait ); 402 if ( $dontwait ) { 403 $message = yourls_s( 'if you are not redirected after 10 seconds, please <a href="%s">click here</a>', $location ); 404 echo <<<REDIR 405 <script type="text/javascript"> 406 window.location="$location"; 407 </script> 408 <small>($message)</small> 409 REDIR; 410 } 411 else { 412 echo '<p>'.yourls_s( 'Please <a href="%s">click here</a>', $location ).'</p>'; 413 } 414 yourls_do_action( 'post_redirect_javascript', $location ); 415 } 416 417 /** 418 * Return an HTTP status code 419 * 420 * @param int $code 421 * @return string 422 */ 423 function yourls_get_HTTP_status( $code ) { 424 $code = intval( $code ); 425 $headers_desc = [ 426 100 => 'Continue', 427 101 => 'Switching Protocols', 428 102 => 'Processing', 429 430 200 => 'OK', 431 201 => 'Created', 432 202 => 'Accepted', 433 203 => 'Non-Authoritative Information', 434 204 => 'No Content', 435 205 => 'Reset Content', 436 206 => 'Partial Content', 437 207 => 'Multi-Status', 438 226 => 'IM Used', 439 440 300 => 'Multiple Choices', 441 301 => 'Moved Permanently', 442 302 => 'Found', 443 303 => 'See Other', 444 304 => 'Not Modified', 445 305 => 'Use Proxy', 446 306 => 'Reserved', 447 307 => 'Temporary Redirect', 448 449 400 => 'Bad Request', 450 401 => 'Unauthorized', 451 402 => 'Payment Required', 452 403 => 'Forbidden', 453 404 => 'Not Found', 454 405 => 'Method Not Allowed', 455 406 => 'Not Acceptable', 456 407 => 'Proxy Authentication Required', 457 408 => 'Request Timeout', 458 409 => 'Conflict', 459 410 => 'Gone', 460 411 => 'Length Required', 461 412 => 'Precondition Failed', 462 413 => 'Request Entity Too Large', 463 414 => 'Request-URI Too Long', 464 415 => 'Unsupported Media Type', 465 416 => 'Requested Range Not Satisfiable', 466 417 => 'Expectation Failed', 467 422 => 'Unprocessable Entity', 468 423 => 'Locked', 469 424 => 'Failed Dependency', 470 426 => 'Upgrade Required', 471 472 500 => 'Internal Server Error', 473 501 => 'Not Implemented', 474 502 => 'Bad Gateway', 475 503 => 'Service Unavailable', 476 504 => 'Gateway Timeout', 477 505 => 'HTTP Version Not Supported', 478 506 => 'Variant Also Negotiates', 479 507 => 'Insufficient Storage', 480 510 => 'Not Extended' 481 ]; 482 483 return $headers_desc[$code] ?? ''; 484 } 485 486 /** 487 * Log a redirect (for stats) 488 * 489 * This function does not check for the existence of a valid keyword, in order to save a query. Make sure the keyword 490 * exists before calling it. 491 * 492 * @since 1.4 493 * @param string $keyword short URL keyword 494 * @return mixed Result of the INSERT query (1 on success) 495 */ 496 function yourls_log_redirect( $keyword ) { 497 // Allow plugins to short-circuit the whole function 498 $pre = yourls_apply_filter( 'shunt_log_redirect', false, $keyword ); 499 if ( false !== $pre ) { 500 return $pre; 501 } 502 503 if (!yourls_do_log_redirect()) { 504 return true; 505 } 506 507 $table = YOURLS_DB_TABLE_LOG; 508 $ip = yourls_get_IP(); 509 $binds = [ 510 'now' => date( 'Y-m-d H:i:s' ), 511 'keyword' => yourls_sanitize_keyword($keyword), 512 'referrer' => substr( yourls_get_referrer(), 0, 200 ), 513 'ua' => substr(yourls_get_user_agent(), 0, 255), 514 'ip' => $ip, 515 'location' => yourls_geo_ip_to_countrycode($ip), 516 ]; 517 518 // Try and log. An error probably means a concurrency problem : just skip the logging 519 try { 520 $result = yourls_get_db()->fetchAffected("INSERT INTO `$table` (click_time, shorturl, referrer, user_agent, ip_address, country_code) VALUES (:now, :keyword, :referrer, :ua, :ip, :location)", $binds ); 521 } catch (Exception $e) { 522 $result = 0; 523 } 524 525 return $result; 526 } 527 528 /** 529 * Check if we want to not log redirects (for stats) 530 * 531 * @return bool 532 */ 533 function yourls_do_log_redirect() { 534 return ( !defined( 'YOURLS_NOSTATS' ) || YOURLS_NOSTATS != true ); 535 } 536 537 /** 538 * Check if an upgrade is needed 539 * 540 * @return bool 541 */ 542 function yourls_upgrade_is_needed() { 543 // check YOURLS_DB_VERSION exist && match values stored in YOURLS_DB_TABLE_OPTIONS 544 list( $currentver, $currentsql ) = yourls_get_current_version_from_sql(); 545 if ( $currentsql < YOURLS_DB_VERSION ) { 546 return true; 547 } 548 549 // Check if YOURLS_VERSION exist && match value stored in YOURLS_DB_TABLE_OPTIONS, update DB if required 550 if ( $currentver < YOURLS_VERSION ) { 551 yourls_update_option( 'version', YOURLS_VERSION ); 552 } 553 554 return false; 555 } 556 557 /** 558 * Get current version & db version as stored in the options DB. Prior to 1.4 there's no option table. 559 * 560 * @return array 561 */ 562 function yourls_get_current_version_from_sql() { 563 $currentver = yourls_get_option( 'version' ); 564 $currentsql = yourls_get_option( 'db_version' ); 565 566 // Values if version is 1.3 567 if ( !$currentver ) { 568 $currentver = '1.3'; 569 } 570 if ( !$currentsql ) { 571 $currentsql = '100'; 572 } 573 574 return [ $currentver, $currentsql ]; 575 } 576 577 /** 578 * Determine if the current page is private 579 * 580 * @return bool 581 */ 582 function yourls_is_private() { 583 $private = defined( 'YOURLS_PRIVATE' ) && YOURLS_PRIVATE; 584 585 if ( $private ) { 586 587 // Allow overruling for particular pages: 588 589 // API 590 if ( yourls_is_API() && defined( 'YOURLS_PRIVATE_API' ) ) { 591 $private = YOURLS_PRIVATE_API; 592 } 593 // Stat pages 594 elseif ( yourls_is_infos() && defined( 'YOURLS_PRIVATE_INFOS' ) ) { 595 $private = YOURLS_PRIVATE_INFOS; 596 } 597 // Others future cases ? 598 } 599 600 return yourls_apply_filter( 'is_private', $private ); 601 } 602 603 /** 604 * Allow several short URLs for the same long URL ? 605 * 606 * @return bool 607 */ 608 function yourls_allow_duplicate_longurls() { 609 // special treatment if API to check for WordPress plugin requests 610 if ( yourls_is_API() && isset( $_REQUEST[ 'source' ] ) && $_REQUEST[ 'source' ] == 'plugin' ) { 611 return false; 612 } 613 614 return yourls_apply_filter('allow_duplicate_longurls', defined('YOURLS_UNIQUE_URLS') && !YOURLS_UNIQUE_URLS); 615 } 616 617 /** 618 * Check if an IP shortens URL too fast to prevent DB flood. Return true, or die. 619 * 620 * @param string $ip 621 * @return bool|mixed|string 622 */ 623 function yourls_check_IP_flood( $ip = '' ) { 624 625 // Allow plugins to short-circuit the whole function 626 $pre = yourls_apply_filter( 'shunt_check_IP_flood', false, $ip ); 627 if ( false !== $pre ) 628 return $pre; 629 630 yourls_do_action( 'pre_check_ip_flood', $ip ); // at this point $ip can be '', check it if your plugin hooks in here 631 632 // Raise white flag if installing or if no flood delay defined 633 if( 634 ( defined('YOURLS_FLOOD_DELAY_SECONDS') && YOURLS_FLOOD_DELAY_SECONDS === 0 ) || 635 !defined('YOURLS_FLOOD_DELAY_SECONDS') || 636 yourls_is_installing() 637 ) 638 return true; 639 640 // Don't throttle logged in users 641 if( yourls_is_private() ) { 642 if( yourls_is_valid_user() === true ) 643 return true; 644 } 645 646 // Don't throttle whitelist IPs 647 if( defined( 'YOURLS_FLOOD_IP_WHITELIST' ) && YOURLS_FLOOD_IP_WHITELIST ) { 648 $whitelist_ips = explode( ',', YOURLS_FLOOD_IP_WHITELIST ); 649 foreach( (array)$whitelist_ips as $whitelist_ip ) { 650 $whitelist_ip = trim( $whitelist_ip ); 651 if ( $whitelist_ip == $ip ) 652 return true; 653 } 654 } 655 656 $ip = ( $ip ? yourls_sanitize_ip( $ip ) : yourls_get_IP() ); 657 658 yourls_do_action( 'check_ip_flood', $ip ); 659 660 $table = YOURLS_DB_TABLE_URL; 661 $lasttime = yourls_get_db()->fetchValue( "SELECT `timestamp` FROM $table WHERE `ip` = :ip ORDER BY `timestamp` DESC LIMIT 1", [ 'ip' => $ip ] ); 662 if( $lasttime ) { 663 $now = date( 'U' ); 664 $then = date( 'U', strtotime( $lasttime ) ); 665 if( ( $now - $then ) <= YOURLS_FLOOD_DELAY_SECONDS ) { 666 // Flood! 667 yourls_do_action( 'ip_flood', $ip, $now - $then ); 668 yourls_die( yourls__( 'Too many URLs added too fast. Slow down please.' ), yourls__( 'Too Many Requests' ), 429 ); 669 } 670 } 671 672 return true; 673 } 674 675 /** 676 * Check if YOURLS is installing 677 * 678 * @since 1.6 679 * @return bool 680 */ 681 function yourls_is_installing() { 682 return (bool)yourls_apply_filter( 'is_installing', defined( 'YOURLS_INSTALLING' ) && YOURLS_INSTALLING ); 683 } 684 685 /** 686 * Check if YOURLS is upgrading 687 * 688 * @since 1.6 689 * @return bool 690 */ 691 function yourls_is_upgrading() { 692 return (bool)yourls_apply_filter( 'is_upgrading', defined( 'YOURLS_UPGRADING' ) && YOURLS_UPGRADING ); 693 } 694 695 /** 696 * Check if YOURLS is installed 697 * 698 * Checks property $ydb->installed that is created by yourls_get_all_options() 699 * 700 * See inline comment for updating from 1.3 or prior. 701 * 702 * @return bool 703 */ 704 function yourls_is_installed() { 705 return (bool)yourls_apply_filter( 'is_installed', yourls_get_db()->is_installed() ); 706 } 707 708 /** 709 * Set installed state 710 * 711 * @since 1.7.3 712 * @param bool $bool whether YOURLS is installed or not 713 * @return void 714 */ 715 function yourls_set_installed( $bool ) { 716 yourls_get_db()->set_installed( $bool ); 717 } 718 719 /** 720 * Generate random string of (int)$length length and type $type (see function for details) 721 * 722 * @param int $length 723 * @param int $type 724 * @param string $charlist 725 * @return mixed|string 726 */ 727 function yourls_rnd_string ( $length = 5, $type = 0, $charlist = '' ) { 728 $length = intval( $length ); 729 730 // define possible characters 731 switch ( $type ) { 732 733 // no vowels to make no offending word, no 0/1/o/l to avoid confusion between letters & digits. Perfect for passwords. 734 case '1': 735 $possible = "23456789bcdfghjkmnpqrstvwxyz"; 736 break; 737 738 // Same, with lower + upper 739 case '2': 740 $possible = "23456789bcdfghjkmnpqrstvwxyzBCDFGHJKMNPQRSTVWXYZ"; 741 break; 742 743 // all letters, lowercase 744 case '3': 745 $possible = "abcdefghijklmnopqrstuvwxyz"; 746 break; 747 748 // all letters, lowercase + uppercase 749 case '4': 750 $possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 751 break; 752 753 // all digits & letters lowercase 754 case '5': 755 $possible = "0123456789abcdefghijklmnopqrstuvwxyz"; 756 break; 757 758 // all digits & letters lowercase + uppercase 759 case '6': 760 $possible = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 761 break; 762 763 // custom char list, or comply to charset as defined in config 764 default: 765 case '0': 766 $possible = $charlist ? $charlist : yourls_get_shorturl_charset(); 767 break; 768 } 769 770 $str = substr( str_shuffle( $possible ), 0, $length ); 771 return yourls_apply_filter( 'rnd_string', $str, $length, $type, $charlist ); 772 } 773 774 /** 775 * Check if we're in API mode. 776 * 777 * @return bool 778 */ 779 function yourls_is_API() { 780 return (bool)yourls_apply_filter( 'is_API', defined( 'YOURLS_API' ) && YOURLS_API ); 781 } 782 783 /** 784 * Check if we're in Ajax mode. 785 * 786 * @return bool 787 */ 788 function yourls_is_Ajax() { 789 return (bool)yourls_apply_filter( 'is_Ajax', defined( 'YOURLS_AJAX' ) && YOURLS_AJAX ); 790 } 791 792 /** 793 * Check if we're in GO mode (yourls-go.php). 794 * 795 * @return bool 796 */ 797 function yourls_is_GO() { 798 return (bool)yourls_apply_filter( 'is_GO', defined( 'YOURLS_GO' ) && YOURLS_GO ); 799 } 800 801 /** 802 * Check if we're displaying stats infos (yourls-infos.php). Returns bool 803 * 804 * @return bool 805 */ 806 function yourls_is_infos() { 807 return (bool)yourls_apply_filter( 'is_infos', defined( 'YOURLS_INFOS' ) && YOURLS_INFOS ); 808 } 809 810 /** 811 * Check if we're in the admin area. Returns bool. Does not relate with user rights. 812 * 813 * @return bool 814 */ 815 function yourls_is_admin() { 816 return (bool)yourls_apply_filter( 'is_admin', defined( 'YOURLS_ADMIN' ) && YOURLS_ADMIN ); 817 } 818 819 /** 820 * Check if the server seems to be running on Windows. Not exactly sure how reliable this is. 821 * 822 * @return bool 823 */ 824 function yourls_is_windows() { 825 return defined( 'DIRECTORY_SEPARATOR' ) && DIRECTORY_SEPARATOR == '\\'; 826 } 827 828 /** 829 * Check if SSL is required. 830 * 831 * @return bool 832 */ 833 function yourls_needs_ssl() { 834 return (bool)yourls_apply_filter( 'needs_ssl', defined( 'YOURLS_ADMIN_SSL' ) && YOURLS_ADMIN_SSL ); 835 } 836 837 /** 838 * Check if SSL is used. Stolen from WP. 839 * 840 * @return bool 841 */ 842 function yourls_is_ssl() { 843 $is_ssl = false; 844 if ( isset( $_SERVER[ 'HTTPS' ] ) ) { 845 if ( 'on' == strtolower( $_SERVER[ 'HTTPS' ] ) ) { 846 $is_ssl = true; 847 } 848 if ( '1' == $_SERVER[ 'HTTPS' ] ) { 849 $is_ssl = true; 850 } 851 } 852 elseif ( isset( $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] ) ) { 853 if ( 'https' == strtolower( $_SERVER[ 'HTTP_X_FORWARDED_PROTO' ] ) ) { 854 $is_ssl = true; 855 } 856 } 857 elseif ( isset( $_SERVER[ 'SERVER_PORT' ] ) && ( '443' == $_SERVER[ 'SERVER_PORT' ] ) ) { 858 $is_ssl = true; 859 } 860 return (bool)yourls_apply_filter( 'is_ssl', $is_ssl ); 861 } 862 863 /** 864 * Get a remote page title 865 * 866 * This function returns a string: either the page title as defined in HTML, or the URL if not found 867 * The function tries to convert funky characters found in titles to UTF8, from the detected charset. 868 * Charset in use is guessed from HTML meta tag, or if not found, from server's 'content-type' response. 869 * 870 * @param string $url URL 871 * @return string Title (sanitized) or the URL if no title found 872 */ 873 function yourls_get_remote_title( $url ) { 874 // Allow plugins to short-circuit the whole function 875 $pre = yourls_apply_filter( 'shunt_get_remote_title', false, $url ); 876 if ( false !== $pre ) { 877 return $pre; 878 } 879 880 $url = yourls_sanitize_url( $url ); 881 882 // Only deal with http(s):// 883 if ( !in_array( yourls_get_protocol( $url ), [ 'http://', 'https://' ] ) ) { 884 return $url; 885 } 886 887 $title = $charset = false; 888 889 $max_bytes = yourls_apply_filter( 'get_remote_title_max_byte', 32768 ); // limit data fetching to 32K in order to find a <title> tag 890 891 $response = yourls_http_get( $url, [], [], [ 'max_bytes' => $max_bytes ] ); // can be a Request object or an error string 892 if ( is_string( $response ) ) { 893 return $url; 894 } 895 896 // Page content. No content? Return the URL 897 $content = $response->body; 898 if ( !$content ) { 899 return $url; 900 } 901 902 // look for <title>. No title found? Return the URL 903 if ( preg_match( '/<title>(.*?)<\/title>/is', $content, $found ) ) { 904 $title = $found[ 1 ]; 905 unset( $found ); 906 } 907 if ( !$title ) { 908 return $url; 909 } 910 911 // Now we have a title. We'll try to get proper utf8 from it. 912 913 // Get charset as (and if) defined by the HTML meta tag. We should match 914 // <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 915 // or <meta charset='utf-8'> and all possible variations: see https://gist.github.com/ozh/7951236 916 if ( preg_match( '/<meta[^>]*charset\s*=["\' ]*([a-zA-Z0-9\-_]+)/is', $content, $found ) ) { 917 $charset = $found[ 1 ]; 918 unset( $found ); 919 } 920 else { 921 // No charset found in HTML. Get charset as (and if) defined by the server response 922 $_charset = current( $response->headers->getValues( 'content-type' ) ); 923 if ( preg_match( '/charset=(\S+)/', $_charset, $found ) ) { 924 $charset = trim( $found[ 1 ], ';' ); 925 unset( $found ); 926 } 927 } 928 929 // Conversion to utf-8 if what we have is not utf8 already 930 if ( strtolower( $charset ) != 'utf-8' && function_exists( 'mb_convert_encoding' ) ) { 931 // We use @ to remove warnings because mb_ functions are easily bitching about illegal chars 932 if ( $charset ) { 933 $title = @mb_convert_encoding( $title, 'UTF-8', $charset ); 934 } 935 else { 936 $title = @mb_convert_encoding( $title, 'UTF-8' ); 937 } 938 } 939 940 // Remove HTML entities 941 $title = html_entity_decode( $title, ENT_QUOTES, 'UTF-8' ); 942 943 // Strip out evil things 944 $title = yourls_sanitize_title( $title, $url ); 945 946 return (string)yourls_apply_filter( 'get_remote_title', $title, $url ); 947 } 948 949 /** 950 * Quick UA check for mobile devices. 951 * 952 * @return bool 953 */ 954 function yourls_is_mobile_device() { 955 // Strings searched 956 $mobiles = [ 957 'android', 'blackberry', 'blazer', 958 'compal', 'elaine', 'fennec', 'hiptop', 959 'iemobile', 'iphone', 'ipod', 'ipad', 960 'iris', 'kindle', 'opera mobi', 'opera mini', 961 'palm', 'phone', 'pocket', 'psp', 'symbian', 962 'treo', 'wap', 'windows ce', 'windows phone' 963 ]; 964 965 // Current user-agent 966 $current = strtolower( $_SERVER['HTTP_USER_AGENT'] ); 967 968 // Check and return 969 $is_mobile = ( str_replace( $mobiles, '', $current ) != $current ); 970 return (bool)yourls_apply_filter( 'is_mobile_device', $is_mobile ); 971 } 972 973 /** 974 * Get request in YOURLS base (eg in 'http://sho.rt/yourls/abcd' get 'abdc') 975 * 976 * With no parameter passed, this function will guess current page and consider 977 * it is the requested page. 978 * For testing purposes, parameters can be passed. 979 * 980 * @since 1.5 981 * @param string $yourls_site Optional, YOURLS installation URL (default to constant YOURLS_SITE) 982 * @param string $uri Optional, page requested (default to $_SERVER['REQUEST_URI'] eg '/yourls/abcd' ) 983 * @return string request relative to YOURLS base (eg 'abdc') 984 */ 985 function yourls_get_request($yourls_site = '', $uri = '') { 986 // Allow plugins to short-circuit the whole function 987 $pre = yourls_apply_filter( 'shunt_get_request', false ); 988 if ( false !== $pre ) { 989 return $pre; 990 } 991 992 yourls_do_action( 'pre_get_request', $yourls_site, $uri ); 993 994 // Default values 995 if ( '' === $yourls_site ) { 996 $yourls_site = yourls_get_yourls_site(); 997 } 998 if ( '' === $uri ) { 999 $uri = $_SERVER[ 'REQUEST_URI' ]; 1000 } 1001 1002 // Even though the config sample states YOURLS_SITE should be set without trailing slash... 1003 $yourls_site = rtrim( $yourls_site, '/' ); 1004 1005 // Now strip the YOURLS_SITE path part out of the requested URI, and get the request relative to YOURLS base 1006 // +---------------------------+-------------------------+---------------------+--------------+ 1007 // | if we request | and YOURLS is hosted on | YOURLS path part is | "request" is | 1008 // +---------------------------+-------------------------+---------------------+--------------+ 1009 // | http://sho.rt/abc | http://sho.rt | / | abc | 1010 // | https://SHO.rt/subdir/abc | https://shor.rt/subdir/ | /subdir/ | abc | 1011 // +---------------------------+-------------------------+---------------------+--------------+ 1012 // and so on. You can find various test cases in /tests/tests/utilities/get_request.php 1013 1014 // Take only the URL_PATH part of YOURLS_SITE (ie "https://sho.rt:1337/path/to/yourls" -> "/path/to/yourls") 1015 $yourls_site = parse_url( $yourls_site, PHP_URL_PATH ).'/'; 1016 1017 // Strip path part from request if exists 1018 $request = $uri; 1019 if ( substr( $uri, 0, strlen( $yourls_site ) ) == $yourls_site ) { 1020 $request = ltrim( substr( $uri, strlen( $yourls_site ) ), '/' ); 1021 } 1022 1023 // Unless request looks like a full URL (ie request is a simple keyword) strip query string 1024 if ( !preg_match( "@^[a-zA-Z]+://.+@", $request ) ) { 1025 $request = current( explode( '?', $request ) ); 1026 } 1027 1028 $request = yourls_sanitize_url( $request ); 1029 1030 return (string)yourls_apply_filter( 'get_request', $request ); 1031 } 1032 1033 /** 1034 * Fix $_SERVER['REQUEST_URI'] variable for various setups. Stolen from WP. 1035 * 1036 * We also strip $_COOKIE from $_REQUEST to allow our lazy using $_REQUEST without 3rd party cookie interfering. 1037 * See #3383 for explanation. 1038 * 1039 * @since 1.5.1 1040 * @return void 1041 */ 1042 function yourls_fix_request_uri() { 1043 1044 $default_server_values = [ 1045 'SERVER_SOFTWARE' => '', 1046 'REQUEST_URI' => '', 1047 ]; 1048 $_SERVER = array_merge( $default_server_values, $_SERVER ); 1049 1050 // Make $_REQUEST with only $_GET and $_POST, not $_COOKIE. See #3383. 1051 $_REQUEST = array_merge( $_GET, $_POST ); 1052 1053 // Fix for IIS when running with PHP ISAPI 1054 if ( empty( $_SERVER[ 'REQUEST_URI' ] ) || ( php_sapi_name() != 'cgi-fcgi' && preg_match( '/^Microsoft-IIS\//', $_SERVER[ 'SERVER_SOFTWARE' ] ) ) ) { 1055 1056 // IIS Mod-Rewrite 1057 if ( isset( $_SERVER[ 'HTTP_X_ORIGINAL_URL' ] ) ) { 1058 $_SERVER[ 'REQUEST_URI' ] = $_SERVER[ 'HTTP_X_ORIGINAL_URL' ]; 1059 } 1060 // IIS Isapi_Rewrite 1061 elseif ( isset( $_SERVER[ 'HTTP_X_REWRITE_URL' ] ) ) { 1062 $_SERVER[ 'REQUEST_URI' ] = $_SERVER[ 'HTTP_X_REWRITE_URL' ]; 1063 } 1064 else { 1065 // Use ORIG_PATH_INFO if there is no PATH_INFO 1066 if ( !isset( $_SERVER[ 'PATH_INFO' ] ) && isset( $_SERVER[ 'ORIG_PATH_INFO' ] ) ) { 1067 $_SERVER[ 'PATH_INFO' ] = $_SERVER[ 'ORIG_PATH_INFO' ]; 1068 } 1069 1070 // Some IIS + PHP configurations puts the script-name in the path-info (No need to append it twice) 1071 if ( isset( $_SERVER[ 'PATH_INFO' ] ) ) { 1072 if ( $_SERVER[ 'PATH_INFO' ] == $_SERVER[ 'SCRIPT_NAME' ] ) { 1073 $_SERVER[ 'REQUEST_URI' ] = $_SERVER[ 'PATH_INFO' ]; 1074 } 1075 else { 1076 $_SERVER[ 'REQUEST_URI' ] = $_SERVER[ 'SCRIPT_NAME' ].$_SERVER[ 'PATH_INFO' ]; 1077 } 1078 } 1079 1080 // Append the query string if it exists and isn't null 1081 if ( !empty( $_SERVER[ 'QUERY_STRING' ] ) ) { 1082 $_SERVER[ 'REQUEST_URI' ] .= '?'.$_SERVER[ 'QUERY_STRING' ]; 1083 } 1084 } 1085 } 1086 } 1087 1088 /** 1089 * Check for maintenance mode. If yes, die. See yourls_maintenance_mode(). Stolen from WP. 1090 * 1091 * @return void 1092 */ 1093 function yourls_check_maintenance_mode() { 1094 $dot_file = YOURLS_ABSPATH . '/.maintenance' ; 1095 1096 if ( !file_exists( $dot_file ) || yourls_is_upgrading() || yourls_is_installing() ) { 1097 return; 1098 } 1099 1100 global $maintenance_start; 1101 yourls_include_file_sandbox( $dot_file ); 1102 // If the $maintenance_start timestamp is older than 10 minutes, don't die. 1103 if ( ( time() - $maintenance_start ) >= 600 ) { 1104 return; 1105 } 1106 1107 // Use any /user/maintenance.php file 1108 $file = YOURLS_USERDIR . '/maintenance.php'; 1109 if(file_exists($file)) { 1110 if(yourls_include_file_sandbox( $file ) == true) { 1111 die(); 1112 } 1113 } 1114 1115 // Or use the default messages 1116 $title = yourls__('Service temporarily unavailable'); 1117 $message = yourls__('Our service is currently undergoing scheduled maintenance.') . "</p>\n<p>" . 1118 yourls__('Things should not last very long, thank you for your patience and please excuse the inconvenience'); 1119 yourls_die( $message, $title, 503 ); 1120 } 1121 1122 /** 1123 * Check if a URL protocol is allowed 1124 * 1125 * Checks a URL against a list of whitelisted protocols. Protocols must be defined with 1126 * their complete scheme name, ie 'stuff:' or 'stuff://' (for instance, 'mailto:' is a valid 1127 * protocol, 'mailto://' isn't, and 'http:' with no double slashed isn't either 1128 * 1129 * @since 1.6 1130 * @see yourls_get_protocol() 1131 * 1132 * @param string $url URL to be check 1133 * @param array $protocols Optional. Array of protocols, defaults to global $yourls_allowedprotocols 1134 * @return bool true if protocol allowed, false otherwise 1135 */ 1136 function yourls_is_allowed_protocol( $url, $protocols = [] ) { 1137 if ( empty( $protocols ) ) { 1138 global $yourls_allowedprotocols; 1139 $protocols = $yourls_allowedprotocols; 1140 } 1141 1142 return yourls_apply_filter( 'is_allowed_protocol', in_array( yourls_get_protocol( $url ), $protocols ), $url, $protocols ); 1143 } 1144 1145 /** 1146 * Get protocol from a URL (eg mailto:, http:// ...) 1147 * 1148 * What we liberally call a "protocol" in YOURLS is the scheme name + colon + double slashes if present of a URI. Examples: 1149 * "something://blah" -> "something://" 1150 * "something:blah" -> "something:" 1151 * "something:/blah" -> "something:" 1152 * 1153 * Unit Tests for this function are located in tests/format/urls.php 1154 * 1155 * @since 1.6 1156 * 1157 * @param string $url URL to be check 1158 * @return string Protocol, with slash slash if applicable. Empty string if no protocol 1159 */ 1160 function yourls_get_protocol( $url ) { 1161 /* 1162 http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax 1163 The scheme name consists of a sequence of characters beginning with a letter and followed by any 1164 combination of letters, digits, plus ("+"), period ("."), or hyphen ("-"). Although schemes are 1165 case-insensitive, the canonical form is lowercase and documents that specify schemes must do so 1166 with lowercase letters. It is followed by a colon (":"). 1167 */ 1168 preg_match( '!^[a-zA-Z][a-zA-Z0-9+.-]+:(//)?!', $url, $matches ); 1169 return (string)yourls_apply_filter( 'get_protocol', isset( $matches[0] ) ? $matches[0] : '', $url ); 1170 } 1171 1172 /** 1173 * Get relative URL (eg 'abc' from 'http://sho.rt/abc') 1174 * 1175 * Treat indifferently http & https. If a URL isn't relative to the YOURLS install, return it as is 1176 * or return empty string if $strict is true 1177 * 1178 * @since 1.6 1179 * @param string $url URL to relativize 1180 * @param bool $strict if true and if URL isn't relative to YOURLS install, return empty string 1181 * @return string URL 1182 */ 1183 function yourls_get_relative_url( $url, $strict = true ) { 1184 $url = yourls_sanitize_url( $url ); 1185 1186 // Remove protocols to make it easier 1187 $noproto_url = str_replace( 'https:', 'http:', $url ); 1188 $noproto_site = str_replace( 'https:', 'http:', yourls_get_yourls_site() ); 1189 1190 // Trim URL from YOURLS root URL : if no modification made, URL wasn't relative 1191 $_url = str_replace( $noproto_site.'/', '', $noproto_url ); 1192 if ( $_url == $noproto_url ) { 1193 $_url = ( $strict ? '' : $url ); 1194 } 1195 return yourls_apply_filter( 'get_relative_url', $_url, $url ); 1196 } 1197 1198 /** 1199 * Marks a function as deprecated and informs that it has been used. Stolen from WP. 1200 * 1201 * There is a hook deprecated_function that will be called that can be used 1202 * to get the backtrace up to what file and function called the deprecated 1203 * function. 1204 * 1205 * The current behavior is to trigger a user error if YOURLS_DEBUG is true. 1206 * 1207 * This function is to be used in every function that is deprecated. 1208 * 1209 * @since 1.6 1210 * @uses yourls_do_action() Calls 'deprecated_function' and passes the function name, what to use instead, 1211 * and the version the function was deprecated in. 1212 * @uses yourls_apply_filter() Calls 'deprecated_function_trigger_error' and expects boolean value of true to do 1213 * trigger or false to not trigger error. 1214 * 1215 * @param string $function The function that was called 1216 * @param string $version The version of WordPress that deprecated the function 1217 * @param string $replacement Optional. The function that should have been called 1218 * @return void 1219 */ 1220 function yourls_deprecated_function( $function, $version, $replacement = null ) { 1221 1222 yourls_do_action( 'deprecated_function', $function, $replacement, $version ); 1223 1224 // Allow plugin to filter the output error trigger 1225 if ( yourls_get_debug_mode() && yourls_apply_filter( 'deprecated_function_trigger_error', true ) ) { 1226 if ( ! is_null( $replacement ) ) 1227 trigger_error( sprintf( yourls__('%1$s is <strong>deprecated</strong> since version %2$s! Use %3$s instead.'), $function, $version, $replacement ) ); 1228 else 1229 trigger_error( sprintf( yourls__('%1$s is <strong>deprecated</strong> since version %2$s with no alternative available.'), $function, $version ) ); 1230 } 1231 } 1232 1233 /** 1234 * Explode a URL in an array of ( 'protocol' , 'slashes if any', 'rest of the URL' ) 1235 * 1236 * Some hosts trip up when a query string contains 'http://' - see http://git.io/j1FlJg 1237 * The idea is that instead of passing the whole URL to a bookmarklet, eg index.php?u=http://blah.com, 1238 * we pass it by pieces to fool the server, eg index.php?proto=http:&slashes=//&rest=blah.com 1239 * 1240 * Known limitation: this won't work if the rest of the URL itself contains 'http://', for example 1241 * if rest = blah.com/file.php?url=http://foo.com 1242 * 1243 * Sample returns: 1244 * 1245 * with 'mailto:[email protected]?subject=hey' : 1246 * array( 'protocol' => 'mailto:', 'slashes' => '', 'rest' => '[email protected]?subject=hey' ) 1247 * 1248 * with 'http://example.com/blah.html' : 1249 * array( 'protocol' => 'http:', 'slashes' => '//', 'rest' => 'example.com/blah.html' ) 1250 * 1251 * @since 1.7 1252 * @param string $url URL to be parsed 1253 * @param array $array Optional, array of key names to be used in returned array 1254 * @return array|false false if no protocol found, array of ('protocol' , 'slashes', 'rest') otherwise 1255 */ 1256 function yourls_get_protocol_slashes_and_rest( $url, $array = [ 'protocol', 'slashes', 'rest' ] ) { 1257 $proto = yourls_get_protocol( $url ); 1258 1259 if ( !$proto or count( $array ) != 3 ) { 1260 return false; 1261 } 1262 1263 list( $null, $rest ) = explode( $proto, $url, 2 ); 1264 1265 list( $proto, $slashes ) = explode( ':', $proto ); 1266 1267 return [ 1268 $array[ 0 ] => $proto.':', 1269 $array[ 1 ] => $slashes, 1270 $array[ 2 ] => $rest 1271 ]; 1272 } 1273 1274 /** 1275 * Set URL scheme (HTTP or HTTPS) to a URL 1276 * 1277 * @since 1.7.1 1278 * @param string $url URL 1279 * @param string $scheme scheme, either 'http' or 'https' 1280 * @return string URL with chosen scheme 1281 */ 1282 function yourls_set_url_scheme( $url, $scheme = '' ) { 1283 if ( in_array( $scheme, [ 'http', 'https' ] ) ) { 1284 $url = preg_replace( '!^[a-zA-Z0-9+.-]+://!', $scheme.'://', $url ); 1285 } 1286 return $url; 1287 } 1288 1289 /** 1290 * Tell if there is a new YOURLS version 1291 * 1292 * This function checks, if needed, if there's a new version of YOURLS and, if applicable, displays 1293 * an update notice. 1294 * 1295 * @since 1.7.3 1296 * @return void 1297 */ 1298 function yourls_tell_if_new_version() { 1299 yourls_debug_log( 'Check for new version: '.( yourls_maybe_check_core_version() ? 'yes' : 'no' ) ); 1300 yourls_new_core_version_notice(YOURLS_VERSION); 1301 } 1302 1303 /** 1304 * File include sandbox 1305 * 1306 * Attempt to include a PHP file, fail with an error message if the file isn't valid PHP code. 1307 * This function does not check first if the file exists : depending on use case, you may check first. 1308 * 1309 * @since 1.9.2 1310 * @param string $file filename (full path) 1311 * @return string|bool string if error, true if success 1312 */ 1313 function yourls_include_file_sandbox($file) { 1314 try { 1315 if (is_readable( $file )) { 1316 require_once $file; 1317 yourls_debug_log("loaded $file"); 1318 return true; 1319 } else { 1320 throw new \Exception('File not readable'); 1321 } 1322 } catch ( \Throwable $e ) { 1323 yourls_debug_log("could not load $file"); 1324 return sprintf("%s (%s : %s)", $e->getMessage() , $e->getFile() , $e->getLine() ); 1325 } 1326 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Mar 28 05:10:25 2025 | Cross-referenced by PHPXref 0.7.1 |