camelCase と snake_case のイメージをそろえる
まずは形の違いを頭の中でハッキリさせましょう。
camelCase はこういうスタイルです。
userName
createdAt
maxRetryCount
先頭の単語は小文字、2単語目以降の先頭が大文字になります。
snake_case はこうです。
user_name
created_at
max_retry_count
単語をすべて小文字にして、間をアンダースコア _ でつなぎます。
今回やりたいのは、
userName → user_name
createdAt → created_at
maxRetryCount → max_retry_count
のように、camelCase を snake_case に変換するユーティリティです。
PHP のコード(プロパティ名・メソッド名)は camelCase、
DB のカラム名や JSON のキーは snake_case、
という設計にしているときに、めちゃくちゃよく出てきます。
変換の考え方を分解してみる
camelCase → snake_case は、考え方としてはとてもシンプルです。
「大文字が出てきたら、その前に _ を入れて、小文字にする」
これだけです。
例えば maxRetryCount なら、文字を順番に見ていくとこうなります。
- m a x まではそのまま
- R が出てきたら、その前に
_を入れてrにする →_retry - C が出てきたら、その前に
_を入れてcにする →_count
結果として max_retry_count になります。
この「大文字の前に _ を入れて、小文字にする」というルールを、コードに落とし込めばOKです。
正規表現を使ったシンプルな実装
PHP でこの「大文字の前に _ を入れる」をやるのに、一番スッキリ書けるのが正規表現です。
基本形の関数
まずは動きが分かりやすい形で書いてみます。
/**
* camelCase を snake_case に変換
*
* @param string $camel
* @return string
*/
function camelToSnake(string $camel): string
{
// 1. 大文字の前にアンダースコアを入れる
$withUnderscore = preg_replace('/([A-Z])/', '_$1', $camel);
// 2. 全部小文字にそろえる
$snake = mb_strtolower($withUnderscore, 'UTF-8');
// 3. 先頭にアンダースコアが付いてしまうケースをケア(先頭が大文字だった場合)
$snake = ltrim($snake, '_');
return $snake;
}
PHPやっていることを言葉で追うと、こうなります。
1つ目の preg_replace で、
'maxRetryCount' → 'max_Retry_Count'
PHPのように、「大文字の前に _ を差し込む」変換をしています。
そのあとで mb_strtolower で全部小文字にして、
'max_Retry_Count' → 'max_retry_count'
PHP最後に、もし先頭に _ が付いていたら ltrim で削ります。
実際に動かしてみる
いくつか例を通して、感覚をつかみましょう。
echo camelToSnake('userName'); // user_name
echo camelToSnake('createdAt'); // created_at
echo camelToSnake('maxRetryCount'); // max_retry_count
echo camelToSnake('id'); // id
echo camelToSnake('UserName'); // user_name(先頭が大文字でもケアできる)
PHPUserName のように、先頭が大文字の camelCase っぽい文字列でも、ltrim($snake, '_') のおかげで user_name に落ち着きます。
重要なポイントを少し深掘りする
なぜ最後に ltrim(‘_’) が必要なのか
preg_replace('/([A-Z])/', '_$1', $camel); は、
「大文字の前に _ を入れる」ので、先頭が大文字の場合はこうなります。
'UserName' → '_User_Name'
PHPこのまま小文字化すると、
'_user_name'
PHPとなり、先頭に余計な _ が付いてしまいます。
そこで、
$snake = ltrim($snake, '_');
PHPで、先頭の _ を削っています。
これで、
UserName → user_name
PHPという、期待どおりの変換になります。
なぜ mb_strtolower を使っているのか
strtolower でも英字だけなら動きますが、
プロジェクト全体を UTF-8 で統一しているなら、
「小文字化は mb_strtolower を使う」と決めておく方が安全です。
- 英字だけの camelCase を扱う前提なら、どちらでもほぼ同じ
- でも、将来アクセント付き文字などが混ざる可能性を考えると、最初から
mb_*に寄せておく方が安心
という判断です。
もう少しだけきれいに書くバリエーション
正規表現を少し工夫すると、「小文字+大文字」の境目だけを狙う書き方もできます。
function camelToSnake(string $camel): string
{
$snake = preg_replace('/([a-z0-9])([A-Z])/', '$1_$2', $camel);
return mb_strtolower($snake, 'UTF-8');
}
PHPこのパターンは、
- 「小文字 or 数字」のあとに「大文字」が来たところだけ
- その間に
_を入れる
という動きになります。
maxRetryCount の場合:
xR→x_RyC→y_C
結果 → max_Retry_Count → 小文字化 → max_retry_count
先頭が大文字のケース(UserName)では、
([a-z0-9])にマッチしないので、先頭には_が入らない- 結果 →
User_Name→ 小文字化 →user_name
となり、ltrim が不要になります。
こちらの方が「余計な _ を後から削る」必要がないので、
正規表現に慣れてきたら、この書き方の方が好みになるかもしれません。
実務での使いどころ
PHP のプロパティ名 → DB カラム名
よくあるパターンはこれです。
- PHP のプロパティ:
$userName - DB のカラム名:
user_name
保存するときに、プロパティ名からカラム名を自動で作りたい、というケースです。
$object = new stdClass();
$object->userName = 'Taro';
$object->createdAt = '2026-01-27 10:00:00';
$data = [];
foreach ($object as $prop => $value) {
$column = camelToSnake($prop);
$data[$column] = $value;
}
// $data は ['user_name' => 'Taro', 'created_at' => '2026-01-27 10:00:00']
PHPこういう「マッピング処理」の中に camelToSnake() を一つ挟むだけで、
DB 側の命名規則(snake_case)と、PHP 側の命名規則(camelCase)をきれいに橋渡しできます。
API のキー変換
自分のアプリ内では camelCase で扱っているけれど、
外部APIには snake_case で送りたい、というケースもよくあります。
$payload = [
'userName' => 'Taro',
'createdAt' => '2026-01-27T10:00:00Z',
];
$converted = [];
foreach ($payload as $key => $value) {
$converted[camelToSnake($key)] = $value;
}
// ['user_name' => 'Taro', 'created_at' => '2026-01-27T10:00:00Z']
PHPまとめ:今日からの「camelCase → snake_case」ユーティリティ
押さえておきたいポイントをコンパクトにまとめると、こうなります。
camelCase → snake_case は、
- 「大文字の前に
_を入れる」 - 「全体を小文字にする」
という2ステップで考えればよいです。
正規表現を使うと、実装はとてもシンプルになります。
まずは、この関数を一つプロジェクトに置いておけば十分です。
function camelToSnake(string $camel): string
{
$snake = preg_replace('/([a-z0-9])([A-Z])/', '$1_$2', $camel);
return mb_strtolower($snake, 'UTF-8');
}
PHP