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 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;
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!
Advertisement
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)