k-holyのPHPとか諸々メモ

Webで働くk-holyがPHP(スクリプト言語)とか諸々のことをメモしていきます。ソースコードはだいたいWindowsで動かしてます。

mysqliのコンストラクタで"No such file or directory"のエラー

mysqli::__construct() で以下のエラーが発生。

mysqli::mysqli(): (HY000/2002): No such file or directory

調べてみると、MySQLのソケットファイル(mysql.sock)が見当たらない場合にこのエラーが発生することがあるようです。

パッケージでインストールした場合はよしなに設定してくれるはずですが、順序によるものか、MySQLの設定とPHPの設定でソケットファイルのパスが異なってしまうケースもあるとのこと。

(そういえば某レンタルサーバの初期設定で同様の問題が起きていました…)

参考

mysqliモジュールの設定をコマンドで確認してみます。

$ php --ri mysqli
mysqli

MysqlI Support => enabled
Client API library version => mysqlnd 5.0.11-dev - 20120503 - $Id: bf9ad53b11c9a57efdb1057292d73b928b8c5c77 $
Active Persistent Links => 0
Inactive Persistent Links => 0
Active Links => 0

Directive => Local Value => Master Value
mysqli.max_links => Unlimited => Unlimited
mysqli.max_persistent => Unlimited => Unlimited
mysqli.allow_persistent => On => On
mysqli.default_host => no value => no value
mysqli.default_user => no value => no value
mysqli.default_pw => no value => no value
mysqli.default_port => 3306 => 3306
mysqli.default_socket => /var/lib/mysql/mysql.sock => /var/lib/mysql/mysql.sock
mysqli.reconnect => Off => Off
mysqli.allow_local_infile => On => On

pdo_mysqlの方も念のため。

$ php --ri pdo_mysql
pdo_mysql

PDO Driver for MySQL => enabled
Client API version => mysqlnd 5.0.11-dev - 20120503 - $Id: bf9ad53b11c9a57efdb1057292d73b928b8c5c77 $

Directive => Local Value => Master Value
pdo_mysql.default_socket => /var/lib/mysql/mysql.sock => /var/lib/mysql/mysql.sock

どちらも /var/lib/mysql/mysql.sock になっています。

findコマンドで探してみても見つからない…それもそのはず、今回はWebサーバとDBサーバを分離していて、PHPが動作しているWebサーバにはMySQLを入れてなかったのです。

(それなのにMySQLソケットファイルを探しに行ってる時点で、おかしいと気付くべきでしたが…)

なお、MySQLソケットファイルの変更は php.ini や ini_set()関数 でこれらの設定値を変更するほか、以下の方法でも可能です。

mysqli::__construct() の第6引数でMySQLソケットファイルのパスを指定する

PHPマニュアルの mysqli::__construct() の通り、コンストラクタの引数はこうなってます。

mysqli::__construct (
    string $host = ini_get("mysqli.default_host"),
    string $username = ini_get("mysqli.default_user"),
    string $passwd = ini_get("mysqli.default_pw"),
    string $dbname = "",
    int $port = ini_get("mysqli.default_port"),
    string $socket = ini_get("mysqli.default_socket")
)

第6引数でソケットファイルへのパスを指定できます。

注意: socket 引数を指定しても、MySQL サーバーへの 接続時の型を明示的に定義することにはなりません。MySQL サーバーへの 接続方法については host 引数で定義されます

この注意書きはちょっと分かりづらいのですが、「接続時の型」とはUNIXドメインソケットで接続するか、それともTCPソケットで接続するか、という意味でしょうか。

MySQLでは "localhost" と "127.0.0.1" が別物というのは有名な話で、"localhost" ではUNIXドメインソケット、"127.0.0.1" ではTCPソケットが利用されるという違いがあり、MySQLのユーザー権限もそれぞれ個別に設定する必要があります。

あとよく引っかかるものとして、MySQLの設定で skip-networking が有効にされている場合、ネットワーク経由の接続を禁止=UNIXドメインソケットしか受け付けなくなるので、host=127.0.0.1 を指定してると接続できない、ということがあります。

しかし、今回の場合はLAN内の別のホストで動いているDBサーバに接続しようとしているはずで、No such file or directory がソケットファイルを探した結果のエラーだとすると、そんなエラーが発生すること自体がおかしいわけで。

あっ、と思ってアプリケーション側の設定ファイルを確認してみると、いつもの癖で接続先ホストを "localhost" って書いてしまってたという単純なオチでした…。

PDO::__construct() の第1引数でMySQLソケットファイルのパスを指定する

もうオチはついたんですが、せっかくなのでPDOの場合も書いておきます。

PHPマニュアルの PDO::__construct の通り、コンストラクタの引数はこうなってます。

public PDO::__construct (
    string $dsn,
    string $username,
    string $password,
    array $driver_options
)

第1引数のDSNに受け付ける内容は使用するPDOドライバによって異なりますが、MYSQLの場合は PDO_MYSQL DSN に書かれており、ソケットを指定する例があります。

unix_socket MySQL の unix ソケットを指定します (host あるいは port と同時に使用することはできません)。

こんな風にDSNでソケットを指定できます。

mysql:unix_socket=/tmp/mysql.sock;dbname=testdb

なお前述した通り、unix_socket=... と指定していなくても、host=localhost と指定している場合、UNIXドメインソケットを使った接続が行われます。

注意: Unix のみ ホスト名を "localhost" にすると、 サーバーへの接続はドメインソケットを使って行われます。 libmysqlclient を使って PDO_MYSQLコンパイルした場合は、 ソケットファイルの場所は libmysqlclient のコンパイル時の場所になります。 mysqlnd を使って PDO_MYSQLコンパイルした場合は、デフォルトのソケットは pdo_mysql.default_socket の設定を使って作られます。

PDO関数だけでなくmysql、mysqli等のエクステンションでは旧来libmysqlclientライブラリを標準で利用していましたが、PHP5.4以降はPHP拡張として実装された mysqlnd (MySQL native driver for PHP) を標準で利用するように変わりましたので、今回の環境でPDO_MYSQLを利用する場合は前述の通り pdo_mysql.default_socket => /var/lib/mysql/mysql.sock を参照することになります。

なお、PHPMySQLを利用する際の様々な問題については、こちらの記事が非常によくまとめられていて、おすすめです。