以前以下の記事でキャッシュファイルのパーミッション設定を結局「umask」を利用しましたが、もっと良い方法がないか調べてみました。
Laravelのファイルパーミッションの問題
他は?
ファイル・ディレクトリの作成がumaskの影響を受けることなら、ログの設定ファイルでパーミッションはどうやるの?調べてみました。たどり着いたのが以下のメソッドです。
Monolog\Handler\StreamHandler.php
/**
* {@inheritdoc}
*/
protected function write(array $record): void
{
if (!is_resource($this->stream)) {
if (null === $this->url || '' === $this->url) {
throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
}
$this->createDir();
$this->errorMessage = null;
set_error_handler([$this, 'customErrorHandler']);
$this->stream = fopen($this->url, 'a');
if ($this->filePermission !== null) {
@chmod($this->url, $this->filePermission);
}
restore_error_handler();
if (!is_resource($this->stream)) {
$this->stream = null;
throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url));
}
}
if ($this->useLocking) {
// ignoring errors here, there's not much we can do about them
flock($this->stream, LOCK_EX);
}
$this->streamWrite($this->stream, $record);
if ($this->useLocking) {
flock($this->stream, LOCK_UN);
}
}
結局chmodか〜〜〜。そうなら俺も作るよ。
カスタムキャッシュドライバを作成
設定ファイル
先ず、設定ファイルを追記して置きましょう!permissionsを追加しました。
config/cache.php
'file' => [
'driver' => 'local',
'path' => storage_path('framework/cache/data'),
'permissions' => [
'dir' => 0775,
'file' => 0664,
],
],
ここのdriverも「file」→「local」に変えます。
ストア作成
そのあと、ドライバー用のストアを作成します。
pp\Extensions\CacheLocalFileStore.php
<?php
namespace App\Extensions;
use Illuminate\Cache\FileStore;
use Illuminate\Filesystem\Filesystem;
use League\Flysystem\Cached\CachedAdapter;
class CacheLocalFileStore extends FileStore
{
/**
* The file cache permissions.
*
* @var array
*/
protected $permissions;
/**
* Create a new file cache store instance.
*
* @param Filesystem $files
* @param $config
*/
public function __construct(Filesystem $files, $config)
{
$this->files = $files;
$this->directory = $config['path'];
$this->permissions = $config['permissions'];
}
/**
* Store an item in the cache for a given number of seconds.
*
* @param string $key
* @param mixed $value
* @param int $seconds
* @return bool
*/
public function put($key, $value, $seconds)
{
$this->ensureCacheDirectoryExists($path = $this->path($key));
$result = $this->files->put(
$path, $this->expiration($seconds).serialize($value), true
);
@chmod($path, $this->permissions['file']);
return $result !== false && $result > 0;
}
/**
* Create the file cache directory if necessary.
*
* @param string $path
* @return void
*/
protected function ensureCacheDirectoryExists($path)
{
$concatPath = $this->directory;
$dir = trim(str_replace($concatPath, '', dirname($path)), '/');
$dirNames = explode('/', $dir);
foreach ($dirNames as $direName) {
$concatPath .= '/'. $direName;
if (! $this->files->exists($concatPath)) {
$this->files->makeDirectory($concatPath, 0777, true, true);
@chmod($concatPath, $this->permissions['dir']);
}
}
}
}
既存のFileStoreを継承して使いました。ensureCacheDirectoryExists中でforeach分を使った理由は作成するpathが「/8/50」ようなディレクトリ構造なのでchmodが親の断層も出来る用法が思い浮かばなかったからです。
プロバイダー作成
登録するためにプロバイダーを作成します。
<?php
namespace App\Providers;
use App\Extensions\CacheLocalFileStore;
use App\Extensions\MongoStore;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;
class CacheServiceProvider extends ServiceProvider
{
/**
* コンテナ結合の登録
*
* @return void
*/
public function register()
{
//
}
/**
* 全アプリケーションサービスの初期起動
*
* @return void
*/
public function boot()
{
Cache::extend('local', function ($app) {
return Cache::repository(new CacheLocalFileStore($app['files'], config('cache.stores.file')));
});
}
}
設定ファイルのドライバー「local」で登録します。上記のプロバイダーをconfig/app.phpに登録します。