【備忘録・調査】PHPデータベース入門 #03 PDOの設定をしていこう 

#目的
ドットインストール講座動画で分からなかったことを調査し、言語化することで、理解を深める。
（思ってたより、長文になりました）
（思考をそのまま垂れ流しております）
（そのため変な日本語いっぱい使ってます）




【分からなかったこと】
・PDO
・例外処理
・try{} catch {}

#まずは色々ググってみた
[【PHP超入門】クラス～例外処理～PDOの基礎](https://qiita.com/7968/items/6f089fec8dde676abb5b)
（本記事の内容は、ほぼこれを元に思考しております）

[PDO(公式ドキュメント)](https://www.php.net/manual/ja/intro.pdo.php)

[Exception(公式ドキュメント)](https://www.php.net/manual/ja/class.exception.php)



## #例外処理について考えてみる
PDOを理解するには、例外処理について理解する必要があるらしいので、先に理解してみた

上記の参考資料によると、例外処理とは以下の事を指す

>スクリプトを実行したときにデータベースが存在しない、読み込もうとしたファイルが存在しないなどのエラー（例外）が発生しても正常にスクリプトが動作するようにすること

例外処理はなぜ、必要かと考えると、、、

**たぶん、エラーが起きないようなコードを0から作るのが面倒くさいからだ！！**

コストかかるし、エラーが起きてもそれを例外として捉えてシステム自体が動けばいいのではみたいな、、、
もちろん、エラーが起きないようなコードをエンジニアさんは書いていると思うけど、複数のコードが絡み合うと、エラーが出てくるんだろうな〜。そう考えると、「例外処理」って便利やっさ。

##例外処理を使う方法
じゃあ、エラーってPHPでは、どうやって認識するのか???それは、実行するコードにtry,catch,Exceptionクラスをかますらしい（[【PHP超入門】クラス～例外処理～PDOの基礎](https://qiita.com/7968/items/6f089fec8dde676abb5b)からコード引用しております）

```ruby:例外処理
function warizan($val1,$val2) {
    try {
        if($val2 === 0) {
            //No.1 例外を投げる
            throw new Exception('0で割ることはできません');
        }
        return $val1 / $val2;

        // No.2 catchで例外を捕捉
    } catch(Exception $e) {

        // No.3 $eに代入されたインスタンスのメソッドへアクセス
        return $e->getMessage();

    }
}
```

PHPでは、エラーを例外として認識するには、
ExceptionクラスあるいはExceptionのサブクラスのインスタンスを使わないといけないらしい
なぜかというと、、、

***PHP公式ドキュメント様がそう言っているからだ***

ここで深掘りすると、沼にハマりそうなので今はこれで理解しておこう

沼にハマりたい変態は、[PHP公式ドキュメントのException](https://www.php.net/manual/ja/class.exception.php)に行って下さい

上記のコードについて説明は以下の通りだ
>
try・・・function warizan()でエラーが起きそうな処理を記述する。
throw new Exception('0で割ることはできません');・・・エラーをExceptionクラスでインスタンス化。これにより、PHPでエラーを例外として認識する。引数には、エラーの代わりになるものを記述する。
catch・・・tryから投げられてくるインスタンスを変数に入れる。
return $e->getMessage();・・・Exceptionクラスのメソッドを通して、引数に入れたメッセージを返す。


Exceptionクラスを使わないとエラーは例外として認識してくれないみたいな書き方をしてきたが、
公式ドキュメントによると

***ErrorExceptionを使えば、どんなクラスでもエラーを例外に変換できるし、新しい定義済みのクラスの一部は、例外を投げてくれる***

らしいです:rolling_eyes:


## まとめ（「例外処理」について）
PHPの例外処理とは、

***コードのエラーを例外として認識し、システムを正常に動作させることだ。通常は、ExceptionクラスあるいはExceptionのサブクラスのインスタンスを使ってエラーを例外として認識するのだが、EroorExceptionを使えば簡単にエラーを例外に変換できるし、新しい定義済みのクラスの一部は、Exceptionクラスみたいに例外を投げてくれる。***

次は、PDOについて理解してみる


# #PDOについて考えてみる
PDO(PHP Data Objects)とはなんだろうか？

PHPから複数、異なるデータベースにアクセスするとき、それぞれの命令文を記述しないといけないところ、このPDOをかますことによって１つの命令で済むらしい。ちなみにPDOはクラスだ。

```:PDOクラスでインスタンス化して変数$dbhに入れる
$dbh = new PDO('mysql:host=サーバー名;dbname=データベース名;charset=文字エンコード','ユーザー名','パスワード',オプション);
```

引数に、サーバー名、データベース名、文字エンコード、ユーザー名、パスワード、オプションを入れている。

文字コードで、間違った設定をすると、セキュリティーに影響を与えるらしい。

オプションの設定に２通りあるのだが、それは、
上記のように、引数に連想配列で値を入れることでインスタンス化するときに設定する方法
下記のように、setAttributeメソッドを使ってインスタンス後に設定する方法

```:setAttributreメソッドを使って、オプションを使う方法
$dbh = new PDO(
    'mysql:host=サーバー名;dbname=データベース名;charset=文字エンコード',
    'ユーザー名',
    'パスワード',
);

$dbh->setAttribute(変更したい属性 , 値);
$dbh->setAttribute(変更したい属性 , 値);
```

setAttributeでは、設定出来ないオプションがあるらしいので、
なるべく、***引数に連想配列を記述する書き方でオプションを設定して行こう***

PHP5.3.5以前の環境では、PDO::MYSQL_ATTR_INIT_COMMANコマンドしようして、以下のようにデータベースに接続していた

```
$dbh = new PDO(
    'DSN',
    'ユーザー名',
    'パスワード',
    array(
        PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
    )
);
```


PDO::MYSQL_ATTR_INIT_COMMANDとは、PDOクラスのメソッドである。
データベースに接続する時に文字コードutf8を設定するようなコマンドを叩いており、インスタンス化する時の引数に代入している。setAttributeメソッドのように、インスタンス化後のオプション設定では使えない

## まとめ（PHPでデータベースに接続）


***あらゆるデータベースに接続する時、PDOクラスを使うと命令するコマンド数が少なくなり、楽になる。接続する時は、PDOのインスタンスの引数にデータベースに必要な情報を入れとく。PHP5.3.5以前は、引数にPDO::MYSQL_ATTR_INIT_COMMANDを使用して、接続していた***

# #オプションで例外を投げる設定に変更する
なぜオプションで例外を投げる？？？？？？？？
データベースと例外の関係性が全くわかんねー

```
$dbh = new PDO(
    'DSN',
    'ユーザー名',
    'パスワード',
    array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    )
);

```

PDO::ATTR_ERRMODEという属性にPDO::ERRMODE_EXCEPTIONの値を設定していることしかわからない。

PDO::ATTR_ERRMODEはスコープ定義演算子使ってるから、メソッドじらーに見えるけど、連想配列でPDO::ERRMODE_EXCEPTIONの受け皿になってるし、実は空っぽ説。
とりあえず、分けてググってみた。

---
【PDO::ATTR_ERRMODE】
引用先：[エラーおよびエラー処理](https://www.php.net/manual/ja/pdo.error-handling.php)
>PDO::__construct() は、接続に失敗した場合は常に PDOException をスローします。 これは、現在設定されている PDO::ATTR_ERRMODE が何であっても同じです。例外を処理しないと、fatal エラーとなります。

情報が少ない、、、
やっぱりただの受け皿かもしれない。
上の文章を解釈してみると、

データベースに接続する際に、例外を処理をしないと、重大エラーを引き起こして、処理が停止するらしい。

***たぶん、プログラミングって何してもエラー起きるんだろうなー***

PDOExceptionクラスでエラーを認識して、受け皿PDO::ATTR_ERRMODEにぶち込む感じか、

PDO::ATTR_ERRMODEはそのエラーをどう処理するんだ？？？？？
なんかいい感じにして、エラーの代わりになるものを出力するのかな、、、

次は、PDO::ERRMODE_EXCEPTIONググる

---
【PDO::ERRMODE_EXCEPTION】

引用先：[エラーおよびエラー処理](https://www.php.net/manual/ja/pdo.error-handling.php)

***お、
PDO::ERRMODE_EXCEPTIONは、PHPからデータベースに接続する時のエラーを処理してくれるものらしい。***

PDO::ERRMODE_SILENT、PDO::ERRMODE_WARNING、PDO::ERRMODE_EXCEPTIONの３つがあり、

PDO::ERRMODE_SILENT・・・
エラーに対して、エラーの種類や内容を表した番号？を貼り付けるだけっぽい（エラーコードを設定）。何もしないやつ。デフォルト設定。

PDO::ERRMODE_WARNIN・・・
エラーコードを設定し、メッセージを出力してくれる。エラーを処理してくれないけど、報告してくれる結構いいやつ。

PDO::ERRMODE_EXCEPTION・・・
エラーコードを設定してくれるし、エラーを認識して例外処理してくれる。クラスのプロパティとして設定される。エラーが発生したら、スクリプト実行を停止してくるぽい。データベース接続の時に役に立つやつ。

##まとめ (PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)



***PHPからデータベースに接続するときに出てくるとエラーを処理をしないと、DB接続が止まるので、PDO::ERRMODE_EXCEPTIONを使うことで、常にエラーを例外として認識して、PDO::ATTR_ERRMODEにぶち込んでいい感じにしてくれる***


# #プリペアドステートメント、プレスホルダー、バインドについて考えてみる

[プリペアドステートメントおよびストアドプロシージャ ](https://www.php.net/manual/ja/pdo.prepared-statements.php)


なんでこいつについて考えないといけないんだ？？？？


>
これは、実行したい SQL をコンパイルした 一種のテンプレートのようなものです。パラメータ変数を使用することで SQL をカスタマイズすることが可能です。プリペアドステートメントには 2 つの大きな利点があります。

プリペアドテートメントとは、データーベースを命令できるSQL的なやつか。
パラメータ変数はなに指してるのかはわからん、、、

【２つの利点】

>①クエリのパース (あるいは準備) が必要なのは最初の一回だけで、 同じパラメータ (あるいは別のパラメータ) を指定して何度でも クエリを実行することができます。クエリを実行するには、準備として クエリの解析やコンパイル、そして実行プランの最適化が行われます。 クエリが複雑になると、この処理には時間がかかるようになります。 同じクエリを異なったパラメータで何度も実行すると、アプリケーションの 動作は目に見えて遅くなるでしょう。 プリペアドステートメントを使用すると、この 解析/コンパイル/最適化 の繰り返しを避けることができます。 端的に言うと、プリペアドステートメントは使用するリソースが少なくいため 高速に動作するということです。

クエリのパースとは、
データベースに対する命令を分解することだ、、、？？？

PHPからデータベースに命令するには、クエリのパース/クエリの解析/コンパイル/実行プランの最適化をする必要があるが、
プリペアドステートメントを使えば、クエリの解析/コンパイル/実行プランを省くことができる。ということは、***処理が軽くなる***のか！

>②プリペアドステートメントに渡すパラメータは、引用符で括る必要は ありません。それはドライバが自動的に行います。 アプリケーションで明示的にプリペアドステートメントを使用するように すれば、SQL インジェクションは決して発生しません (しかし、もし信頼できない入力をもとにクエリの他の部分を構築している のならば、その部分に対するリスクを負うことになります)。

？？？調べてみよう! 

SQLインジェクションとは、
アプリケーションのセキュリティ上の不備を意図的に利用し、アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のこと。

SQLインジェクションについては、この記事がわかりやすかった
[SQLインジェクションとは？Webサイトのセキュリティ対策を学ぼう]
(https://academy.gmocloud.com/advance/20150608/700)

>ユーザーの値を元にSQL文を組み立てるのだが、入力された値によっては意図しないSQLが生成され、結果として不正にDBのデータが読み取られたり、データが改ざんまたは削除されたりするなどの被害をこうむる可能性があります。

だから、プリペアドステートメントを推奨しているのかな、、、、


SQLには`'A' = 'A'`という「全て」を意味する命令文があり、ユーザーの値がこれを記入し、それを元にSQL文が組まれるとと、全ての情報が表示される可能性がある。だから、SQL文において、シングルクォートのような特別な意味をもつ記号文字を取り除く必要があるのだが（エスケープ処理）

***プレースホルダが自動でエスケープをしてくれる***

便利感出てるけど、どういうことだ？？？

>プリペアドステートメントとは、実行したいSQLをコンパイルした一種のテンプレートのようなものであり、テンプレート中に変数の場所を示す箇所（プレースホルダ）を置いておき、プログラム実行時に実際の値をセット（バインド）します。

###プレースホルダ、バインドについて

```ruby:sql文&疑問プレースホルダ
SELECT name FROM fruit WHERE price= ? AND price = ?
```

`?`の部分を疑問プレースホルダと呼ぶ。
`?`の部分に値を割り当てることをバインドすると呼ぶ
上記コードは、SQL文で書いてるけど、プリペアドステートメントでも同じことができるんだはず。

プレースホルダのメリットは
・エスケープ処理
・後から値を割り当てることができること

名前プレースホルダというものもある

```ruby:sql文&名前プレースホルダ
SELECT name FROM fruit WHERE price= :price;
```

名前プレースホルダと疑問プレースホルダーの違いは何？
[SQLで名前つきでない疑問符プレースホルダがよく使われるのはなぜでしょうか？](https://teratail.com/questions/109351)

ググった感じ
ほぼ役割は同じみたい！

ただ、疑問符プレースホルダーの方が一般的みたい！
ここは経験積んでから、使い分けよう

プレースホルダーには、２種類ある
>・静的プレースホルダー・・・データベース側でバインド
・動的プレースホルダー・・・バインドしてからデータベース側へ

***静的プレースホルダーの方が、セキュリティー安全そう、、、
動的プレースホルダーは処理が速そう、、、***

>静的プレースホルダを使用するには、オプションを設定する必要があります。
PDOには、プリペアドステートメントを***エミュレート***する機能があります。
エミュレートとはプリペアドステートメントの'ふり'をするということです。
もっと、端的にいうと動的プレースホルダを使う機能ということです。

PDOで、プリペアドステートメントで静的プレースホルダを利用するときには、オプションを設定する必要がある。エミュレートをoffにしないといけない。

>エミュレートをOFFにするには、オプションでPDO::ATTR_EMULATE_PREPARESの属性にfalseの値を指定します。

```
$dbh = new PDO(
    'DSN',
    'ユーザー名',
    'パスワード',
    array(
        PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false,
    )
);
```

徐々に理解してきた。

##まとめ　（プリペアステートメント、プレースホルダー、バインド）

***プリペアステートメントを使うことで、PHPからデータベースを命令する際の工程を省くことができ、処理を速くすることができる。そして、プログラム実行時に値をバインドするため、SQLインジェクションに対してのセキュリティーを高くすることができる。プリペアステートメントを使用するときに、静的プレースホルダーを使うと、データベース側で値をバインドしているためより、セキュリティー安全性が高くなるが動的プレースホルダーと比べると、処理速度は遅い。静的プレースホルダーを設定するには、プリペアステートメントのエミュレート機能をoffる。***


#例外処理しながらデータベースに接続するについて考えてみる
やっと、ドットインストールの講座と同じ内容だ〜〜

```
try {

    $dbh = new PDO(
        'mysql:host=サーバー名;dbname=データベース名;charset=utf8',
        'ユーザー名',
        'パスワード',
        array(
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
        )
    );

} catch (PDOException $e) {

    $error = $e->getMessage();

}
```
データベースと例外の関係性わかった気がするーーー！！！１:clap:

上記コードを説明してみる！

　PHPからデータベースに接続する際には、`new PDO`を使うのだが、ほぼほぼ高確率でエラーが起きる。エラーが起きると、データベースに接続できなくなるため、エラーを例外処理する必要があり、try()catch(PDOException)がその役目を果たす。PHPでは、エラーを例外として認識するためには、Exception、あるいはサブクラスのインスタンスを使うのが一般的である。Catchする際に、PDOExceptionでエラーを例外として認識し、変数$eに入れる。変数$eはこれで、PDOExceptionのメソッドが使えることができるようになり、$e->getMessage();を使って、$errorにエラーの代わりになるもの？を代入する。

***new PDOでデータベースに接続するまで、出現するエラーをtry,catach(PDOException $e)で例外処理し続ける構造だ***

new PDOの引数には、データベースに必要な情報と、セキュリティーを高くする静的プレースホルダーの設定を記述

`PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION`でエラーにエラーコードを設定し、PDO::ATTR_ERRMODEに取得したエラーをぶち込んでるはず！

#データベースのデータを取得して表示する

>データベースのデータを取得して表示するには、いくつかのメソッドを使います。
一つのメソッドでSQL文を実行してデータを取得できるわけではありません。
データを取得するには主に下記の手順で行います。

実行、取得、表示を別々に考える必要がある

>
| No | 手順 | メソッド |
|--:|:--|:--|
| 01 | SQL文を実行する準備を行う | PDO::prepare |
| 02 | 値をバインドする | PDOStatement::bindValue または PDOStatement::bindParam |
| 03 | プリペアドステートメントを実行する | PDOStatement::execute |
| 04 | データを配列などで取得する | PDOStatement::fetch または PDOStatement::fetchAll など |

##まずは、`PDO::prepare`についてドキュメントみてみてた！




```ruby:疑問符パラメータを用いてSQLステートメントを準備する
<?php
/* 値の配列を渡してプリペアドステートメントを実行する */
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < ? AND colour = ?');
$sth->execute(array(150, 'red'));
$red = $sth->fetchAll();
$sth->execute(array(175, 'yellow'));
$yellow = $sth->fetchAll();
?>
```

そっか、
今まで、プリペアどステートメントにプレースホルダーを指定すると思ってたけど、
***プリペアステートメントはSQLをコンパイルしたテンプレートだから、そのままやってしますと読みづらい、、、***
SQL文にプレースホルダーを含めてから、プリペアどステートメントを実行する。つまり、コンパイルしてテンプレートにする！！そのための`PDO::prepare`かな

>
返り値 ：
もしデータベースサーバーが正常に文を準備する場合、 PDO::prepare() は PDOStatement オブジェクトを返します。 もしデータベースサーバーが文を準備できなかった場合、 PDO::prepare() は FALSE を返すか PDOException を発行します (エラー処理 の方法に依存します)。

PDO::prepare()は、PDOStatmentオブジェクトを返すか、PDOExceptionを返すか、FALSE返す

>注意:
プリペアドステートメントをエミュレートする際にデータベースサーバーとの通信は行いません。 したがって PDO::prepare() はステートメントのチェックを行いません。


これは、動的プレースホルダーつまりPHP側で値をバインドするときは、データベースの通信は行わない。逆に、静的プレースホルダーの時は、サーバー側でバインドするために、データベースとの通信を行う


>
注意:
パラメータマーカーが表せるのは、データリテラルだけです。 リテラルの一部やキーワード、識別子、その他のクエリのパーツをパラメータにバインドすることはできません。 たとえば、SQL 文の IN() 句などで、 ひとつのパラメータに複数の値を割り当てることはできません。

[データ型とリテラル](http://wisdom.sakura.ne.jp/programming/php/php5.html)
データリテラルとは、
***プログラム実行中に変化することがない、データのことを言うらしい。つまり、直接書かれた定数だ！***

プレースホルダーにバインドできる値は、必ず定数じゃないといけない。一つのプレスホルダーに複数の値はバインドできない。

次は、ドキュメントで説明されているパラメーターについて考えてみる

```
public PDO::prepare ( string $statement [, array $driver_options = array() ] ) : PDOStatement
```


>パラメータ:
statement
・これは対象のデータベースサーバーに対して有効な SQL 文のテンプレートでなければなりません。

>driver_options
・この配列は、このメソッドによって返される PDOStatement オブジェクトに対して 1 もしくはそれ以上の key=>value の組を含みます。 通常、スクロール可能なカーソルを要求するために PDO::ATTR_CURSOR に PDO::CURSOR_SCROLL を設定する場合に使用することになるでしょう。 いくつかのドライバには、準備する際に利用可能なドライバ固有の オプションがあります。


statementは、SQL文のこと

driver_optionsは、PDO::prepareが実行され、PDOstatmentを呼び出すときに、連想配列key=>valueの形で変数？値？を引き渡せる感じか、

###まとめ（PDO::prepare）
***PDO::prepareは、ユーザー視点で見ると、プレースホルダーの数を設定できる！プレースホルダーはデータリテラルじゃないといけない
PHPからすると、設定されたSQL文とプレースホルダーにより、PDOStatment、PDOException、FALSEを返すかを判断する***

---

##次は、02の`PDOStatement::bindValue または PDOStatement::bindPara`について考えてみる

```
public bindValue ( mixed $parameter , mixed $value [, int $data_type = PDO::PARAM_STR ] ) : bool
```
[PDOStatement::bindValue](https://www.php.net/manual/ja/pdostatement.bindvalue.php)
値をパラメータにバインドするってさ！！

`$parameter`には、パラメーターID
`$value`には、パラメータにバインドする値
`$data_type`には 値のデータの型を判断する

***返り値は、TRUEかFALSEを返す***

パラメーターを複数設定したとき、どういうコードの書き方をすればいんだろう？？
bindValueを繰り返すのかな

```ruby:公式ドキュメント
<?php
/* バインドされた PHP 変数によってプリペアドステートメントを実行する */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < ? AND colour = ?');
$sth->bindValue(1, $calories, PDO::PARAM_INT);
$sth->bindValue(2, $colour, PDO::PARAM_STR);
$sth->execute();
?>
```
公式ドキュメントでは、bindValueが２回連続行われてるから、
***プレースホルダーを２回設定すると、bindValueも２回設定するイメージでいいのかな、、、***

***`$calories`,`$clour`がユーザーの値になるのかな、、、***

##次に、`PDOStatement::bindParam()`をみてみよう
[PDOStatement::bindParam()](https://www.php.net/manual/ja/pdostatement.bindparam.php)
>指定された変数名にパラメータをバインドする

bindValueとの違いに混乱する:rolling_eyes:

ゆっくり、落ち着いて比べてみよう
ドキュメントのコード例を比較すると、ほぼ一緒だが、bindParmの引数に、lengthがある。

```
public PDOStatement::bindParam ( mixed $parameter , mixed &$variable [, int $data_type = PDO::PARAM_STR [, int $length [, mixed $driver_options ]]] ) : bool
```

```
<?php
/* INOUT パラメータを持つストアドプロシージャをコールする */
$colour = 'red';
$sth = $dbh->prepare('CALL puree_fruit(?)');
$sth->bindParam(1, $colour, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 12);
$sth->execute();
print("After pureeing fruit, the colour is: $colour");
?>
```

>
length
データ型の長さを指定します。パラメータがストアドプロシージャからの OUT パラメータであることを示す場合、 明示的に長さを設定しなければなりません。

データ型の長さとは？？？？
***文字数的な意味合いかな、、、***


ストアドプロ先輩からのOUTパラメータとは、どう言うことだ、、、？？
Out、、出力、、、？？？？

[PHP でのストアード・プロシージャーの呼び出し (PDO)](https://www.ibm.com/support/knowledgecenter/ja/SSEPGG_9.7.0/com.ibm.swg.im.dbclient.php.doc/doc/t0023502.html)

>
ストアドプロシージャ
データベースに対する一連の処理をまとめた手続きにして、関係データベース管理システム (RDBMS) に保存（永続化）したもの

全然ピントこないなー

[ストアドプロシージャの基本的ななにか](https://qiita.com/setsuna82001/items/e742338eb93e3a48ba46)
>DB上での一連処理に, 名前をつけて関数のように, 呼び出して使用できるもの。
DB上で動作を完結させちゃうから, 開発言語に依存しないよ！

>Ruby とか PHP だとか Perl でも Python だろうと
CALL できれば結果は同じになるはずだよ！

わかりやすい記事！！！あざます！！
さらプロ先輩、ググってみた！！

プロ先輩とは、
>プログラミングにおいて複数の処理を一つにまとめたものをいう。 手続きとするのが定訳である。

【発見】
DB上では、まず一連の処理をまとめることができてそれを関数にすることができる。そしてそれを呼び出すことができる→ストアドプロシージャ

##一連の処理をまとめるときの関数の引数には、INとOUtのパラメータがある！

```ruby:INパラメーター
CREATE PROCEDURE sample02( IN x INT )
SELECT x + 1;
```
これで関数を作成。


```ruby:実行結果
mysql> CALL sample02( 2 );
+-------+
| x + 1 |
+-------+
|     3 |
+-------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)
```

意味的には、sample02（X）をcallすると、X+1の値が呼び出される。カラムもX+1で表示。

`IN x int`ってなに？
intはデータの型で整数(4バイト）を示すもの、、
***引数に入ると同時に、データの型を指定しているのか！***


OUTパラメータもみてみよう

```
CREATE PROCEDURE sample03( OUT x INT )
SET x = 3;
```
値を設定している

```ruby:実行結果
mysql> CALL sample03( @x );
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @x;
+------+
| @x   |
+------+
|    3 |
+------+
1 row in set (0.00 sec)
```

ここだと、
`mysql> CALL sample03( @x );`で
`@x`は3だよーと指定している

`mysql> SELECT @x;`
で取得できるようになる、、、

混乱するーー:innocent:

***inは、引数で値が変わる、、、***
***outは、受け身な感じ、、、かな***

知識も溜まったし、先ほど、引用をまた解釈してみよう

>
length
データ型の長さを指定します。パラメータがストアドプロシージャからの OUT パラメータであることを示す場合、 明示的に長さを設定しなければなりません。

ストアドプロシージャーってことは、データベース側の話。ってことは、静的プレースホルダー限定の話か、、、
データベース側でプレースホルダーに値をバインドするときに、lengthを指定しているってことは、lengthに当てまはるデータじゃないとバインドされないってことかな、、、

・・・
あーーわかった気がする！！！:raised_hands:

PDOstatement::bindParam()のドキュメントで、わざわざストアドプロシージャーのOUTパラメータの話してる理由って、***バインドされる値の情報がすでにある程度決まっている？把握している？影響されない？値をガッチガチに固めたい時だ！***

***どういう状況で使うかは分からないけど、値をガッチガチにして固めてから、データベースからPHP側に流すんだはず！***

***ストアドプロシージャーのOUTパラメータは、ただ引数に値を設置しているだけし、PHP側は、lengthでわざわざ文字数決めて、引き受ける値を制限してる***


##まとめ(PDOStatement::bindValueとPODstatement::bindParam()の違い）

値をプレースホルダーに入れてくれる。
引数は、
・パラメーターID　→`PDO::prepare`の`?`で決まる
・値 
→　値→ストアドプロシージャーで変形→返す
→　そのまま返す
→　DBで指定した値を返す
（いろんな方法で値をバインドできるんだはず）
・データの型
返り値は、FALSEかTRUE
プレースホルダーを２回設定すると、bindValueメソッドも２回使用する

bindParamは追加で、引数にlengthが加わる

***データ取得する際、に柔軟性？自由度が要求される時は、PDOStatement::bindValueを使用する***

***データ取得する際に、ガチガチ？厳密？クローズな感じが要求される時は、PDOstatement::bindParam()を使用する***

#次は、`PDOStatment::execute`についてだ！

PDOStatment::execute

>プリペアドステートメントを実行します。 もし、プリペアドステートメントがパラメータマーカを含む場合、次のいずれかを行わなければなりません。

>・パラメータマーカに PHP 変数や値をバインドするため PDOStatement::bindParam() や PDOStatement::bindValue() をコールする。 関連づけされたパラメータマーカがあれば、バインドされた変数は入力値を渡す もしくは出力値を受け取ります。

>・あるいは、入力専用のパラメータ値の配列を渡す


プリペアステートメント実行する。つまりは、SQLをコンパイルしてテンプレート化する！
PDOstatmentクラスのexecuteメソッドなんだけど、SQL文にプレースホルダーがあるときは値バインド担当のPDOStatement::bindParam() や PDOStatement::bindValue()がないと使えない見たい！逆にプレースホルダーなかったら、そのまま実行できるんだはず

```ruby:値をプレースホルダーにバインドするとき
<?php
/* 変数や値のバインドを伴うプリペアドステートメントの実行 */
$calories = 150;
$colour = 'gre';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour LIKE :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindValue(':colour', "%{$colour}%");
$sth->execute();
?>
```
***executeに引数がない！***

```ruby:入力値をexecuteに引数入れるとき
<?php
/* 入力値の配列を伴うプリペアドステートメントの実行 */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
?>
```

>注意:
ドライバによっては、次のステートメントを実行する前に カーソルを閉じ なければならないものもあります。

`ドライバ`ってなに？？？
[PDOドライバ](https://www.php.net/manual/ja/pdo.drivers.php)
データーベースに接続するときに、ドライバーってやつが必要みたい！

```
public PDOStatement::execute ([ array $input_parameters ] ) : bool
```

>
input_parameters
実行される SQL 文の中のバインドパラメータと同数の要素からなる、 値の配列。すべての値は PDO::PARAM_STR として扱われます。

[PDO::PARAM_STR](https://www.php.net/manual/ja/pdo.constants.php) とは？
SQL CHAR, VARCHAR, または他の***文字列データ型***を表します。

***PDO::bindvalueを使わないときに、executeの引数に配列として、値を代入することができるんだ。その際に値は全て、PDO::PARAM_STRとして認識される***

>プリペアドステートメントを実行することで、PDOStatementのインスタンスに結果セットが格納されます。
結果セットとはデータベースから取り出されたデータを一時的に保持する仮想的なテーブルのようなものです。
この後に結果セットにあるデータを配列で取得します。

結果セットって言葉覚えておこう

##まとめ(PDOStatment::execute)
***プリペアドステートメントを実行する。要するにPDO::prepare()で作成したSQl文とプレースホルダーの数の設定、PDO::bindValueでプレースホルダーに何を代入するかの設定をコンパイル、テンプレート化して実行してくれる。PDO::bindValueを使わない時は、execute()の引数に配列として、値を入れることができるが。文字型データ列として扱われる***

#データを取得する
>
データを取得するには、PDOStatement::fetchまたはPDOStatement::fetchAllなどのメソッドを使います。
データを取得するメソッドは、他にもありますので、PHPマニュアルで確認してください。

`PDOStatement::fetch`・・・ 結果セットから次の行を取得する

```
public PDOStatement::fetch ([ int $fetch_style [, int $cursor_orientation = PDO::FETCH_ORI_NEXT [, int $cursor_offset = 0 ]]] ) : mixed

```

>次のレコードを呼び出し元に返す方法を制御します。 この値は、PDO::FETCH_* 定数のどれかで、 デフォルトは PDO::ATTR_DEFAULT_FETCH_MODE の値 (そのデフォルトは PDO::FETCH_BOTH) です。

そろそろこの項目飽きてきたから、流し読みでいきます。

##まとめ(データを取得する)
PDOstatement::fetchで１行だけ取れる。PDOStatement::fetchAllで全ての値。
レコードの呼び出し元は[こちら](https://www.php.net/manual/ja/pdostatement.fetch.php)でみる。

#終わり