Класс PHP не найден из другого пространства имен

В настоящее время я работаю над проектом Symfony2 с Phpspec, и у меня возникают проблемы с расширением класса Spec, описанного в другом пространстве имен.

Например, в моем проекте у меня есть следующий класс, описанный в spec/Acme/Model/Foo/FooSpec.php :

namespace spec\Acme\Model\Foo;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

abstract class FooSpec extends ObjectBehavior{
    //some code here
}

И у меня есть еще один класс в spec/Acme/Model/Bar/BarSpec.php, расширяющий FooSpec:

namespace spec\Acme\Model\Bar;

use spec\Acme\Model\Foo\FooSpec;

class BarSpec extends FooSpec{
    //some code here
}

Когда я пытаюсь запустить phpspec, у меня возникает следующая ошибка:

PHP Fatal error: Class 'spec\Acme\Model\Foo\FooSpec' not found in /home/user/Projects/Acme/spec/Acme/Model/Bar/BarSpec.php on line 9

Единственный способ заставить это работать, это добавить следующую строку в spec/Acme/Model/Bar/BarSpec.php:

include('./spec/Acme/Model/Foo/FooSpec.php');

Я не знаю, почему я должен включать этот конкретный файл, чтобы он работал, особенно когда другие классы (например, PhpSpec\ObjectBehavior) найдены правильно.

У вас есть идеи, почему это происходит?


Редактировать:

Как предложили @Phil и @Sheikh Heera в комментариях, я попытался настроить autoload для регистрации пространства имен моей спецификации, но это тоже не работает. Вот что я пробовал до сих пор:

require_once getcwd() . '/vendor/composer/ClassLoader.php';

$loader = new \Composer\Autoload\ClassLoader();

// register classes with namespaces
$loader->add('spec', getcwd().'/spec');

// activate the autoloader
$loader->register();

Я также попытался изменить файл vendor/composer/autoload_namespaces.php, чтобы добавить это:

return array(
    //some code here

    'spec' => array(getcwd() . '/spec'),
    //some more code

);

Но все та же ошибка. Я также пробовал с 'spec' => array(getcwd()) или $loader->add('spec', getcwd());, просто чтобы посмотреть, что произойдет, и на этот раз я получаю Cannot redeclare class в другом классе спецификации.


Моя версия php PHP 5.4.9-4ubuntu2.4 (cli).

Спасибо заранее за вашу помощь.


person Ianlet    schedule 17.02.2014    source источник
comment
Настроили ли вы автозагрузчик для пространства имен spec, о котором знает PHPSpec?   -  person Phil    schedule 17.02.2014
comment
Проверьте это, это может помочь .   -  person The Alpha    schedule 17.02.2014
comment
@SheikhHeera и Фил, я обновил свой вопрос   -  person Ianlet    schedule 17.02.2014
comment
Я считаю, что это связано с тем, что создатели PHPSpec не предполагали, что ваши тесты будут расширять другие тесты. Почему вы это делаете?   -  person Carl Owens    schedule 17.02.2014
comment
@CarlOwens, я делаю это, потому что у меня есть абстрактный класс, который наследуется тремя другими классами (на данный момент). Я не хочу копировать и вставлять спецификации абстрактного класса в каждого дочернего элемента, но я хочу быть уверенным, что 3 дочерних элемента имеют одинаковое поведение. Следовательно, я хочу, чтобы они расширяли абстрактный класс. Вы можете ознакомиться с этим обсуждением: PhpSpec — Issue #54 то же самое, что я хочу сделать.   -  person Ianlet    schedule 18.02.2014
comment
@Lanlet хммм интересно. Я не чувствовал необходимости специфицировать абстрактные классы, но я вижу вашу проблему. Вы не хотите дублировать тестовый код в трех ваших конкретных классах. Я думаю, что в основном согласен с комментариями Everzet относительно неполных классов, но было бы хорошо, если бы phpspec позволял каким-то образом делиться вашими тестами спецификаций для таких случаев, как ваш. Не думаю, что я могу помочь извините :)   -  person Carl Owens    schedule 18.02.2014
comment
Назовите свой FooSpec FooBehavior. Это не спецификация, а базовый класс для ваших спецификаций (как оригинальный ObjectBehavior).   -  person Jakub Zalas    schedule 18.02.2014
comment
@JakubZalas, вы правы, я внес необходимые изменения в свой код. Поскольку я говорил о поведении в своем вопросе, я должен был подумать о том, чтобы правильно назвать его.   -  person Ianlet    schedule 19.02.2014
comment
@CarlOwens Я также согласен с комментариями Эверзета. Я думаю, что необходимость описания поведения (неполного) абстрактного класса является доказательством плохого дизайна. Почему я на самом деле решил сделать это, потому что в моем случае мой FooSpec (или FooBehavior, как заявил Якуб Залас) завершен, но никогда не будет создан в моем приложении. Чтобы быть уверенным, что этого не будет, я решил определить его как абстрактный. Я использую его только для того, чтобы дать полную базу другим детям. Я был бы рад продолжить эту дискуссию, если у вас есть какие-либо предложения (или что-то еще), чтобы сказать. Я нашел не такой уж плохой способ автозагрузки моего пространства имен.   -  person Ianlet    schedule 19.02.2014


Ответы (2)


Более общее решение — поместить каталог spec в конфигурацию autoload-dev вашего composer.json:

"autoload-dev": {
    "psr-0": {
        "spec\\":""
    }
},

Таким образом, composer сгенерирует пространство имен также для спецификаций, которые должны находиться в корне репозитория:

return array(
     'spec\\' => array($baseDir . '/'),
     ...
);
person Alberto Fernández    schedule 07.01.2015
comment
В итоге я использовал это решение, но забыл обновить свой ответ. Я принял ваш ответ, чтобы другие могли его увидеть. Спасибо - person Ianlet; 09.01.2015

Наконец, что я на самом деле сделал, чтобы «решить» свою «проблему», как предложили Фил и Шейх Хира в комментариях, так это автозагрузить мое пространство имен, чтобы оно распознавалось спецификацией.

Вот что я сделал, чтобы заставить его работать:

  • Я добавил следующую строку в файл `vendor/composer/autoload_namespaces.php`:
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
    //some code
    'spec\\Acme\\Behavior' => array(getcwd()),
    //some code
);
  • Затем я создал следующую структуру папок:
    │── spec
    │  └── Acme
    │     └── Behavior
    │        └── FooBehavior.php
  • Я объявил пространство имен в `spec/Acme/Behavior/FooBehavior.php` следующим образом:
namespace spec\Acme\Behavior;
  • И использовал его в `spec/Acme/Model/Bar/BarSpec.php` следующим образом:
use spec\Acme\Behavior\FooBehavior;

class BarSpec extends FooBehavior{
    //...
}

Я знаю, что это не лучшая практика, потому что каждый раз, когда кто-то будет работать над этим проектом, ему придется вручную воспроизводить первый шаг. Поэтому, если у вас есть идея получше, пожалуйста, не стесняйтесь комментировать или публиковать ответ.

person Ianlet    schedule 19.02.2014