HTML入力フォームで指定した文字列をデータベースから検索して表示する方法

#1 はじめに
Webアプリ作成のための基礎知識をえるため、HTML入力フォームで
指定した文字列をデータベースから検索して表示する方法をまとめました。


#2 環境
VMware Workstation 15 Player上の仮想マシン(1台)を使いました。
仮想マシンのOS版数、カーネル版数は以下のとりです。
本サーバのIPアドレスは、`192.168.2.115`です。

##2.1 各種版数

```console:CentOS版数の確認
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 8.3.2011
```

```console:カーネル版数の確認
[root@server ~]# uname -r
4.18.0-240.el8.x86_64
```

```console:PHP版数
[root@server ~]# php -v
PHP 7.2.24 (cli) (built: Oct 22 2019 08:28:36) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
```

```console:MariaDB版数
[root@server ~]# mariadb -V
mariadb  Ver 15.1 Distrib 10.5.8-MariaDB, for Linux (x86_64) using readline 5.1
```

##2.2 SELinuxの状態
SELinuxの状態は`Enforcing`です。無効にはしません！

```console:SElinuxの状態
[root@server ~]# getenforce
Enforcing
```


#3 SELinuxルールの作成
SELinuxが有効だとhttpdがデータベースにアクセスできないので、
audit2allowコマンドを使って、アクセス拒否の理由を調べまてみました。
そして、正しいルールを作成して、httpdがデータベースにアクセスできるようにしました。

##3.1 アクセス拒否理由の確認
audit2allowコマンドを実行すると、`php-fpm`コマンドが`connect`システムコールを実行したとき、
アクセス拒否が発生していることがわかります。
アクセス拒否は、次のboolean値のいずれかが正しく設定されていないため、とあります。
・httpd_can_network_connect
・httpd_can_network_connect_db 

```console:アクセス拒否理由
[root@server ~]# audit2allow -w -a
-snip-
type=AVC msg=audit(1610774947.277:78): avc:  denied  { name_connect } for  pid=913 comm="php-fpm" dest=3306 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:mysqld_port_t:s0 tclass=tcp_socket permissive=0
        Was caused by:
        One of the following booleans was set incorrectly.
        Description:
        Allow httpd to can network connect

        Allow access by executing:
        # setsebool -P httpd_can_network_connect 1
        Description:
        Allow httpd to can network connect db

        Allow access by executing:
        # setsebool -P httpd_can_network_connect_db 1
```

デフォルトのboolean値を確認します。
`httpd_can_network_connect`と`httpd_can_network_connect_db`の
boolean値は、ともにoffとなっていることがわかります。

```console:boolean値の確認
[root@server ~]# getsebool -a | grep httpd_can_network
httpd_can_network_connect --> off
httpd_can_network_connect_cobbler --> off
httpd_can_network_connect_db --> off
httpd_can_network_memcache --> off
httpd_can_network_relay --> off
```

##3.2 ルールの作成
`3.1`のメッセージにしたがって、boolean値をonにしました。
メッセージによると、`httpd_can_network_connect`または`httpd_can_network_connect_db`の
boolean値をonにする、と説明があるので、私の環境では、`httpd_can_network_connect`を
onにしました。

```console:httpd_can_network_connectのon
[root@server ~]# setsebool -P httpd_can_network_connect 1
```


`httpd_can_network_connect`がonになったことがわかります。

```console:boolean値の確認
[root@server ~]# getsebool -a | grep httpd_can_network
httpd_can_network_connect --> on
httpd_can_network_connect_cobbler --> off
httpd_can_network_connect_db --> off
httpd_can_network_memcache --> off
httpd_can_network_relay --> off
```


#4 テスト用データの作成
データベース(`testdb`)を作成します。
なお、MariaDBの使い方は、[MariaDBの使い方](https://qiita.com/hana_shin/items/4d7e9f07e206e989834b)を参照してください。

```console:データベース作成
MariaDB [testdb]> create database testdb;
```

使用するデータベースを`testdb`に切り替えます。

```console:データベースの切り替え
MariaDB [testdb]> use testdb;
```

`testdb`に`shop`テーブルを作成します。
`shop`テーブルには、お店の住所、電話番号等を登録します。

```console:テーブルの作成
MariaDB [testdb]> create table shop(id int unsigned, pref varchar(8), shop varchar(32), address varchar(128), tel varchar(32));
Query OK, 0 rows affected (0.006 sec)
```

```console:店舗情報の登録
MariaDB [testdb]> insert into shop values(1,"東京","リンガーハット","東京都町田市木曽東3丁目8-24","042-xxx-xxxx");
MariaDB [testdb]> insert into shop values(2,"東京","喜多方坂内","東京都多摩市落合1-45-1","042-xxx-xxxx");
MariaDB [testdb]> insert into shop values(3,"東京","洋庖丁","東京都新宿区高田馬場3-1-4","03-xxxx-xxxx");
MariaDB [testdb]> insert into shop values(4,"神奈川","丸亀製麺","神奈川県川崎市中原区上小田中4-2-1","044-xxx-xxxx");
MariaDB [testdb]> insert into shop values(5,"香川","手打十段うどんバカ一代","香川県高松市多賀町1-6-7","087-xxx-xxxx");
```

店舗情報を登録したテーブルを確認します。

```console:テーブルの確認
MariaDB [testdb]> select * from shop;
+------+-----------+-----------------------------------+-------------------------------------------------+--------------+
| id   | pref      | shop                              | address                                         | tel          |
+------+-----------+-----------------------------------+-------------------------------------------------+--------------+
|    1 | 東京      | リンガーハット                    | 東京都町田市木曽東3丁目8-24                     | 042-xxx-xxxx |
|    2 | 東京      | 喜多方坂内                        | 東京都多摩市落合1-45-1                          | 042-xxx-xxxx |
|    3 | 東京      | 洋庖丁                            | 東京都新宿区高田馬場3-1-4                       | 03-xxxx-xxxx |
|    4 | 神奈川    | 丸亀製麺                          | 神奈川県川崎市中原区上小田中4-2-1               | 044-xxx-xxxx |
|    5 | 香川      | 手打十段うどんバカ一代            | 香川県高松市多賀町1-6-7                         | 087-xxx-xxxx |
+------+-----------+-----------------------------------+-------------------------------------------------+--------------+
5 rows in set (0.000 sec)
```

#4 HTMLファイルの作成

```console:HTMLファイルの作成
[root@server ~]# cat /var/www/html/shop.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <form method="get" action="shop.php">
    <select name="pref">
      <option value="東京">東京</option>
      <option value="神奈川">神奈川</option>
      <option value="香川">香川</option>
    </select>
    <input type="submit" name="submit" value="検索">
  </form>
</body>
</html>
```


#5 PHPファイルの作成
HTMLファイルのactionタグから呼び出すPHPファイルを作成します。
なお、エラー処理は意図的に省略しています。

```console:PHPファイルの作成
[root@server ~]# cat /var/www/html/shop.php
<?php
  $res = "";
  $pdo = new PDO('mysql:host=127.0.0.1;dbname=testdb', 'root', 'admin');
  $sql = "select * from shop where pref=?";
  $stmt = $pdo->prepare($sql);
  $array = array($_GET{'pref'});
  $stmt->execute($array);

  $res = "<table>\n";
  while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    $res .= "<tr><td>" .$row['id'] .
            "</td><td>" .$row['pref'].
            "</td><td>" .$row['shop'].
            "</td><td>" .$row['address'].
            "</td><td>" .$row['tel'].
            "</td></tr>\n";
  }
  $res .= "</table>\n"
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <?php echo $res; ?>
</body>
</html>
```

#6 実行結果
ブラウザから下記URLにアクセスします。
http://192.168.2.115/shop.html
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146813/beb5b2f6-f2a5-d2d7-bc9b-1092f52dd9b0.png)

「東京」を選択して「検索」をクリックすると、東京の店だけが表示されていることがわかります。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146813/8f6fd443-10b1-d68a-f574-1267797e83c4.png)

#Z 参考情報
[PHPとMySQLのツボとコツがゼッタイにわかる本](https://www.amazon.co.jp/PHP%E3%81%A8MySQL%E3%81%AE%E3%83%84%E3%83%9C%E3%81%A8%E3%82%B3%E3%83%84%E3%81%8C%E3%82%BC%E3%83%83%E3%82%BF%E3%82%A4%E3%81%AB%E3%82%8F%E3%81%8B%E3%82%8B%E6%9C%AC-%E6%A8%AA%E5%B1%B1-%E9%81%94%E5%A4%A7/dp/4798042234/ref=tmm_hrd_swatch_0?_encoding=UTF8&qid=1610779055&sr=8-1)
[HTML入力フォームの作成、徹底ガイド](https://www-creators.com/archives/2486#_8212_input_select_button)
[10日でおぼえるHTML5入門教室](https://www.amazon.co.jp/10%E6%97%A5%E3%81%A7%E3%81%8A%E3%81%BC%E3%81%88%E3%82%8BHTML5%E5%85%A5%E9%96%80%E6%95%99%E5%AE%A4-%E5%8F%A4%E7%B1%8F-%E4%B8%80%E6%B5%A9/dp/4798124184/ref=tmm_other_meta_binding_swatch_0?_encoding=UTF8&qid=1610793499&sr=8-2)