PHP URL参数重定向-使用通配符/正则表达式

2022-04-09 00:00:00 regex wildcard parameters redirect php

我最近found this solution做php url变量到头位置重定向。

与用于批量重定向的htaccess相比,它更易于管理,然而,我接下来要解决的一件事是,我如何使用正则表达式来实现您可以使用htaccess在请求/(.*)去往目的地/$1的情况下所能做的事情。

我的理解是您使用了preg_Match或preg_place或其他什么。我如何才能做到下面这样的事情,如果可能的话,最好是这样简短。(我知道这是错误的,顺便说一句,只是为了说明原因)。

preg_match($redirects['request(.*)'] = "$domain/destination/1");

基本上,假设我要将doma.in/pics重定向到domain.tld/pictures,我让htaccess将其重定向到doma.in/index?req=pics,其中index是脚本文件,req是使用的参数。

然后使用脚本,我有一行类似$redirects['pics'] = "$domain/pictures";的代码,其中$domain变量绑定到http://domain.tld

这很好用,但是我想用正则表达式更进一步,将pics/*stuff*,也就是doma.in/index?req=pics/*stuff*发送到$domain/pictures/*stuff*

这里有一个使用此脚本执行多次重定向的示例。

$redirects['request'] = "$domain/dest";
$redirects['request2'] = "$domain/dest2";
$redirects['request3'] = "$domain/dest3";

尽管我已经链接了顶部的帖子,我得到了我正在使用的脚本,但以下是代码:

if(isset($_GET['req']) && isset($redirects[$_GET['req']])) {
    $loc = htmlspecialchars($redirects[$_GET['req']]);
    header("Location: " . $loc);
    exit();
}
    header("Location: $domain");

我在包含的文件中包含了$reDirects行,这些行包含在这上面。


解决方案

已更新脚本

我彻底修改了这个脚本,现在我更多地意识到这是我们在这里做的所有数组,每个重定向都只是添加到它上面。此外,我还学到了更多关于函数的知识,现在它非常便携,同时还可以做更多的事情。

这里是完整的新脚本。和前一次相比,这是一只野兽,但前一次太乱了。

function redirects($config) {                           // Create a redirects(); function with the inputted array set as $config

    // Config Variables
    $req = $config['param'];                                // Assign $req to the $config param value 
    $dir = $config['dir'];                                  // Assign $dir to the $config dir value
    $domain = $config['domain'];                            // Assign $domain to the $config domain value
    $_404   = $config['404'];                               // Assign $_404 to this $config 404 value

    $_referer = $config['referer'];                         // Assign $referer_ to the referer value
    $referer = 'referer='.$_referer;                        // Assign $referer to the full referer param

    if (isset($_GET[$_referer])) {
        if ($_GET[$_referer] == $_referer) {                    // If this script's referer exists,
            echo "Do not loop back to the script!";             // Throw a warning
            exit();                                             // & exit
        }                                                       // Otherwise continue
    }

    if (isset($_GET[$req]) && !empty($_GET[$req])) {                // If a req parameter exists & isn't empty, continue
        $req = $_GET[$req];                                         // Assign $req to $_GET[$req]

        // Create the arrays
        $redirects = $wildcards = $halfwilds = array();             // Create the arrays needed, so if there's not at least one redirect done, which would be what creates the array otherwise, there won't be a 500 error due to it. 

        // Redirect includes
        foreach (glob($dir) as $filename) {                         // Search for all redirect php files at $dir location
            include $filename;                                      // Include those files
        }

        // Leading & Trailing Slashes
        $req = '/'.trim($req, '/').'/';                             // Add a leading & trailing slash to $req param if non existent
            
        function redir_editkeys($array) {                           // Create an editkeys(); function and pass the specified array as $array
            $keys = array_keys($array);                                 // Extract the keys of the array as values of $keys array
            foreach ($keys as &$value) {
                $value = '/'.trim($value, '/').'/';                     // Add a leading & trailing slash to $keys values if non existent
            }
            return array_combine($keys, $array);                        // Replace the original array's keys with the modified keys & return
        }

        // Referer
        function referer($redir, $referer, $domain) {               // Create a referer(); function and pass to it $redir, $referer, & $domain.
            if (strpos($redir, $domain) !==false) {                     // Using $domain, check $redir for a match.
                $redir = $redir.'?'.$referer;                           // If there's a match, add the referer param
            }   return $redir;                                          // Return the edited $redir, or if no match, return without editing
        }

        // Redirects
        $redirects = redir_editkeys($redirects);                    // Edit $redirects keys using editkeys();
        if (isset($redirects[$req])) {                              // Check that $redirects[$req] exists
            $redir = $redirects[$req];                              // Assign $redir to $redirects[$req];
            $redir = referer($redir, $referer, $domain);            // If it does, run referer();
            header("Location: $redir");                             // & Redirect
            exit();                                                 
        }

        // Wildcards
        $wildcards = redir_editkeys($wildcards);                    // Edit $wildcards keys using editkeys();
        foreach ($wildcards as $key => $value) {                    // Assign variables to $wildcards keys & values
            if (strpos($req, $key) !== false) {                     // Using $key, check $req for a match
                $wild = substr($req, strlen($key));                 // Extract everything after the match as $wild
                $req = substr($req, 0, strlen($key));               // Extract the matching part at the beginning as $req
                if (isset($wildcards[$req])) {                      // Check that $wildcards[$req] exists
                    $redir = $wildcards[$req];                      // Assign $redir to $wildcards[$req]
                    $redir = $redir.$wild;                          // Attach $wild onto $redir
                    $redir = referer($redir, $referer, $domain);    // If it does, run referer();
                    header("Location: $redir");                     // & Redirect
                    exit();             
                }
            }
        }

        // Half Wilds
        $halfwilds = redir_editkeys($halfwilds);                    // Edit $halfwilds keys using editkeys();
        foreach ($halfwilds as $key => $value) {                    // Assign variables to $halfwilds keys & values
            if (strpos($req, $key) !== false) {                     // Using $key, check $req for a match
                $req = substr($req, 0, strlen($key));               // Extract the matching part at the beginning as $req
                if (isset($halfcards[$req])) {                      // Check that $halfwilds[$req] exists
                    $redir = $halfwilds[$req];                      // Assign $redir to $halfwilds[$req]
                    $redir = referer($redir, $referer, $domain);    // If it does, run referer();
                    header("Location: $redir");                     // & Redirect
                    exit();             
                }
            }
        }

        // 404
        $req = "req=".trim($req,'/')."&";                           // Attach the param name to the $req value for the 404 redirect if it's not empty and remove the slashes.
    }   
    else { $req = ''; }                                         // If no req param, or if empty, unset $req
    header("Location: ".$domain.$_404."?".$req.$referer);       // If there's no match, redirect to this location.
}

我们按以下条件调用此脚本:

redirects(
    array( // Config
        'param'   => 'req',                   //  Param name                 ||  Set the name of the url param. Here it is "req".
        'dir'     => 'redirects/*/*.php',     //  Redirect file(s) location  ||  Set the location to look for the file(s) you store your redirects. Here I have my files in sub folders of a rediects folder. Do not put a leading slash infront, use "../" etc as it must be relative, not absolute..
        'domain'  => 'http://domain.tld',     //  Your domain                ||  Set your website's domain name here. This'll be used to check whether redirects go to it and if the referer is needed.
        '404'     => '/',                     //  404 location               ||  Set the location 404s will go to. Hereit is just my homepage, so I've put "/".
        'referer' => 'redirector'             //  Referer param              ||  Set the value of the referer param that will be used in redirects to the same site so we can stop 404s resulting in a loop.
    )
);

要执行简单的重定向,我们可以这样做:

$redirects['request1'] = "$domain/dest1";
$redirects['request2'] = "$domain/dest2";

要使用通配符,我们可以这样做:(确保在末尾添加尾随斜杠,除非它是目标,如查询字符串)

$wildcards['request3'] = "$domain/dest3/";
$wildcards['request4'] = "$domain/dest4/";

和我还添加了半个通配符,所以如果您只想将request5/anypath发送到$domain/dest5,而不是dest5/anypath,我们可以这样做:

$halfwilds['request5'] = "$domain/dest5";
$halfwilds['request6'] = "$domain/dest6";

让我们将其分成块,看看每个部分都在做什么:


  1. 配置:

首先,我们使用$config数组作为参数启动redirects();函数。然后我们将变量分配给每个设置。

function redirects($config) {                           // Create a redirects(); function with the inputted array set as $config

    // Config Variables
    $req = $config['param'];                                // Assign $req to the $config param value 
    $dir = $config['dir'];                                  // Assign $dir to the $config dir value
    $domain = $config['domain'];                            // Assign $domain to the $config domain value
    $_404   = $config['404'];                               // Assign $_404 to this $config 404 value

    $_referer = $config['referer'];                         // Assign $referer_ to the referer value
    $referer = 'referer='.$_referer;                        // Assign $referer to the full referer param

我们有两个用于Referer的变量,一个用于Referer值,另一个作为完整参数加入以在重定向中使用。


  1. 推荐人检查

我首先检查请求中是否存在此脚本的引用参数,只需说"Do not loop back to the script!",然后exit();脚本。

    if (isset($_GET[$_referer])) {
        if ($_GET[$_referer] == $_referer) {                    // If this script's referer exists,
            echo "Do not loop back to the script!";             // Throw a warning
            exit();                                             // & exit
        }                                                       // Otherwise continue
    }

  1. 数组和包含

接下来,如果请求中的Referer参数没有匹配项,当请求参数存在且不为空时,我们打开一个if,创建空数组,然后包括我们在配置中声明的放置重定向的位置。

    if (isset($_GET[$req]) && !empty($_GET[$req])) {                // If a req parameter exists & isn't empty, continue
        $req = $_GET[$req];                                         // Assign $req to $_GET[$req]

        // Create the arrays
        $redirects = $wildcards = $halfwilds = array();             // Create the arrays needed, so if there's not at least one redirect done, which would be what creates the array otherwise, there won't be a 500 error due to it. 

        // Redirect includes
        foreach (glob($dir) as $filename) {                         // Search for all redirect php files at $dir location
            include $filename;                                      // Include those files
        }

根据我对php工作方式的理解,将Includes放在IF中实际上有一个好处,即如果不满足条件,则甚至不会处理Includes。


  1. 前导&;尾随斜杠

对于前面的脚本,我们必须确保在重定向键的末尾添加了斜杠,并在参数末尾添加了斜杠向脚本发送请求。让我们通过使用trim();来消除这样做的需要,因为如果它存在,则将其与添加回来混合在一起,并加上一个前导斜杠,以确保当我们想要查找‘Dog/’时,strpos();不会与‘CAT-Dog/’匹配。

        // Leading & Trailing Slashes
        $req = '/'.trim($req, '/').'/';                             // Add a leading & trailing slash to $req param if non existent
            
        function redir_editkeys($array) {                           // Create an editkeys(); function and pass the specified array as $array
            $keys = array_keys($array);                                 // Extract the keys of the array as values of $keys array
            foreach ($keys as &$value) {
                $value = '/'.trim($value, '/').'/';                     // Add a leading & trailing slash to $keys values if non existent
            }
            return array_combine($keys, $array);                        // Replace the original array's keys with the modified keys & return
        }

这里我们创建了一个名为editkeys();的函数,它为您传递给它的任何数组的键创建一个新的数组,执行Foreach来修改这些键,然后将它们放回原始数组中,替换原始的键。


  1. 目标域检查是否需要引用参数

如果我们将目的地的404请求发送到脚本,我们希望停止以404结尾的到该目的地的重定向循环。我们可以只添加Referer参数,但这可能会根据目的地的网站配置对查询字符串的处理而出现问题。因此,让我们做一个函数来检查传递的目的地是否去往$domain,如果为真,则向其添加Referer并返回它。

        // Referer
        function referer($redir, $referer, $domain) {               // Create a referer(); function and pass to it $redir, $referer, & $domain.
            if (strpos($redir, $domain) !==false) {                     // Using $domain, check $redir for a match.
                $redir = $redir.'?'.$referer;                           // If there's a match, add the referer param
            }   return $redir;                                          // Return the edited $redir, or if no match, return without editing
        }

  1. 重定向

下一步是将重定向组合在一起。首先,我们有简单的重定向。首先,我们通过editkeys();函数运行$redirects数组,然后它检查匹配的键是否在数组中,在目标上运行上面的referer();函数,然后重定向到它。

        // Redirects
        $redirects = redir_editkeys($redirects);                    // Edit $redirects keys using editkeys();
        if (isset($redirects[$req])) {                              // Check that $redirects[$req] exists
            $redir = $redirects[$req];                              // Assign $redir to $redirects[$req];
            $redir = referer($redir, $referer, $domain);            // If it does, run referer();
            header("Location: $redir");                             // & Redirect
            exit();                                                 
        }

第二个是通配符。我们通过editkeys();函数运行$wildcards数组,对该数组运行foreach();以查找匹配键,将匹配后的所有内容获取为$wild,仅获取匹配的$req,检查$req值是否作为数组中的键存在,获取该键的目的地,附加$WildOn,然后运行检查以确定是否需要Referer、重定向和bam。

        // Wildcards
        $wildcards = redir_editkeys($wildcards);                    // Edit $wildcards keys using editkeys();
        foreach ($wildcards as $key => $value) {                    // Assign variables to $wildcards keys & values
            if (strpos($req, $key) !== false) {                     // Using $key, check $req for a match
                $wild = substr($req, strlen($key));                 // Extract everything after the match as $wild
                $req = substr($req, 0, strlen($key));               // Extract the matching part at the beginning as $req
                if (isset($wildcards[$req])) {                      // Check that $wildcards[$req] exists
                    $redir = $wildcards[$req];                      // Assign $redir to $wildcards[$req]
                    $redir = $redir.$wild;                          // Attach $wild onto $redir
                    $redir = referer($redir, $referer, $domain);    // If it does, run referer();
                    header("Location: $redir");                     // & Redirect
                    exit();             
                }
            }
        }

然后我们有一半的通配符,它们的作用大致相同。我自己真的没有任何真正的理由这样做,但肯定有原因,比如你已经摆脱了整个投资组合图库,想要发送请求到那个和子请求的图像等回到投资组合,这将完成工作。因此它是脚本中包含的一部分。

        // Half Wilds
        $halfwilds = redir_editkeys($halfwilds);                    // Edit $halfwilds keys using editkeys();
        foreach ($halfwilds as $key => $value) {                    // Assign variables to $halfwilds keys & values
            if (strpos($req, $key) !== false) {                     // Using $key, check $req for a match
                $req = substr($req, 0, strlen($key));               // Extract the matching part at the beginning as $req
                if (isset($halfcards[$req])) {                      // Check that $halfwilds[$req] exists
                    $redir = $halfwilds[$req];                      // Assign $redir to $halfwilds[$req]
                    $redir = referer($redir, $referer, $domain);    // If it does, run referer();
                    header("Location: $redir");                     // & Redirect
                    exit();             
                }
            }
        }

我确实想试着做一个函数来保存这个大量重复的代码,但foreach();太笨拙了。


  1. 404

然后,我们有404重定向,它将任何与请求参数不匹配的请求发回给脚本,或者如果没有请求参数或它是空的,则只与Referer参数匹配。

        // 404
        $req = "req=".trim($req,'/')."&";                           // Attach the param name to the $req value for the 404 redirect if it's not empty and remove the slashes.
    }   
    else { $req = ''; }                                         // If no req param, or if empty, unset $req
    header("Location: ".$domain.$_404."?".$req.$referer);       // If there's no match, redirect to this location.
}

这就是剧本。


现在,将死请求发送到脚本。

apache/litespeed/oplitespeed(Htaccess)


在您的htaccess底部添加以下内容:

############# Rewrite dead requests to redirector with uri as query
RewriteCond %{QUERY_STRING} !referer=redirector [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /redirect.php?req=$1 [R=301,L]
#############

Nginx


将此代码添加到服务器的(Vhost)conf:

############# Send dead requests to redirector with uri as query
error_page 404 = @redirects;
location @redirects {
    if ($query_string !~ referer=redirector {
        rewrite ^/(.*)(.php?) /redirect.php?req=$1 redirect;
    }
}
#############

我现在使用nginx,不再使用OpenLitespeed,因为出现了一个奇怪的问题,而nginx根本没有这个问题。所以,弄清楚Nginx Conf的做事方式是很有趣的。我注意到的一件事是,通过重写没有扩展的扩展请求,发送到其他地方的请求在末尾添加了.php。所以我在重写时排除了这个扩展名。与尝试在if中设置忽略现有文件/文件夹相比,在error_page 404的位置执行这些通配符重定向要容易得多。


正在目标上删除请求的引用参数。


在尝试对nginx执行与我对htaccess所做的相同操作时,删除引用参数,这是我尝试尝试的乐趣所在,但我意识到这无论如何都是一个有一点缺陷的解决方案,因为它删除了整个查询。 取而代之的是使用PHP,此函数就是为了实现这一点,删除了单个参数:

function strip_param($url,$param) {
  if (isset($_GET[$param])) {                     // Check that param exists
    $base_url = strtok($url,'?');                 // Get the base url
    $parsed_url = parse_url($url);                // Parse it
    $query = $parsed_url['query'];                // Get the query string
    parse_str($query,$params);                    // Convert parameters into array
    unset($params[$param]);                       // Delete the one you want
    $new_query = http_build_query($params);       // Rebuild query string
    $new_url = $base_url.'?'.$new_query;          // Rebuild the url
    $url = rtrim($new_url,'?');                   // If now no query, remove ending ?
    return header("Location: $url");              // Redirect
  } 
}

//  How to use
//
//  Place the above function anywhere you include above content of where you need it, such as a functions file.
//  The below, put this somewhere at the top before content.

// Strip params from requests
strip_param($webpage_URI,'referer');              // The $webpage_URI value is:    "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];

此函数我们可以give props to this person for。 这就是为什么我开始按照评论的方式克隆它。这是一种如此整洁、可读性强的做事方式。尽管我以前的剧本很短,但它并不整洁,也不是很甜蜜。但是,哦,我很高兴现在有了功能。非常推荐!!


再次感谢Mick提供的几条改进这一点的技巧。 我对500个错误有一点兴趣,因为当我决定将这一半通配符命名为$half_wildcards而不是$wildcards_half时,我搞砸了,我没有更新所有的通配符。我现在将其更改为$halfwilds,因为我突然想到它,这是与$wildcards$redirects相同的字符数,所以这是非常好的一致性。它也使它更具区分性,这反过来只会让事情变得不那么混乱。

这也带来了一个问题,如果有人只对$wildcards$redirects使用这个词,或者在我的情况下,两者都使用,但不是$halfwilds..如果不存在关联数组的重定向,则不会创建该数组,这将导致500。因此,我已经创建了空数组,因此它们将始终存在,不会导致错误。


更新了脚本,稍微移动了数组键变量赋值,如果您没有将它们设置为忽略未定义数组和变量等的警告,则它们不会向日志发送垃圾邮件。我现在已将这些日志设置为Critical,因为使用此脚本懒惰是令人讨厌的,但在这里不是这样。在意识到嵌套函数仍然很好地作为全局函数定义它们时,还将编辑键函数名称编辑为redir_editkeys。Grr php有它的怪癖。

相关文章