[PHP]Notice: Undefined offsetを解決するには

Read More

下記のようなNoticeが出た。
Notice:  Undefined offset:  2 in /hogehoge.php on line 320
Notice:  Undefined offset:  1 in /hogehoge.php on line 320


このNoticeは配列の未定義の要素を利用しようとしたことによるもの。



まずは該当するプログラムの箇所を見てみる。
■問題のプログラム
<?php
//■電話番号
if(!empty($_SESSION['MailForm']["user_tel"])){
  list($_SESSION['MailForm']["user_tel1"],$_SESSION['MailForm']["user_tel2"],$_SESSION['MailForm']["user_tel3"])
  = explode('-',$_SESSION['MailForm']["user_tel"]);
}
?>



この例を使って説明すると、
$_SESSION['MailForm']["user_tel"]の中身に電話番号が入るのだが"0120-000-000"のようなものだったら問題はない。

だが今回Noticeが出たのはこれが"0120000000"のようにハイフンなしで文字列が続けて入ったようだ。

そうするとハイフンを区切り文字として文字列を分割するという処理が想定したように動かない。
3つに分割されるはずの文字列が1つにしか分割されないわけだ。

そして一つ目の配列である$_SESSION['MailForm']["user_tel1"]に"0120000000"という値が入る。
2つ目と3つ目の配列には文字列が分割できなかったのだから何も代入されない。

つまりここで配列として存在しない未定義の要素を利用しようとしているから出たNoticeなわけだ。



ということでこの例のを解決させたいのならば以下の2つが考えられる。
■案1
<?php
//■電話番号
if(!empty($_SESSION['MailForm']["user_tel"]) and substr_count($_SESSION['MailForm']["user_tel"], '-')==2) {
  list($_SESSION['MailForm']["user_tel1"],$_SESSION['MailForm']["user_tel2"],$_SESSION['MailForm']["user_tel3"])
  = explode('-',$_SESSION['MailForm']["user_tel"]);
}
?>



■案2
<?php
//■電話番号
if(!empty($_SESSION['MailForm']["user_tel"])) {
  $ary = explode('-',$_SESSION['MailForm']["user_tel"]);
  if(is_array($ary) and count($ary)==3) {
    list($_SESSION['MailForm']["user_tel1"],$_SESSION['MailForm']["user_tel2"],$_SESSION['MailForm']["user_tel3"]) = $ary;
  }
}
?>


案1では先に区切り文字の数を確認しておくやり方。
案2では分解後の配列が予定した数であるかを確認するやり方。



ちなみに俺ならたぶん以下のように書く。

■案3
<?php
// ↑ここに来る前に入力チェックを済ませておく
$ary = explode('-', $_SESSION['MailForm']['user_tel']);
$_SESSION['MailForm']['user_tel1'] = $ary[0];
$_SESSION['MailForm']['user_tel2'] = $ary[1];
$_SESSION['MailForm']['user_tel3'] = $ary[2];
?>


そう、プログラムの書き方うんぬんよりも、そこに辿り着くより先に入力チェックを行う。

先に行う入力チェックとは、
・値が入っていることと
・半角数値であること(ハイフン除く)
・ハイフンが2つついていること
・桁がおかしくないこと

これらの入力チェックを行いいずれかが引っ掛かればそもそもその後の処理を実行させない。
リダイレクトして入力画面に戻すから。


またlist()を使わない。
この程度の代入ならば関数を使わず一つ一つ書く。
# もし数が多いならfor文で回す


list()を使う時やっちゃダメなのがにそこに長い変数名を使うことだ。
長い変数名を複数使うとどんどん横に伸びてしまうのでそのぶん可読性が下がるからだ。
もし可読性を考慮してlist()内の","(カンマ)で改行を入れるようならば、
そもそもlist()を使う必要すらなくなる。


実際これを自分に聞いてきた子はここの処理が何をしているかよくわかっていなかった。

でも最初から案3のように書いてあったらきっと俺に聞かなくてもわかってたと思う。



…まあそんなことよりそもそも俺はNoticeなんか気にしない派なんだけど。






さてここからは余談だが、
俺ならこの問題、プログラムうんぬん、入力チェックうんぬん以前にまず前提条件が違う。


ここでいう前提条件とは、先述した電話番号のデータをDBに格納する時のことを指したものだが、
自分の場合おそらくカラムを3つにわけて登録するようなことをしない。

というか、もしそうならばUIの電話番号の入力フィールドを最初から3つに分けるだろう。
# あそこでexplode()を使っているということは入力フィールドはきっと一つなんだろう。


まず最初に設計する段階で電話番号をDBに登録する形を考える。

これは作る人のセンスが問われるものであるが、それは、組む時にどれだけメンドクサイ作業が増えるかを予想できるかどうかにかかってくる。
メンドクサイ作業とは時間がかかる作業、つまり予算的なことを含むのだからここは特に重要となる。


電話番号をDBに登録するには以下のパターンが考えられる。
1.カラムを3つに分けて登録する
2.カラムを1つにしてハイフン有りのデータを登録にする
3.カラムを1つにしてそのままの生のデータを登録する
4.カラム1つでハイフン無しのデータを登録する


1に関しては何かと手間がかかるからたぶんやらない。
1つの電話番号に対して3つもカラムを使うのはちょっとねぇ。
FAX番号とかもあるだろうし、仮に電話番号系のものが10個必要だとしたらカラムはx3だから30になるわけでしょ。
絶対いや。
テーブルを正規化して電話番号だけを別テーブルにするのは手間が増えるのでもっといや。
入力チェックやDB登録/更新、画面出力の時も手間が増えるのでイヤ。
それでも、どうしてもこれで行くってなったらUIの入力フィールドを最初から3つに分割して入力させる方法をとるかね。


2に関しては入力チェックでハイフンが付いていることを調べなきゃならん。
それは良いとしてもユーザに「ハイフンがないからハイフン付きで入力して」って要求するのがイヤ。
番号としては間違ってないのに画面が戻されるのがイヤ。再入力しなきゃならんのがイヤ。
まあこちらもUIで入力フィールドを3つに分ければいいんだけど、入力フィールドが3つってことはプログラム内で管理するデータが1つじゃなくて3つってこと。
それがめんどい。


3が一番俺らしい。
ハイフン有りでも無しでもどっちでもイイよってスタンス。
正規表現で入力チェックだけしてそのままDBにぶち込めばいい。
表示する時も何も考えずそのまま表示すればいいだけだし。
モバイルのTELリンクのようにハイフンが邪魔な時のみハイフンを除去する処理を通せばOKだし。
UIの入力フィールドも1つでいい。


4に関しては、わざわざハイフン無しにする理由がない。
だってハイフンはあった方が表示する時見栄えがいいもの。
それにもしユーザがハイフン付きの電話番号を入力してきたらどうするの?
弾くの?それともプログラム側でユーザが入力した値を加工するの?
ユーザが入力したものを加工してDBに登録ってあーた、それ一番邪道だよ。
手を加える時間も無駄だしそういうことはするべきじゃない。



ってことで個人的には3がオススメ。