エンジニアのひよこ_level10

毎日更新してた人。たまに記事書きます。

【思考メモ】自動テストの目的・意味の違い?【682日目】

思考メモ

ちょっと雑に思ったのをこちらに書き写した感じなので、上手くまとまってないかもですm( )m

外部の関数と引数、どちらに注目する?

(あえてdataProvidor使わず書きました)

function testA()
{
    Mockery::shouldRecieve('aaa')->andReturn('a');
    ....
}

function testB()
{
    Mockery::shouldRecieve('aaa')->andReturn('b');
    ....
}

function testC()
{
    Mockery::shouldRecieve('aaa')->andReturn('c');
    ....
}

これと、

function testA()
{
    $param = 'x'
    Mockery::shouldRecieve('aaa')->with('x')->andReturn('a');
    Mockery::shouldRecieve('aaa')->with('y')->andReturn('b');
    Mockery::shouldRecieve('aaa')->with('z')->andReturn('c');
    ....
}

function testB()
{
    $param = 'y'
    Mockery::shouldRecieve('aaa')->with('x')->andReturn('a');
    Mockery::shouldRecieve('aaa')->with('y')->andReturn('b');
    Mockery::shouldRecieve('aaa')->with('z')->andReturn('c');
    ....
}

function testC()
{
    $param = 'z'
    Mockery::shouldRecieve('aaa')->with('x')->andReturn('a');
    Mockery::shouldRecieve('aaa')->with('y')->andReturn('b');
    Mockery::shouldRecieve('aaa')->with('z')->andReturn('c');
    ....
}

このテストの違い。

関数のテスト?引数依存???

前者は該当関数のreturn値に対するテスト。

後者はこのテストコードの引数に対するテスト。

inputに対して、outputが正しいことを自動テストがしていると考えるなら、
後者のほうが‘aaa’に対しては、より本質に近いのではφ(・

とはいえ、外部に環境が依存している、例えばDBやapiをモックする必要があったら、その時は前者が正しいのかもしれないφ(・

【PHP】null合体演算子(??) エルビス演算子(?:)の違い【669日目】

??と?:

$a ?: null$a ?? nullという表記はよく見ると思います。

特に右は結構書く機会多いですね。

では、これらが何をしてるんでしょうφ(・

両方とも、if文の省略

どんな演算子かというと、両方ともよく使うif文の省略系だったりします。

三項演算子をご存じの方は、ただのif文と考えると思いますが、今回の ?:は微妙に動作が違います。

これは、三項演算子ではなく、エルビス演算子と呼ばれるものです。

条件式が引数自体か、issetの結果かの違い

$a = "";

// $a ?: null -> return null
if ($a) {
    return $a;
} else {
    return null;
}
$a = "";

// $a ?? null -> return $a
if (isset($a)) {
    return $a;
} else {
    return null;
}

このように、 ??はissetの結果で、trueであればそのまま。そうでなければ右の値を返します。
?:は左の引数をそのまま判定に使って、trueであればそのまま。そうでなければ右の値を返します。

三項演算子を知ってる人は、もしかすると戻り値がvoidなのではないかと思うかもしれませんが、
実は違うので、間違えないようにしましょうφ(・

(私は今日勘違いしてました)

公式ドキュメント

https://www.php.net/manual/ja/language.operators.comparison.php

【PHP】エラーの時に出るスタックトレースのようなものを出力する【663日目】

スタックトレースっぽいのを見たい

エラーが発生した時とかに見れる、どこの関数で何が呼ばれて・・・

そんなスタックトレースを、エラーを発生させずに確認したい。ログに吐きたい。

スタックトレースじゃない

バックトレースってのが出ます。

出てくるのは、関数の呼び出された場所とか行とか。

実際の出力結果は以下にあるので見てみましょう

debug_print_backtrace()

<?php

class Parents {
    public function __construct()
    {
        debug_print_backtrace();
    }

}

class Child extends Parents {
    public function __construct()
    {
        parent::__construct();
    }
}
#0  Parents->__construct() called at [/workspace/Main.php:15]
#1  Child->__construct() called at [/workspace/Main.php:19]

公式ドキュメント

https://www.php.net/manual/ja/function.debug-print-backtrace.php

【PHP】変数未定義と配列の未定義の違いの考察【654日目】

注意

根拠のない個人的考えです!ようするにポエムです!

こういう理由があるんだよ!って意見があれば、ぜひ教えていただけると嬉しいです。

issetで疑問に思った

PHPには、issetという関数が存在します。

https://www.php.net/manual/ja/function.isset.php

変数がセットされていること、そして NULL でないことを検査する

この説明から、ふと疑問に思いました。

変数がセットされてるかだけのチェックをする関数ってないのかな?

$x = null;

xxx($x); // true
xxx($y); // false

こんな状態。今だと、以下のような書き方すれば実現しますが、

isset($x) || $x === null

empty関数や、is_null関数とかいろいろあるけど、未定義だけを判定する関数なんで存在しないんだろう?

配列とかにはある

ちなみに、配列で特定のキーがあるかとか、オブジェクトに該当の値があるかとかを確かめる関数は存在します。

■array_key_exists

https://www.php.net/manual/ja/function.array-key-exists.php

■property_exists

https://www.php.net/manual/ja/function.property-exists.php

なら変数にもあってもおかしくないはず・・・?

実際のケースを考える

この疑問で思った最初のケースを考え直してみよう。

最初はユーザーからくるリクエストを想定していて、該当する配列に値がない場合とかを指していた。

array_key_exists('hoge', $_REQUEST);

なるほど、配列では必要そう。

単体の変数が未定義のケースはあるのか!?

では、変数が未定義の場合って?

・・・あれ?ほとんどなくないか?

ユーザーからの値が未定義などは、自分のコードの関心の外であり、コーダーが把握することは不可能。DBの状態なんてその時によって違うし。
でも、こういった値は、配列の中身が未定義であって、値が未定義であるケースは、たしかに思い浮かばない。

変数が未定義のコード

変数が未定義と定義がの両方の状態が現れるコードは、以下のようなコード。

if ($request == true) {
    $var = 3;
}

var_dump($var);

でも、これは以下のように書き換えることも出来る。

$var = null;

if ($request == true) {
    $var = 3;
}

var_dump($var);

『絶対に未定義であるかを判定する必要がある』コードってなかなかないのでは?

変数が未定義なのは、コーダーの事情だし、どちらかというとコードの書き方のミスなのではないかφ(・

『必須の関数か』と考えるとそうではないのかもしれない。
長いことPHPが発展してきても、該当関数が生まれなかったのは、こういった理由なのかもしれない。

個人的な結論

変数の未定義と、配列のキー内での未定義は、意味・用途が違う。

該当関数が存在しないのは、『不要』だからではないか。

感想。

変数の未定義と、配列のキー内での未定義は、明確に違うのがわかった。

もしかすると、過去のPHPのアップデートで、同じ思いで未定義を判定する関数ほしいって意見があったけど、却下とかされていたかもしれないφ(・

なんにしても、『意味が違う』と、『なんでこの関数が存在するのか』という視点が得られたのは、良い学びだったと思います!

みなさんも、関数の意味や、なぜ存在するのかを考えてみると、面白いかもしれません(`・ω・´)ゞ

【PHP】正規表現の区切り文字を変えて、エスケープを減らす【650日目】

正規表現

/ringo/

こんなふうにスラッシュではさみますよね

挟む文字を変えれる

#ringo#

こんなふうに書くことも出来ます。

何がいいの?

スラッシュをエスケープするのを省ける

/uri\/ringo/
#url/ringo#

こんなふうに、正規表現のパターンでスラッシュを使いたい時に、 \/のようにバックスラッシュを追加してエスケープする必要があります。

これを別な記号に変えることで、スラッシュをエスケープする必要がなくなります。これは見やすいですね!

【PHP】配列の中身をすべて書き換える処理の書き方3つ【647日目】

配列の中身をすべて書き換えたい

例えば、数字が入った配列をすべて2倍にしたい。

[1, 2, 3, 4]が、 [2, 4, 6, 8]になるようにしたい。

1.forと配列のインデックスを使う

$array1 = [1, 2, 3, 4];
$array_length = count($array1);

for($i = 0; $i < $array_length; $i++) {
    $array1[$i] = $array1[$i] * 2;
}
var_dump($array1);

$array_lengthで配列の最大の値を取って、ひたすら処理を回す。

良い点は、順番の値である $i変数を使った処理が書ける。
悪い点は、countで最大値を取ってfor文を回しているので、穴あきの配列を使うと動かない。

2.foreachを使う

$array2 = [1, 2, 3, 4];
foreach ($array2 as &$value) {
    $value = $value * 2;
}
unset($value);
var_dump($array2);

foreachで取り出す$valueの部分をリファレンス参照にする。

すると、配列の各値の参照先を直接取得・書き換えることが出来るので、
データの書き換えが可能。

良い点は、穴あきの配列でも動く。array以外にも、foreachで回せるオブジェクトでも対応可能。
悪い点は、参照渡しなので、unsetで値を開放しそこねると、思わぬバグが生まれたり、値の書き換えだけにしか使えない。

使い方を間違えなければ結構良さそう。
ただ、もとの配列を書き換えたくないときとかには次の書き方の方が良さそう。

3.array_mapで関数を回す

function double(int $num)
{
    return $num * 2;
}

$array3 = [1, 2, 3, 4];
$array3 = array_map("double", $array3);

var_dump($array3);

配列の各要素に動かしたい関数を渡す。

良い点は、関数の処理の使い回しもできるし、コードが心なしか読みやすい?
悪い点は、arrayにしか使えない。2.のようにオブジェクトにはこの手は使えない。

使い分けよう

良い点悪い点を私の主観で書きましたが、どれも良い点悪い点がありますので、うまく使い分けていきましょう(‘ω‘ )

他にも良い書き方がありましたら、ぜひ教えてください!

参考公式ドキュメント

2.の参考
https://www.php.net/manual/ja/control-structures.foreach.php

3.の参考
https://www.php.net/manual/ja/function.array-map.php

読みやすいプログラムを書くために。判定用の数値は判定の時に算出する【640日目】

学んだこと

$has_という変数名をつけず、データの取得と、countなどの判定とで処理を二段階に分けるのも手

has_xxxのような変数を作っていた

$has_user_item = !($items->where('item_type', 'user')->empty());

if (!$has_user_item) {

このように、if文内で変数一つで扱えるよう、判定用の変数を用意していた。

判定用の数値は判定の時に算出する

$user_items = $items->where('item_type', 'user');

// 判定用の数値に変える
if (!($user_item->empty())) {

}

このように書くことも出来る。ただ、一長一短ある。

どちらがより再利用されるプログラムか。

注)これに正解はなく、ケースバイケースです。

$has_user_item = !($items->where('item_type', 'user')->empty());

if (!$has_user_item) {
     xxx_func($has_user_item);
}

このように、判定結果を引数等で他の場面で使いたい場合もあるかもしれない。先程のように分割すると

$user_items = $items->where('item_type', 'user');

// 判定用の数値に変える
if (!($user_item->empty())) {
     xxx_func($user_item->empty());
}

こんなコードになったりするので、一気にまた見にくくなり、メンテナンスも面倒。なのでケースバイケース。

おまけ

ちなみに、メモリにどれくらい使うか・・・みたいな話があるが、それは考えるべきプログラムなのか?私達はそこまで切り詰めたサーバーなのか?って考えると、
一旦考えなくても良い現場が多いだろう。

読みやすいプログラムを書くために。否定の否定を書かない【638日目】

学んだこと

if (! $has_no_ )って否定の否定のコードは、正しく解釈するのが難しくなる

否定の否定

$has_no_items = .....

// ない場合
if ($has_no_items) {
 ....
}

// ある場合
if (!$has_no_items) {
 ....
}

言うまでもなく見づらい

変数名を変える

$has_items = .....

// ない場合
if (!$has_items) {
 ....
}

// ある場合
if (!$has_items) {
 ....
}

これで少し見やすくなる

変数に入れ直す

$has_items = .....
$has_no_items = !$has_items;

// ない場合
if ($has_no_items) {
 ....
}

// ある場合
if ($has_items) {
 ....
}

ただ、こちらは一度処理を先にする。

直感で$has_itemsの変数との関係が見づらくなる可能性もあるので、要検討。
$has_no_itemsの算出方法が変わると、バグの危険もあるので、扱い注意。