質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

3回答

516閲覧

関数の中で1つずつ丁寧に値を展開しないで済む方法

munekun

総合スコア40

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2024/05/05 13:00

編集2024/05/05 13:04

知りたいこと

関数の中で1つずつ丁寧に値を展開しないで済む方法はありますか?

現状のソースコード

以下のように1つずつ丁寧に値を展開する手間をなくしたいという意図です。

php

1$conditions = [ 2 'name' => 'a', 3 'age' => 1, 4]; 5 6hoge($conditions); 7 8function hoge($conditions) { 9 // 1つずつ丁寧に値を展開する手間をなくしたい 10 $name = $conditions['name'] ?? null; 11 $age = $conditions['age'] ?? null; 12 $phone = $conditions['phone'] ?? null; 13 14 // 展開した値を使っていろいろやる 15 var_dump($name, $age, $phone); 16}

実現したいこと

こういう記述が理想です。

php

1function hoge($conditions){ 2 // 1行で済ませたい 3 [$name, $age, $phone] = $conditions; 4 5 // 展開した値を使っていろいろやる 6 var_dump($name, $age, $phone); 7}

JavaScrriptでこういう記述ができたので、phpはどうかと思いました。

JavaScript

1const conditions = { 2 name: 'a', 3 age: 1 4} 5 6hoge(conditions); 7 8function hoge(conditions){ 9 // 1行で済む 10 const {name, age, phone} = conditions; 11 12 // 展開した値を使っていろいろやる 13 console.log({name, age, phone}); 14}

試したこと

以下のextractArrayValues()を作ってみましたが、$conditions['phone']の値がないと "Undefined array key 2" のエラーになってしまいます。

return $conditions[$key] ?? null;のようにNULL合体演算子を書いているのになぜ "Undefined" なのか・・?)

php

1$conditions = [ 2 'name' => 'a', 3 'age' => 1, 4 'phone' => '090', 5]; 6 7hoge($conditions); 8 9function hoge($conditions) { 10 // 1行で済ませたい 11 [$name, $age, $phone] = extractArrayValues($conditions); 12 13 // 展開した値を使っていろいろやる 14 var_dump($name, $age, $phone); 15} 16 17function extractArrayValues(array $conditions) { 18 return array_map(function($key) use ($conditions) { 19 return $conditions[$key] ?? null; 20 }, array_keys($conditions)); 21}

そして以下のようにextract()を使うと、いざ値を使うというときに$phone ?? nullを指定しないといいけませんし・・
それにextract()ではその変数がどこで宣言されたのかわかりにくい点も微妙ですし・・

php

1$conditions = [ 2 'name' => 'a', 3 'age' => 1, 4]; 5 6hoge($conditions); 7 8function hoge($conditions) { 9 // 1行で済ませたい 10 extract($conditions); 11 12 // 展開した値を使っていろいろやる 13 var_dump($name, $age, $phone ?? null); 14}

何か良い方法はないでしょうか?

バージョン

php は 8.2 です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

melian

2024/05/05 14:04

extract() を使う前に NULL で初期化しておくというはどうでしょうか。(2行になってしまいますが) $name = $age = $phone = NULL; extract($conditions, EXTR_OVERWRITE);
usm2030

2024/05/05 14:18

ここに書いてあるけど、array valuesとかと組み合わせればいいんじゃない https://qiita.com/okumurakengo/items/9c781a17c15ea2916566 extractが微妙の前に書くのが苦になるくらい大量の、連想配列から変数への代入って処理がそもそも微妙では? そもそも、なかったらnullを入れるってのがスーパー微妙。他言語とかも触る人からしたらPHPがイカレに見えてくるレベルのおかしなコード(普通はnullをほかの値に変換するもの) 質問上、連想配列のキーが存在しないケースがどのタイミングで産まれるのかも分からないし(そもそもそこを絶てよって話) 可変変数つかってる社内フレームワークの保守とかしたことあるけど、laravel使えば?って感じしかしないな。 もっとシンプルに考えてこんなことをしないといけないレベルで「複雑な処理をするページ」を作っちゃうのが単純に良くない。 この質問の内容のペースで クラスとか作り始めたら「もっと」保守性が悪くなる ま、別に仕方ないんだろうけど
munekun

2024/05/05 16:55

melian様、ありがとうございます。そのように = で連結しまとめて NULL を代入できたとは、知らなかったです。勉強になりました。
munekun

2024/05/05 17:24

usm2030様、そうなんですか?あちこちでガンガン null 入れちゃったし、すでにクラスとか作り始めちゃってましたw でも例えばこのリンクのような User::find() というメソッドを考え、検索方法に「age」と「name」があるとしますよね。 https://3v4l.org/dHj0U これだけで $conditions の展開が6つ (15~22行目) になってしまいますし、これって普通の記述ではないのでしょうか?
munekun

2024/05/06 05:18

ひとまずベストアンサー決定し、質問を閉じさせていただきます。 アドバイスありがとうございました。
guest

回答3

0

ベストアンサー

filter系の関数はあまり使わないですか?

以下のように1つずつ丁寧に値を展開する手間をなくしたい

やりたい事は、与えられるデータのバリデーションって事だと思うのですが?
そう考えるなら、受け入れたいモノが何であるのかを明示する必要があるのではありませんか?
受け入れたいモノを

PHP

1$args = array( 2'name'=>FILTER_DEFAULT, 3'age'=>FILTER_DEFAULT, 4'phone'=>FILTER_DEFAULT 5);

の様に明らかにしておいて

PHP

1$result = filter_var_array($conditions, $args);

とすれば、$resultarray('name' =>'a', 'age' =>'1', 'phone' => '090') になります。
二つ目の例だと array('name' =>'a', 'age' =>'1', 'phone' => null) になります。

やっている事はバリデーションですから、こんな手抜きじゃなく、もっとキチンとやる事も可能です。
PHPのマニュアル filter_var_array

その後、extract()でも使えばお望み通りになりますし、予定外のキーが存在して競合や上書きの危険性も無いでしょう。

1つずつ丁寧に値を展開する手間をなくしたい

そもそも、キーと同じ名前の変数に置き換えるのが必要なのでしょうか?
階層が余程深いとかなら「理解のし易さを優先する」と言えなくも無いでしょうが、1次元の配列ならそれは通らないと思うのだけれど。
__「タイプ量が~」と言うのはプログラマが吐く言葉じゃないです

投稿2024/05/05 18:20

tezcello

総合スコア211

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

munekun

2024/05/06 05:18

> やりたい事は、与えられるデータのバリデーションって事だと思うのですが? あ、いえ。今回の処理はバリデーションのステップはクリアして、そのあとの取得のステップというつもりでした。 コントローラクラスで $_POST を受け取りバリデーションし、そのコントローラクラス内のメソッド ( 以下の UserContorller::searchAction() ) で実行する取得メソッドが hoge() ということです。 概略はこういう流れです。 ```php class UserContorller { __function searchAction(): void __{ ____// バリデーションのステップ ____$validatedRequest = $this->validate($_POST); ____// 取得のステップ ____$UserMapper = new UserMapper(); ____$data = $UserMapper->hoge($validatedRequest); ____// 出力のステップ ____echo json_encode($data); ____exit; __} } ``` > そう考えるなら、受け入れたいモノが何であるのかを明示する必要があるのではありませんか? おっしゃる通りで、extract() だけだとこれができず微妙な点だと思っていました。 > そもそも、キーと同じ名前の変数に置き換えるのが必要なのでしょうか? ここは根本的な悩みでした。いちいち置き換えることが必要なのか全く判断がつかないのです。 ひとまず「2回以上その値を使うなら置き換える」というルールで統一しているので置き換えたという感じなのですが、その結果今回の質問が生じてしまいました。
tezcello

2024/05/06 08:22

> 今回の処理はバリデーションのステップはクリアして、そのあとの取得のステップというつもり 「バリデーション」というのはブラウザからデータを受け取る場合のみを対象にしている訳ではありません。 受け取る値が、意味のある値(=価値(valid)がある値)であるかを確認する(であるように操作加工する)作業では? 言い換えれば、どんな値が来るのか、値が信用できるのか等が不明の場合にする作業でしょう。 バリデーションのステップをクリアした『はずの』$validatedRequestを hoge()に引き渡した際に再度チェックし無ければいけない状況と言うのは、そのバリデーションは不完全なモノだったという事になるのではありませんか? __必要な値が不足なく返される様に validate()が設計されていない 自前のバリデータの結果が信用できないって事はバグでしかないと感じますが... > extract() だけだとこれができず微妙 微妙なのはそこではない様な... 意図していないかもしれない変数が突然発生する事じゃないかと... > その結果今回の質問が生じて そのルールが適切ではないって事は考えられませんか? 元の値を加工せずには使えない状況(大方はそうだろうけれど)で、元の値を保持したいのなら、別の変数を用意するしかありませんが、素のまま使うなら何回使おうとも置き換える意味が無いのではありませんか?
munekun

2024/05/06 20:41

> 受け取る値が、意味のある値(=価値(valid)がある値)であるかを確認する(であるように操作加工する)作業 なるほど、「意味のある値であるかの確認が必要なタイミングでバリデーション」というルール、採用させて頂きます。「POST直後にバリデーション」というルールにしてしまいちょうど混乱していたところでした。ありがとうございます。 > バリデーションのステップをクリアした『はずの』$validatedRequestを hoge()に引き渡した際に再度チェックし無ければいけない状況と言うのは、そのバリデーションは不完全なモノだったという事になるのではありませんか? 実際には hoge() はユーザ検索メソッドで、例えば「ユーザ検索時に age, name のいずれかまたは両方がありうる」というケースを考えて頂ければと思います。 POST された age も name も適切な値であることを確認した上で $validatedRequest が持っていても、それでも尚、hoge($conditions){} 内においては「この $conditions は age を持つのか name を持つのか」は分かりませんよね。 なので「バリデーションは不完全なモノだった」というわけでなくて、完全なバリデーションはできているけれど、それでも $conditions が何を持つのか確認したい。というケースでした。 > 素のまま使うなら何回使おうとも置き換える意味が無い あ、このルールにすればよかったのですね。素のままであるということがその都度含意、暗示される点も便利ですね。このルールも採用させて頂きます。 ちょっとした質問なのにあちこちの改善点を見つけることができました。ありがとうございます。
tezcello

2024/05/07 01:31

> 完全なバリデーションはできているけれど、それでも $conditions が何を持つのか確認したい 残念ながら、バリデーション済みの $validatedRequest が「何を持つのか」に懸念があるなら、バリデーション出来ていないという感想は変わりません。 何の為のバリデーションかと言えば、hoge()に渡す為でしょ? 外から来る値は制御できない場合もありますが、内部で使用する値は不足なく存在する事が必要ではありませんか? なので、(例えば checkboxとか)フォームデータが揃っているかを確認(ある意味、正規化)するのもバリデーションの一環だと思います。 他にも以下の様な事も。 範囲外の値を デフォルト値(nullを含む)/ 上限値 / 下限値 にする 全角を半角に統一する ひらがな(カタカナ)に統一する 前後の空白系文字を排除する(途中の連続する空白系文字を対象とする場合も) > 「POST直後にバリデーション」というルール 不正な値ならば再入力を促す POSTされた値によって処理が変わる などが大方の場合でしょうから、アレコレやってからバリデーションって事は少ないのでは? __バリデーションの為の下準備や __権限確認、アクセス経路や種類の確認(リロードも含まれる)とかは __当然バリデーション前に行われるべきだと
munekun

2024/05/07 03:46 編集

> 内部で使用する値は不足なく存在する事が必要ではありませんか? うーん、そうでしょうか。「name だけ、age だけ、name と age の2つ」という複数の検索ができるメソッド hoge() においては、「この引数はなんの値をもつのか」を確認しないといけないのではないでしょうか? > 他にも以下の様な事も。 その個々の値の適切さのバリデーションはコントローラクラスに任せています。そのコントローラクラスでの検証を見事クリアした $validatedRequest だけど、それを hoge($validatedRequest) のように渡した際に「この引数はなんの値をもつのか」まではわからないから hoge() 内での確認を、と思っている次第です。 それにしてもこれは新たな疑問になりますので、また折をみて別途質問を立てさせていただくかもしれません。もしお目に留まりましたらまたよろしくお願い致します。ありがとうございました。
tezcello

2024/05/07 04:17

> 「この引数はなんの値をもつのか」を確認しないといけないのではないでしょうか? 僕はそうは思いません。 検索対象のキーが全て揃っていて過不足ないなら、ノーチェックでループで自動処理で SQLの構築が出来ます。 __キーはあるけれど値がnullならスキップ モデルのメソッドなら、カラムの情報を持っているはず。 それと比較すれば検索対象のキーに過不足があっても自動処理で SQLの構築が出来ます。 後者は「確認」をしていると言えなくはないけれど、 > 「name だけ、age だけ、name と age の2つ」 その様に条件分けしてから構築してる訳じゃないです。 なので、 > hoge() 内での確認 という事はしないで、気分的にはノーチェックで SQLを構築しています。 > これは新たな疑問になりますので 承知しました。 とりあえずは、今回の疑問が解消されたのは良かったです。
munekun

2024/05/11 01:38

> キーはあるけれど値がnullならスキップ あ、なるほど。hoge() の引数はこれだけでいいわけですね。 ・キーがありその値がすでに検証されていれば問題なし ・キーがなければただスキップ 私は「キーがなければただスキップ」でなく「キーがなければnullを代入し、nullならスキップ」としようとしていて、そのため質問の「うまくnullを代入する方法」に至りました。 なぜ「キーがなければnullを代入し、nullならスキップ」かといえば、「2回以上その値を使うなら置き換える」というルールでしたので、以下のコードのような部分は ```php // 2回以上その値を使う if (!empty($conditions['name'])) { __$sql .= 'WHERE name = ' . $conditions['name']; } ``` 以下のコードのようにしなければならなかったのです。 ```php // 2回以上その値を使うなら置き換える $name = $conditions['name'] ?? null; if ($name) { __$sql .= 'WHERE name = ' . $name; } ``` でもこのルールをやめて「キーがなければただスキップ」なら前者のコードで済みます。 と、そんな感じでした。改めてありがとうございます。
tezcello

2024/05/11 02:25

クドイ返信で申し訳ないですが... > キーがなければただスキップ 外部から得る情報は不安定なのでバリデーション必須です。 その代わり、内部で流通する情報はバリデーション時に正規化され、「キーが無い」なんて事が起きない様にすると、無駄な検証が不要になると思います。 期待しているキーが必ず存在するなら、以降の処理は共通化出来ます。 __もちろん値によって(結果的に)何もしないという処理はあり得ます バリデーション時に、外部から届かなかったモノは、値として null をセットするとか、デフォルト値をセットするとかで、(上記の)共通化がとても楽になると思いませんか? ごく簡単な例で... ページネーションでページが指定されない場合は「0ページ目」だとすると、表示範囲の計算が同じ式で可能だったりするでしょ? 明示的に「正順」「逆順」を指定も出来るけれど、何も指定しなかったら「正順」ってのは、バリデーション時に無指定の場合でも $request['order-type'] = 'ASC' とでもしておけば、何も考えずに連結するだけで済むとか。
munekun

2024/05/11 03:05

> 内部で流通する情報はバリデーション時に正規化され、「キーが無い」なんて事が起きない様にすると、無駄な検証が不要になる > バリデーション時に、外部から届かなかったモノは、値として null をセットするとか、デフォルト値をセットするとかで、(上記の)共通化がとても楽になると思いませんか? ああ!なるほど。POSTの検証時点で name に値がなければ null をセットするとか、デフォルト値をセットするとかしておけば、hoge() の中で上記のようなNULL合体演算子での代入も !empty() での判定も不要で、下記だけでええやんというわけですね。 ```php // 不要 // $name = $conditions['name'] ?? null; // 不要 // if (!empty($conditions['name'])) { // 下記だけでええやん if ($conditions['name']) { __$sql .= 'WHERE name = ' . $conditions['name']; } ``` 丁寧にありがとうございます。助かりました。
guest

0

extractはmagic_quotes_gpcを思い出してやな感じですね。まあ、内部生成の配列なら避ける必要は無いのでしょうが。

return $conditions[$key] ?? null;のようにNULL合体演算子を書いているのになぜ "Undefined" なのか・・?

[$name, $age, $phone] = extractArrayValues($conditions);の代入の左辺が要素数3で、右辺が要素数2なので、[2]の要素が無いと言うメッセージです。右辺の方が多ければ余分は捨てられますが、足りないとそのメッセージが出ます。

そのお書きのextractArrayValuesのようにキーの値は無視して、順番に入れるのでいいなら、

PHP

1 [$name, $age, $phone] = array_values($conditions+[NULL,NULL,NULL]);

$conditionsのキーの順序を信用しない場合は、元のコードを少し書き直して、第二引数以降で指示したキーの順で取り出すことにします。元のコードは「存在するキーの順に値を取り出す」なのでarray_valuesと同じです。

PHP

1function extractArrayValues(array $a, ...$k) { 2 return array_map(function($key) use ($a) { 3 return $a[$key] ?? null; 4 }, $k); 5} 6[$name, $age, $phone] = extractArrayValues($conditions, "name", "age", "phone");

痒いところに手が届くRubyだとこの機能のメソッドが標準装備です(values_at)。

別案としては、単独変数に代入するのを止めて、

PHP

1$c = array_merge(['name'=>NULL,'age'=>NULL,'phone'=>NULL],$conditions);

しておいて、$c["name"]のように使うか。

投稿2024/05/05 19:14

otn

総合スコア84804

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

munekun

2024/05/06 05:23

> [2]の要素が無いと言うメッセージです。 うーん、ですが、 return $conditions[$key] ?? null; を書いていますから、もしその "[2]の要素が無い" ならば null を出してくれればいいと思っているのですが・・ > values_at Ruby はまったく書いたことがないのですが、やはり JavaScript でも Ruby でもあるのですから、 php でも用意してほしいですよね。 > $c["name"] なるほど、そうすれば、"$conditions["name"] がほしいけどなければ null がほしい" というのが "$c" で実現できるようになる感じですね。 ありがとうございます。参考にさせていただきます。
otn

2024/05/06 06:47

> うーん、ですが、 勘違いされているようですが、 キーが name と age のつしかないと、array_keys($conditions) は、["name","age"] で、 この2要素の配列に対してmapするので、ループは2回しか回りません。 mapした結果として return されるのは ["a",1] です。 [$name, $age, $phone] = ["a",1] ; は、右辺の要素が足りないのでエラーです。回答に書いたように、 [$name, $age, $phone] = ["a",1] + [NULL,NULL,NULL]; と右辺要素を3個以上にする必要あり。 ["a",1] を得るなら、関数を定義する必要は無くて、array_values($conditions) で十分といいうのも回答に書きました。
munekun

2024/05/06 20:36

> は、右辺の要素が足りないのでエラーです。 ああ、失礼いたしました。 return $conditions[$key] ?? null; でエラーが出ているのだと勘違いしていましたが [$name, $age, $phone] = extractArrayValues($conditions); だったのですね。 丁寧なご説明を誠にありがとうございます。
otn

2024/05/07 13:44 編集

> [$name, $age, $phone] = extractArrayValues($conditions); > だったのですね。 最初から回答にそう書いてありますけど? 右辺の配列が2要素だと、$nameと$ageにそれらが代入されて、$phoneにはNULLが自動t的に代入されるという仕組みの構文だという誤解があるのかと思って回答を書いたのですが、 (右辺の要素が余る場合、足りない場合、それぞれどうなるかを書いた) 「$conditionsが2要素でもextractArrayValues($conditions) は3要素に増えているはずだ」と思っていたということなのでしょうか?extractArrayValuesはご自分でゼロから書いたので仕様はご存じと言うことかと思っていたのですが。
guest

0

$phone ?? null しなくてよい書き方を試しにいくつか。

PHP

1extract(array_merge(['name'=>NULL,'age'=>NULL,'phone'=>NULL,],$conditions));

【【PHP】かゆいところに手が届く配列につかう関数まとめ #PHP - Qiita】
https://qiita.com/okasir4444/items/932ab46cf63b95ce82e8#1-array_merge

PHP

1foreach( ['name','age','phone'] as $name) $$name = $conditions[ $name ] ?? NULL;

【PHPの変数名を変数にする「可変変数」の使い方を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン】
https://magazine.techacademy.jp/magazine/31655

投稿2024/05/05 14:16

kei344

総合スコア69458

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

munekun

2024/05/05 17:26 編集

なるほど、array_merge() の第一引数で各値に null を入れつつ、第二引数に $conditions を渡して null を上書きするわけですね。思いつかなかったです。 可変変数の方はまったく知らないテクニックでした。 $conditions にキーがないとき ・null 以外に個別に指定したい場合は前者の array_merge() の方法を ・null をまとめて指定したい場合は後者の可変変数の方法を といった感じの使い分けができそうですね。 勉強になりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問