2

有没有偶然发现一个你觉得很有价值但解释不正确的教程?这就是我的困境。我知道本教程有一些价值,但我就是无法理解。

  1. 你在哪里调用每个函数?
  2. 应该首先调用哪个函数,然后调用哪个函数,然后调用哪个函数?
  3. 是否会在应用程序的所有文件中调用所有函数?
  4. 有谁知道治疗“后退按钮忧郁症”的更好方法?

我想知道这是否会引发一些包括文章作者在内的良好对话。我特别感兴趣的部分是控制后退按钮,以防止在按下后退按钮时将重复条目输入数据库。基本上,您希望通过在应用程序中执行脚本期间调用以下三个函数来控制后退按钮。教程中并不清楚调用函数的确切顺序(参见上面的问题)。

所有向前移动都是使用我的 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

当任何脚本完成其处理时,它通过调用我的 scriptPrevious 函数终止。这将从堆栈数组的末尾删除当前脚本并重新激活数组中的前一个脚本。

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函数,也可以是因为浏览器中的BACK按钮,它会调用下面的函数根据程序栈的内容来验证它是当前脚本,并如果不是,请采取适当的措施。

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 数组中的最后一个条目。这是预期的情况。全场畅饮!
4

6 回答 6

9

这是一个很好的讨论,但更重要的是,您应该研究 Post Redirect Get (PRG),也称为“Get after Post”。

http://www.theserverside.com/patterns/thread.tss?thread_id=20936

于 2009-01-09T00:41:08.903 回答
3

如果您不理解我的文章,那么您应该仔细查看图 1,该图描述了用户通过一系列屏幕的典型场景——登录、菜单、列表、搜索、添加和更新。当我描述 FORWARDS 的移动时,我的意思是当前屏幕被暂停,而新屏幕被激活。当用户按下当前屏幕中的链接时,就会发生这种情况。当我将移动描述为 BACKWARDS 时,我的意思是用户终止当前屏幕(通过按下 QUIT 或 SUBMIT 按钮)并返回到前一个屏幕,从中断处继续处理。这可能包括合并在刚刚终止的屏幕中所做的任何更改。

这是维护独立于浏览器历史的页面堆栈至关重要的地方 - 页面堆栈由应用程序维护并用于验证所有请求。就浏览器而言,这些可能是有效的,但可能会被应用程序识别为无效并进行相应处理。

页栈由两个函数维护:

  • scriptNext() 用于处理 FORWARDS 移动,它在堆栈末尾添加一个新条目并激活新条目。
  • scriptPrevious() 用于处理 BACKWARDS 移动,它从堆栈中删除最后一个条目并重新激活前一个条目。

现在以示例中的情况为例,用户导航到 LIST 屏幕的第 4 页,进入 ADD 屏幕,然后返回到 LIST 屏幕的第 5 页。ADD 屏幕中的最后一个操作是按下 SUBMIT 按钮,该按钮使用 POST 方法将详细信息发送到服务器,这些信息已添加到数据库中,之后它自动终止并返回到 LIST 屏幕。

因此,如果您在 LIST 屏幕的第 5 页中按下 BACK 按钮,浏览器历史记录将生成对 ADD 屏幕上最后一个操作的请求,即 POST。就浏览器而言,这是一个有效的请求,但就应用程序而言不是。应用程序如何确定请求无效?通过检查其页面堆栈。当 ADD 屏幕被终止时,它的条目从页面堆栈中删除,因此任何对不在页面堆栈中的屏幕的请求总是被视为无效。在这种情况下,可以将无效请求重定向到堆栈中的最后一个条目。

因此,您的问题的答案应该是显而易见的:

  • 问:你在哪里调用每个函数?
  • 答:当用户选择向前导航到新屏幕时调用 scriptNext() 函数,当用户终止当前屏幕时调用 scriptPrevious() 函数。
  • 问:应该首先调用哪个函数,然后调用哪个函数,然后调用哪个函数?
  • 答:每个函数都是根据用户选择的操作调用的,因此一次只使用一个函数。
  • 问:是否会在应用程序的所有文件中调用所有函数?
  • 答:所有函数都应该在应用程序的所有文件中都可用,但只有在用户选择时才调用。

如果您希望看到这些想法付诸实施,那么您可以下载我的示例应用程序

于 2009-01-10T09:02:00.133 回答
0

我特别感兴趣的部分是控制后退按钮,以防止在按下后退按钮时将重复条目输入数据库。

你的前提是错误的。如果您将应用程序设计为 Web 应用程序,则没有“Back Button Blues”之类的东西。如果您在没有任何服务器端状态的情况下设计应用程序,那么您将永远不会在第一种情况下遇到此问题。这种 Web 应用程序的简约方法非常有效,通常称为 REST。

于 2009-01-09T21:34:18.477 回答
0

@特罗尔斯克

如果您设计的应用程序没有任何服务器端状态......

设计一个没有状态的有效应用程序是不可能的,否则你所拥有的只是一个不相互通信的单个页面的集合。由于在客户端上维护状态充满了问题,因此除了维护服务器上的状态外,没有其他有效的选择。

于 2009-01-10T07:47:55.883 回答
0

@马斯顿。

我用 post/redirect/get 解决了这个问题,但我相信本教程有一些优点,也许 Tony Marston 可以详细说明。以及如何使用它来解决不一定是我的特定问题,但也许是类似的问题。或者如果这些功能实际上可以用于解决我的特定问题,它比发布/重定向/获取更好。我认为这将是对这里社区的一个很好的补充。

于 2009-01-10T08:06:56.750 回答
0
if ($_POST) {
    process_input($_POST);
    header("Location: $_SERVER[HTTP_REFERER]");
    exit;
}
于 2009-01-10T09:04:59.877 回答