カテゴリー
Laravel

Laravel が急に動かなくなった Late 2020。Undefined index: name 編

ついこの間まで動いていたLaravelが急に動かなくなった。何もしてないのに動かなくなった。むしろ何もしてないから動かなくなったんやろな。Dockerで環境用意してあるやつだけども、いつものように起動してアクセスしてみたらこんなエラーが。

あ、ホンマ…(絶句)

Undefined index: name

なんやねん、これ…。

検討つかんねんけど、コアでこう言うんが発生する場合は、コードの問題やないな。きっとなんかPHPんとこのコンテナがおかしそうやな…。これは教育やろな。

再ビルドっと…。

ビルド時にcomposerがインストールされて依存ライブラリも自動インストールされるんやけども、なんやパッケージが見つからんとか、依存関係がいろいろおかしいで…?

[InvalidArgumentException] has a PHP requirement incompatible with your PHP version, PHP extensions and Composer version

ん?そんな、さすがにPHPのバージョンは固定しとるで…。

Composer (version 2.0.7) successfully installed

そんなもん、オマエ…

なるほど、どうやらComposerの方が生まれ変わったようや…。

なんかいろいろ影響ありそうで、直すの大変やなっちゅうわけで、Composerの方のバージョンを1系に固定して完了や。

Composer、バージョンあがったでっちゅうのをね、伝え羅れればと。

そらね、同じことの繰り返しよ。

カテゴリー
Laravel

LaravelのCacheのPermissionについて (failed to open stream: Permission denied)

artisanを操作するユーザーとWebサーバーの実行ユーザーが違うのは多々あるはずやねん。

artisanを使うときはahraユーザー、webはapacheとかね。ほんでね、お互いグループは共有してるとするやで。つまり、グループでのパーミッション指定があってればええっちゅうこっちゃね。

せやけども、キャッシュ機構(ファイルにストアするやつね)を使ってる箇所があるとするやろ?

そうすると、storage/framework/cache なディレクトに作られるファイルのパーミッションが、artisanのときは、ahra:ahra になるし、webのときは apache:apache になるわけよ。

せやけども、問題はこのファイルのパーミッションが644になってる場合があるわけよね。

ほんで、ahraユーザーで、artisan clear:cache するとさ、

Application cache cleared!

って出るんやけども、実際ね、クリアできてへん。

ほんで、実際にキャッシュ操作するコマンド作って動かしたらさ、webで作られてるとこけるわけやねん。

failed to open stream: Permission denied

やね。

まあ、つまり、キャッシュをファイルに書き出す時のパーミッションをね、グループも書き込み可能にしたいわけやねん。

調べるとさ、カスタムのファイルストアマネージャ作ったとか、ファイルマスク変更するとか、いろいろあったわけやねんけどね、今のLaravel(7.x)ならもっとこう、なんかあるんやないかなって。

あった。Illuminate/Cache/FileStore.phpやね。

    /**
     * Create an instance of the file cache driver.
     *
     * @param  array  $config
     * @return \Illuminate\Cache\Repository
     */
    protected function createFileDriver(array $config)
    {
        return $this->repository(new FileStore($this->app['files'], $config['path'], $config['permission'] ?? null));
    }

出たわね。

パーミッション指定できるやん。というわけで、config/cache.phpのココを

        'file' => [
            'driver' => 'file',
            'path' => storage_path('framework/cache/data'),
            'permission' => 0664,
        ],

こうかな。

これでwebの方で作られたキャッシュファイルを見てみると、確かに、グループが書き込み可能になっとった。これでキャッシュの更新自体は成功や…!たしかに、ahraユーザーでキャッシュ更新する処理は成功するようになったンゴ。

だがしかし、artisan clear:cache の削除はやっぱり効かへん…。

    /**
     * Create the file cache directory if necessary.
     *
     * @param  string  $path
     * @return void
     */
    protected function ensureCacheDirectoryExists($path)
    {
        if (! $this->files->exists(dirname($path))) {
            $this->files->makeDirectory(dirname($path), 0777, true, true);
        }
    }

ふーむ、おかしいな。ディレクトリのパーミッションは指定してるんやが…。

調べるンゴねぇ。

内部的に、mkdir() で、指定のpermissionを渡しているんだが、それでもそうならないのは、mkdir()はumaskによる制限がかかるんやて。

なるほど。

umask()を、ahraユーザーとwebでそれぞれ実行してみた結果。

ahraは0002、webやと0022や。

そら(0777のディレクトリをmkdir()で作ることは)そう(できない)よ。

これは教育やろなぁ。

というわけであとはumaskの設定をってことになるんやが、手取り早いのは、laravelのAppServiceProvider.phpあたりで

    public function register()
    {
        // overwrite umask
        umask(0002);

かな。悪手な気がするけれども。サーバー設定いじれないときはこうするしかない。

これで、ahraユーザー、webのどちらで作られたキャッシュファイルでも同じように操作できるようになったで。

ええんちゃうか、最高ちゃう。

ついでやけども、ログ(config/loggin.php)も同様やね。

        'single' => [
            'driver' => 'single',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
            'permission' => 0664,
        ],

        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
            'days' => 14,
            'permission' => 0664,
        ],

single と daily で作られるファイルのパーミッションが指定可能や。

アプリケーションが稼働し続けることによって自動で作成されたファイルでややこしいことにならないように、最初にはっきりしておきたいところやね。

追記(2020.11.16)

あかん。あかんで。結局オーナーとグループが違うわけやから、動かん。コマンドが先に実行しちゃった場合は、グループがapacheにならへん。

そこで、SGIDよ。

ディレクトリに設定すれば、そのディレクトリ配下に作成されるやつのグループがそいつと同じものになるっちゅうやつやね。

つまり、Laravelなら、storageとbootstrap/cacheに設定しておけばええと思うで。

例えば、storageディレトリのグループがapacheなら。

chmod g+s storage

で。

そしたら、その中に作られるファイルやディレクトリは基本的にapacheになるっちゅうことやね。

セキュリティ的にあれやけども。しゃーない、切り替えていけ。

カテゴリー
Laravel

サブディレクトリにLaravelをインストールしたらTelecsopeが使えないンゴ…

そら(サブディレクトリにLaravelインストールしたら)そう(Telescopeは動かん)よ、とレスされてる人がTelescopeのissueにたくさんおったで…。

のっぴきならない事情でサブディレクトリにLaravelを配置することになったとしても、使いたいやん?
実際、何とかしてくれてる人がおったで。

https://github.com/laravel/telescope/pull/281

   /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {

      if ($this->app->isLocal()) {
        View::composer(
            ['telescope::layout'],
            function ($view) {
                $view->with('telescopeScriptVariables', [
                    'path' => 'subfolder/admin/telescope',
                    'timezone' => config('app.timezone'),
                    'recording' => ! cache('telescope:pause-recording'),
                ]);
            });
      }
    }

こんな感じでViewServiceProviderに追記すればええ。

⊂(^ω^)⊃ セフセフ!!

カテゴリー
Laravel

UnitTestでconfig()が使えないンゴ…

LaravelのEloquentのUnitTestを書いていて、Eloquentの中にconfig()を利用するものがあったんやけども、そこがエラーになった。

 Target class [config] does not exist.

  at vendor/laravel/framework/src/Illuminate/Container/Container.php:807
    803| 
    804|         try {
    805|             $reflector = new ReflectionClass($concrete);
    806|         } catch (ReflectionException $e) {
  > 807|             throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
    808|         }
    809| 
    810|         // If the type is not instantiable, the developer is attempting to resolve
    811|         // an abstract type such as an Interface or Abstract Class and there is

コンテナにconfigが登録されてないでってことらしいで。ということなので、UnitTest実行時にコンテナに登録してみた。

app()->bind('config', function () {
                return new class {
                    public function get(...$args)
                    {
                        return 'なんてすごいんだ……(恍惚)';
                    }
                };
            });

とりあえずテキストが返ればよかったので、こんな感じで解決できた。

やっぱりEloquentの中でconfig()直接使うなってことなんやなと。横着せずに、ちゃんと外から渡して疎結合にしろってはっきりわかんだね。

カテゴリー
Laravel

UnitTestでEloquentの日付情報を操作するとエラーになるンゴ…

LaravelのEloquentをUnitTestするときに、日付の属性をセットしようとすると下記のエラーになった。

 Call to a member function connection() on null

  at vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1342
    1338|      * @return \Illuminate\Database\Connection
    1339|      */
    1340|     public static function resolveConnection($connection = null)
    1341|     {
  > 1342|         return static::$resolver->connection($connection);
    1343|     }

データベースのコネクションを探しているようや。これは、日付の属性をセットするときは、データベースへのアクセスが必要になるためらしいんやが…。

    /**
     * Get the format for database stored dates.
     *
     * @return string
     */
    public function getDateFormat()
    {
        return $this->dateFormat ?: $this->getConnection()->getQueryGrammar()->getDateFormat();
    }

なるほど、日付のフォーマットをデータベースタイプから参照してくれる機能のおかげみたいや。

    /**
     * Set the date format used by the model.
     *
     * @param  string  $format
     * @return $this
     */
    public function setDateFormat($format)
    {
        $this->dateFormat = $format;

        return $this;
    }

フォーマットは直接指定できるようなので、Eloquentで指定しておけばええみたいや。

これで、データベースのコネクションエラーにならずにEloquentの日付属性を利用するようなUnitTestも動くようになったで。