パーフェクトPHP 6章のコードが古いのでPHP7に対応できるよう書き換えてみた

こんにちは!  この記事を見ている方は、恐らくパーフェクトPHP第6章のひとこと掲示板が上手く表示されない、データが保存されないといった状況に陥っている方が多いと思います。 僕も、第6章でなかなかデータを保存することができず時間がかかったので、PHP7でも実行できるように書き換えたコードをメモとして残しておきます。

mysql_*関数は「非推奨」又は「使えない」

パーフェクトPHPに記載されている、mysql_connect等のmysql_とつく関数はPHP5.5以降は非推奨、PHP7では削除されています。
そのため、PHP7の環境の場合は書籍に書いてあるコードのままでは動作しません。(PHP5系なら一応動くと思います。)
非推奨になった理由としては、セキュリティの脆弱性であったり、mysqlの最新の機能に対応してない等が主な理由らしいです。
こちらのLIGさんの記事がまとめてあって読みやすかったです。

使っちゃいけない! PHPのmysql_* 系関数の取り扱いに関する海外の見解 | 東京のWeb制作会社LIG

mysqli関数を使って書き換えてみる

上記の理由から、PHP7に対応していて、書き方も書籍と似ているmysqli関数を使いたいと思います。
mysqli関数はオブジェクト型と手続き型の2種類の書き方があるのですが、サンプルコードに一番近い形にするため手続き型を使用しています。

以下全体のコード

<?php
    $link = mysqli_connect('localhost', 'root', '');    
    
    if(!$link) {
        die('データベースに接続できません:' . mysqli_error());
    }

    // データベースを選択する
    mysqli_select_db($link ,'oneline_bbs');
    $errors = array();

    // POSTなら保存処理実行
    if($_SERVER['REQUEST_METHOD']  ===  'POST') {
        $name = null;
        if(!isset($_POST['name']) || !strlen($_POST['name'])) {
            $errors['name'] = '名前を入力してください';
        } else if(strlen($_POST) > 40) {
            $errors['name'] = '名前は40文字以内で入力してください';
        } else {
            $name = $_POST['name'];
        }

        // ひとことが正しく入力されているかチェック
        if(!isset($_POST['comment']) || !strlen($_POST['comment'])) {
            $errors['comment']  = 'ひとことを入力してください';
        } else if(strlen($_POST['comment' ])  > 200) {
            $errors['comment']  = 'ひとことは200文字以内で入力してください';
        } else {
            $comment = $_POST['comment'];
        }

        // エラーがなければ保存
        if(count($errors)  === 0) {
            $sql = "insert into post (name, comment, created_at) values (
                '" . mysqli_real_escape_string($link,$name) . "','"
                . mysqli_real_escape_string($link,$comment) . "','"
                . date('Y-m-d H:i:s') . 
                "')";

            // 保存する
            mysqli_query($link, $sql);
            mysqli_close($link);
            header('Location: http://' .$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
        }        
    }
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ひとこと掲示板</title>
</head>
<body>
    <h1>ひとこと掲示板</h1>

    <form action="bbs.php" method="post">
        <?php if (count($errors) > 0): ?>
            <ul class="error_list">
                <?php foreach($errors as $error): ?>
                    <li>
                        <?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?>
                    </li>
                <?php endforeach; ?>
            </ul>
        <? endif; ?>
        名前: <input type="text" name="name" size="60"><br>
        ひとこと: <input type="text" name="comment"><br>
        <input type="submit" name="submit" value="送信">
    </form>
    <?php  
        // 投稿された内容を取得するSQLを作成して結果を取得
        $sql = "select * from post order by created_at desc;";
        $result = mysqli_query($link, $sql);
    ?>
    <?php if($result !== false && mysqli_num_rows($result)): ?>
        <ul>
            <?php while($post = mysqli_fetch_assoc($result)): ?>
                <li>
                    <?php echo htmlspecialchars($post['name'], ENT_QUOTES, 'UTF-8'); ?>:
                    <?php echo htmlspecialchars($post['comment'], ENT_QUOTES, 'UTF-8'); ?>
                    - <?php echo htmlspecialchars($post['created_at'], ENT_QUOTES, 'UTF-8'); ?>
                </li>
            <?php endwhile; ?>
        </ul>
    <?php endif; ?>

    <?php 
        mysqli_free_result($result);
        mysqli_close($link);
    ?>
</body>
</html>

注意点

ほとんどmysqlがmysqliになっただけのような気がしますが、注意して欲しいのは引数の順番です。

mysqli_select_db($link, 'oneline_bbs');
mysqli_query($link, $sql);

この2箇所は書籍と引数の順番が逆になっています。mysqliでは第一引数に接続先のmysqlオブジェクトが必要です。

また、

$sql = "insert into post (name, comment, created_at) values (
                '" . mysqli_real_escape_string($link,$name) . "','"
                . mysqli_real_escape_string($link,$comment) . "','"
                . date('Y-m-d H:i:s') . 
                "')";

この文にある、mysqli_real_escape_string関数は手続き型の書き方では引数に文字列と接続先のmysqlオブジェクトが必要です