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

I have a Zend Framework 1 (1.12) MVC application that has been running in production for years. It runs on my laptop under Windows 10 using xampp 1.8.2.6 (PHP 5.4.34) as well as on Amazon Linux under the same versions of xampp/PHP (well PHP 5.4.31).

I just tried installing xampp 7.4.12 (PHP 7.4.12) on my laptop, and (after minimal customization changes to php.ini and apache config taken from the working version) find that the same application now ends every request with a status code 200 and empty body, seemingly for no reason in the middle of loading php files by the ZF1 autoloader. None of the PHP code has changed (it’s all under git). I do notice that the new version of PHP has PHP_INT_SIZE = 8 instead of 4. Though I also know that on Amazon Linux, PHP_INT_SIZE was 8 under PHP 5.4.31.

I’ve tried everything I can think of to determine why the request processing ends. Using xdebug (version 3.0.0) with netbeans (version 12.0), I can step-debug up through the auto-loader loading (using include_once) file Zend/Rest/Route.php. That file contains 4 require_once statements followed by a single class definition

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...
}

Single stepping, I can get past each of the require_once statements, then when netbeans displays the class definition as the next statement, one more step (either over or into) ends the request with status code 200 and an empty body. Adding code following the class definition, that code never executes. I tried deleting the other methods besides the constructor (which never got called of course), and that did allow me to step past the class definition, but the request ended prematurely in similar fashion somewhere else.

I finally resorted to adding the following code at the top of the source file:

<?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');

And this is the output:

Tick

Tick

Tick

Tick

Tick

Tick

Tick

Tick

Tick

Tick

C:xampp7412htdocsWWWlibraryZendRestRoute.php:11:
array (size=2)
  'C:xampp7412htdocsWWWlibraryZendRestRoute.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:xampp7412htdocsWWWlibraryZendControllerRouterRouteModule.php' => 
    array (size=2)
      24 => int 1
      290 => int 1

I note that Module.php is the second require_once in Route.php. Here is a line-numbered listing of the first 81 lines in Route.php, so you can verify that the request never got past processing the class definition. It appears to process the three protected variable declarations inside the class, and then exit.

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;

I’m totally stumped – how can I figure out what’s causing the request to end? As I said, this exact same code ran perfectly under PHP 5.4. It ends in the middle of Zend Framework code, nothing that I wrote. Single-stepping showed that the auto-loader was working fine – the bootstrap had auto-loaded lots of files with no problems.

EDIT 1: I now have two complete side-by-side trees for xampp, one for version 1.8.2.6 (PHP 5.4.34) and the other for version 7.4.12 (PHP 7.4.12), with identical htdocs directories (which contain all the PHP code). I temporarily modified the copy of WWWlibraryZendRestRoute.php in the tree that works so that it calls exit(0) immediately after the closing brace for the class definition (i.e. as the last line of the file). And I also modified the shutting_down() function in both trees to be:

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

This makes it easier to review the output in the log file instead of on the screen. What I found was that the code coverage output from both trees was identical (except for the root of the file paths and timestamps). I guess this just says that PHP never got to the code following the include_once issued by the autoloader (which I already knew by single-stepping). Any ideas how to get more information about why PHP did not reach the code following the include_once or what information I could provide here that might help? I can’t build PHP from source – does it have any sort of debugging hooks or output other than declare(ticks=1) to give insight about what it’s doing internally and why it decided to stop processing code?

EDIT 2 regarding the accepted answer: The accepted answer notes “Zend 1.x deprecations for PHP7, Specifically the extra parameter needed in the assemble function in file Zend/Rest/Route.php“. In fact, this was the root problem that I discovered myself a while back, but I was unable to come up with a reproducible testcase so I didn’t post it.

Since the request terminated with status code 200 in the middle of including file Zend/Rest/Route.php, and that file had been included by the Zend autoloader, I was suspicious of the autoloader itself. So to eliminate that as a possible cause of the problem, I instrumented it (actually the file it uses to do the loading, function loadFile() in file Zend/Loader.php) to record the name of each file immediately after the include_once statement it uses to load the file. I then converted the complete list of files loaded by the autoloader when processing a request into a script that simply did an include_once on each file in sequence, and included that list before bootstrapping the application, completely eliminating any action by the autoloader. Doing that produced a “fatal PHP error” referring to the declaration of function assemble() in file Zend/Rest/Route.php, even though the mismatch of parameters between a declaration in a child class and in its parent does not produce a fatal error (just a warning) in isolation. But the fatal error does explain the fact that PHP simply stopped processing the request. And sure enough, fixing that mismatch allowed my application to run normally using the autoloader, as it did under PHP 5.4!

Answer

While it does not answer your ‘why’ question, have you fixed the Zend 1.x deprecations for PHP7? Specifically the extra parameter needed in the assemble function in Zend/Rest/Route.php:

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