我需要检测提供的URL是否与当前导航的URL匹配。请注意,以下是有效但语义相同的URL:
https://www.example.com/path/to/page/index.php?parameter=value
https://www.example.com/path/to/page/index.php
https://www.example.com/path/to/page/
https://www.example.com/path/to/page
http://www.example.com/path/to/page
//www.example.com/path/to/page
//www/path/to/page
../../../path/to/page
../../to/page
../page
./
如果给定的网址指向当前网页,则最终功能必须返回true
,如果不是,则返回false
。 我没有预期网址列表;这将用于只想在链接到当前页面时禁用链接的客户端。 请注意,我希望忽略参数,因为这些并不表示此网站上的当前页面。我使用以下正则表达式:
/^((https?:)?\/\/www(\.example\.com)\/path\/to\/page\/?(index.php)?(\?.+=.*(\&.+=.*)*)?)|(\.\/)$/i
其中https?
,www
,\.example\.com
,\/path\/to\/page
和index.php
使用$_SERVER["PHP_SELF"]
动态检测并制作正则表达式,但与../../to/page
之类的相对网址不匹配。
编辑:我对正则表达式有点了解:refiddle.com/gv8 现在我只需要PHP来动态创建任何给定页面的正则表达式。
答案 0 :(得分:2)
首先,无法预测将导致显示当前页面的有效URL的总列表,因为您无法预测(或控制)可能链接回页面的外部链接。如果有人使用TinyURL或bit.ly会怎么样?正则表达式不会削减芥末。
如果您需要确保链接不会导致同一页面,那么您需要测试它。这是一个基本概念:
每个页面都有唯一的ID。称之为序列号。它应该是持久的。序列号应嵌入页面中可预测的位置(尽管可能是不可见的)。
在创建页面时,PHP需要遍历每个页面的所有链接,访问每个页面,并确定链接是否解析为序列号与调用页面序列号匹配的页面
如果序列号不匹配,请将链接显示为链接。否则,显示其他内容。
显然,这将是一个艰巨的,资源密集的页面制作过程。你真的不想以这种方式解决你的问题。
考虑到您的“终极目标”评论,我怀疑您的最佳方法是近似。以下是一些策略......
第一个选项也是最简单的。如果您正在构建一个USUALLY以一种格式创建链接的内容管理系统,那么只需支持该格式。维基百科的方法之所以有效,是因为[[link]]
是他们生成的东西,所以他们知道它是如何格式化的。
第二次更像是你的问题所带走的方向。 URL的元素是“协议”,“主机”,“路径”和“查询字符串”。您可以将它们分解为正则表达式,并且可能使其正确。您已经声明要打算忽略查询字符串。所以...从'((https?:)?//(www\.)?example\.com)?' . $_SERVER['SCRIPT_NAME']
开始并添加结尾以适应。其他答案已经帮到你了。
第三个选项相当复杂,但可以让您对测试进行更细粒度的控制。与最后一个选项一样,您拥有各种URL元素。您可以使用正则表达式测试每个没有的有效性。例如:
$a = array(); // init array for valid URLs
// Step through each variation of our path...
foreach([$_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']] as $path) {
// Step through each variation of our host...
foreach ([$_SERVER['HTTP_HOST'], explode(".", $_SERVER['HTTP_HOST'])[0]] as $server) {
// Step through each variation of our protocol...
foreach (['https://','http://','//'] as $protocol) {
// Set the URL as a key.
$a[ $protocol . $server . $path ] = 1;
}
}
// Also for each path, step through directories and parents...
$apath=explode('/', $path); // turn the path into an array
unset($apath[0]); // strip the leading slash
for( $i = 1; $i <= count($apath); $i++ ) {
if (strlen($apath[$i])) {
$a[ str_repeat("../", 1+count($apath)-$i) . implode("/", $apath) ] = 1;
// add relative paths
}
unset($apath[$i]);
}
$a[ "./" . implode("/", $apath) ] = 1; // add current directory
}
然后简单地测试链接(减去其查询字符串)是否是数组中的索引。或调整适合;我相信你明白了。
我最喜欢这个第三种解决方案。
答案 1 :(得分:0)
实际上不需要正则表达式来剥离所有查询参数。您可以使用strok()
:
$url = strtok($url, '?');
并且,检查URL数组的输出:
$url_list = <<<URL
https://www.example.com/path/to/page/index.php?parameter=value
https://www.example.com/path/to/page/index.php
...
./?parameter=value
./
URL;
$urls = explode("\n", $url_list);
foreach ($urls as $url) {
$url = strtok($url, '?'); // remove everything after ?
echo $url."\n";
}
作为一项功能(可以改进):
function checkURLMatch($url, $url_array) {
$url = strtok($url, '?'); // remove everything after ?
if( in_array($url, $url_array)) {
// url exists array
return True;
} else {
// url not in array
return False;
}
}
答案 2 :(得分:0)
您可以使用此方法:
function checkURL($me, $s) {
$dir = dirname($me) . '/';
// you may need to refine this
$s = preg_filter(array('~^//~', '~/$~', '~\?.*$~', '~\.\./~'),
array('', '', '', $dir), $s);
// parse resulting URL
$url = parse_url($s);
var_dump($url);
// match parsed URL's path with self
return ($url['path'] === $me);
}
// your page's URL with stripped out .php
$me = str_replace('.php', '', $_SERVER['PHP_SELF']);
// assume this is the URL you are matching against
$s = '../page/';
// compare $me with $s
$ret = checkURL($me, $s);
var_dump($ret);
答案 3 :(得分:0)
由于我在过去的几天里得到了报酬,我不仅仅是坐在那里等待答案。我想出了一个在我的测试平台上工作的;其他人都在想什么?它感觉有点臃肿,但也感觉防弹。
调试回声留下,以防你想要回复一些东西。
global $debug;$debug = false; // toggle debug echoes and var_dumps
/**
* Returns a boolean indicating whether the given URL is the current one.
*
* @param $otherURL the other URL, as a string. Can be any URL, relative or canonical. Invalid URLs will not match.
*
* @return true iff the given URL points to the same place as the current one
*/
function isCurrentURL($otherURL)
{global $debug;
if($debug)echo"<!--\r\nisCurrentURL($otherURL)\r\n{\r\n";
if ($thisURL == $otherURL) // unlikely, but possible. Might as well check.
return true;
// BEGIN Parse other URL
$otherProtocol = parse_url($otherURL);
$otherHost = $otherProtocol["host"] or null; // if $otherProtocol["host"] is set and is not null, use it. Else, use null.
$otherDomain = explode(".", $otherHost) or $otherDomain;
$otherSubdomain = array_shift($otherDomain); // subdom only
$otherDomain = implode(".", $otherDomain); // domain only
$otherFilepath = $otherProtocol["path"] or null;
$otherProtocol = $otherProtocol["scheme"] or null;
// END Parse other URL
// BEGIN Get current URL
#if($debug){echo '$_SERVER == '; var_dump($_SERVER);}
$thisProtocol = $_SERVER["HTTP_X_FORWARDED_PROTO"]; // http or https
$thisHost = $_SERVER["HTTP_HOST"]; // subdom or subdom.domain.tld
$thisDomain = explode(".", $thisHost);
$thisSubdomain = array_shift($thisDomain); // subdom only
$thisDomain = implode(".", $thisDomain); // domain only
if ($thisDomain == "")
$thisDomain = $otherDomain;
$thisFilepath = $_SERVER["PHP_SELF"]; // /path/to/file.php
$thisURL = "$thisProtocol://$thisHost$thisFilepath";
// END Get current URL
if($debug)echo"Current URL is $thisURL ($thisProtocol, $thisSubdomain, $thisDomain, $thisFilepath).\r\n";
if($debug)echo"Other URL is $otherURL ($otherProtocol, $otherHost, $otherFilepath).\r\n";
$thisDomainRegexed = isset($thisDomain) && $thisDomain != null && $thisDomain != "" ? "(\." . str_replace(".","\.",$thisDomain) . ")?" : ""; // prepare domain for insertion into regex
// v this makes the last slash before index.php optional
$regex = "/^(($thisProtocol:)?\/\/$thisSubdomain$thisDomainRegexed)?" . preg_replace('/index\\\..+$/i','?(index\..+)?', str_replace(array(".", "/"), array("\.", "\/"), $thisFilepath)) . '$/i';
if($debug)echo "\r\nregex is $regex\r\nComparing regex against $otherURL";
if (preg_match($regex, $otherURL))
{
if($debug)echo"\r\n\tIt's a match! Returning true...\r\n}\r\n-->";
return true;
}
else
{
if($debug)echo"\r\n\tOther URL is NOT a fully-qualified URL in this subdomain. Checking if it is relative...";
if($otherURL == $thisFilepath) // somewhat likely
{
if($debug)echo"\r\n\t\tOhter URL and this filepath are an exact match! Returning true...\r\n}\r\n-->";
return true;
}
else
{
if($debug)echo"\r\n\t\tFilepath is not an exact match. Testing against regex...";
$regex = regexFilepath($thisFilepath);
if($debug)echo"\r\n\t\tNew Regex is $regex";
if($debug)echo"\r\n\t\tComparing regex against $otherFilepath...";
if (preg_match($regex, $otherFilepath))
{
if($debug)echo"\r\n\t\t\tIt's a match! Returning true...\r\n}\r\n-->";
return true;
}
}
}
if($debug)echo"\r\nI tried my hardest, but couldn't match $otherURL to $thisURL. Returning false...\r\n}\r\n-->";
return false;
}
/**
* Uses the given filepath to create a regex that will match it in any of its relative representations.
*
* @param $path the filepath to be converted
*
* @return a regex that matches a all relative forms of the given filepath
*/
function regexFilepath($path)
{global $debug;
if($debug)echo"\r\nregexFilepath($path)\r\n{\r\n";
$filepathArray = explode("/", $path);
if (count($filepathArray) == 0)
throw new Exception("given parameter not a filepath: $path");
if ($filepathArray[0] == "") // this can happen if the path starts with a "/"
array_shift($filepathArray); // strip the first element off the array
$isIndex = preg_match("/^index\..+$/i", end($filepathArray));
$filename = array_pop($filepathArray);
if($debug){var_dump($filepathArray);}
$ret = '';
foreach($filepathArray as $i)
$ret = "(\.\.\/$ret$i\/)?"; // make a pseudo-recursive relative filepath
if($debug)echo "\r\n$ret";
$ret = preg_replace('/\)\?$/', '?)', $ret); // remove the last '?' and add one before the last '\/'
if($debug)echo "\r\n$ret";
$ret = '/^' . ($ret == '' ? '\.\/' : "((\.\/)|$ret)") . ($isIndex ? '(index\..+)?' : str_replace('.', '\.', $filename)) . '$/i'; // if this filepath leads to an index.php (etc.), then that filename is implied and irrelevant.
if($debug)echo'\r\n}\r\n';
}
这似乎与我需要匹配的所有内容相匹配,而不是我不需要它。