对于存在的文件,在composer update上抛出ReflectionException

时间:2017-10-19 16:31:06

标签: php laravel laravel-5 composer-php

为了让这更有趣,如果我运行composer dump-autoload -o,事情就可以了。但我很好奇为什么在我首先运行composer update时会出现错误?我需要深究这一点。快速修复不会让我内心开心。

aligajani at Alis-MBP in ~/Projects/saveeo on master ✗                                                                                    [faaba41c]  4:53
> composer update
> php artisan clear-compiled
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Package guzzle/guzzle is abandoned, you should avoid using it. Use guzzlehttp/guzzle instead.
Generating autoload files
> php artisan optimize


  [ReflectionException]                                           
  Class Saveeo\Board\Observers\BoardEventListener does not exist  

BoardEventListener.php (置于Saveeo / Board / Observers中)

<?php

namespace Saveeo\Board\Observers;

use Saveeo\Services\HashIds\Contracts\HashIds as HashIdService;

class BoardEventListener {
    private $hashIdService;

    public function __construct(HashIdService $hashIdService) {
        $this->hashIdService = $hashIdService;
    }

    public function whenBoardIsCreated($event) {
        $this->hashIdService->syncHashIdValueOnModelChanges($event, 'board');
    }

    public function whenBoardIsUpdated($event) {
        $this->hashIdService->syncHashIdValueOnModelChanges($event, 'board');
    }

    public function subscribe($events) {
        $events->listen(
            'Saveeo\Board\Observers\Events\BoardHasBeenCreated',
            'Saveeo\Board\Observers\BoardEventListener@whenBoardIsCreated'
        );

        $events->listen(
            'Saveeo\Board\Observers\Events\BoardHasBeenUpdated',
            'Saveeo\Board\Observers\BoardEventListener@whenBoardIsUpdated'
        );

    }
}

EventServiceProvider.php (置于Saveeo / Providers中)

<?php

namespace Saveeo\Providers;

use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        //
    ];

    /**
     * The subscriber classes to register.
     *
     * @var array
     */
    protected $subscribe = [
        'Saveeo\Board\Observers\BoardEventListener',
    ];

    /**
     * Register any other events for your application.
     *
     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
     * @return void
     */
    public function boot(DispatcherContract $events) {
        parent::boot($events);

        //
    }
}

这是文件夹结构。这里看不出有什么问题?

https://imgur.com/BI44Lq6

Composer.json

{
    "name": "laravel/laravel",
    "description": "The Laravel Framework.",
    "keywords": [
        "framework",
        "laravel"
    ],
    "license": "MIT",
    "type": "project",
    "require": {
        "php": ">=5.5.9",
        "laravel/framework": "5.2.*",
        "firebase/php-jwt": "~2.0",
        "guzzlehttp/guzzle": "5.*",
        "guzzlehttp/oauth-subscriber": "0.2.0",
        "laravel/socialite": "2.*",
        "league/flysystem-aws-s3-v3": "~1.0",
        "aws/aws-sdk-php": "3.*",
        "bugsnag/bugsnag-laravel": "1.*",
        "vinkla/hashids": "^2.3"
    },
    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
        "phpunit/phpunit": "~4.0",
        "phpspec/phpspec": "~2.1",
        "tymon/jwt-auth": "0.5.*",
        "symfony/dom-crawler": "~3.0",
        "symfony/css-selector": "~3.0"
    },
    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "Saveeo\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/TestCase.php"
        ]
    },
    "scripts": {
        "post-install-cmd": [
        "php artisan clear-compiled",
        "php artisan optimize"
    ],
    "pre-update-cmd": [
        "php artisan clear-compiled"
    ],
    "post-update-cmd": [
        "php artisan optimize"
    ],
    "post-root-package-install": [
        "php -r \"copy('.env.example', '.env');\""
    ],
    "post-create-project-cmd": [
        "php artisan key:generate"
    ]
    },
    "config": {
        "preferred-install": "dist"
    }
}

3 个答案:

答案 0 :(得分:7)

我们似乎已将 app / 目录中的类从artisan app:name(默认值)更改为{自动加载命名空间(手动或使用App){ composer.json 中的{1}}:

Saveeo

当我们理解其含义时,完全很好。当我们看一下导致异常的文件的项目文件夹结构(括号中的命名空间)时,我们可以看到问题中描述的问题:

"autoload": {
    "psr-4": {
        "Saveeo\\": "app/"
    }
},

为了与PSR-4兼容,app (Saveeo) ├── Saveeo (Saveeo\Saveeo) │   ├── Board (Saveeo\Saveeo\Board) │ │   ├── Observers (Saveeo\Saveeo\Board\Observers) │ │ │   ├── BoardEventListener └── ... 的命名空间应为BoardEventListener,因为它存在于嵌套在 app / Saveeo / 目录中>。 PSR-4 autoloading实现基于文件名和路径解析类文件而不是文件中声明的命名空间。 [Composer 从类文件中读取命名空间以创建优化的类映射,只要顶级命名空间匹配即可。见更新。]

如果我们不想更改应用程序的目录结构,并且我们不想在命名空间中使用两个Saveeo\Saveeo\Board\Observers,我们可以将Composer配置为在自动加载类时将两个目录合并到同一名称空间中:

Saveeo

...并记住"autoload": { "psr-4": { "Saveeo\\": [ "app/", "app/Saveeo/" ] } },

它有效,但我不建议在实践中使用它。它偏离了PSR-4标准,使应用程序容易受到名称空间冲突的影响,并且可能会使其他从事该项目的人感到困惑。我建议您展平composer dump-autoload命名空间和目录,或者为嵌套目录中的类选择不同的命名空间。

  

...如果我运行Saveeo,事情就会正常工作但我很好奇为什么当我运行composer dump-autoload -o时会出现错误?

生成自动加载缓存文件时,Composer实际上并不执行您的代码。但是,composer update命令在大多数Laravel应用程序中运行artisan optimize之后(直到5.5),会启动应用程序以执行其操作,因此问题代码会执行,我们看到例外。

<强>更新

  

如果使用composer update错误而不是Saveeo\Board\etc,那么整个应用程序周围的其他文件将无效,但确实如此。

我们可以在生成优化自动加载器时,技术上使用与项目目录结构不匹配的非PSR-4命名空间,就像我们准备应用程序一样制作:

Saveeo\Saveeo\Board\etc

这是有效的,因为Composer将扫描每个类文件以查找名称空间以创建静态类图。当我们没有指定composer dump-autoload --optimize 时,Composer依赖于动态自动加载,它将文件路径与名称空间匹配,因此非标准名称空间或目录结构无法解析。

相关项目的命名空间大部分都适用,即使它不遵循PSR-4,因为我们使用--optimize -o选项手动转储优化的自动加载器}。但是,我们看到错误消息,因为dump-autoload 在运行更新之前删除了缓存的类映射,因此,当composer update命令运行时,类映射不再包含类我们抛弃了。

我们可以通过在 composer.json 中设置artisan optimize配置指令,将Composer配置为always optimize the autoloader

optimize-autoloader

...应修复此项目的"config": { "optimize-autoloader": true } install命令。获得非标准命名空间来解决这个问题有点麻烦。使用这种方法,请记住,每当我们在开发中添加不遵循PSR-4的新文件时,我们都需要转储自动加载器。

答案 1 :(得分:6)

这似乎与Composer PSR自动加载有关。默认情况下,Laravel将包括

"autoload": {
    "psr-4": {
        "App\\": "app/"
    }
}

表示“App命名空间的根目录在app文件夹中,并从那里匹配命名空间到文件夹结构”。

您的Saveeo命名空间尚未注册(它不在App层次结构中),因此Composer不知道它的位置。

如果你在composer.json中添加另一行(然后dump-autoload

,它应该可以工作
"autoload": {
    "psr-4": {
        "App\\": "app/",
        "Saveeo\\": "app/Saveeo"
    }
}

或者,您可以像其他答案所指出的那样,将整个Saveeo命名空间置于App内(例如,App\Saveeo\Board\Observers\Events\BoardHasBeenCreated)。但是,您的Saveeo命名空间似乎是一种自包含的模块,将它作为单独的命名空间而不是在其所有文件中重命名命名空间可能更合理。

答案 2 :(得分:3)

我相信如果您只是将名称空间更改为:

App\Saveeo\Board\Observers代替Saveeo\Board\Observers

所有问题都应该解决。如果您有理由创建这个新的命名空间,请告诉我,而不是仅仅从基本的 App 命名空间扩展出来。