摘要:在ThinkPHP学习过程中使用系统Controller类库提供的success()/error()跳转方法,官方《ThinkPHP3.2.3快速入门 》文档、《ThinkPHP3.2.3完全开发手册 》文档,都没有讲清楚跳转其它控制器/操作的原理,这里将从TP的源码讲解跳转过程,明确问题所在,以及怎样修正。
标签的href属性
href 属性的值可以是任何有效文档的相对或绝对 URL,包括片段标识符和 JavaScript 代码段。如果用户选择了 标签中的内容,那么浏览器会尝试检索并显示 href 属性指定的 URL 所表示的文档,或者执行 JavaScript 表达式、方法和函数的列表。
举例解释:
a标签href属性实例 | 说明 |
---|---|
href=’http://www.baidu.com’ | 点击后直接跳转至百度首页 |
href=’test.html’ | 跳转至文档当前目录下test.html文件 |
href=’javascript:history.back(-1);’ | 运行JS代码,返回上页 |
href=’’ | 返回操作前页面 |
下面要讲到的TP的Controller类库的success/error方法其实是在模板中使用了第二种方法来跳转的。
先说问题出在哪里,当我在Index控制器里面希望跳转至Login控制器里面,按照官方文档的说法是这样做:
$this->error(‘马上跳转!’,’Login/index’,1);
可事实并不是这样
URL会从
http://localhost/ThinkPHP_Test/index.php/Home/Index/index
跳转至
http://localhost/ThinkPHP_Test/index.php/Home/Index/Login/index
这显然不是我想要的,现将问题追踪的思路列举如下,并提出修正方法。
上我自己的测试代码。
//IndexController.class.php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index(){
$this->display();
}
public function click(){
$this->error('马上跳转!','../Login/index',1);
//$this->redirect('Login/index');
}
}
?>
//这是模板文件Index/index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index indextitle>
head>
<body>
<h1>Index indexh1>
<a href="{:U('Index/click')}">跳转a>
<br/>
<a href="../Login/index">不用U的跳转a>
body>
html>
//LoginController.class.php
namespace Home\Controller;
use Think\Controller;
class LoginController extends Controller{
public function index(){
$this->display();
}
}
?>
//这是模板文件Login/index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login indextitle>
head>
<body>
<h1>Login indexh1>
body>
html>
下面来做说明:
1).TP的配置声明:
2).运行TP
http://localhost/ThinkPHP_Test/index.php/Home/Index/index
3).点击“跳转”链接
当前页面源码里面可以查看到,这个“跳转”链接的href已经被ThinkPHP模板引擎替换为:
/ThinkPHP_Test/index.php/Home/Index/click.html
说明U()方法是将’Index/click’这样的字符串处理成了URL绝对地址,这样的访问是最安全的
4).Index控制器的click方法被调用
执行跳转命令。
官方文档是这样写的:
$this->error(‘马上跳转!’,’Login/index’,1);
下面我们用这种错误的用法来trace一下TP的源码,看看会出现什么问题
5).调用Think\Controller类库里面的error方法
把error方法源码贴上:
这里说明:success/error方法都调用这个dispatchJump,方法,不同的是success方法传入的$status参数为1,error方法传入的$status参数为0.
private function dispatchJump($message,$status=1,$jumpUrl='',$ajax=false) {
if(true === $ajax || IS_AJAX) {// AJAX提交
$data = is_array($ajax)?$ajax:array();
$data['info'] = $message;
$data['status'] = $status;
$data['url'] = $jumpUrl;
$this->ajaxReturn($data);
}
if(is_int($ajax)) $this->assign('waitSecond',$ajax);
if(!empty($jumpUrl)) $this->assign('jumpUrl',$jumpUrl);
// 提示标题
$this->assign('msgTitle',$status? L('_OPERATION_SUCCESS_') : L('_OPERATION_FAIL_'));
//如果设置了关闭窗口,则提示完毕后自动关闭窗口
if($this->get('closeWin')) $this->assign('jumpUrl','javascript:window.close();');
$this->assign('status',$status); // 状态
//保证输出不受静态缓存影响
C('HTML_CACHE_ON',false);
if($status) { //发送成功信息
$this->assign('message',$message);// 提示信息
// 成功操作后默认停留1秒
if(!isset($this->waitSecond)) $this->assign('waitSecond','1');
// 默认操作成功自动返回操作前页面
if(!isset($this->jumpUrl)) $this->assign("jumpUrl",$_SERVER["HTTP_REFERER"]);
$this->display(C('TMPL_ACTION_SUCCESS'));
}else{
$this->assign('error',$message);// 提示信息
//发生错误时候默认停留3秒
if(!isset($this->waitSecond)) $this->assign('waitSecond','3');
// 默认发生错误的话自动返回上页
if(!isset($this->jumpUrl)) $this->assign('jumpUrl',"javascript:history.back(-1);");
$this->display(C('TMPL_ACTION_ERROR'));
// 中止执行 避免出错后继续执行
exit ;
}
}
AJAX提交的方式我们不看。
可以看到’Login/index’这个字符串是直接赋值给了$jumpUrl,可以看到在第10行赋值给模板了,名字也是jumpUrl。
6).再去看看TP的默认模板文件是怎么处理的
这里只摘出关键代码:
页面自动 <a id="href" href="">跳转a>
等待时间: <b id="wait"> echo($waitSecond); ?>b>
<script type="text/javascript">
(function(){
var wait = document.getElementById('wait'),href = document.getElementById('href').href;
var interval = setInterval(function(){
var time = --wait.innerHTML;
if(time <= 0) {
location.href = href;
clearInterval(interval);
};
}, 1000);
})();
script>
’Login/index’字符串直接赋值给href属性,在等待$waitSecond后,将页面定位到href属性值,这里出了问题,当把’Login/index’直接赋值给href属性的时候,浏览器会将现在的URL:
http://localhost/ThinkPHP_Test/index.php/Home/Index/index
跳转成:
http://localhost/ThinkPHP_Test/index.php/Home/Index/Login/index
这样就达不到我们的要求了
所以正确的做法是:
在需要跳转的时候这么做:
$this->error(‘马上跳转!’,’../Login/index’,1);
相当于向上去一个目录,替换掉Index控制器,这样才能跳转成功。
<完>