途中return禁止、goto禁止の時のdo~while(0)

2018-11-18
php%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E9%9B%91%E8%AB%87
    

目次

はじめに

細かいロジックが多い手続き型のプログラムを書こうと思った時のメモ。

1つのプログラム内において、いくつかの処理を行いそれらが失敗したらエラーを出力して終了するといったフローのプログラムを考えてみます。

※言語=PHPで、かつ、クラスとかは利用しないことを前提として話を進めます。

_log("START");

startProc();

if (!proc1()) {
    _log("ERROR1");
    endProc();
    _log("END");
    exit;
}
_log("SUCCESS1");

if (!proc2()) {
    _log("ERROR2");
    endProc();
    _log("END");
    exit;
}
_log("SUCCESS2");

if (!proc3()) {
    _log("ERROR3");
    endProc();
    _log("END");
    exit;
}
_log("SUCCESS3");

//...

endProc();
_log("END");
exit;

正しく実行されれば

START
SUCCESS1
SUCCESS2
SUCCESS3
END

func2でエラーが発生した場合は

START
SUCCESS1
ERROR2
END

このような出力を期待値とします。

構造化

このように書くと、エラー時の処理が見苦しくなります。

なので、 条件に引っかかったらreturnさせると言う感じで実装に変更してみます。

function main() {

    _log("START");

    startProc();

    $result = _sub();
	
    endProc();
	
    _log("END");

    return $result;
}

function _sub() {
    if (!proc1()) {
        _log("ERROR1");
        return false;
    }
    _log("SUCCESS1");
    
    if (!proc2()) {
        _log("ERROR2");
        return false;
    }
    _log("SUCCESS2");
    
    if (!proc3()) {
        _log("ERROR3");
        return false;
    }
    _log("SUCCESS3");

    return true;
}

個人的には問題ないと思っておりましたが、「関数の途中でreturnを書くな」と指摘されたことがあります。

いろいろ理由はあるそうですが、このケースは置いといて別のパターンを考えてみます。

参考

https://anopara.net/2014/06/27/do-not-write-deep-nested-code/

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1226287612

goto

こんな時こそgotoの出番ではないんでしょうか?

function main() {
    $result = true;

    _log("START");

    startProc();

    $result = proc1();
    if (!$result) {
        _log("ERROR1");
        goto finalize;
    }
    _log("SUCCESS1");
    
    $result = proc2();
    if (!$result) {
        _log("ERROR2");
        goto finalize;
    }
    _log("SUCCESS2");
    
    $result = proc3();
    if (!$result) {
        _log("ERROR3");
        goto finalize;
    }
    _log("SUCCESS3");
    goto finalize;

    finalize:

    endProc();

    _log("END");
    return $result;
}

キレイに書けました。

do~while(0)を使う

しかしながら、コーディング規約や組織のポリシー的に「とにかくgotoは使ってはいけない!」というケースがあると思います。

そんな時に見つけたテクニックがdo~while(0)を使うと言うもの。

初めて見たときは正直気持ち悪いなと思いましたが、ググると広く使われているテクニックのようでした。

それでも、なんか改めて見てみるとモヤモヤします。

ソースがこちら。

function main() {
    $result = true;

    _log("START");

    startProc();

    do {
        $result = proc1();
        if (!$result) {
            _log("ERROR1");
            break;
        }
        _log("SUCCESS1");
        
        $result = proc2();
        if (!$result) {
            _log("ERROR2");
            break;
        }
        _log("SUCCESS2");
        
        $result = proc3();
        if (!$result) {
            _log("ERROR3");
            break;
        }
        _log("SUCCESS3");

    } while(0);

    endProc();

    _log("END");
    return $result;
}

https://stackoverflow.com/questions/243967/do-you-consider-this-technique-bad

https://ja.stackoverflow.com/questions/1510/do-whilefalse%E3%81%AE%E5%88%A9%E7%82%B9%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B

http://php.net/manual/ja/control-structures.do.while.php

PHPは関数名の文字列を関数として実行できるので以下のように構造的に書くこともできました。

function main() {
    $result = true;

    $funcs = [
        "proc1" => [
            "error"   => "ERROR1",
            "success" => "SUCCESS1",
        ],
        "proc2" => [
            "error"   => "ERROR2",
            "success" => "SUCCESS2",
        ],
        "proc3" => [
            "error"   => "ERROR3",
            "success" => "SUCCESS3",
        ]
    ];

    _log("START");

    startProc();
    
    foreach ($funcs as $key => $value) {
        $result = $key();
        if (!$result) {
            _log($value["error"]);
            break;
        }
        _log($value["success"]);
    }

    endProc();

    _log("END");
}

try ~ catchを使う

ググると、try~catchの実装例も出てきます。 ただ、ロジック的なエラーなのか、例外的なエラーなのかでぐちゃぐちゃになってしまいそうなので、個人的には好きではありません。

function main() {
    $result = true;

    _log("START");

    startProc();

    try 
    {
        $result = proc1();
        if (!$result) {
            throw new Exception("ERROR1");
        }
        _log("SUCCESS1");
        
        $result = proc2();
        if (!$result) {
            throw new Exception("ERROR2");
        }
        _log("SUCCESS2");
        
        $result = proc3();
        if (!$result) {
            throw new Exception("ERROR3");
        }
        _log("SUCCESS3");
    } 
    catch(Exception $e) {
        _log($e->getMessage());
    } 
    finally {
        endProc();
        _log("END");
        return $result;
    }
}

その他

環境

PHP 7.1.19

    
s-yoshiki
s-yoshiki
githubtwitterqiita
Web作ってますが、インタラクティブなプログラミングも好きです。
JavaScript / Vue / node.js / PHP / AWS / OpenCV

関連記事

JavaScriptによる2分探索(バイナリサーチ) のサンプルコード
2分探索について ソース 参考 JavaScriptで2分探索(バイナリサーチ)を実装してみました。…

WordPressのDBから記事データを抽出する
WordPressのDB関連図 公開記事一覧の取得 タグ・カテゴリの取得 サムネイルの取得 おまけ: PHPスクリプト化しました 参考にしたところ WordPressにため込んだデータMarkdown化しGatsby…

ハイフンとかマイナスとかダッシュとか

PHPerkaigi 2020 資料まとめ
資料 もっと気軽にOSSに Pull Requestを出そう!/ Let's make a PR to OSS more easily PHP で JVM を実装して、 HelloWorld を出力してみる Deep Module in PHP 磯野ー、MySQL…

少なくとも、これら説明できない奴、エンジニアとして、マジでやばいので自覚した方がいいぞ。について
元ネタ(?) 派生 最後に Twitterでネタ(?)になっていたのでメモ。 元ネタ(?) https://twitter.com/t0riumi/status/1225422385093865472 派生 https://twitter.com/eg19/status…

Qiitaでバズるとこうなる。アクセスの分析や原因考察をする
投稿した記事 アクセス分析 Qiitaでバズった原因の考察 終わりに Qiitaに数年前から投稿していますが、延3日間で「1000 いいね」をもらいました。今までの最大いいねが20…

JavaScriptで幅優先探索 (bfs) を実装する
bfsソースコード 前提 bfs関数 テストコード JavaScriptで幅優先探索 (bfs) を実装し簡単な最短経路の探索問題を解いてみました。 AtCoderの問題を参考にしています bfsソースコード 前提 bfs 関数の定義について 引数 table…

ソースコードレビューのポイントをまとめる
ソースコードレビュー時のポイントを各所のブログから集めてまとめました。以下 https://gist.github.com/s-yoshiki/9e446d69cf388703a4711f7e69cba173

AWSソリューションアーキテクト参考資料集
ブラックベルト サービス別資料 AWS Well-Architected フレームワーク 特に目を通すべき資料の一覧です。 ブラックベルト https://aws.amazon.com/jp/aws-jp-introduction/ サービス別資料 https://aws…

AWS ソリューションアーキテクト不合格体験記を集める
AWSソリューションアーキテクトアソシエイトに1回落ちたけど勉強し直して合格した話 AWS認定ソリューションアーキテクトアソシエイト(SAA)不合格体験記 非エンジニアがAWSソリューションアーキテクトに合格するまで AWSソリューションアーキテクト認定に落ちた話 AWS…

最新の投稿

GitHub Actions で Gatsby をビルドし Amazon S3 にデプロイする
GitHub Actions について あらかじめ準備しておくもの AWS IAM ユーザを環境変数にセットする workflowの記述 ビルド バッジを利用する 終わりに 参考にしたところ Gatsbyで作った静的サイトを、GitHub Actions…

cloudinaryによる画像ファイルの管理 はじめてみる
目的 cloudinary について 他のサービスとの比較 料金プラン アカウントの登録 利用してみる ダッシュボード 画像の編集 APIベースでのアクセス 感想 参考 画像の管理や配信、さらには加工といった事ができるsaas型のcloud…

JavaScriptによる2分探索(バイナリサーチ) のサンプルコード
2分探索について ソース 参考 JavaScriptで2分探索(バイナリサーチ)を実装してみました。…

Vue.jsで作成された、ちょっと面白くて役立ちそうなサービス
UIコンポーネント VueSocial CKEditor 5 Vue.Draggable Vuetable 2 vuejs-datepicker Kalendar Vue Apexcharts Vue.js Google Charts vue-cart WebIDE…

GitHubのリポジトリをGitLabに同期する GitLabのミラーリング機能
GitLabのミラーリングについて GitHubのリポジトリをGitLabに反映する その他 参考 GitLabのミラーリング機能によりGitHubなどの外部のリポジトリとのミラーリングを行うことができます。 これを使ってGitHub…

WordPressやめます Gatsbyに移行しました
これまでのWordPress運用 なぜWordPressを捨てるのか? なぜGatsbyを利用するのか? gatsbyについて WordPressから記事の救出 移行対象記事の抽出 記事の置換 Gatsbyテーマの作成 Gatsby…

WordPressのDBから記事データを抽出する
WordPressのDB関連図 公開記事一覧の取得 タグ・カテゴリの取得 サムネイルの取得 おまけ: PHPスクリプト化しました 参考にしたところ WordPressにため込んだデータMarkdown化しGatsby…

ハイフンとかマイナスとかダッシュとか

Firebase + Nuxt で認証付きページを作るときに参考にしたいところ
Webアプリケーションのセッション管理にJWT導入を検討する際の考え方 Service Worker によるセッション管理 ユーザー セッションの管理 Nuxt.jsとFirebaseでSPA×SSR×PWA×サーバーレスを実現する CookieとセッションとJWT SSR…

GolangをCGIとして実行する
環境 golang パッケージ ソースと実行 ビルドとサーバ実行 標準ライブラリのみ Golang を CGIとして実行する際のメモ 環境 golang パッケージ 以下のモジュールを利用しています。 github.com/gorilla/mux…

Tags

Dates

© 2020   404 motivation not found