在PHPUnit中使用composer's自动加载时发生致命错误


Name already in use fatal error using composer's autoload in PHPUnit

使用PSR-4自动加载与Composer,当我尝试测试这个类:

namespace User;
use User'Contracts'UserId;
use User'Contracts'User as UserContract;
class User implements UserContract
{
    private $id;
    public function __construct(UserId $id)
    {
        $this->id = $id;
    }
    public function getId()
    {
        return $this->id;
    }
}
使用PHPUnit):

use User'Contracts'UserId;
class UserTest extends 'PHPUnit_Framework_TestCase
{
    public function test_the_identifier_can_be_used_as_string()
    {
        $identifier = m::mock(UserId::class);
        $identifier->shouldReceive('__toString')->once()->andReturn('foo');
        $user = new User($identifier);
        $this->assertSame('foo', (string) $user->getId());
    }
}

我在控制台收到这个错误

PHP Fatal error:  Cannot use User'Contracts'UserId as UserId because the name is already in use in /src/User/User.php on line 5

文件/文件夹结构:

src
`-- User
    |-- Contracts
    |    |-- User.php
    |    `-- UserId.php
    |-- User.php
    `-- UserId.php
tests
`-- User
    |-- UserIdTest.php
    `-- UserTest.php

关于这个问题我已经做了很多搜索,但是我还没有找到任何解决方案。

  • PHP版本:5.6.13-1+deb.sury.org~ trusted +3
  • PHPUnit版本:5.0.3
  • OS: Ubuntu 14.04.3 LTS

我该如何解决这个问题?

不能将一个类导入到已经在命名空间中声明了相同别名的命名空间中。

已存在'User'UserId,希望将'User'Contracts'UserId导入'User命名空间。这将使快捷键UserId产生歧义。

http://php.net/manual/en/language.namespaces.importing.php # 117334

您可以在导入时使用别名,或者直接使用相对路径:

namespace User;
class User implements Contracts'User
{
    private $id;
    public function __construct(Contracts'UserId $id) {}
}

更新2018-03-20:

有一个bug报告https://bugs.php.net/bug.php?id=66773,抱怨PHP在没有操作码缓存的情况下会触发这个错误,否则不会。原因是使用opcache时,编译是针对每个文件进行的,因此只考虑文件的内容是合理的期望。然而,错误似乎是先前加载文件中的现有类定义在导入"冲突"类时妨碍了。

它在PHP 7.0.13中以正确的方式修复了:每个单独的文件只说明它自己,如果您希望导入一个类并使用它的缩写名,您可以这样做,而不考虑名称空间中可能存在的任何其他类。您只是失去了对这些类的访问权限—这无关紧要,因为您的代码一开始就没有使用它们。如果你想改变它,你必须改变导入。

由于opcache通常在命令行上被禁用,所以在使用PHPUnit时很可能触发此错误。

尝试如下:

use User'Contracts'UserId as UserIdContract;

可能你得到一个命名冲突因为你在做

use User'Contracts'UserId;

,但你也有UserIdUser类的同一命名空间。

所以…

namespace User;
use User'Contracts'UserId as UserIdContract;
use User'Contracts'User as UserContract;
class User implements UserContract
{
    private $id;
    public function __construct(UserIdContract $id)
    {
        $this->id = $id;
    }
    public function getId()
    {
        return $this->id;
    }
}

希望有帮助!