第二个技术包含动态URL地址。当使用动态URL地址时,解决方案有些不同。在这种情况下,不必再处理mod_rewrite,但是需要一个方法从动态URL地址中读取并删除会员ID,然后重定向到URL的这个版本。
在接下来的练习中,将创建一个名为aff_test.php的简单会员页面。当加载这个脚本时带有查询字符串参数aff_id,将保留会员ID的值,然后删除参数aff_id并完成一个301重定向到新的URL地址。
当删除查询字符串参数时,需要特别注意不要改变已经存在的参数,因此将创建一些特殊的函数来完成这些工作。
(一)在seophp/include文件夹下创建新文件url_utils.inc.php,然后键入以下代码:
<?php
function parse_query_string($query_string)
{
$items=explode("&",$query_string);
$qs_array=array();
foreach($items as $i)
{
$pair=explode("=",$i);
$qs_array[urldecode($pair[0])]=urldecode($pair[1]);
}
return $qs_array;
}
function remove_query_param($url,$param){
$tokens=explode('?',$url);
$url_path=$tokens[0];
$query_string=$tokens[1];
$qs_array=parse_query_string($query_string);
unset($qs_array[$param]);
$new_query_string='';
if($qs_array)
{
foreach($qs_array as $name=>$value)
{
$new_query_string.=($new_query_string==''?'?':'&').urlencode($name).'='.urlencode($value);
}
}
return $url_path.$new_query_string;
}
?>
(二)在seophp文件夹下,创建新脚本aff_test.php,并键入如下代码:
<?php
require_once 'include/url_utils.inc.php';
require_once 'include/config.inc.php';
session_start();
if(isset($_REQUEST['aff_id']))
{
$_SESSION['aff_id']=$_REQUEST['aff_id'];
$clean_url=SITE_DOMAIN.remove_query_param($_SERVER['REQUEST_URI'],'aff_id');
header('HTTP/1.1 301 moved Permanently');
header('Location: '.$clean_url);
}
echo 'You got here through affiliate:';
if(!isset($_SESSION['aff_id']))
{
echo '(no affiliate)';
}else{
echo $_SESSION['aff_id'];
}
?>
(三)加载http://seophp.example.com/aff_test.php
(四)加载http://seophp.example.com/aff_test.php?a=1&aff_id=34&b=2,期待被重定向到http://localhost/seophp/aff_test.php?a=1&b=2,并且会员ID被保留。
尽管需要编写一些代码,但这些代码很直观。没有包括任何URL地址重写,因此这次将只分析一览无余的PHP脚本。
aff_test.php脚本首先加载url_utils.inc.php和config.inc.php,并启动PHP会话:
<?php
require_once 'include/url_utils.inc.php';
require_once 'include/config.inc.php';
session_start();
然后验证查询字符串参数aff_id是否存在。如果存在,将它的值保存到用户会话中,并重定向到不包含aff_id的URL地址版本。来自URL程序库的函数remove_query_param()就在手边,因为它正确地保持了所有已有的参数:
if(isset($_REQUEST['aff_id']))
{
$_SESSION['aff_id']=$_REQUEST['aff_id'];
$clean_url=SITE_DOMAIN.remove_query_param($_SERVER['REQUEST_URI'],'aff_id');
header('HTTP/1.1 301 Moved Permanently');
header('Location:'.$clean_url);
}
最后,aff_test.php检查是否设置了会话参数aff_id。如果设置了,用户就会通过会员链接得到页面,而应用程序就要处理这个特定用户。为了达到简单练习的目的,在此可以只显示那个会员ID;如果页面是第一次加载且不带aff_id参数,则显示“(no affiliate)”。
echo 'You got here through affiliate:';
if(!isset($_SESSION['aff_id']))
{
echo '(no affiliate)';
}else{
echo $_SESSION['aff_id'];
}
?>
同样值得分析的是,在来自url_utils.inc.php的函数remove_query_param()里面发生了什么。在删除查询字符串参数之后采取的策略是,将它转换为关联数组,然后在删除了aff_id单元后再将它回构成查询字符串。
为了保持代码更清晰,专门有单独的函数将查询字符串转换为关联数组。这个函数名为parse_query_string(),接收查询字符串为参数,例如“a=1&aff_id=34&b=2”,并返回一个附带以下单元的关联数组:
'a'=>'1'
'aff_id'=>'34'
'b'=>'2'
这个函数开始时调用explode(),按照"&"字符分离查询字符串:
function parse_query_string($query_string){
$items=explode('&',$query_string);
如果$query_string是“a=1&aff_id=34&b=2”,那么items数组将有以下三个单元:
$items[0]=>'a=1'
$items[1]=>'aff_id=34'
$items[2]=>'b=2'
然后可以用foreach来遍历这里的每个单元,并且使用explode把每个单元按照“=”字符分享。利用查询数据可以建立关联数组$qs_array,它将在最后被返回。在保存每个查询字符串参数时了使用urldecode()函数,因为它可以编码特殊字符。
$qs_array=array();
foreach($items as $i){
$pair=explode('=',$i);
$qs_array[urldecode($pair[0])]=urldecode($pair[1]);
}
return $qs_array;
}
因为被remove_query_param()调用,下面看看parse_query_string()。它是一个有趣的函数——如乐所记得的,它从aff_test.php中被调用来从查询字符串中删除参数aff_id。
function remove_query_param($url,$param){
$tokens=explode('?',$url);
$url_path=$tokens[0];
$query_string=$tokens[1];
接下来,使用函数parse_query_string()来将查询字符串转换为关联数组:
$qs_array=parse_query_string($query_string);
如前所述,关联数组将如下所示:
'a'=>'1'
'aff_id'=>'34'
'b'=>'2'
关联数组的好处以及为什么它有时值得使用,指的是它们容易操作。现在已经将查询字符串分断为一个关联数组,只需使用unset()来删除希望删除的参数。在这里,$param将是'aff_id':
unset($qs_array[$param]);
现在有了一个关联数组,它包含了在新的查询字符串中需要的单元。因此把这些单元以查询字符串的格式又链接成字符串。注意,只有在关联数组不为空时才能这么做,并且要使用三元运算符来组成查询字符串:
$new_query_string='';
if($qs_array)
{
foreach($qs_array as $name=>$value)
{
$new_query_string.=($new_query_string==''?'?':'&').urlencode($name).'='.urlencode($value);
}
}
在生成新的查询字符串之后,再在最初被剥离出来的URL路径中加入它,返回如下结果:
return $url_path.$new_query_string;
}
希望这是个有趣的字符串处理练习。如果你喜欢正则表达式,肯定已经猜到它们能成功用于从查询字符串中剥离参数aff_id。如果觉得奇怪,请看这里:
function remove_query_param($url,$param)
{
$new_url=preg_replace("#aff_id=?(.*?(&|$))?#",'',$url);
if(substr($new_url,-1)=='?'||substr($new_url,-1)=="&")
{
$new_url=substr($new_url,0,strlen($new_url) -1);
}
return $new_url;
}
我们将把这个正则表达式作为作业留给读者去理解。