为什么我的请求在 php 代码中以状态 200 和空正文结束?

问题描述

我有一个在生产环境中运行多年的 Zend Framework 1 (1.12) MVC 应用程序。它在我的 Windows 10 笔记本电脑上运行,使用 xampp 1.8.2.6 (PHP 5.4.34) 以及相同版本的 Amazon Linux xampp/PHP(以及 PHP 5.4.31)。

我刚刚尝试在我的笔记本电脑上安装 xampp 7.4.12 (PHP 7.4.12),并且(在从工作版本中对 PHP.ini 和 apache 配置进行最少的自定义更改之后)发现同一个应用程序现在以状态代码 200 和空正文结束每个请求,似乎在 ZF1 自动加载器加载 PHP 文件的过程中毫无理由。 PHP 代码没有任何改变(都在 git 下)。我确实注意到新版本的 PHPPHP_INT_SIZE = 8 而不是 4。虽然我也知道在 Amazon Linux 上,PHP_INT_SIZEPHP 5.4 下是 8。 31.

我已经尝试了所有我能想到的方法来确定请求处理结束的原因。使用 xdebug(版本 3.0.0)和 netbeans(版本 12.0),我可以逐步调试自动加载器加载(使用 include_once文件 Zend/Rest/Route.PHP。该文件包含 4 个 require_once 语句,后跟单个类定义

require_once 'Zend/Controller/Router/Route/Interface.PHP';

require_once 'Zend/Controller/Router/Route/Module.PHP';

require_once 'Zend/Controller/dispatcher/Interface.PHP';

require_once 'Zend/Controller/Request/Abstract.PHP';

class Zend_Rest_Route extends Zend_Controller_Router_Route_Module
{
 // Some protected attributes...

 public function __construct(Zend_Controller_Front $front,array $defaults = array(),array $responders = array()
    ) {
        $this->_defaults = $defaults;

        if ($responders) {
            $this->_parseResponders($responders);
        }

        $this->_front      = $front;
        $this->_dispatcher = $front->getdispatcher();
    }

  // More methods...
}

单步执行,我可以通过每个 require_once 语句,然后当 netbeans 将类定义显示为下一个语句时,再执行一步(结束或进入)以状态代码 200 和一个空的身体。在类定义之后添加代码,该代码永远不会执行。我尝试删除除构造函数之外的其他方法(当然从未被调用过),这确实允许我跳过类定义,但请求在其他地方以类似方式过早结束。

我终于在源文件的顶部添加了以下代码

<?PHP
declare(ticks=1);
// A function called on each tick event
function tick_handler()
{
    echo "<p>Tick</p>\n";
}
register_tick_function('tick_handler');
xdebug_start_code_coverage();
function shutting_down() {
    var_dump(xdebug_get_code_coverage());
}
register_shutdown_function('shutting_down');

这是输出

Tick

Tick

Tick

Tick

Tick

Tick

Tick

Tick

Tick

Tick

C:\xampp7412\htdocs\WWW\library\Zend\Rest\Route.PHP:11:
array (size=2)
  'C:\xampp7412\htdocs\WWW\library\Zend\Rest\Route.PHP' => 
    array (size=12)
      6 => int 1
      7 => int 1
      11 => int 1
      13 => int 1
      37 => int 1
      42 => int 1
      47 => int 1
      52 => int 1
      64 => int 1
      70 => int 1
      76 => int 1
      81 => int 1
  'C:\xampp7412\htdocs\WWW\library\Zend\Controller\Router\Route\Module.PHP' => 
    array (size=2)
      24 => int 1
      290 => int 1

我注意到 Module.PHPRoute.PHP 中的第二个 require_once。这是 Route.PHP 中前 81 行的按行编号的列表,因此您可以验证请求从未经过处理类定义。貌似处理了类内部的三个protected变量声明,然后退出

1:<?PHP
2:declare(ticks=1);
3:// A function called on each tick event
4:function tick_handler()
5:{
6:    echo "<p>Tick</p>\n";
7:}
8:register_tick_function('tick_handler');
9:xdebug_start_code_coverage();
10:function shutting_down() {
11:    var_dump(xdebug_get_code_coverage());
12:}
13:register_shutdown_function('shutting_down');
14:/**
15: * Zend Framework
16: *
17: * LICENSE
18: *
19: * This source file is subject to the new BSD license that is bundled
20: * with this package in the file LICENSE.txt.
21: * It is also available through the world-wide-web at this URL:
22: * http://framework.zend.com/license/new-bsd
23: * If you did not receive a copy of the license and are unable to
24: * obtain it through the world-wide-web,please send an email
25: * to [email protected] so we can send you a copy immediately.
26: *
27: * @category   Zend
28: * @package    Zend_Rest
29: * @copyright  copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
30: * @license    http://framework.zend.com/license/new-bsd     New BSD License
31: * @version    $Id: Route.PHP 23421 2010-11-21 10:03:53Z wilmoore $
32: */
33:
34:/**
35: * @see Zend_Controller_Router_Route_Interface
36: */
37:require_once 'Zend/Controller/Router/Route/Interface.PHP';
38:
39:/**
40: * @see Zend_Controller_Router_Route_Module
41: */
42:require_once 'Zend/Controller/Router/Route/Module.PHP';
43:
44:/**
45: * @see Zend_Controller_dispatcher_Interface
46: */
47:require_once 'Zend/Controller/dispatcher/Interface.PHP';
48:
49:/**
50: * @see Zend_Controller_Request_Abstract
51: */
52:require_once 'Zend/Controller/Request/Abstract.PHP';
53:
54:/**
55: * Rest Route
56: *
57: * Request-aware route for RESTful modular routing
58: *
59: * @category   Zend
60: * @package    Zend_Rest
61: * @copyright  copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
62: * @license    http://framework.zend.com/license/new-bsd     New BSD License
63: */
64:class Zend_Rest_Route extends Zend_Controller_Router_Route_Module
65:{
66:    /**
67:     * Specific Modules to receive RESTful routes
68:     * @var array
69:     */
70:    protected $_restfulModules = null;
71:
72:    /**
73:     * Specific Modules=>Controllers to receive RESTful routes
74:     * @var array
75:     */
76:    protected $_restfulControllers = null;
77:
78:    /**
79:     * @var Zend_Controller_Front
80:     */
81:    protected $_front;

我完全被难住了 - 我怎样才能找出导致请求结束的原因?正如我所说,这个完全相同的代码PHP 5.4 下完美运行。它在 Zend Framework 代码的中间结束,我没有写任何东西。单步执行表明自动加载程序运行良好 - 引导程序自动加载了大量文件,没有出现任何问题。

编辑 1: 我现在有两个完整的 xampp 并排树,一个用于版本 1.8.2.6 (PHP 5.4.34),另一个用于版本 7.4.12 (PHP 7.4.12),具有相同的 htdocs 目录(包含所有 PHP 代码)。我临时修改了树中 WWW\library\Zend\Rest\Route.PHP 的副本,以便它在类定义的右大括号之后立即调用 exit(0) (即作为文件的最后一行)。我还将两棵树中的 shutting_down() 函数修改为:

function shutting_down() {
    error_log(var_export(xdebug_get_code_coverage(),true));
}

这样可以更轻松地查看日志文件中的输出而不是屏幕上的输出。我发现两棵树的代码覆盖率输出是相同的(除了文件路径和时间戳的根)。我想这只是说 PHP 从未得到过自动加载器发出的 include_once 之后的代码(我已经通过单步执行知道了)。如何获取有关为什么 PHP 没有到达 include_once 后面的代码的更多信息的任何想法,或者我可以在此处提供哪些可能有帮助的信息?我无法从源代码构建 PHP - 除了 declare(ticks=1) 之外,它是否有任何类型的调试钩子或输出来深入了解它在内部做什么以及它为什么决定停止处理代码

关于接受的答案的编辑 2: 接受的答案指出“Zend 1.x 弃用 PHP7,特别是文件 Zend/Rest/Route.PHP 中的 assemble 函数所需的额外参数”。事实上,这是我不久前发现自己的根本问题,但我无法想出一个可重现的测试用例,所以我没有发布它。

由于请求在包含文件 Zend/Rest/Route.PHP 的中间以状态代码 200 终止,并且该文件已被 Zend 自动加载器包含,因此我怀疑自动加载器本身。因此,为了消除该问题的可能原因,我对其进行了检测(实际上是它用于加载的文件文件 loadFile() 中的函数 Zend/Loader.PHP)在include_once 语句用于加载文件。然后,我将处理请求时自动加载器加载的文件的完整列表转换为一个脚本,该脚本只是按顺序对每个文件执行 include_once,并在引导应用程序之前包含该列表,完全消除了自动加载器的任何操作.这样做会产生一个“致命的 PHP 错误”,指的是文件 assemble函数 Zend/Rest/Route.PHP() 的声明,即使子类中的声明与其父类中的声明之间的参数不匹配不会产生孤立的致命错误(只是一个警告)。但致命错误确实解释了 PHP 只是停止处理请求的事实。果然,修复这个不匹配可以让我的应用程序使用自动加载器正常运行,就像在 PHP 5.4 下一样!

解决方法

虽然它没有回答您的“为什么”问题,但您是否修复了 PHP7 的 Zend 1.x 弃用问题?特别是 Zend/Rest/Route.php 中 assemble 函数所需的额外参数:

public function assemble($data = array(),$reset = false,$encode = true,$partial = false)