0

I posted the solution as an answer

I'm running into an odd issue while migrating some PHP code to a new server. The old server is running PHP 5.3 (x86) and the new server is running PHP 7.0 (x64). Both are running on IIS 7.5.

The code causing the issue has been in production without issues for almost three years. It runs fine on the current production server as well as two development systems. All three are running PHP 5.3 (x86).

These errors do not occur when I use PHP 5.3 (x86) on the new server.

A shared file (named _common_funcs.php) contains common functions that are called from many scripts. Since some of these scripts can interact I use require_once('_common_funcs.php') to include the file. This is approach pretty basic.

On the new server the first time a the script runs it generates a 'Cannot redeclare' error. Refreshing the page does not display the error - it displays the page as expected. For example:

  • //localhost/app/display_account.html?account=123456 generates the error.
  • //localhost/app/display_account.html?account=123456 again does not display the error.

Restarting the IIS server causes the error to be generated again on the first run.

Now for the odd part. Note the capitalization of the drive letters in the error below:

Cannot redeclare acct_has_graduated() (previously declared in D:\app_directory\includes\_common_funcs.php:2061) in d:\app_directory\includes\_common_funcs.php on line 2061

If I hardcode the full path in one require_once() line, and use a lowercase 'd', then the error in the log will swap the capitalization. This results is 'd:\app ...' and 'D:\app ...'.

If I hardcode both calls that happen to get called when this script runs with the error is not generated no matter what mix of capitalization I use.

And to make this just a little more interesting, the acct_has_graduated() function is the last function in the _common_funcs.php file. But if I disable OpCache in php.ini I get the following error:

Cannot redeclare update_ledger() (previously declared in D:\app_directory\includes\_common_funcs.php:7) in d:\app_directory\includes\_common_funcs.php on line 42

Two things of note: update_ledger() is the first function in the _common_funcs.php file, and "line 42" is the closing bracket of the update_ledger() function (line 7 is the declaration).

If anyone can point me in the right direction on this I would greatly appreciate it.

Mr Glass
  • 1,186
  • 1
  • 6
  • 14
  • If function exists, then prevent redeclartion using [function_exists](http://php.net/manual/en/function.function-exists.php) – Karlo Kokkak May 29 '18 at 02:23
  • 1
    The sole purpose of require_once() is to prevent the file from being included more than once. – Mr Glass May 29 '18 at 03:09
  • Do you have multiple files called `_common_funcs.php`? –  May 29 '18 at 03:15
  • @Terminus, there is only one file named _common_funcs.php. If you look at the paths in the error message you'll see it is including the same file more than once. – Mr Glass May 29 '18 at 03:17
  • Didn't think that would be the issue but, it pays to do sanity checks; especially once you've exhausted other options. I'll try and think of what this could be... –  May 29 '18 at 03:21
  • "If I hardcode both calls that happen to get called when this script runs with the error is not generated no matter what mix of capitalization I use." Sorry, the quoted line is unclear to me. Could you reword? –  May 29 '18 at 03:30
  • @Terminus, the errors always display with different drive letter capitalization. In order to test if it was a capitalization issue I tried hardcoding the absolute path at both locations the file was being included. I tried with both calls using the same capitalization as well as different capitalization. Neither situation generated the error. – Mr Glass May 29 '18 at 03:36
  • So if you hardcode the file path, there is no error? –  May 29 '18 at 03:37
  • @Terminus, that is correct. But this file is called/included in close to 200 other files. There are other shared files that are called/included in many other files. Hardcoding everything is not a practical solution. – Mr Glass May 29 '18 at 03:40
  • The only thing that comes to my mind is that there are multiple files on the server. [See here for what i mean](https://stackoverflow.com/questions/6434587/php-require-once-tries-to-include-a-second-time-on-my-production-server-only). It could be a PHP PATH related issue. PHP may be finding the file in a completely unexpected directory. –  May 29 '18 at 03:44
  • 1
    OK so the problem doesn't seem to be the code around the `require_once`, it's around how the base path is being resolved (so the difference between `d:` and `D:`, making the fully-qualified path "different". Have you got a mix of drive capitalisation in some config somewhere (check PHP and IIS). Maybe log some debug of the current stack trace at the beginning of `_common_funcs.php` to see if a pattern emerges of where the drive lettering is different. – Adam Cameron May 29 '18 at 05:42
  • @AdamCameron, I looked at the stack trace. The first time _common_funcs.php in included it uses 'D:', the 2nd time 'd:'. This code logs tracking information for security auditing purposes. The drive letter capitalization in the audit log and error log varies. But, while testing all of the logging I did stumble upon the answer. I will post it above. – Mr Glass May 29 '18 at 11:08
  • 1
    I'd like to know why this was downvoted. It's a solidly researched and presented question, it'll be useful to some other (admittedly outlier) people, and has a good answer. What's yer problem? – Adam Cameron May 29 '18 at 22:45

2 Answers2

3

Solution

I found the issue and the file is absolutely being included twice. This happens under a specific set of circumstances. It affects PHP 7.0 but does not affect PHP 5.3. I did not test any other versions of PHP.

This scenario is certainly an outlier, but here it is:

  1. I have a directory symlink from c:\inetpub\wwwroot\app_location to d:\app_location.
  2. The IIS configuration for this site points to c:\inetpub\wwwroot\app_location.
  3. The application's configuration file defines the include_path using c:\inetpub\wwwroot\app_location as the base.
  4. All the files for this site are located in d:\app_location.

Using the configuration above all entries in the application's log files (tracking, audit, error and backtrace) show D:\app_location as the base location of all files.

Calling require_once() on a file more than once, while allowing the include_path to resolve the location of the file in one or both of the calls, generates the 'Cannot redeclare' error. The error reports the opposite capitalization of the 'D:' drive letter (see error text in the post above).

Changing the base of the include_path (#3 above) to d:\app_location resolved this issue.

Mr Glass
  • 1,186
  • 1
  • 6
  • 14
-1

The question is very simple.

require_once('D:\file.php');
require_once('file.php');

will run twice, because you included the absolute path!

Solution Use relative path

require_once('../core/file.php');
Wils
  • 1,178
  • 8
  • 24
  • I think you misunderstood the issue. The problem occurs when both use require_once('_common_funcs.php'). I used the absolute path to test that the drive letter capitalization would change (which it did). – Mr Glass May 29 '18 at 03:06
  • then you have two function with the same name. how is this related to require_once – Wils May 29 '18 at 03:08
  • I think you have included same function file in both the files that's why this conflict is coming – Dinesh Ghule May 29 '18 at 03:09
  • 1
    @Dinesh, the include_once() language construct specifically exists to prevent the file from being included more than once. – Mr Glass May 29 '18 at 03:13
  • @Wils, please re-read my post. I have once function with that name. The file happens happens to get called more than once sometimes, which require_once() is supposed to prevent. Since this code runs properly in 5.3 it appears this issue exists in 7.0. Changing close to 200 files that call include this shared file is not a practical solution, and does not actually solve the problem. – Mr Glass May 29 '18 at 03:16