题
您是否曾经偶然发现过一个您认为很有价值但解释不正确的教程?这就是我的困境。我知道 本教程 有一些价值,但我就是无法得到它。
- 每个函数在哪里调用?
- 哪个函数应首先称为哪个功能,以及哪个函数以及哪个函数?
- 是否会在应用程序的所有文件中调用所有函数?
- 有谁知道治疗“后退按钮布鲁斯”的更好方法吗?
我想知道这是否会引发一些包括本文作者在内的良好对话。我特别感兴趣的部分是控制后退按钮,以防止按下后退按钮时将表单重复条目写入数据库。基本上,您希望在应用程序中执行脚本期间通过调用以下三个函数来控制后退按钮。本教程中并不清楚到底以什么顺序调用函数(参见上面的问题)。
通过使用我的ScriptNext函数执行所有正向运动。这是在当前脚本中调用的,以激活新脚本。
function scriptNext($script_id) // proceed forwards to a new script { if (empty($script_id)) { trigger_error("script id is not defined", E_USER_ERROR); } // if // get list of screens used in this session $page_stack = $_SESSION['page_stack']; if (in_array($script_id, $page_stack)) { // remove this item and any following items from the stack array do { $last = array_pop($page_stack); } while ($last != $script_id); } // if // add next script to end of array and update session data $page_stack[] = $script_id; $_SESSION['page_stack'] = $page_stack; // now pass control to the designated script $location = 'http://' .$_SERVER['HTTP_HOST'] .$script_id; header('Location: ' .$location); exit; } // scriptNext
当任何脚本完成其处理后,它通过调用我的脚本Previous函数来终止它。这将从堆栈数组的末尾删除当前脚本,并在数组中重新激活上一个脚本。
function scriptPrevious() // go back to the previous script (as defined in PAGE_STACK) { // get id of current script $script_id = $_SERVER['PHP_SELF']; // get list of screens used in this session $page_stack = $_SESSION['page_stack']; if (in_array($script_id, $page_stack)) { // remove this item and any following items from the stack array do { $last = array_pop($page_stack); } while ($last != $script_id); // update session data $_SESSION['page_stack'] = $page_stack; } // if if (count($page_stack) > 0) { $previous = array_pop($page_stack); // reactivate previous script $location = 'http://' .$_SERVER['HTTP_HOST'] .$previous; } else { // no previous scripts, so terminate session session_unset(); session_destroy(); // revert to default start page $location = 'http://' .$_SERVER['HTTP_HOST'] .'/index.php'; } // if header('Location: ' .$location); exit; } // scriptPrevious
每当激活脚本时,可以通过scriptnext或scriptprevious函数或由于浏览器中的返回按钮,它将调用以下函数以验证它是根据程序堆栈的内容和如果不是,请采取适当的措施。
function initSession() // initialise session data { // get program stack if (isset($_SESSION['page_stack'])) { // use existing stack $page_stack = $_SESSION['page_stack']; } else { // create new stack which starts with current script $page_stack[] = $_SERVER['PHP_SELF']; $_SESSION['page_stack'] = $page_stack; } // if // check that this script is at the end of the current stack $actual = $_SERVER['PHP_SELF']; $expected = $page_stack[count($page_stack)-1]; if ($expected != $actual) { if (in_array($actual, $page_stack)) {// script is within current stack, so remove anything which follows while ($page_stack[count($page_stack)-1] != $actual ) { $null = array_pop($page_stack); } // while $_SESSION['page_stack'] = $page_stack; } // if // set script id to last entry in program stack $actual = $page_stack[count($page_stack)-1]; $location = 'http://' .$_SERVER['HTTP_HOST'] .$actual; header('Location: ' .$location); exit; } // if ... // continue processing } // initSession
采取的操作取决于当前脚本是否存在程序堆栈中。有三种可能性:
- 当前脚本不在$ page_stack数组中,在这种情况下,不允许继续进行。相反,它被数组末尾的脚本所取代。
- 当前脚本在$ page_stack数组中,但这不是最后一个条目。在这种情况下,删除了数组中的所有以下所有条目。
- 当前脚本是$ page_stack数组中的最后一个条目。这是预期的情况。全饮酒!
解决方案
这是一个很好的讨论,但更重要的是,您应该研究 Post Redirect Get (PRG),也称为“Get after Post”。
http://www.theserverside.com/patterns/thread.tss?thread_id=20936
其他提示
如果你看不懂我的文章那么你应该仔细看看 图1 它描述了一个典型的场景,其中用户经过一系列屏幕——登录、菜单、列表、搜索、添加和更新。当我描述向前移动时,我的意思是当前屏幕暂停,同时激活新屏幕。当用户按下当前屏幕中的链接时会发生这种情况。当我将移动描述为“向后”时,我的意思是用户终止当前屏幕(通过按“退出”或“提交”按钮)并返回到上一个屏幕,该屏幕从中断处恢复处理。这可能包括合并在刚刚终止的屏幕中所做的任何更改。
这是维护独立于浏览器历史记录的页面堆栈至关重要的地方 - 页面堆栈由应用程序维护并用于验证所有请求。这些对于浏览器而言可能是有效的,但可能被应用程序识别为无效并进行相应处理。
页堆栈由两个函数维护:
- ScriptNext()用于处理Afforts运动,该运动在堆栈末端添加了一个新条目并激活新条目。
- ScriptPrevious()用于处理向后移动,该移动将从堆栈中删除最后一个条目并重新激活上一个条目。
现在以示例中的情况为例,其中用户导航到 LIST 屏幕的第 4 页,进入 ADD 屏幕,然后返回到 LIST 屏幕的第 5 页。ADD 屏幕中的最后一个操作是按 SUBMIT 按钮,该按钮使用 POST 方法将添加到数据库的详细信息发送到服务器,之后自动终止并返回到 LIST 屏幕。
因此,如果您在 LIST 屏幕的第 5 页中按下 BACK 按钮,浏览器历史记录将生成对 ADD 屏幕上最后一个操作(即 POST)的请求。就浏览器而言,这是一个有效的请求,但就应用程序而言,这不是一个有效的请求。应用程序如何判定请求无效?通过检查其页面堆栈。当 ADD 屏幕终止时,其条目将从页面堆栈中删除,因此对不在页面堆栈中的屏幕的任何请求始终可以被视为无效。在这种情况下,无效请求可以被重定向到堆栈中的最后一个条目。
因此,您的问题的答案应该是显而易见的:
- 问:每个函数在哪里调用?
- A:当用户选择向前浏览到新屏幕时,您可以调用ScriptNext()函数,并在用户终止当前屏幕时调用ScriptPrevious()函数。
- 问:哪个函数应首先称为哪个功能,以及哪个函数以及哪个函数?
- A:每个函数都会响应用户选择的操作,因此一次只使用一个功能。
- 问:应用程序中的所有文件中都会调用所有函数吗?
- A:所有功能都应在应用程序中的所有文件中可用,但仅在用户选择时调用。
如果您希望看到这些想法付诸实践,那么您可以下载我的 示例应用程序.
我特别感兴趣的部分是控制后退按钮,以防止按下后退按钮时将表单重复条目写入数据库。
你的前提是错误的。如果您将应用程序设计为 Web 应用程序,则不存在“后退按钮蓝调”之类的东西。如果您设计的应用程序没有任何服务器端状态,那么在第一种情况下您将永远不会遇到此问题。这种 Web 应用程序的简约方法效果非常好,通常称为 REST。
@特罗尔斯克恩
如果您设计的应用程序没有任何服务器端状态......
不可能设计一个没有状态的有效应用程序,否则您所拥有的只是一组彼此不通信的单独页面。由于在客户端上维护状态充满了问题,因此除了在服务器上维护状态之外没有有效的替代方案。
@马斯顿。
我用 post/redirect/get 解决了这个问题,但我相信该教程有一些优点,也许托尼·马斯顿可以详细说明。以及如何使用它来解决不一定是我的特定问题,但也许可以解决类似的问题。或者,如果这些函数实际上可以用于解决我的特定问题,那么它比 post/redirect/get 更好吗?我认为这将是对这里社区的一个很好的补充。
if ($_POST) {
process_input($_POST);
header("Location: $_SERVER[HTTP_REFERER]");
exit;
}