yf_rewrite class| File | plugins/rewrite/classes/yf_rewrite.class.php |
|---|---|
| Use | _class('rewrite') |
A site sets them in its config with $PROJECT_CONF['rewrite']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$BUILD_RULES |
[] |
|
$DEFAULT_HOST |
'' |
|
$DEFAULT_PORT |
'' |
|
$FORCE_NO_DEBUG |
null |
|
$PARSE_RULES |
[] |
|
$QUERY_ROUTE_ON_EMPTY_PATHbool |
false |
REWRITE_MODE: on an empty path, ?object=&action= route like /object/action (off: they are dropped, the default page shows) |
$REWRITE_PATTERNS |
[] |
|
$URL_ADD_BUILTIN_PARAMS |
true |
|
$USE_WEB_PATH |
null |
|
$_time_start |
null |
|
$allowed_url_params |
['utm_source', 'utm_medium', 'utm_content', 'utm_campaign', 'utm_term'] |
|
$builtins |
['debug', 'error', 'no_cache', 'no_core_cache', 'host'] |
|
$special_links |
['../', './', '/'] |
public function get_admin_home_url($params = [])
Get admin side home page url.
public function get_admin_home_url($params = [])
{
// TODO
}
public function get_current_url($params = [])
Generate current url, using internal params.
public function get_current_url($params = [])
{
// TODO
}
public function get_user_home_url($params = [])
Get user side home page url.
public function get_user_home_url($params = [])
{
// TODO
}
public function is_current_url($url = '', $params = [])
Checks if given url is same as internal one.
public function is_current_url($url = '', $params = [])
{
// TODO
}
public function is_urls_same($url1 = '', $url2 = '', $params = [])
Checks if two input urls are the same.
public function is_urls_same($url1 = '', $url2 = '', $params = [])
{
// TODO
}
_ hooks and helpers; not routable as page actionspublic function __call($name, $args)
Catch missing method call.
public function __call($name, $args)
{
return main()->extend_call($this, $name, $args);
}
public function _correct_protocol($url)
public function _correct_protocol($url)
{
if ( ! strlen($url)) {
return false;
}
$main = main();
$request_is_https = $main->is_https();
$is_http = false;
$is_https = false;
$change_to_http = false;
$change_to_https = false;
$matched = false;
if ($main->HTTPS_ENABLED_FOR) {
foreach ((array) $main->HTTPS_ENABLED_FOR as $item) {
if (is_callable($item)) {
if ($item($url)) {
$matched = true;
break;
}
} elseif (preg_match('@' . $item . '@ims', $url)) {
$matched = true;
break;
}
}
}
// Return links to the http protocol
if (substr($url, 0, 2) == '//') {
$url = str_replace('//', 'http://', $url);
}
if (substr($url, 0, 8) == 'https://') {
$is_https = true;
} elseif (substr($url, 0, 7) == 'http://') {
$is_http = true;
}
if ($request_is_https) {
$change_to_https = true;
} elseif ($is_https) {
if ($main->HTTPS_ENABLED_FOR) {
if ( ! $matched) {
$change_to_http = true;
}
} elseif ( ! $main->USE_ONLY_HTTPS) {
$change_to_http = true;
}
} elseif ($is_http) {
if ($main->USE_ONLY_HTTPS) {
$change_to_https = true;
} elseif ($main->HTTPS_ENABLED_FOR) {
if ($matched) {
$change_to_https = true;
}
}
}
if ($is_http && $change_to_https) {
$url = str_replace('http://', 'https://', $url);
} elseif ($is_https && $change_to_http) {
$url = str_replace('https://', 'http://', $url);
}
return $url;
}
public function _get_unique_links($text = '', $for_iframe = false)
public function _get_unique_links($text = '', $for_iframe = false)
{
$unique = [];
$pattern = '/(action|location|href|src)[\s]{0,1}=[\s]{0,1}["\']?(\.\/\?[^"\'\>\s]+|\.\/)["\']?/ims';
preg_match_all($pattern, $text, $matches);
foreach ((array) $matches['2'] as $k => $v) {
if (strlen($v) && ! in_array($v, $unique)) {
$unique[] = $v;
}
}
return $unique;
}
public function _init()
YF module constructor.
public function _init()
{
$this->REWRITE_PATTERNS = [
'yf' => _class('rewrite_pattern_yf', 'classes/rewrite/'),
];
if ( ! $this->DEFAULT_HOST) {
if (defined('WEB_DOMAIN') && strlen(WEB_DOMAIN)) {
$this->DEFAULT_HOST = WEB_DOMAIN;
}
if ( ! $this->DEFAULT_HOST && defined('WEB_PATH')) {
$host = parse_url(WEB_PATH, PHP_URL_HOST);
if ($host && ! (main()->web_path_was_not_defined && $host === '127.0.0.1')) {
$this->DEFAULT_HOST = $host;
}
}
if ( ! $this->DEFAULT_HOST && ($_SERVER['HTTP_HOST'] ?? null)) {
$this->DEFAULT_HOST = ($_SERVER['HTTP_HOST'] ?? null);
}
}
if ( ! $this->DEFAULT_PORT) {
if (defined('WEB_PATH') && strlen(WEB_PATH)) {
$port = parse_url(WEB_PATH, PHP_URL_PORT);
if ($port && ! in_array($port, ['80', '443'])) {
$this->DEFAULT_PORT = $port;
}
}
if ( ! $this->DEFAULT_PORT && ($_SERVER['SERVER_PORT'] ?? null) && ! in_array($_SERVER['SERVER_PORT'], ['80', '443'])) {
$this->DEFAULT_PORT = $_SERVER['SERVER_PORT'];
}
if ( ! $this->DEFAULT_PORT) {
$this->DEFAULT_PORT = '80';
}
}
}
public function _is_our_url($url)
public function _is_our_url($url)
{
$result = parse_url($url);
$host = $result['host'];
$u = preg_replace('/\.htm.*/', '', $result['path']);
$u = trim($u, '/');
$u_arr = explode('/', $u);
parse_str($result['query'], $s_arr);
$arr = $this->REWRITE_PATTERNS['yf']->_parse($host, (array) $u_arr, (array) $s_arr, $url, $this);
$new_url = $this->_url($arr, WEB_DOMAIN);
return $url == $new_url;
}
public function _process_url($url = '', $force_rewrite = false, $for_site_id = false)
public function _process_url($url = '', $force_rewrite = false, $for_site_id = false)
{
if (strpos($url, 'http://') === false && strpos($url, 'https://') !== 0) {
$url = $this->_rewrite_replace_links($url, true, $force_rewrite, $for_site_id);
}
// fix for rewrite tests
return str_replace(['http:///', 'https:///'], './', $url);
}
public function _query_route($url_parts, $object, $action)
Path segments for the parser: with QUERY_ROUTE_ON_EMPTY_PATH, an empty path and a plain ?object= name give [object, action]. The parser builds a query string from the segments, so only names of its own character set pass (a decoded "&" would inject id / page / object[]); anything else keeps the path.
$url_parts array public function _query_route($url_parts, $object, $action)
{
$name = '~^[A-Za-z0-9_-]+$~D';
if ( ! $this->QUERY_ROUTE_ON_EMPTY_PATH || (string) ($url_parts[0] ?? '') !== '' || ! is_string($object) || ! preg_match($name, $object)) {
return $url_parts;
}
if ($action === null || $action === '') {
return [$object];
}
return is_string($action) && preg_match($name, $action) ? [$object, $action] : $url_parts;
}
public function _replace_special_links($body = '', $links = [])
Special processing for short links '/', './', '../'.
public function _replace_special_links($body = '', $links = [])
{
$rewrite_to_url = $this->_url('/');
foreach ((array) $this->special_links as $link) {
$regex = '~(?P<part1>(href|src)\s*=\s*["\']{1})\s*' . preg_quote($link, '~') . '?\s*(?P<part2>["\']{1}[\s>]?)~ims';
$replace_into = '\1' . $rewrite_to_url . '\3';
$body = preg_replace($regex, $replace_into, $body);
}
return $body;
}
public function _rewrite_replace_links($body = '', $standalone = false, $force_rewrite = false, $for_site_id = false)
Replace links for url rewrite.
public function _rewrite_replace_links($body = '', $standalone = false, $force_rewrite = false, $for_site_id = false)
{
if (MAIN_TYPE_ADMIN && ! $force_rewrite) {
return $body;
}
$trace = null;
$exec_time = null;
if (DEBUG_MODE && ! $this->FORCE_NO_DEBUG) {
$trace = main()->trace_string();
$this->_time_start = microtime(true);
}
// Special processing for short links '/', './', '../' == this case mostly used in redirects like js_redirect('./')
if (in_array(trim($body), $this->special_links)) {
$out = $this->_url('/');
if (DEBUG_MODE && ! $this->FORCE_NO_DEBUG) {
debug('rewrite[]', [
'source' => $body,
'rewrited' => $out,
'trace' => $trace,
'exec_time' => (microtime(true) - $this->_time_start),
]);
}
return $out;
}
$links = $standalone ? [$body] : $this->_get_unique_links($body);
if ( ! empty($links) && is_array($links)) {
$r_array = [];
$has_special = false;
foreach ($links as $link) {
if (in_array($link, $this->special_links)) {
$has_special = true;
continue;
}
$arr = [];
$url = parse_url($link);
parse_str($url['query'] ?? '', $arr);
if (MAIN_TYPE_ADMIN && in_array($arr['task'], ['login', 'logout'])) {
continue;
}
// Support for custom url params
$tmp = $arr;
foreach (['object', 'action', 'id', 'page'] as $v) {
if (isset($tmp[$v])) {
unset($tmp[$v]);
}
}
if (count((array) $tmp) > 0) {
foreach ($tmp as $k => $v) {
unset($arr[$k]);
}
$arr['_other'] = urldecode(http_build_query($tmp));
}
unset($tmp);
$replace = $this->_url($arr) . (strlen($url['fragment'] ?? '') ? '#' . $url['fragment'] : '');
$r_array[$link] = $replace;
}
// Fix for bug with similar shorter links, sort by length DESC
uksort($r_array, function ($a, $b) {
$sa = strlen($a);
$sb = strlen($b);
if ($sa == $sb) {
return 0;
}
return ($sa < $sb) ? +1 : -1;
});
$body = str_replace(array_keys($r_array), array_values($r_array), $body);
if ($has_special) {
$body = $this->_replace_special_links($body, $links);
}
if (DEBUG_MODE && ! $this->FORCE_NO_DEBUG) {
$exec_time = (microtime(true) - $this->_time_start);
foreach ((array) $r_array as $s => $r) {
debug('rewrite[]', [
'source' => $s,
'rewrited' => $r,
'trace' => $trace,
'exec_time' => $exec_time,
]);
}
}
}
if (DEBUG_MODE && ! $this->FORCE_NO_DEBUG) {
debug('rewrite_exec_time', debug('rewrite_exec_time') + $exec_time);
}
return $body;
}
public function _url($params = [], $host = '', $url_str = '', $for_section = null)
public function _url($params = [], $host = '', $url_str = '', $for_section = null)
{
$time_start = null;
if (DEBUG_MODE && ! $this->FORCE_NO_DEBUG) {
$time_start = microtime(true);
}
if ( ! is_array($params) && is_string($params)) {
$url_str = trim($params);
$orig_url_str = $url_str;
$params = [];
$params['fragment'] = parse_url($url_str, PHP_URL_FRAGMENT);
if (($params['fragment'] ?? false) && strlen($params['fragment'])) {
$url_str = str_replace('#' . $params['fragment'], '', $url_str);
}
if (preg_match('~[a-z0-9_\./]+~ims', $url_str)) {
$_other = '';
if (strpos($url_str, '?') !== false) {
list($url_str, $_other) = explode('?', $url_str);
}
// Example: ./test/oauth/github, ../test/oauth/github
if (($url_str[0] ?? '') == '.') {
$url_str = ltrim($url_str, '.');
}
if (($url_str[0] ?? '') == '/') {
if (($url_str[1] ?? '') == '/') {
// Example: //test/test_action/&k1=v1&k2=v2 => object=test, action=test_action, k1=v1, k2=v2
list(, , $params['object'], $params['action'], $params['_other']) = array_pad(explode('/', $url_str), 5, null);
} else {
// Example: /test/oauth/github => object=test, action=oauth, id=github
list(, $params['object'], $params['action'], $params['id'], $params['page'], $params['_other']) = array_pad(explode('/', $url_str), 6, null);
}
} else {
// Example: test2.dev/test/oauth/github => host=test2.dev, object=test, action=oauth, id=github
list($params['host'], $params['object'], $params['action'], $params['id'], $params['page'], $params['_other']) = array_pad(explode('/', $url_str), 6, null);
}
if ( ! $params['_other'] && $_other) {
$params['_other'] = $_other;
}
}
if (is_array($host)) {
$params += (array) $host;
$host = $params['host'] ?? null;
}
}
if ( ! is_array($params) && empty($url_str)) {
return false;
}
if ( ! $for_section || ($for_section !== 'user' && $for_section !== 'admin')) {
$for_section = MAIN_TYPE;
}
// Support for other params passed by http encoded string (&k1=v1&k2=v2)
if (isset($params['_other'])) {
parse_str(trim($params['_other'], '&?'), $tmp);
foreach ((array) $tmp as $k => $v) {
$k = trim($k);
if ( ! strlen($k)) {
continue;
}
if (is_array($v)) {
foreach ($v as $k1 => $v1) {
$k1 = trim($k1);
if ( ! strlen($k1)) {
continue;
}
if (is_array($v1)) {
foreach ($v1 as $k2 => $v2) {
$k2 = trim($k2);
if ( ! strlen($k2)) {
continue;
}
// TODO: support for several deepness levels of url arrays
$v2 = trim($v2);
if (strlen($v2)) {
$params[$k][$k1][$k2] = $v2;
}
}
} else {
$v1 = trim($v1);
if (strlen($v1)) {
$params[$k][$k1] = $v1;
}
}
}
} else {
$v = trim($v);
if (strlen($v)) {
$params[$k] = $v;
}
}
}
unset($params['_other']);
}
// Ensure correct order of params
$p = [];
foreach (['object', 'action', 'id', 'page'] as $name) {
if (isset($params[$name])) {
$p[$name] = $params[$name];
}
}
foreach ((array) $params as $k => $v) {
$p[$k] = $v;
}
$params = $p;
unset($p);
// Add built-in url params, if needed
if ($this->URL_ADD_BUILTIN_PARAMS) {
$has_builin = false;
foreach ($this->builtins as $b) {
if (isset($_GET[$b])) {
$has_builin = true;
break;
}
}
if ($has_builin) {
$params['debug'] = $_GET['debug'] ?? null;
$params['error'] = $_GET['error'] ?? null;
$params['get_host'] = $_GET['host'] ?? null;
$params['no_cache'] = isset($_GET['no_cache']) ? 'y' : '';
$params['no_core_cache'] = isset($_GET['no_core_cache']) ? 'y' : '';
}
}
if (empty($url_str)) {
if (isset($params['action']) && empty($params['action'])) {
$params['action'] = 'show';
}
}
if (($params['object'] ?? null) == '@object') {
$params['object'] = $_GET['object'] ?? null;
}
if (($params['action'] ?? null) == '@action') {
$params['action'] = $_GET['action'] ?? null;
}
if (($params['id'] ?? null) == '@id') {
$params['id'] = $_GET['id'] ?? null;
}
if (($params['page'] ?? null) == '@page') {
$params['page'] = $_GET['page'] ?? null;
}
foreach ((array) $params as $k => $v) {
if (empty($v) && ($v !== '0')) {
unset($params[$k]);
continue;
}
}
// patterns support here
if (empty($params['host'])) {
$params['host'] = ! empty($host) ? $host : $this->DEFAULT_HOST;
}
if (empty($params['port'])) {
$port = $this->DEFAULT_PORT;
if ($port && ! in_array($port, ['80', '443'])) {
$params['port'] = $port;
}
}
$REWRITE_ENABLED = $GLOBALS['PROJECT_CONF']['tpl']['REWRITE_MODE'] ?? null;
if ($REWRITE_ENABLED && $for_section != 'admin') {
$link = $this->REWRITE_PATTERNS['yf']->_build($params, $this);
} else {
$arr_out = [];
foreach ((array) $params as $k => $v) {
if (in_array($k, ['host', 'port', 'fragment', 'path', 'admin_host', 'admin_port', 'admin_path', 'is_full_url'])) {
continue;
}
$arr_out[$k] = $v;
}
$u = '';
if ( ! empty($arr_out)) {
$u .= (strpos($u, '?') === false ? '?' : '&') . urldecode(http_build_query($arr_out));
}
$http_protocol = main()->USE_ONLY_HTTPS ? 'https' : 'http';
if ($for_section == 'admin') {
if ($params['admin_host'] ?? null) {
$_host = $params['admin_host'];
$_port = ($params['admin_port'] ?? null) ?: '80';
$_path = ($params['admin_path'] ?? null) ?: '/admin/';
$link = $this->_correct_protocol($http_protocol . '://' . $_host . ($_port && ! in_array($_port, ['80', '443']) ? ':' . $_port : '') . ($_path ?: '/') . $u);
} else {
$link = ADMIN_WEB_PATH . $u;
if ($params['is_full_url'] ?? null) {
$protocol_scheme = _class('api')->_detect_protocol_scheme();
$link = $protocol_scheme . ':' . $link;
}
}
} else {
$_host = $params['host'];
$_port = ($params['port'] ?? null) ?: '80';
$_path = ($params['path'] ?? null) ?: '/';
$link = $this->_correct_protocol($http_protocol . '://' . $_host . ($_port && ! in_array($_port, ['80', '443']) ? ':' . $_port : '') . ($_path ?: '/') . $u);
}
if ($params['fragment'] ?? null) {
$link .= '#' . $params['fragment'];
}
}
if (DEBUG_MODE) {
debug(__FUNCTION__ . '[]', [
'params' => $params,
'rewrited_link' => $link,
'host' => $params['host'],
'port' => $params['port'],
'url_str' => $url_str,
'time' => microtime(true) - $time_start,
'trace' => main()->trace_string(),
]);
}
return $link;
}
public function _url_admin($params = [], $host = '', $url_str = '')
Generate url for admin section, no matter from where was called.
public function _url_admin($params = [], $host = '', $url_str = '')
{
return $this->_url($params, $host, $url_str, $for_section = 'admin');
}
public function _url_user($params = [], $host = '', $url_str = '')
Generate url for user section, no matter from where was called.
public function _url_user($params = [], $host = '', $url_str = '')
{
return $this->_url($params, $host, $url_str, $for_section = 'user');
}