3

I am getting a strange behavior with PHPUnit and I cannot figure if I'm doing something wrong or if it is a bug from the test framework. Here is my case:

My project has several classes: BsInput, BsEmail, BsHidden, BsNumber. BsEmail, BsHidden and BsNumber extend BsInput. All classes have unit test classes (BsInputTest for BsInput, BsEmailTest for BsEmail, etc.).

Each unit test executed individually goes fine.

Now if I try to run all tests in my project, i get an error and message "PHP Fatal error: cannot redeclare class B in /some/path/to/B.php on line 80".

Here is the content of each file:

BsInput.php

require_once __DIR__ . "/BsInputControl.php";

class BsInput extends BsInputControl {
    ...
}

BsEmail.php

require_once __DIR__ . "/BsInput.php";

class BsEmail extends BsInput {
    ...
}

BsHidden.php

require_once __DIR__ . "/BsInput.php";

class BsHidden extends BsInput {
    ...
}

BsNumber.php

require_once __DIR__ . "/BsInput.php";

class BsNumber extends BsInput {
    ...
}

BsInputTest.php

require_once "../colibri/bs/BsInput.php";

class BsInputTest extends PHPUnit_Framework_TestCase {
    ...
}

BsEmailTest.php

require_once "../colibri/bs/BsEmail.php";

class BsEmailTest extends PHPUnit_Framework_TestCase {
    ...
}

BsHiddenTest.php

require_once "../colibri/bs/BsHidden.php";

class BsHiddenTest extends PHPUnit_Framework_TestCase {
    ...
}

BsNumberTest.php

require_once "../colibri/bs/BsNumber.php";

class BsNumberTest extends PHPUnit_Framework_TestCase {
    ...
}

Now the last little thing that drives me insane: if I comment out all code in BsHidden.php and BsHiddenTest.php, PHPUnit All Tests execution goes smooth over BsNumber and following classes !

Has anyone already seen something similar ? Any clues what I should look at to get my problem solved ?

I have tried an ugly workaround: inserting the following code in BsInput.php

var_dump(class_exists('BsInput', FALSE));
if (class_exists('BsInput', FALSE)) { return; }

And I get this result:

bool(true)
PHP Fatal error: Cannot redeclare class BsInput in /.../BsInput.php on line 87

I have looked at many issues up to now including the ones above, but up to now I couldn't find any solution.

I have also tried updating PHPUnit to the latest version (4.6), with no more success.

My platform is: Mac OS X 10.10 PHP 5.5.20 PHPUnit 4.6.6 Netbeans 8.0.2 (if this has anything related)

Any clue anyone ?

After trying to solve this i finally deleted my BsHidden.php file, the attached test file and recreated them exactly (I mean with exact same content I copy/pasted)... And now it works ! This really makes me think of a bug somewhere between PHP and PHPUnit.

However a quick & dirty workaround seems to be:

  1. copy-paste file and unit test file contents to another file
  2. delete problematic file and unit test file if any)
  3. re-create file and unit test file and paste contents
Community
  • 1
  • 1
shadock
  • 406
  • 5
  • 9
  • Kudos forrequire_once... Everyone use include () when they want require_once :o – hanshenrik May 23 '15 at 15:22
  • In several places you have something like: `require_once __DIR__ . "/BsNumber.php";` and then `class BsNumber extends BsInput`. Am I missing something here, or class `BsNumber` is not defined in `__DIR__ . "/BsNumber.php"` but in different file? – Aleksander Wons May 24 '15 at 14:03
  • No. I define one class per file, and each file is named after the class it defines. – shadock May 26 '15 at 10:37
  • Can you give us a brief explanation why are you requiring the same file your class is defined in? You have ` BsNumber` class and at the top `require_once "BsNumber.php"` which is the exact same file... And the reason is? – Aleksander Wons May 27 '15 at 08:17
  • My apology. There were mistakes in the source code examples, now corrected. Files `BsEmail.php`, `BsHidden.php` and `BsNumber.php` all `require_once __DIR__ . "/BsInput.php";`. – shadock May 27 '15 at 12:59

2 Answers2

3

I have finally identified my issue. It seems that there is a PHP bug, at least on my platform.

PHP's manual says (http://php.net/manual/en/function.include-once.php) that names resolution in PHP4 are case-insensitive on case-insensitive systems, while PHP5 handles such problems.

Here is the behavior I get on my platform (Mac OS X 10.10, PHP 5.5) :

Assuming I have a file A.php that defines a A class and the following code:

<?php
require_once 'A.php';
require_once 'a.php';
?>

Second require_once fires the Can not redeclare class error. So in PHP5.5 on Mac OS X at least names resolution is case insensitive, but PHP still considers they are different files anyway.

shadock
  • 406
  • 5
  • 9
0

Is you just use class_exists, use it like this:

if (!class_exists('BsInput', FALSE)) { 
    class BsInput extends BsInputControl {
        // ToDo
    }
}

I do not know why this error is otherwise generated - do you use everywhere require_once explicit?

Richard
  • 2,840
  • 3
  • 25
  • 37
  • Yes, my workaround wasn't so nice. I have checked my whole code for any `require` instead of `require_once`, but i have none. – shadock May 23 '15 at 20:07