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

Why does my request end with status 200 and empty body in php code?

我有一个 Zend Framework 1 (1.12) MVC 应用程序,它已经 运行 投入生产多年。它 运行 在我的笔记本电脑上 Windows 10 使用 xampp 1.8.2.6 (PHP 5.4.34 ) 以及 Amazon Linuxxampp/PHP 的相同版本(以及 PHP5.4.31).

我刚刚尝试在我的笔记本电脑上安装 xampp 7.4.12 (PHP 7.4.12),并且(在对 php.ini 和从工作版本中获取的 apache 配置)发现同一个应用程序现在以状态代码 200 和空主体结束每个请求,似乎无缘无故地在 ZF1 自动加载器加载 php 文件的过程中。 PHP代码的None已经改变(都在git下)。我确实注意到 PHP 的新版本有 PHP_INT_SIZE = 8 而不是 4。虽然我也知道在亚马逊 Linux,PHP_INT_SIZEPHP 5.4.31.

下是 8

我已尽我所能来确定请求处理结束的原因。使用 xdebug(版本 3.0.0)和 netbeans(版本 12.0),我可以通过自动加载器加载(使用 include_once)文件 Zend/Rest/Route.[=123 来逐步调试=]。该文件包含 4 个 require_once 语句,后跟一个 class 定义

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 将 class 定义显示为下一个语句时,再一步(over 或 into)结束请求状态代码为 200 且正文为空。在 class 定义之后添加代码,该代码永远不会执行。我尝试删除除构造函数之外的其他方法(当然从未被调用过),这确实允许我跳过 class 定义,但请求在其他地方以类似的方式过早结束。

我最终求助于在源文件的顶部添加以下代码:

<?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 行的行号列表,因此您可以验证请求从未通过处理 class 定义。它似乎处理了 class 中的三个受保护变量声明,然后退出。

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 license@zend.com 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 代码的中间结束,不是我写的。单步显示自动加载器工作正常 - bootstrap 自动加载了很多文件没有问题。

编辑 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 的副本,使其在 class 定义的右大括号之后立即调用 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 函数所需的额外参数”。事实上,这是我不久前发现的根本问题,但我无法想出一个可重现的测试用例,所以我没有 post 它。

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

下一样

虽然它没有回答您的 'why' 问题,但您是否修复了 PHP7 的 Zend 1.x 弃用问题?具体来说 Zend/Rest/Route.php:

中 assemble 函数需要的额外参数
public function assemble($data = array(), $reset = false, $encode = true, $partial = false)