【问题标题】:Curing the "Back Button Blues"治愈“后退按钮蓝调”
【发布时间】:2010-09-30 09:49:30
【问题描述】:

曾经偶然发现一个您认为很有价值但解释不正确的教程?这就是我的困境。我知道THIS TUTORIAL 有一些价值,但我就是不明白。

  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

当任何脚本完成时 处理它通过调用 my 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

每当一个脚本被激活时, 既可以通过脚本下一个 或 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 数组中。这是 预期的情况。全部喝完 圆!

【问题讨论】:

    标签: php forms button back


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      如果您不理解我的文章,那么您应该仔细查看figure 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() 用户终止时的功能 当前屏幕。
      • 问:应该调用哪个函数 第一个,哪个下一个,哪个 第三个?
      • A: 每个函数都被调用 对所选择的动作的响应 用户,所以只使用了一个功能 一次。
      • 问:是否会调用所有函数 应用程序中的所有文件?
      • A:所有功能都应该可用 在应用程序的所有文件中,但 仅在用户选择时调用。

      如果您希望看到这些想法付诸实践,那么您可以下载我的sample application

      【讨论】:

      • 我想我理解得更好了。迷人的概念。感谢您的工作和回复。
      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        @troelskn

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

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

        【讨论】:

        • 嗨,很高兴你能进来。我按照这里的建议解决了我的 post/redirect/get 模式问题,但请你详细说明一下教程。我非常想更好地理解它。
        • 我不同意你的观点,但根据经验,我相当肯定我们无法以有意义的方式讨论这个问题。可以说除了你还有其他的观点,让人们自己去寻找答案。
        【解决方案5】:

        @马斯顿。

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

        【讨论】:

          【解决方案6】:
          if ($_POST) {
              process_input($_POST);
              header("Location: $_SERVER[HTTP_REFERER]");
              exit;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-01-09
            • 2020-02-26
            • 1970-01-01
            • 2021-12-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多