かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

MySQL カラム名にハマる

バグ踏みの達人です。
PHP から MySQL のデータを更新しようとして珍しいバグを踏んだのでメモ

何故かエラーになる

あるテーブルに次のようなコードでデータを入れようとしたらエラーになりました。

<?php
/*
 * id: INT AUTO_INCREMENT NOT NULL PRIMARY KEY
 * token: VARCHAR(255)
 * exp: timestamp NOT NULL
 * revoke: TINYINT(1) NOT NULL DEFAULT 0
 */
function create_token($token, $exp) {
  $pdo = db_connect();
  $sql = "INSERT INTO {$table} (id, token, exp, revoke) VALUES (null, :token, :exp, 0)";
  $stmt = $pdo->prepare($sql);
  $pdo->beginTransaction();
  $stmt->bindValue(':token', $token, PDO::PARAM_STR);
  $stmt->bindValue(':exp', $exp, PDO::PARAM_INT);
  $stmt->execute();
  $pdo->commit();
}

👇

Uncaught Exception: INSERT TOKEN: SQLSTATE[42000]: Syntax error or access violation: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use
near 'revoke) VALUES (2, 'token...'

You have an error in your SQL syntax SQL 文にシンタックスエラーがあるとのこと。
ただどう見ても SQL 文に間違いは無さそう…

カラム名 revoke が原因

revokeMySQL予約語になっていて、この SQL が渡された時に revoke を実行しようとすると解釈してしまうために不正な SQL になっていたっぽい。

13.7.1.6 REVOKE 構文 MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.7.1.6 REVOKE 構文

これ。

予約語カラム名にしている場合は ` バッククオートでカラム名を囲めばエラーにならない

<?php
$sql = "INSERT INTO {$table}
  (id, token, exp, `revoke`) 
  VALUES
  (null, :token, :exp, 0)";

こんな感じで `revoke` とすればエラーにならずに SQL が正しく実行されました。 (作っていたものは、まだ初期段階だったのでカラム名を変更しました。)

所感

エラーがシンタックスエラーとししか出なかったので、順番にカラムを消したり値を変えたりで試して原因を特定するのが大変でした。
SQLカラム名の指定に ` (バッククオート) なしでも動作するけど、mysqlAdmin で SQL 作る時にデフォルトで ` が付けられるのはこういう理由があったからなのか〜と納得したのでした。
DBのカラム名ローマ字読みな日本語にするの案外このバグを踏むこと無さそうなので良いのかもしれない…


[参考]

SQLアンチパターン

SQLアンチパターン

  • 作者:Bill Karwin
  • 発売日: 2013/01/26
  • メディア: 大型本

アガツマ ミュークルドリーミー おしゃべりしようみゃ

アガツマ ミュークルドリーミー おしゃべりしようみゃ

  • 発売日: 2020/03/27
  • メディア: ウェア&シューズ

バグ踏み体操で荒んだ心を可愛い画像で癒そう…

PHP 定数を文字列展開させたい。

久々に PHP 書いてます。
なんも覚えてねぇ…

PHP は定数は文字列展開できない。

PHP " (ダブルコーテーション) な文字列なら下記のように変数を文字列展開できるかと思います。

<?php
$table = 'users';
$sql = "INSERT INTO {$table} (id, name, ...)";
// => "INSERT INTO users (id, name …)";

定数は展開されない…

<?php
define('TABLE', 'users');
$sql = "INSERT INTO {TABLE} (id, name, ...)";
// => "INSERT INTO {TABLE} (id, name …)";

そういう仕様らしい…
不便… ʕ;ᴥ;ʔ

<?php
$sql = 'INSERT INTO ' . TABLE . ' (id, name, ...)';

でも良いのですが、定数の前後にスペース忘れたりしそうだったのでなんとか文字列展開させたい…

PHP は文字列展開のブランケット内で関数を呼ぶことはできない

それなら文字列返すだけの関数作れば良いのでは?と思ったがダメだった

<?php
define('TABLE', 'users');
function S_($str) { return $str; }
$sql = "INSERT INTO {S_(TABLE)} (id, name, ...)";
// => "INSERT INTO {S_(TABLE)} (id, name, ...)"

(´◦ω◦`):

どんなスカラー変数、配列の要素あるいはオブジェクトのプロパティの文字列表現であっても この構文で含めることができます。 文字列の外側に置く場合と同様に式を書き、これを { と } の間に含めてください。'{' はエスケープすることができないため、 この構文は $ が { のすぐ後に続く場合にのみ認識されます (リテラル "{$" を指定するには、"{\$" を使用してください)。
cf. 複雑な (波括弧) 構文

変数で関数を呼び出すのはOK

$ から始まるもの、つまり変数しか認識できないっぽいので、関数を呼び出す変数を作成すればOKっぽい

<?php
define('TABLE', 'users');
$_ = function($str) { return $str; };
$sql = "INSERT INTO {$_(TABLE)} (id, name, ...)";
// => "INSERT INTO users (id, name, ...)"

₍ ᐢ. ̫ .ᐢ ₎ 👌 デキタ

ただし変数なのでスコープに注意する必要がある。

上記のように同じスコープにベタが書きすることってほぼ無いと思うのでちょっと面倒くさい。

<?php // helper.php
$_ = function($str) { return $str; };
<?php // main.php
require_once __DIR__ . '/helper.php';
function create_user($data) {
  global $_; // スコープ外なので global 変数として使えるようにする
  $sql = "INSERT INTO {$_(TABLE)} (id, name, ...)";
}

めんどい…
もっと良き方法あれば教えて下さい!


[参考]

四畳半タイムマシンブルース

四畳半タイムマシンブルース

Mac OS Catalina PHP インストールにハマる (phpenv諦めてphpbrew)

Mac OS Catalina にアップデートして Composer を使おうとしたら動作しなくなっていました… ( Catalina 開発トラブル多すぎない?)
PHPを再インストールしたら動くという情報を目にしたので、折角なので phpenv でインストールをしようとしたのですが、永遠に様々なエラーが出てきて phpenv install が動作する状態に辿り着けそうになかったので phpbrew を再インストールして PHP のインストールに成功したのでメモ。
あくまでログなので無駄な部分あるかもですが。

phpenv で挫折した時参考にしていたサイトさま

永遠にエラーがループして根本的にどこに問題があるのか理解でなかったので私は諦めてしまいました。

PHP なんもわからん…

1 phpbrew をインストール

https://github.com/phpbrew/phpbrew/blob/master/README.ja.m

$ curl -L -O https://github.com/phpbrew/phpbrew/releases/latest/download/phpbrew.phar
$ chmod +x phpbrew.phar

# $PATH の通っているディレクトリにファイルを移動します
$ sudo mv phpbrew.phar /usr/local/bin/phpbrew

# シェルで bash スクリプトを初期化します
$ phpbrew init

.bashrc に下記を追加

[[ -e ~/.phpbrew/bashrc ]] && source ~/.phpbrew/bashrc

2. 必要なパッケージをインストール

$ brew install autoconf pkg-config bison re2c bzip2 icu4c krb5 libedit libiconv libjpeg libpng libxml2 libzip oniguruma openssl@1.1 pkg-config tidy-html5

.bashrc にパスを設定 (正直どれが本当に必要なのか判ってない)

# openssl@1.1
export PATH=/usr/local/opt/openssl@1.1/bin:$PATH
export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig"
export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"

# libxml2
export PATH="/usr/local/opt/libxml2/bin:$PATH"

PHP_BUILD_CONFIGURE_OPTS="--with-openssl=$(brew --prefix openssl) --with-libxml-dir=$(brew --prefix libxml2)"

PKG_CONFIG_PATH="/usr/local/opt/krb5/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libedit/lib/pkgconfig:/usr/local/opt/libjpeg/lib/pkgconfig:/usr/local/opt/libpng/lib/pkgconfig:/usr/local/opt/libxml2/lib/pkgconfig:/usr/local/opt/libzip/lib/pkgconfig:/usr/local/opt/oniguruma/lib/pkgconfig:/usr/local/opt/openssl@1.1/lib/pkgconfig:/usr/local/opt/tidy-html5/lib/pkgconfig

変更を反映

$ source ~/.bachrc

3. phpbrew で PHP がインストールできればOK

# インストールできるバージョンを確認
$ phpbrew known
# インストール
$ phpbrew install 7.4.7 +default

コケなければOK

PHPのバージョンをを切り替え

# 一時的な切り替え
$ phpbrew use 7.4.7
# 恒久的な切り替え (デフォルのバージョンを指定)
$ phpbrew switch 7.4.7
$ php -v
PHP 7.4.7

切り替えたバージョンになっていればOK

 
Composer 使いたかっただけなのに、ggったり方向転換で一晩潰してしまいました…
PHPローカルの環境づくり辛すぎて触りたくない…
最初から docker の世界だけで触る練習をしておけばよかったと少し後悔しました。。。


[参考]

過去記事見ても PHP 環境作りの辛さしか感じられなかった…

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

カタリナ…は悪くないよ。Mac OS Catalina…君は…