こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

$_POSTを一括してサニタイズし、表示する方法

PHP 初心者デス。よろしくお願いします。

$_POSTの中身を配列構造を維持したまま、一括でサニタイズして、
一括で表示、受け渡しする方法をご教授いただけませんでしょうか。

勉強のため、PHPにてメールフォームを作成してます。

index.htmlには以下のようなフォームが入ってます。
(基本的にはtableで区切ってますが、tableの文は省略させていただいております。)

<form method="post" action="confirm.php">
<input type="text" name="テキスト" size="30" />
<input type="text" name="email" size="50" maxlength="50" />

<input type="radio" name="ラジオ" value="選択1" /> 選択1
<input type="radio" name="ラジオ" value="選択2" /> 選択2
<input type="radio" name="ラジオ" value="選択3" /> 選択3

<input type="checkbox" name="チェックボックス[]" value="チェック1" /> チェック1
<input type="checkbox" name="チェックボックス[]" value="チェック2" /> チェック2
<input type="checkbox" name="チェックボックス[]" value="チェック3" /> チェック3

<select name="セレクトボックス[]">
<option value="" selected="selected">選択して下さい</option>
<option value="1">1 </option>
<option value="2">2 </option>
<option value="3">3 </option>
</select>

<select name="セレクトボックス[]">
<option value="" selected="selected">選択して下さい</option>
<option value="4">4 </option>
<option value="5">5 </option>
<option value="6">6 </option>
</select>

<textarea name="テキストエリア" rows="10" cols="70"></textarea>
</form>

------------------------

confirm.php(入力確認用の画面)には以下が入ってます。

<?php
function sanitize($a) {
$_a = array();
foreach($a as $key=>$value) {
if (is_array($value)) {
$_a[$key] = sanitize($value);
} else {
$_a[$key] = htmlspecialchars($value);
}
}
return $_a;
}
?>

上記の内容はこちらのURLを参考にして

http://php.to/tips/4/

一括して入力パラメータのサニタイジングを行っています。
ですが、自分はまだ駆け出しのため、イマイチこの文の内容がちゃんと理解できていません…

とりあえず、サニタイズされたここから、
$key と $value を一括して表示し、
メール送信処理用のファイル[sende.php(仮)]などに受け渡したいのですが、
うまくいかずに悩んでいます。

単純に、
print_r($_sanitized_POST);
と入れても、チェックボックス、セレクトボックスの値がArrayとなって取得できません…

他にこんな感じやってみても同じでした。
foreach($_sanitized_POST as $key=>$var) {
$body.="[".$key."]".$var."\n";
 print $body ;
}

何かうまい方法があればご教授いただけませんでしょうか。

とりあえず考えているのは、confirm.php内で、サニタイズされた値を
<input type="hidden" name="<?=$key?>" value="<?=$var?>" ><?=$var?>

上記のような形で一括で表示して次のファイルに値を渡せたらな、と考えている次第です。
また、index.htmlの内容に追加や、削除など流動的に変更しても
confirm.phpの内容に特に手をつけずに反映させるようにしたいと思っています。
そんなのは無理でしょうか?

初歩的な私ですが、どうぞアドバイスよろしくお願いいたします。

投稿日時 - 2011-08-02 21:16:21

QNo.6917000

困ってます

質問者が選んだベストアンサー

foreach($_POST as $key=>$var) {
if (is_array($key)) {
foreach($_POST[] as $key => $var) {
$body.="[".$_POST[$key]."]".$_POST[$var]."<br />\n";
}

} else {
$body.="[".$key."]".$var."<br />\n";
}
}


は感じとしては近いんですが、
foreachについて曖昧な感じなんだと思います。
PHPは配列が結構特徴的で便利に使えるので、
最初に十分理解しておくと後々楽です。

foreach($array as $key => $val){
//ループ内の処理
}
とした場合、
「$arrayをその要素の数だけループする」
「毎回ループが始まる前に、$keyにその時点の添字(キー)を、$valにその時点の値をコピーする」
という処理になります。


例えばこんな一次元配列があったとして

$array = array('県名'=>'滋賀','名物'=>'琵琶湖');

foreachでループすると

foreach($array as $key => $val){
echo $key.":".$val."<br />";
}

こんな感じに表示されます。
県名:滋賀
名物:琵琶湖

一周目では、
$keyに「県名」がコピーされ、$valに「琵琶湖」がコピーされてから
{}の中が処理されるんです。
二週目では$keyと$valに何がコピーされるかはわかると思います。


これを踏まえた上で、こんな二次元配列があったとして
$array = array('県名'=>'滋賀',
        '名物'=>array('自然'=>'琵琶湖',
'キャラ'=>'ひこにゃん'
               )
        );

これをさっきと同じforeachで回すと
foreach($array as $key => $val){
echo $key.":".$val."<br />";
}

県名:滋賀
名物:array
と表示されます。

なんでこうなるかというと、
一周目はさっきと同じく
$keyに「県名」、$valに「滋賀」がコピーされ
二週目は
$keyに「名物」、$valに「array('自然'=>'琵琶湖',
'キャラ'=>'ひこにゃん')」と定義された配列
がコピーされます
echo に配列を表示する能力はないので、しょうがなく配列を意味する「array」
と表示されます。

ここで重要なのが値がコピーされるのは$valにコピーされる点で、
配列かどうかチェックする対象はこの$valなわけです。
*最初のソースの if (is_array($key)) { のどこが間違ってるかわかると思います。

ということで、
答えを書いても面白く無いので、以下を考えてみてください。

この配列
$array = array('県名'=>'滋賀',
        '名物'=>array('自然'=>'琵琶湖',
'キャラ'=>'ひこにゃん'
               )
        );
をforeachでループさせて、
要素の値が配列の場合、値が配列です!表示できません
と表示する処理を考えてみてください。
こんな感じで
県名:滋賀
名物:値が配列です!表示できません






これが出来たら、次は
ifの中でその時点で配列である$valをもう一度foreachで回すといいかんじです。
foreachの中でもう一度foreachを回す場合、
$keyと$valをつかい回すと上書きされちゃうので、別の変数を使って下さい
$k と $vとかでもいいです

投稿日時 - 2011-08-04 23:19:57

補足

大変おまたせしまして申し訳ありません。
しかし、
おかげさまで、ついにたどりつけました!
ありがとうございます!!

まだちょっと表示やコードが粗いですが、
以下内容でなんとか$POSTから配列を含むデータを受け渡しできそうです!

------------------------------------------------------

foreach($_POST as $key=>$val) {
if (is_array($val)) {
foreach($val as $k => $v) {
if($k != 0){
$body.= $v."\n";
}else{
$body.="[".$key."]".$v."<br />\n";
}
}

} else {
$body.="[".$key."]".$val."<br />\n";
}
}
print_r($body);

------------------------------------------------------

サニタラズも必要な際に、合間に
htmlspecialchars($val);などをかませれば、
なんとかなりました!

途中、foreach($val as $k => $v)の中に、
if($k != 0)に入れたのは、
同じkeyをひとつにまとめるために、入れてみました!

入れないと、
[セレクトボックス] 1
[セレクトボックス] 2
[セレクトボックス] 3
みたいになってしまうためです。

おかげさまで、
考え方、返り値や再帰など自身のいたらない点、今後の学ぶべきところを把握できました。
あらためてありがとうございます。

最後までずうずうしくて申し訳ないのですが、
よろしければ、参考に
すっきりまとまったコード?
こうすればもっと良い点など、

を教えていただけませんか?
ぜひ今後の参考にしたいです。

投稿日時 - 2011-08-08 09:59:16

お礼

毎回ご丁寧なアドバイスありがとうございます。
なんだかじれったいですが、おもしろくなってきました!
まだ全文ちゃんと読みきれてないですが、
もうちょっとがんばって考えてみます。

申し訳ないですが、少しお時間を、2、3日いただきます。
よろしくお願いします。
PS.ひこにゃんカワイイです。

投稿日時 - 2011-08-05 10:27:59

このQ&Aは役に立ちましたか?

1人が「このQ&Aが役に立った」と投票しています

回答(5)

とりあえず、一度サニタイズについては忘れましょう。

また参考のURLでは再帰という手法を使って、
配列が何次元になっていてもその構造を崩さずに
その中身について操作できるように工夫されています。

関数の中で同じ関数が呼び出されている部分が再帰です。

ただし、これも理解するのは少し時間と練習が必要なので、ここでは忘れましょう。
(使いこなせると非常に便利ですが)

もう少し仕様に制約をつけて、目的を絞るとやるべきことが明確になります。

具体的には
制約
・$_POSTの配列の深さは1次元か2次元しか存在しないとする(現在のHTMLがそんな感じ)
・セキュリティ面はとりあえず忘れる
・正しい形のデータしか飛んでこない

目的
・フォーム値を同じ関数で複数のファイル間で取り回せるようにする
ととりあえずしておきます。

すると、目標は
「値として値と配列を持つ(最大)二次元配列について
末端の値と添え字を表示できる関数を作る」
ことになります。

添字と値が表示できれば、それを組み合わせてHTMLを作れるので、
hiddenなりなんなりで次のファイルに引き渡せます。

流れとしては
・$_POSTをforeachでループさせる
・ループ中に値が配列かどうかチェック
配列だったら→配列をもう一度foreachでループさせる→ループの中で添字と値を表示する
配列じゃなかったら→添字と値を表示する
という感じです。

この関数が作れれば、今回やろうとしていることは出来るので、
頑張ってみてください。

投稿日時 - 2011-08-04 02:15:42

お礼

いろいろアドバイスありがとうございます。

一日考え通してあれやこれやと試したのですが、ぜんぜんできませんでした…
考え方や理屈は大変よくわかるのですが、
コードの書き方というか…まったくわかってません。
以下が自分の考えた限界です…

foreach($_POST as $key=>$var) {
if (is_array($key)) {
foreach($_POST[] as $key => $var) {
$body.="[".$_POST[$key]."]".$_POST[$var]."<br />\n";
}

} else {
$body.="[".$key."]".$var."<br />\n";
}
}

print_r($body);

当然、できてません…
if (is_array($key))部分以降をいろいろ書き換えてためしたのですが、
POST中で配列の部分がどうしてもArrayで表示されてしまいます。

きっと何か初歩的な間違いなんだろうなとは感じるのですが、
もはや自分では何がいけないのか全くわかりません。

大変恐縮ですが、答えを教えていただけると幸いです。

そして自分の限界を知りました。
返り値、再帰、多次元配列、セキュリティ
それ以前にもっと初歩的なところから出直してきます…

投稿日時 - 2011-08-04 22:38:13

>confirm.php内を手動で全部入力しておりましたので、
>何か、一括でできないかなと思い、今回質問してみた次第です。
こちらのソースの
http://php.to/tips/4/
一部では無く、全部をそのままconfirm.phpに入力して実行した場合はどうなるでしょうか?

おそらく、関数の戻り値についての理解が追いついていないように思いますよ。



>ブラウザ側でhidden値をいじれてしまうというのが、
>ちょっとよくわからなかったのですが、
>あまりよろしくないやり方だったでしょうか?
フォームの送信内容については、
フォームが表示されている、されていない、input typeがXXである、フォームが存在しない
など、あらゆる条件を無視してブラウザは自由に内容を変更して送ることが出来ます。

そのため、hiddenにサニタイズ済みの値があったとしても、
悪意のあるユーザがこれを改竄して送りつけることは可能なので、
再度サニタイズが必要になります。

>サニタイズはあまり必要なかったでしょうか?
必要ではあるんですが、用途に合わせてサニタイズする必要があるので、
htmlspecialchars($value);
だけだと足りなかったり、逆に用途に合わなかったりします。

htmlspecialchars()ごく簡単に言うと、
入力された文字列(例えばHTMLタグが組み込まれたような文字列)
をそのままHTMLに組み込んでも単なる文字列として表示されるような形に変換する
ための関数なので、メール送信やデータベースへの入力には適さないのです。

とはいえ、このあたりは基本的なところが出来た段階であとでじっくり勉強される方が理解が早いと思いますので、まずは今やろうとしている方法を実現されるのがいいかと思います。

投稿日時 - 2011-08-03 15:52:34

お礼

ありがとうございます。
セキュリティ、サニタイズについてあらたに勉強になりました。

そしてたしかに、私はご指摘のとおり、
関数の戻り値についての理解が足りてません。すみません。
もっと勉強して出直します。

とりあえず参考にしたURLの全部を使用した場合ですが、
サニタイズされてPOSTのデータはたしかにすべて表示されるのですが、
string(0) や Array ( [0] => [1] => [2] =>)などの表示もいっしょにされてしまい、
必要な値だけがうまく次のファイルsend.phpに渡す方法がおもいつかず苦悩しておりました。

サニタイズ以前に、
POSTのデータの中で、配列構造が混じっていると、
うまく一括で値を表示や受け渡しする方法がおもいついてない状態です。
参考にしたURLの以下のあたりを応用してうまくできないか模索はしているのですが…

foreach($a as $key=>$value) {
if (is_array($value)) {
$_a[$key] = sanitize($value);
} else {
$_a[$key] = htmlspecialchars($value);
}
}

無理でしょうか?
あともう一歩ヒントをいただけると幸いです。

投稿日時 - 2011-08-03 22:07:47

サニタイジングを行ったら、
ユーザの手に渡る前に処理をしてしまわないと全く意味がないですよ。
(サニタイジング自体も今回のメール送信という処理から考えると適合していませんが、少し別の話なので・・・)

例えば、
****
とりあえず考えているのは、confirm.php内で、サニタイズされた値を
<input type="hidden" name="<?=$key?>" value="<?=$var?>" ><?=$var?>

上記のような形で一括で表示して次のファイルに値を渡せたらな、と考えている次第です。
****
としても、ブラウザ側でhidden値は自由にいじれてしまいますので無意味です。

なので、例えばサニタイジングした変数を一度セッションに格納して、
send.phpではPOST値ではなくSessionのデータを使うなどすれば少し楽です

投稿日時 - 2011-08-03 11:41:47

お礼

ありがとうございます。
サニタイズして、hiddenで表示していたのは、
購入した初心者用のPHP書籍にあったので、まねてみておりました。

ただその書籍は一括で表示するなどではなく、
POSTの値をhtmlspecialcharsしてからhiddenなどに値を入れるなど、
confirm.php内を手動で全部入力しておりましたので、
何か、一括でできないかなと思い、今回質問してみた次第です。

ブラウザ側でhidden値をいじれてしまうというのが、
ちょっとよくわからなかったのですが、
あまりよろしくないやり方だったでしょうか?
サニタイズはあまり必要なかったでしょうか?

Sessionでの方法もがんばって考えてみます。ありがとうございました。

投稿日時 - 2011-08-03 14:45:19

ANo.1

nameにマルチバイト文字(日本語とか)は使えないですよ
これをやっている限りおそらく何をやってもバグはとれません

投稿日時 - 2011-08-02 22:52:51

お礼

すみません。日本語部分は訂正させていただきます。
とりあえず半角英字 仮定でよろしくお願いします。

投稿日時 - 2011-08-03 09:30:58

あなたにオススメの質問