開発, PHP, SEノウハウ

バグのないプログラム

 

値の判定

 

値があること

if (isset($item['key'])) {
    // 値がある時の処理
}

 

空配列判定

$array = [];
$array = $this->i_members->where('birth', '11'))->get();
if (!empty($array)) {
    // 空配列でない時のお処理
}

 

collectionがあるか

$instance = $this->i_collection->where([
    'id' => $id
])->get();

if (!is_null($instance)) {
    // collectionがある時の処理
}

 

配列の判定

if (is_array($items)) {
    foreach ($items as $key => $value) {
        // 配列の処理
    }
}

 

配列にキー’hoge’があるか判定

if (array_key_exists('hoge', $array)) {
 //
}

 

オブジェクトにプロパティ’hoge’があるか判定

if (property_exists($object, 'hoge')) { 
    //
}

 

 

 

=========================

アプリ会社入社前に書いたもの

 

バグのないプログラムを書きたい

 

綺麗に書く

  • マジックナンバーはconstで定義する
  • 関数のdocを書く
  • 第三者が読めるように変数を命名
  • 目的がぱっとわかりにくい処理は日本語でコメントを書く

 

ORMを使う場合

  • ORMのメソッドの戻り値を全て把握
  • id検索でのレコード検索がすかった時の考慮した処理が必要

 

 

フェイルファスト

想定外は処理を止めるのが安全

投稿されたファイルを読み込んで拡張子で判断し、CSVFileReaderクラスかXMLFileReaderクラスに振り分けるクラス
(PHPによるデザインパターン入門 Factory Methodパターンより)

private function createReader($filename) {
    $poscsv = stripos($filename, '.csv');
    $posxml = stripos($filename, '.xml');

    if ($poscsv !== false) {
        $r = new CSVFileReader($filename);
        return $r;
    } else if ($posxml !== false) {
        $r = new XMLFileReader($filename);
        return $r;
    } else {
        die('.csv, .xmlの拡張子で投稿してください。 投稿されたファイル名: ' . $filename);
    }
}

.csvか.xml以外の拡張子がきたらdie()で処理を止めています。

もしくはthrow newで例外を投げて止めてエラーメッセージを出す。

 

値があるかの取得など

nullを返り値で使う場合は、取得側で必ず関数からnullが返ってきていないかチェックする必要がある。

        while($buffer = fgets($fp, 'r')) {
            $order_id = trim(subnstr($buffer, 0, 10));
            $item_ids = trim(substr($buffer, 10));

            $order = new Order($order_id, $order_name);
            foreach (explode(',', $item_ids) as $item_id) {
                $item = $item_dao->findById;
                if(!is_null($item)) { ←●!is_nullでない場合にオブジェクトに追加している
                    $order->addItem($item);
                }
            }
            $this->orders[$order->getId()] = $order;
            
        }

if(!is_null($item))でnullでないかチェックして、ループで回してオブジェクトに代入している。

 

 

The・null

 

  • 変数に対して初期化処理をしておく
    $hoge = (string) ''; // 空文字で初期化
    or 
    $hoge = (string) "abc" // デフォルト値で初期化
  • nullの場合は空のオブジェクトを返す
    return json_encode(new stdClass());

     

 

関数の戻り値のnull

配列の場合

return int[0];

 

リストの場合

return list();

or

return []; // PHP7

 

 

オブジェクトの場合

return json_encode(new stdClass());

 

文字列の場合

return '';

 

戻り値がない場合

function think(): void
{
    処理
}

戻り値をvoidで指定

 

変数のnullチェック

  • 基本はisset()で判定
    ・nullと未定義変数をfalseと判定する関数 = setされていない
    空文字はtrueと判定 = 空文字がsetされている
  • is_null()も良く使われる
    ・nullであればtrue
  • if (!$hoge)
    ・null, 未定義変数, 空文字をtrueと判定
    →初期化していない変数に使う

 

配列が空配列であるかのチェック

$hairetu = array()になっている状態

  • 基本はempty()で判定する
    $hairetu = array();
    empty($hairetu);
    →trueが返る

empty()色々なものを空と判定してまう!。。ので配列でないものにはisset()を利用する。

“” (空文字列)
0 (整数 の 0)
0.0 (浮動小数点数の 0)
“0” (文字列 の 0)
NULL
FALSE
array() (空の配列)
$var; (変数が宣言されているが、値が設定されていない)

@see https://qiita.com/shinkuFencer/items/48718f0c8d203ca513db

 

配列中のnullチェック

  • 配列をループで回してisset()で1つずつ判定する。

 

isset()とempty()は空文字判定

空文字は”のこと。

  • 空文字を見つけたい
    →empty()で判定する

empty()は空文字列を『空である』→ trueと判定

isset()はは空文字列を『セット(set)されている』→trueと判定

 

is_null()の用途

  • nullと未定義変数をtrueと判定する

 

ifでの判定

$exampleString = '';

// nullと未定義を判定、空文字は通す場合
if ($exampleString == null) {
  echo "null or Undefined";
}

// nullと未定義, 空文字を判定
if (!$exampleString) {
  echo "null or Undefined or 空文字";
}

  • if ($hoge == null)
    nullと未定義を判定
  • if(!$hoge)
    null, 未定義, 空文字を判定

 

 

 

PHP7 null合体演算子と!empty()の併用

$product = $product ?? '';

if(!empty($product)) {
    $cart[] = $product;
} else {
    // 異常値による例外
    echo '[Code:401001]:' . get_class() . ':カートに入れる商品がありません。';
    error_log('[Code:401001]:' . get_class() . ':異常値によるエラー。$productに異常値が入っている。');
    exit();
}

nullの時に、

  1. $productに空文字の”が代入される
  2. !empty($product)で判定してfalseと判定
    →『カートに入れる商品がありません。』と出力し強制終了させる。

 

三項演算子でのnullの処理

//変数
$item_name = (isset($_POST['item_name']))? $_POST['item_name'] : '';

//配列
$items = (isset($_POST['item']))? $_POST['item'] : array();

 

  • 変数にnullが入らないようにする
  • nullがきたら空文字, 空配列を入れる

 

@see

 

null以外でのバグ

 

  • 条件比較の認識ミス
    empty(), isset()
  • 文法ミス
  • 設計書の理解不足
  • 設計書の間違い
    設計書の間違いを探す力が必要
    →業務知識やサービスの理解が必要
  • 結合でのミス
    渡す変数、型、長さ、数が違うとバグになる
  • バグを直す時に影響範囲の確認をしない、意識しない
    バグを直しているのか、生み出しているのかわからない。

 

テストファースト

  • テストで設計を行う
  • 正しい処理を行うか
  • 誤った値の時に正しい処理をしないか
  • エラーハンドリングが行えているか
  • ログがでているか

確認の為のテスト

バグを見つける為のテスト

 

バリデーションのルールとテスト

バリデーションで行うことは、テストでも書いておく。

テストを見ればそのクラスのバリデーションで何をチェックしているか、何をチェックし終わったら正常なのかがわかる。

 

 

エラーの扱い

  • 開発時は表示
  • 運用時は記録

 

オブジェクト指向で書く

ざっくり

  • データの扱い方
  • 場合わけの書き方
  • プログラムの分割の仕方
  • 名前付け

不安定にならないようにするポイント

  • オブジェクトを生成する時は必ず操作対象のデータをコンストラクタの引数として渡す
    →データを操作するロジックがばらけないようにする。
  • オブジェクトの内部のパラメータgetXXX()の形式で取得する。内部のインスタンス変数を要求しない。
  • オブジェクト内部のインスタンス変数を外から変更しないsetter()メソッドを使わない
    →インスタンス変数を書き換える操作はプログラムの動作が不安定になりやすい。
    →データを引数で渡すと、そのデータを利用した加工、計算のロジックがどこに書いてあるかわからなくなる。
  • インスタンス変数を上書きしないようにする。
  • データとロジックを一緒のクラスにまとめる

 

Amazonおすすめ

iPad 9世代 2021年最新作

iPad 9世代出たから買い替え。安いぞ!🐱 初めてならiPad。Kindleを外で見るならiPad mini。ほとんどの人には通常のiPadをおすすめします><

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)