2
0

More than 3 years have passed since last update.

LaravelのRedisで存在しないキーを取得しようとするとnullが返ってくるお話

Last updated at Posted at 2020-03-07

false が返ってきたり null が返ってきたりする楽しい世界です。

前提

動作環境

  • Laradock
    • Laravel 6.12
    • Redis 5.0.3
    • phpredis 5.1.1

DBの中身

Redis の中には key1 のみが入っているとします。

127.0.0.1:6379> SET key1 value1
OK
127.0.0.1:6379> KEYS *
1) "key1"

要約

Redis

Redis で存在しないキーを指定すると、 nil が返ってきます。

127.0.0.1:6379> GET key1
"value1"
127.0.0.1:6379> GET key2
(nil)

PhpRedis

PhpRedis では、boolean 型の false が返ってきます。

$redis = new Redis();
$redis->connect('redis', 6379);
$value = $redis->get('key2');
var_dump($value);
string(6) "value1"
bool(false)

Laravel

Laravel で Redis に接続するときに PhpRedis を使っている場合、null 型の null が返ってきます。

use Illuminate\Support\Facades\Redis;

Redis::set('key1', 'value1');
$value = Redis::get('key1');  //--> "value1"
$value = Redis::get('key2');  //--> null

解説

Redis

Redis の GETMGET で存在しないキーを取得しようとすると、 nil が返ってきます。

"nil" という 文字列SET することはできますが、 nil という 特別な値SET することはできません。1

127.0.0.1:6379> GET key2
(nil)
127.0.0.1:6379> SET key3 nil
OK
127.0.0.1:6379> GET key3
"nil"

PhpRedis

PhpRedis の getmget で存在しないキーを取得しようとすると、 boolean 型の false が返ってきます。

このことは 公式ドキュメント にも明記されています。

Return value

String or Bool: If key didn't exist, FALSE is returned. Otherwise, the value related to this key is returned.

しかし、ここでぜひ思い出していただきたいのは、PHP では "0" や空文字と false は等しく扱われるということです。

// 値として "0" をセットしてみる。
$redis->set('key4', 0);
$val4 = $redis->get('key4');

// キーの存在チェックのつもり...
if (!$val4) {
    echo "key4は存在しない";
}

// 値として空文字をセットしてみる。
$redis->set('key5', "");
$val5 = $redis->get('key5');

// これもキーの存在チェックのつもり...
if (!$val5) {
    echo "key5は存在しない";
}

さて、結果はどうでしょうか?

key4は存在しない
key5は存在しない

これを防ぐには、 === 演算子による厳密比較しかありません。

// 正しいキーの存在チェック
if ($val4 === false) {
    echo "key4は存在しない";
}

Laravel

さて、ようやく本題です。

Laravel では、Redis にアクセスする際に PhpRedis を使うことができます。

PhpRedis を使っているのですから、 get()mget()false を返してきそうなものですが、実際には null が返ってきます。

その理由は Illuminate\Redis\Connections\PhpRedisConnection.php にあります。

PhpRedisConnection.php
public function get($key)
{
    $result = $this->command('get', [$key]);

    return $result !== false ? $result : null;
}

public function mget(array $keys)
{
    return array_map(function ($value) {
        return $value !== false ? $value : null;
    }, $this->command('mget', [$keys]));
}

取得した値が false かどうかをわざわざチェックして、 false だったときには null を返すようにしていることが分かります。

なぜこんな面倒なことをしているのか?

かつて PhpRedis が返す false をそのまま使った結果、キャッシュ周りで バグ が起きました。そのときに null を返すようにしたようです。

感想

Laravel で Redis にアクセスするときに PhpRedis を使っているからといって、PhpRedis と同じ結果になるとは限りません。

バグ修正時の ある開発者のコメント をもじって言うならば、

僕らは Laravel を使っているのであって、PhpRedis を直接使っているわけではない

といったところでしょうか。2

参考


  1. この Redis の動作は明瞭で、あとで見る PHP のゆるゆるな型システムから戻ると、とても気持ちよく感じます(笑) 

  2. nullを返すこと自体は別に構わないのですが、ドキュメントに記載されていないのは、少し不親切に感じるところです。フレームワークのソースコードを見れば分かるだろ!と言われれば、それまでですが... 

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0