CUIでPHPアプリケーションをつくる


<br>
エンジニアYoutuberの山浦清透さんが運営する独学エンジニアの「レッスン4： 読書ログサービスを作ろう②テキスト版」を学習したので、そのアウトプットを実施する。
レッスン４では、以下がタスクとタスクばらしである。
<br>
<タスク>
<br>テキスト版（CUI）のアプリケーションを作成する

<タスクばらし>
* 読書ログを表示する
* 読書ログを登録する
* メニュー化し、アプリケーションとして動作する
* 複数の読書ログを表示

# 読書ログを表示する
表示するデータ
* 書籍名 
* 著者名
* 読書状況
* 評価（５点満点の整数）
* 感想

<strong>プログラムを書く際のポイントは１つ処理を書いたら、動作確認を実施すること。</strong>
ソースコード
```php:qiita.php
<?php

// PHP_EQLは改行を表す。「.」は文字列の連結
echo '書籍名：銀河鉄道の夜' . PHP_EOL;
echo '著者名：宮沢賢治' . PHP_EOL;
echo '読書状況：読了' . PHP_EOL;
echo '評価：5' . PHP_EOL;
echo '感想：ほんとうの幸せとは何だろうかと考えさせられる作品だった。' . PHP_EOL;
```

実行結果

```linux:ターミナル
書籍名：銀河鉄道の夜
著者名：宮沢賢治
読書状況：読了
評価：5
感想：ほんとうの幸せとは何だろうかと考えさせられる作品だった。
```
# 読書ログを登録する
<現状>
プログラムの中にデータを記載し、それを出力していた。
<理想>
ユーザーが入力したデータを出力できるようにする。

* 標準入力（standard input）
キーボードから入力した値は、基本的に標準入力に割り当てられる。その標準入力の値を読み取ればタスクを実現できる。
* fgets(変数)
()で指定したものから1行データを取得する
fgets($handle)
$handle：読み取るデータ元

標準入力の値を取得するには以下のようにする。

```php:qiita.php
<?php
fgets(STDIN);
```

* 変数
現状だと、ユーザーが入力した値をすぐに出力してしまう。
理想はユーザーが入力した値を保持し、後で表示する。そのために変数を用いる。変数のメリットは「あとから使える」「何度も使える」ところ。

※変数の値はコンピュータのメモリに一時的に保存される。

* trim
入力フォームではユーザーが空白をつけて入力してしまうという事象が多く起こるため、その対策をする必要がある。文字列の先頭及び末尾にあるホワイトスペースを取り除ける。

※trimでは全角スペースは削除できない。全角スペースを削除するためには、別の処理が必要らしいが今回は割愛する。

上記のことを活かしてコードを記載すると以下のようになる。
ソースコード
```php:qiita.php
<?php
// 読書ログを登録する
echo '読書ログを登録してください' . PHP_EOL;
echo '書籍名：';
$title = trim(fgets(STDIN));
echo '著者名：';
$author = trim(fgets(STDIN));
echo '読書状況（未読,読んでる,読了）：';
$status = trim(fgets(STDIN));
echo '評価（5点満点の整数）：';
$score = trim(fgets(STDIN));
echo '感想：';
$summary = trim(fgets(STDIN));
echo '登録が完了しました' . PHP_EOL . PHP_EOL;

// 読書ログを表示する
echo '読書ログを表示します' . PHP_EOL;
echo '書籍名：' . $title . PHP_EOL;
echo '著者名：' . $author . PHP_EOL;
echo '読書状況：' . $status . PHP_EOL;
echo '評価：' . $score . PHP_EOL;
echo '感想：' . $summary . PHP_EOL;
```
# PHPの概要
次のタスクに取り組む前にPHPの基礎文法が必要なため、この章を挟んだ。

* PHPはHTMLに埋め込んで記述ができる。そのため、コードは<?php php>タグで囲む。(ただし、終了タグの下に改行があると、誤作動を生む可能性があるため、終了タグは省略する事が多い。)

* 特徴として、簡単に・素早くWebサイトが作れる。一方で、金融システムなどの厳密に作りたいアプリケーションには向いていない。

* var_dump()
指定した式の型や値を返す

* var_export(変数名)
変数の中身を表示。var_dumpとの違いは値だけ表示する点。var_dumpは型+値で表示する。  

* if文のコツは最初に処理の全体像を記載。処理の中身はコメントで、後で書く。

* while文で無限ループを起こしたいときは式にtrueを指定する。

* 無限ループを終了させるにはbreakを使う。

* 配列を使うことで、同じ用途の複数の値を1つの変数に格納できる。

* ラベルを付けた配列を「連想配列」と呼ぶ。

* 配列の基本操作は、初期化→要素の追加→要素へのアクセス

* PHPの内部では配列と連想配列は区別されていない。キーに数字を使うか、文字列を使うかの違い。

* 配列は同じ種類の値を順序立ててまとめるのに向いている。

* 連想配列は同じグループだが、属性の異なる値をラベルを付けてまとめるのに向いている。

* 配列の全要素をループするときはforeachを使う。

* 関数を使うメリット
1, 名前が付いて読みやすくなる
2, 何回も同じ処理を書かずに済む

* コーディング規約
[PSR-1（日本語訳）](https://www.ritolab.com/entry/91)
[PSR-12(日本語訳)](https://www.ritolab.com/entry/208)

# メニュー化し、アプリケーションとして動作する
<現状>
都度登録し、表示される。
<理想>
メニューから登録するか、表示するかを選べるようにする。

* プログラム実行の基本は逐次実行。上から下に実行する。

* メニュー化するには
1, ユーザーが入力した数値に応じて処理分岐する
2, 処理を繰り返す

ソースコード
```php:qiita.php
<?php
// while処理に入る前に空文字を変数に入れておかないと、メニューでいきなり２を選択された際に、変数を定義していないよとエラーが起きてしまう。
$title = '';
$author = '';
$status = '';
$score = '';
$summary = '';
// while(true)で無限ループを実現
while (true) {
    echo '1. 読書ログを登録' . PHP_EOL;
    echo '2. 読書ログを表示' . PHP_EOL;
    echo '9. アプリケーションを終了' . PHP_EOL;
    echo '番号を選択してください（1,2,9）：';
    $num = trim(fgets(STDIN));

    if ($num === '1') {
        echo '読書ログを登録してください' . PHP_EOL;
        echo '書籍名：';
        $title = trim(fgets(STDIN));

        echo '著者名：';
        $author = trim(fgets(STDIN));

        echo '読書状況（未読,読んでる,読了）：';
        $status = trim(fgets(STDIN));

        echo '評価（5点満点の整数）：';
        $score = trim(fgets(STDIN));

        echo '感想：';
        $summary = trim(fgets(STDIN));

        echo '登録が完了しました' . PHP_EOL . PHP_EOL;
    } elseif ($num === '2') {
        echo '登録されている読書ログを表示します' . PHP_EOL;
        echo '書籍名：' . $title . PHP_EOL;
        echo '著者名：' . $author . PHP_EOL;
        echo '読書状況：' . $status . PHP_EOL;
        echo '評価：' . $score . PHP_EOL;
        echo '感想：' . $summary . PHP_EOL;
    } elseif ($num === '9') {
        // break;がある時点で繰り返し処理は強制終了となる
        break;
    }
}
```
# 複数の読書ログを表示
<現状>
読書ログを複数登録しようとしても、最後の読書ログしか登録されない。
<理想>
複数の読書ログを表示できるようにする。

複数の読書ログを表示できるようにするためには、配列を用いる。

ソースコード
```php:qiita.php
<?php

<?php

$reviews = [];

while (true) {
    echo '1. 読書ログを登録' . PHP_EOL;
    echo '2. 読書ログを表示' . PHP_EOL;
    echo '9. アプリケーションを終了' . PHP_EOL;
    echo '番号を選択してください（1,2,9）：';
    $num = trim(fgets(STDIN));

    if ($num === '1') {
        echo '読書ログを登録してください' . PHP_EOL;
        echo '書籍名：';
        $title = trim(fgets(STDIN));

        echo '著者名：';
        $author = trim(fgets(STDIN));

        echo '読書状況（未読,読んでる,読了）：';
        $status = trim(fgets(STDIN));

        echo '評価（5点満点の整数）：';
        $score = trim(fgets(STDIN));

        echo '感想：';
        $summary = trim(fgets(STDIN));

        $reviews[] = [
            'title' => $title,
            'author' => $author,
            'status' => $status,
            'score' => $score,
            'summary' => $summary,
        ];

        echo '登録が完了しました' . PHP_EOL . PHP_EOL;
    } elseif ($num === '2') {
        echo '登録されている読書ログを表示します' . PHP_EOL;

        /**
         * ここを修正
         *
         * 読書ログを収納している配列の全要素に対して表示処理を行う
         */
        foreach ($reviews as $review) {
            echo '書籍名：' . $review['title'] . PHP_EOL;
            echo '著者名：' . $review['author'] . PHP_EOL;
            echo '読書状況：' . $review['status'] . PHP_EOL;
            echo '評価：' . $review['score'] . PHP_EOL;
            echo '感想：' . $review['summary'] . PHP_EOL;
            echo '-------------' . PHP_EOL;
        }
    } elseif ($num === '9') {
        break;
    }
}
```
実行結果

```linux:ターミナル
1. 読書ログを登録
2. 読書ログを表示
9. アプリケーションを終了
番号を選択してください（1,2,9）：2
登録されている読書ログを表示します
書籍名：ナウシカ
著者名：宮崎駿
読書状況：読了
評価：5
感想：映画よりさらに素晴らしい
-------------
書籍名：スラムダンク
著者名：井上雄彦
読書状況：読了
評価：5
感想：人生の書
-------------
1. 読書ログを登録
2. 読書ログを表示
9. アプリケーションを終了
番号を選択してください（1,2,9）：
```
# リファクタリング

ソースコード
```php:qiita.php

<?php

function createReview()
{
    echo '読書ログを登録してください' . PHP_EOL;
    echo '書籍名：';
    $title = trim(fgets(STDIN));

    echo '著者名：';
    $author = trim(fgets(STDIN));

    echo '読書状況（未読,読んでる,読了）：';
    $status = trim(fgets(STDIN));

    echo '評価（5点満点の整数）：';
    $score = trim(fgets(STDIN));

    echo '感想：';
    $summary = trim(fgets(STDIN));

    echo '登録が完了しました' . PHP_EOL . PHP_EOL;

    return [
        'title' => $title,
        'author' => $author,
        'status' => $status,
        'score' => $score,
        'summary' => $summary,
    ];
}

/**
 * ここを追加
 *
 * 読書ログを一覧表示するので、listReviews と名付けた
 */
function listReviews($reviews)
{
    echo '登録されている読書ログを表示します' . PHP_EOL;

    foreach ($reviews as $review) {
        echo '書籍名：' . $review['title'] . PHP_EOL;
        echo '著者名：' . $review['author'] . PHP_EOL;
        echo '読書状況：' . $review['status'] . PHP_EOL;
        echo '評価：' . $review['score'] . PHP_EOL;
        echo '感想：' . $review['summary'] . PHP_EOL;
        echo '-------------' . PHP_EOL;
    }
}

$reviews = [];

while (true) {
    echo '1. 読書ログを登録' . PHP_EOL;
    echo '2. 読書ログを表示' . PHP_EOL;
    echo '9. アプリケーションを終了' . PHP_EOL;
    echo '番号を選択してください（1,2,9）：';
    $num = trim(fgets(STDIN));

    if ($num === '1') {
        $reviews[] = createReview();
    } elseif ($num === '2') {
        /**
         * ここを追加
         *
         * 関数を呼び出す
         */
        listReviews($reviews);
    } elseif ($num === '9') {
        break;
    }
}
```
# 参考にしたサイト
[独学エンジニア](https://dokugaku-engineer.com/course/serverside)