🌞

PHP mysqli, pdo_mysql + libmysqlclient + my.cnf の作りはおかしい

2025/04/08に公開

PHP mysqli, pdo_mysql + libmysqlclient + my.cnf の作りはおかしい

2025-04-08


(1).何が起きるか

  • my.cnf に host, socket, user, password 指定
  • PHP で my.cnf を read させる
    • my.cnfhost , socket , user , password の指定値が無視《むし》される
/foo/bar/my.cnf
[client]
user = xxxx
password = yyyy
host = zzzz
fail.php
$myi = mysqli_init();
mysqli_options($myi, MYSQLI_READ_DEFAULT_FILE, "/foo/bar/my.cnf");
mysqli_real_connect($myi, NULL, NULL, NULL, NULL, 0, NULL); // ex.1, no effect
mysqli_real_connect($myi); //ex.2, no effect

$pdo = new PDO("mysql:", NULL, NULL,
               [PDO::MYSQL_ATTR_READ_DEFAULT_FILE  =>  "/foo/bar/my.cnf"] );
// ex.3 no effect

PHPスクリプトをどのように記述しても my.cnf の host などの値が渡らない。
PHP *MySQL* ドライバーの中身(C言語)の作りが問題。


(2).PHPソースの何がまちがっているか

  • PHP 内で使用されている C の mysql_real_connect() 引數《ひきすう》に NULL が渡らない
  • PHP スクリプト内で引數に NULL を與《あた》えてもダメ
  • PHP 中身(Cソース)の初期値がダメ
  • PHP 中身(Cソース)のデフォルト値使用の判定がダメ

直して〜。
しかし PHP 8 が mysqlnd 中心に移行しているため、本家は直さないだろうなぁ。


(3).環境

  • Ubuntu 24.04
  • PHP 5.6.40, 7.4.33, 8.1.2, 8.4.5
  • MySQL 5.0.96, 5.6.36, 8.4.4

(4).もう少し中身をみると

mysql_real_connect() の引數《ひきすう》を "" にすれば my.cnf 使うじゃん!」と安易に考えてはならない。
NULL なのよ...。
my.cnf の接續《せつぞく》指定を利用するのは NULL なのだ。

MySQL 5 までの libmysqlclient の動き

  • mysql_real_connect()host などに NULLport0 をセットすると
    • my.cnf の値を利用する
    • mysql_options MYSQL_READ_DEFAULT_FILE でファイルを指定可能
  • NULL (or 0 ) 以外の値の場合は、その値を使用する
  • NULL"" は違う動きをする (除く一部)

libmysqlclient 内の、mysql_real_connect() の引數《ひきすう》判定のおおよその流れは以下のようになっている。

mysql_real_connect()内
// my.cnf の値をみる

// check args
if (!host || !host[0]) {
    // 引數 host = NULL の時、my.cnf 値セット
    // 引數 host = "" の時は、このブロックに入らない。
}
//同じく
if (!user || !user[0]) { ... }
if (!passwd) { .... }
if (!db || !db[0]) { .... }
if (!unix_socket) { ... }
if (!port) { ... }
// port は int につき、0 で判定

C言語において:

  • char *str において

  • str = NULL の時、if (!str)TRUE

  • str = "" の時、if (!str)FALSE。∵ ""TRUE

  • if (!str) の記述、個人的に好きになれない理由がこれ。

    • みんな〜、if (str != NULL) って書こうよ〜
  • 言語によって仕樣《しよう》が違うため、こんがらがる。特にいくつかの言語を行ったりきたりしている時。頭が切りかわらずにハマる。

test.php
<?php
var_dump( NULL? "true" : "false" ); //-> string(5) "false"
var_dump( ""  ? "true" : "false" ); //-> string(5) "false"
var_dump( "0" ? "true" : "false" ); //-> string(5) "false"
var_dump( 0   ? "true" : "false" ); //-> string(5) "false"
test.c
#include <stdio.h>
void main() {
  printf("%s\n", NULL? "true" : "false" ); // -> false
  printf("%s\n", ""  ? "true" : "false" ); // -> true
  printf("%s\n", "0" ? "true" : "false" ); // -> true
  printf("%s\n", 0   ? "true" : "false" ); // -> false
}

MySQL 8 以上の libmysqlclient の動き

  • 中身まで追えていない。MySQL 5 と別物になった。
  • MySQL 8.4 マニュアルをみると、
    • NULL は全てで default (or my.cnf 値)となるようだ
    • 引數の一部は "" でも動くようになっているらしい
      • 何にせよ my.cnf の指定を使いたければ、安パイは NULL だろう

If host is NULL or the string "localhost", a connection to the local host is assumed:

If user is NULL or the empty string "", the current user is assumed.

If passwd is NULL, only entries in the user table for the user that have a blank (empty) password field are checked for a match.

If db is not NULL, the connection sets the default database to this value.

If unix_socket is not NULL, the string specifies the socket or named pipe to use.

If port is not 0, the value is used as the port number for the TCP/IP connection.

PHP はどのようにしているか

  • mysql, pdo_mysql は、内部で使用する C 變數《へんすう》の初期値が "", "/tmp/mysql.sock", 3306 になっている。
    • mysqli の内部の變數の初期値は NULL だが、その後の處理《しょり》が問題
  • mysqli_ , pdo:: 函數《かんすう》の arg についての動き
    • PHP スクリプト内でセットする arg が NULL の場合、
      • php.ini の設定で上書き
      • あるいは、C 變數の初期値を利用
      • ⇒ 結果、PHP スクリプト内で NULL 指定してもムダになる
    • PHP スクリプト内で "" をセットすると
      • libmysqlclient の mysql_real_connect()"" がセット
        • mysql_real_connect() に値がセットされたと判定され、my.cnf 値は使われない

ということで、

PHPスクリプトをかえようが、php.ini をかえようが、my.cnf をよみこませるようにしようが、
host, user, socket などに my.cnf の値は渡らない。
PHP スクリプト内で全てをセットする必要が出てくる。

なお、

  • my.cnf に書いても無駄なのは、mysql_real_connect() の引數だけ。他の libmysqlclient オプション ( default-character-set など )は渡る
  • php.iniほにゃらら = と値を空にすると、 "" になる
  • portphp.ini0 にし、PHP スクリプト内も 0 にしておけば良い。これだけは助かる

(5).その他

PHP MySQL は libmysqlclient をリンクする方法が好き。
理由:

  • my.cnf を使えるから
  • my.cnf を利用できれば、リンクしている libmysqlclient が持つ機能が利用可能になるから

がっかり:

  • 最近の PHP は libmysqlclient をリンクさせないように動き始めている
    • PHP 8 は configure で libmysqlclient を蹴るようにしている
      • PHP 8.4 の mysqli は、ソースからも libmysqlclient 部分をカットしていた
        • ⇐ 對策: PHP 8.1.2 の mysqli のソースを利用。PHP 8.4.5 でコンパイルできた

以上

Discussion