実務4年目のWEBエンジニアが「PHP本格入門（下）」を読んで学んだこと 12選

## はじめに

今回は「__実務4年目のWEBエンジニアが「PHP本格入門（下）」を読んで学んだこと 12選__」というテーマでお伝えします。

PHP本格入門（下）（以下、本書）

https://gihyo.jp/book/2020/978-4-297-11470-1

本書を手に取った経緯などは、下記記事をご参照ください！

https://qiita.com/Yazmatto/items/90f854e11a5b3cd88999

それでは、本題です。

ここからは、本書を読んで学んだこと 12選をお伝えしていきます。

## 結論

私が本書を読んで学んだことは、以下の通りです。

:::note 
学んだこと 12選
1. クラスの関係性を表すキーワード
1. 神クラス（アンチパターン）
1. なんでもpublicなクラス（アンチパターン）
1. クラスに動的にアクセスする方法
1. イテレータを使用した繰り返し処理
1. ジェネレーター
1. メソッドチェーンに対応したクラスの作り方
1. HTMLおよびJavaScriptのコメントには注意する
1. セキュリティリスクを少なくするためのphp.iniの設定
1. SQLインジェクション攻撃とその対策
1. オープンリダイレクトの脆弱性
1. PHP_CodeSniffer
:::

ここからは1つずつ詳細をお伝えしていきます。

## 1. クラスの関係性を表すキーワード

本書では、クラスの関係性を表すキーワードについて述べられています。

そのキーワードとは、以下の3つです。

- is-a の関係
- has-a の関係
- 関連

順に説明していきます！

### is-a の関係

is-a の関係とは、「__●●（サブクラス）は▲▲（スーパークラス）である__」という言葉で表せる関係です。

例えば、
- 「デジタル時計（サブクラス）は時計（スーパークラス）である」
- 「アナログ時計（サブクラス）は時計（スーパークラス）である」

と言い表せます。


is-aの関係を図で表すと以下のようになります。
```mermaid
 classDiagram
 class 時計　{
 スーパークラス
 }
 class デジタル時計 {
 サブクラス
 }
 class アナログ時計 {
 サブクラス
 }
      時計 <|-- デジタル時計
      時計 <|-- アナログ時計

```

is-a の関係の場合、クラス間の関係性は**継承**となります。


### has-aの関係

has-a の関係とは「**あるクラスが別のクラスを持っている**」関係です。

has-a の関係は、さらに**コンポジション**と**集約**に分けられます。

#### コンポジション

コンポジションとは「**●●は▲▲で構成される**」という言葉で表せる関係です。

例えば、**自動車**:blue_car:をイメージするとわかりやすいです。

「**自動車はボディ、タイヤ、ハンドルで構成される**」と言い表せます。

```mermaid
 classDiagram
 class 自動車 {
 1
 }
 class ボディ {
 1
 }
 class タイヤ {
 4
 }
 class ハンドル {
 1
 }
      自動車 *-- ボディ
      自動車 *-- タイヤ
      自動車 *-- ハンドル

```

上記図中の数字は「**多重度**」と呼ばれ、**紐づくクラスの数**を表しています。

コンポジションには「**構成物の所有者が消えると構成物も消える**」という特徴があります。

**自動車が消えると、ボディ、タイヤ、ハンドルも消える**ということです。


#### 集約

集約とは「**●●は▲▲を持ちえるが、●●と▲▲は独立している**」という関係性です。

例えば、**コインパーキング:u7a7a:**と**自動車**:blue_car:をイメージしてください。

「**コインパーキングは自動車を持ちえるが、コインパーキングと自動車は独立している**」といえます。

```mermaid
 classDiagram
 class 自動車 {
 0..*
 }
 class コインパーキング {
 1
 }
      コインパーキング o-- 自動車
```

#### 関連

**is-a でも has-a でもないが、クラス間が何かしらの関係性を持つ**ことを「**関連**」といいます。

例えば、**自動車**:blue_car:と**ディーラー**:person_with_blond_hair_tone2:のような関係性です。

自動車とディーラーはゆるやかな関係性を持っています。

```mermaid
 classDiagram
 class 自動車 {
 
 }
 class ディーラー {
 
 }
       自動車 -- ディーラー
```

設計を行う上で、is-a と has-a の見極めが難しいケースがあります。

基本的に、「**迷ったら has-a を選ぶ**」のが安全です！

なぜなら、has-a であれば**カプセル化が保てる**からです。

逆に is-a を選んでしまうと、カプセル化が保てなくなり、**クラス間に依存関係が生じてしまいます**。

## 2. 神クラス（アンチパターン）

アンチパターンとして、「**神クラス**」と呼ばれるクラスがあります。

全知全能、なんでもこなしてしまう（**凝集度の低い**）クラスのことです。

基本的に、**神クラスは作らない**ようにしましょう！

ありがちな神クラスの例を以下に示します。

```mermaid
 classDiagram
メインルーチン --|> POSレジ : 使う
class POSレジ {
 - 購入商品の配列
 - 会員データ
 - レシートプリンタのインク残量
 - ログ出力ディレクトリ
 + バーコードで商品を読み取る()
 + 合計金額を計算する()
 + 会員カードを読み取る()
 + 会員ポイントを加算する()
 + 購入データをログ出力する()
 + レシートを印刷する()
 + 本部サーバーに購入データを送る()
}

```

POSレジクラスは、なんでもかんでもやりすぎています。

こうしたクラスは、以下のように分割しましょう。

```mermaid
 classDiagram
メインルーチン --|> POSレジ : 使う
メインルーチン --|> 会員カードリーダー : 使う
メインルーチン --|> 会員データ : 使う
メインルーチン --|> 本部サーバー送信 : 使う
メインルーチン --|> ロガー : 使う
      POSレジ *-- 計算機
      POSレジ *-- レシートプリンタ
      POSレジ *-- バーコードリーダー
class POSレジ {
 - 計算機
 - レシートプリンタ
 - バーコードリーダー
 + バーコードで商品を読み取る()
 + 合計金額を計算する()
 + レシートを印刷する()
}
class 会員カードリーダー {
+ 会員カードを読み取る()
}
class 会員データ {
- 会員ポイント
+ 会員ポイントを加算する()
}
class 本部サーバー送信 {
+ データ送信する()
}
class ロガー {
- ログ出力ディレクトリ
+ データをログ出力する()
}
class 計算機{
- 購入商品の配列
+ 合計金額を計算する()
}
class レシートプリンタ {
- インク残量
+ レシートを印刷する()
}
class バーコードリーダー {
+ バーコードを読み取る()
}

```

できるだけ機能を分割し、凝集度を高めることを意識しましょう！

## 3. なんでもpublicなクラス（アンチパターン）

アンチパターンとして「**なんでもpublicなクラス**」があります。

例えば以下のような「**画像ファイル加工クラス**」を考えます。

「+」が「public」を意味しています。

```mermaid
 classDiagram
 class 画像ファイル加工{
 + 対象ファイルパス
 + 幅
 + 高さ
 + ファイルサイズ
 + MIMEタイプ
 + 画素数
 + リサイズする()
 + 回転する()
 + 対象ファイルパスをセットする()
 + 画像のMIMEタイプを読み取る()
 + 画像の幅を調べる()
 + 画像の高さを調べる()
 }

```

このクラスのどこがいけないかというと、「**利用者が使いづらい構造になっている**」という点です。

仮に、利用者が「**リサイズするメソッド」を使いたいケース**を考えます。

利用者は、単に**「リサイズする」メソッドをコールするだけで良いのでしょうか？**

もしかしたら、**あらかじめ「幅」「高さ」「画素数」などをセットしておかないと、正しくリサイズできない**かもしれません。

このように、メソッドやプロパティが**なんでもpublicで定義されていると、利用者が迷ってしまいます**。

そこで、以下のように**適切なアクセス修飾子を与える**ことを意識しましょう！

「-」が「private」を意味しています。

```mermaid
 classDiagram
 class 画像ファイル加工{
 - 対象ファイルパス
 - 幅
 - 高さ
 - ファイルサイズ
 - MIMEタイプ
 - 画素数
 + リサイズする()
 + 回転する()
 + 対象ファイルパスをセットする()
 - 画像のMIMEタイプを読み取る()
 - 画像の幅を調べる()
 - 画像の高さを調べる()
 }

```

クラスの使い方としては、以下が想定されていることが予想できます。

- 呼び出し時に対象ファイルパスを渡す
- 画像をリサイズおよび回転したい場合は、それぞれメソッドをコールする

本来privateなものまでpublicにしてしまうと、**そのメソッドやプロパティをコールしなければならない**、という誤解を与える可能性があるので避けましょう！

## 4. クラスに動的にアクセスする方法

続いては、**条件によってアクセスするクラスを切り替える場合の記述方法**です。

例として、**消費税の計算クラスを切り替える**プログラムを考えます。

あるお弁当屋では、**「テイクアウト」「イートイン」**のいずれかの注文区分が存在するとします。

```mermaid
 classDiagram
 class AbstractTaxCalculator{
 （消費税計算のための抽象クラス）
 + culculate()
 }
 class TaxCalculatorTakeOut{
 （テイクアウト時の消費税を計算するクラス）
 + culculate()
 }
 class TaxCalculatorDining{
 （イートイン時の消費税を計算するクラス）
 + culculate()
 }
      AbstractTaxCalculator <|-- TaxCalculatorTakeOut
      AbstractTaxCalculator <|-- TaxCalculatorDining
```

具体的な実装イメージは以下の通りです。

```php: クラスに動的にアクセスする例
$taxClassName = 'TaxCalculator' . $order['type']; // クラス名を作成

$taxInstance = new $taxClassName(); // クラスを動的にインスタンス化
```

このように、**インスタンス化する際のクラス指定部分を変数化すると、動的にクラスをインスタンス化することができます！**

## 5. イテレータを使用した繰り返し処理

イテレータとは、「**繰り返し可能であるもの**」の総称です。

イテレータを使用すると、**クラスを配列と同じように繰り返し処理できる**ようになります。

本書では、イテレータの一種である「IteratorAggregateインターフェース」を使った実装例が紹介されています。

```php:ShoppingCart.php
<?php

class ShoppingCart implements IteratorAggregate
{
  private $item = [];

  public function addItem($item): boid 
  {
    // 商品を追加する処理
  }
  
  // foreachループの時に内部的にコールされるメソッド。
  // IteratorAggregateインターフェース内に定義されている。
  // 戻り値はTraversable型出なければならない。
  public function getIterator(): Traversable
  {
    return new ArrayIterator($this->item);
  }
}
```

```php:メインルーチン
(インスタンス化)
<?php $cart = new ShoppingCart(); ?>

(IteratorAggregateを実装していることで、インスタンスを繰り返し処理可能)
<?php foreach ($cart as $itemNumber => $item): ?>
  <p><?= $itemNumber ?>:<?= $item ?></p>
<?php endforeach; ?>
```

上記のように、**IteratorAggregateインターフェースを使用すると、クラスのインスタンスを連想配列として扱える**ようになります！

## 6.ジェネレーター

ジェネレーターとは、**複雑な規則性を持つ配列の要素を、1要素ずつ順々に返す特殊な関数**です。

以下のメリットがあります。

:::note
ジェネレーターのメリット
- 配列処理のメモリ消費を抑えられる
- 複雑な配列生成処理を隠蔽できる（配列を扱う時は配列生成について気にしなくて良い）
:::

ジェネレーター関数は、returnの代わりに**yield**を使用して順々に値を返します。

```php:yieldの例
yield 値;
```

省メモリ化の例として、以下の処理を見てみましょう。

処理内容は、「**AからZまでを返却したあと、AAからZZまでを返却する**」というものです。

```php: ジェネレーターの使用例
<?php
// ジェネレーター
function yieldColumnName()
{
  // AからZまでを返却
  foreach (range('A', 'Z') as $columnName) {
    yield $columnName;
  }

  // AAからZZまでを返却
  foreach (range('A', 'Z') as $columnName1) {
    foreach (range('A', 'Z') as $columnName2) {
      yield $columnName1 . $columnName2;
    }
  }
}

// メインルーチン
foreach (yieldColumnName() as $columnName) {
  echo $columnName, PHP_EOL;
}
```

上記処理を実行すると以下の通りの結果となります。

```: 実行結果
A
B
C
〜 中略 〜
X
Y
Z
AA
AB
AC
〜 中略 〜
ZX
ZY
ZZ
```

ジェネレーターでは**1カラム分のメモリ使用**で済みますが、returnで実装すると配列の大きさ分のメモリが必要となります。

このようにジェネレーターを使用することで、**メモリの節約**ができます！

## 7. メソッドチェーンに対応したクラスの作り方

インスタンスに対するメソッド呼び出しをまとめて書く記法を「**メソッドチェーン**」といいます。

メソッドチェーンを使用することで、**コンパクトに処理を書ける**だけでなく、**関連性もわかりやすくなる**というメリットがあります！

このメソッドチェーンは、自作のクラスでも使用することができます。

メソッドチェーンに対応したクラスを作る方法は以下の通りです。

1. メソッドの戻り値を$thisにする
1. 結果を得るためのメソッドを作る
1. コンストラクタをprivateにし、代わりにpublicメソッドを作る

サンプルコードは以下の通りです。

### 1. メソッドの戻り値を$thisにする

例として、四則演算をするクラスを作成します。

```php:メソッドの戻り値を$thisにする
Class Calculator 
{
  private $result; // 計算結果

  public function add($value) 
  {
    $this->result += $value;
    // 戻り値を$thisにする
    return $this;
  }

  public function subtract($value) 
  {
    $this->result -= $value;
    // 戻り値を$thisにする
    return $this;
  }
```

### 2. 結果を得るためのメソッドを作る
```php:Calculatorクラス内に、結果を得るためのメソッドを作る
  public function getResult() 
  {
    // 計算結果を返す
    return $this->result;
  }
```

### 3. コンストラクタをprivateにし、代わりにpublicメソッドを作る

インスタンス生成も含めて1ステップで書けるようにするために、コンストラクタをprivateにし、代わりにpublicかつstaticなメソッドを作ります。

```php:コンストラクタをprivateにし、代わりにpublicメソッドを作る
  // Calculateインスタンスを取得する。
  // 引数$startValueには演算前の初期値を入れる。
  public static function newInstance(int $startValue): Calculator 
  {
    return new Calculator($startValue);
  }

  // コンストラクタはprivateとし、newInstanceメソッドからのみ呼び出せるようにする。
  private function __construct(int $startValue)
  {
    （省略）
```

これで、呼び出し側は下記のように1ステップで全ての計算処理を書けるようになります！

```php:メソッドチェーンを使用できるようになった
 $result = Calculate::newInstance(0)->add(10)->substract(7)->getResult();

// $resultは、3となります。
// 0 + 10 - 7 = 3
```

## 8. HTMLおよびJavaScriptのコメントには注意する

HTMLおよびJavaScriptのコメントは、**そっくりそのままユーザーに開示されている**ので取り扱いに注意しましょう！

避けるべきコメントをご紹介します。

下記のHTMLコメントでは、デバッグ用画面のURLが記載されてしまっています。

```html:避けるべきHTMLコメントの例
<a href="mypage.php">マイページ</a>
<!-- a href="mypage-debug.php">マイページ（デバッグ用）</a -->
```

下記のJavaScriptコメントでは、「**debugを1に書き換えるとユーザーの情報を抜き取れそう**」という情報をユーザーに与えてしまいます。

```javascript:避けるべきJavaScriptコメントの例
$.ajax({
  data: {
    'debug': 0, // 1にするとユーザーの全データを表示
    'userId': userId,
  }
```

コメントを残す際は、情報漏洩リスクを意識するように気をつけましょう！

## 9. セキュリティリスクを少なくするためのphp.iniの設定

php.iniは、PHPの設定を行うファイルです。

このphp.iniについて、本書では以下の通り設定することを推奨しています。

|オプション名|	デフォルト値|	おすすめの設定値|
|----|----|----|
|expose_php|	On|	Off|
|allow_url_fopen|	On|	Off|
|display_errors|	On|	本番環境ではOff、開発環境ではOn|
|open_basedir|	空欄|	/var/files/ など|

ここから、各項目について説明していきます。

### expose_php

Onになっていると、**HTTPレスポンスヘッダにPHPのバージョン情報が含まれる**ようになります。

**PHPのバージョン情報は、外部に漏れないようにしましょう**！

なぜかというと、PHPの特定バージョンに脆弱性があった場合に、悪意のあるユーザーにヒントを与えてしまうことになるからです。

特に不都合はないので、Offにしておきましょう。

### allow_url_fopen

Onになっていると、**ファイルを読み書きする関数（file_get_content関数など）にURLを指定できる**ようになります。

外部のWebサイトのファイルを読み込ませることができる、ということになってしまいます。

外部サーバーとのやりとりが必要な場合は、**cURL関数などで実装**しましょう。

原則として、allow_url_fopenはOffにしておきましょう。

### display_errors

エラーメッセージにはさまざまな**攻撃のヒント**が含まれているので、本番環境ではOffにしましょう！

逆に、**開発環境ではOnにした上で、error_reportingもE_ALLに設定**しましょう。

**本番環境ではエラーメッセージを表示せず、開発環境ではNoticeレベルでも積極的に表示**すると良いでしょう。

### open_basedir

このオプション値にディレクトリを指定することで、**そのディレクトリ以下のファイルしかオープンできない**ようになります。

サーバー内の重要ファイルにアクセスされないよう、値を設定しておくと良いでしょう。

## 10. SQLインジェクション攻撃とその対策

SQLインジェクションとは、**プログラマーがPHPに埋め込んだSQLを不正に使用し、データベース操作を行う**攻撃方法です。

例えば、以下のプログラムがあったとします。

```php:脆弱性のあるPHP
$sql = "SELECT 
          * 
        FROM 
          users 
        WHERE 
          login_id = '{$loginId}' 
          AND password = '{$password}'";
```

このプログラムは、**誰でも簡単に他人のアカウントでログインできる**という脆弱性があります。

どういうことでしょうか？

ログインIDとパスワードに、以下の値を設定してみましょう。

- ログインID : `dummy`
- パスワード : `' or '1' = '1`

生成されるSQLは以下の通りです。

```sql:SQLインジェクションを行なわれた例
SELECT 
  * 
FROM 
  users 
WHERE 
  login_id = 'dummy' 
  AND password = '' or '1' = '1'
```

注目すべきは**WHERE句**です。

**or '1' = '1'の判定は常に正**となります。

よってこのSQLは、「**usersテーブルからすべてのレコードを取得する**」という結果となってしまいます！

悪意のあるユーザーは、上記SQLを実行することで、1レコード目に見つかったユーザーになりすましてログインすることが可能です。

SQLインジェクションに対応するために、下記のように**プリペアードステートメント**を使用しましょう！

```sql:プリペアードステートメントを使用した例
$sql = "SELECT 
          * 
        FROM 
          users 
        WHERE 
          login_id = :login_id 
          AND password = :password";

$statement = $pdo->prepare($sql);
$statement->bindValue(':login_id', $loginId, PDO::PARAM_STR);
$statement->bindValue(':password', $password, PDO::PARAM_STR);
$statement->execute();
```

プリペアードステートメントを使用すると、あらかじめSQLをプリコンパイルしておき、後ほど値をバインドさせる処理が行われます。

プリコンパイルのおかげで、**バインド値にどのような記号類が含まれていても、SQL構文を書き換えることはできなくなります！**

## 11. オープンリダイレクトの脆弱性
オープンリダイレクトとは、**どんなURLでもリダイレクトできてしまうプログラム**のことです。

例えば下記のようなプログラムです。

```php:オープンリダイレクトの例（https://example.com/login.php）
// https:expample.com/login.php内の処理

if (ログイン成功したら) {
  if ($_GET['page']が指定されていたら) {
    header('Location: ' . $_GET['page']); // オープンリダイレクト
  }
}
```

攻撃者がオープンリダイレクトを用いることで、**罠サイトへと自動遷移**させることができます:fearful:

下記のリンクを**スパムメールなどに埋め込み、ユーザーにクリックさせる**と、ログイン済みのユーザーを罠サイトへ自動遷移させることができます。

`https://example.com/login.php?page=http://crack.com`

上記の「`http://crack.com`」が罠サイトです。

では、オープンリダイレクトにならないようにするにはどうしたらいいでしょうか？

答えは「**GETパラメーターの値を厳密にバリデーションチェックする**」ことです！

バリデーションチェックの例として、ホワイトリストを用意することが挙げられます。

```php:オープンリダイレクトさせない例
$allowedUrls = [
  'start-page.php',
  'mypage.php',
];

if ($_GET['page']が指定されていたら) {
  if (in_array($_GET['page'], $allowedUrls)) {
    header('Location: ' . $_GET['page']); // 安全なページにのみ遷移させる
  } else {
    header('Location: start-page.php'); // 安全でない場合はデフォルトページに遷移させる
  }
}
```

オープンリダイレクトにならないよう注意しましょう！

## 12. PHP_CodeSniffer

PHP_CodeSnifferは、**PHPの外部ライブラリ**の一種です。

コードがコーディング規約に沿っているか、**自動でチェック・整形**してくれます！

https://github.com/squizlabs/PHP_CodeSniffer

実際に試してみました。

まずはPHP_CodeSnifferをインストールします。

今回は以下の記事を参考にさせていただきました。

https://blog.photosynthesic.jp/2021/01/phpcs-wordpress-01/

（実行環境はM1 MacBook Proです）

```bash:composerをインストール
％ composer global require squizlabs/php_codesniffer 
```

PHP_CodeSnifferをインストールしたら、下記コマンドでパスを通します。

```bash:パスを通す
% echo 'export PATH=$HOME/.composer/vendor/bin:$PATH' >> ~/.zshrc
```

続いて下記コマンドで設定を反映させます。

```bash:設定を反映
% source ~/.zshrc
```

では動作確認です。バージョンを表示してみましょう。

```bash:バージョンを表示するコマンド
% phpcs --version
```

無事に動作確認できました！

```bash:composerをインストール
PHP_CodeSniffer version 3.6.2 (stable) by Squiz (http://www.squiz.net)
```

下記のPHPファイルを使用して、挙動を試してみます。

```php
<?php
// テストの関数
function echoStringWithEOL(string $string)
{
  echo $string , PHP_EOL;
}


```

（わざと気持ち悪い書き方をしてみました）

では、コーディング規約（今回はPSR-12）に即しているかをチェックしてみます！

```bash:コーディング規約に即しているかをチェックするコマンド
% phpcs --standard=PSR12 sample.php
```

結果は以下の通りです。

```:phpcsコマンドの実行結果
FILE: /path/to/sample.php
------------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 2 LINES
------------------------------------------------------------------------
 1 | ERROR | [x] Header blocks must be separated by a single blank line
 6 | ERROR | [x] Expected 1 blank line at end of file; 2 found
------------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
------------------------------------------------------------------------

Time: 29ms; Memory: 6MB
```

下記の2点でエラーが発生しました。

:::note alert
エラー内容
1行目： PHPタグの開始部分に空行を入れるべき（今回は0行）
6行目： ファイルの終了部分に1行の空行を入れるべき（今回は2行）
:::

それでは、自動整形も試してみます。

phpcsコマンドと同時にインストールされるphpcbfコマンドを使用することで、自動整形ができます！

```bash: phpcbfコマンドの使用例
% phpcbf --standard=PSR12 sample.php
```

実行結果は以下の通りです。

```:phpcbfコマンドの実行結果
PHPCBF RESULT SUMMARY
----------------------------------------------------------------------
FILE                                                  FIXED  REMAINING
----------------------------------------------------------------------
/path/to/sample.php                          2      0
----------------------------------------------------------------------
A TOTAL OF 2 ERRORS WERE FIXED IN 1 FILE
----------------------------------------------------------------------

Time: 31ms; Memory: 6MB
```

正常に整形が終了しました。

```php:自動整形後
<?php

// テストの関数
function echoStringWithEOL(string $string)
{
    echo $string , PHP_EOL;
}

```

ちゃんとコーディング規約通りになっていますね！

下記画像を参照いただくと、どういう風に自動整形されるのかお分かりいただけると思います。

![24491dbe627213597ec2a1a2c1b0d028.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238817/a088f8ff-a3ec-4bc2-d431-b29dbb3e425c.gif)

これは便利そうですね！

もし機会があれば、実務で使用しているコードに対して自動整形をやってみたいところです。

## おわりに

今回は、「**実務4年目のWEBエンジニアが「PHP本格入門（下）」を読んで学んだこと 12選**」というテーマでお伝えしました。

12選の内容は以下の通りです。

:::note 
学んだこと 12選
1. クラスの関係性を表すキーワード
1. 神クラス（アンチパターン）
1. なんでもpublicなクラス（アンチパターン）
1. クラスに動的にアクセスする方法
1. イテレータを使用した繰り返し処理
1. ジェネレーター
1. メソッドチェーンに対応したクラスの作り方
1. HTMLおよびJavaScriptのコメントには注意する
1. セキュリティリスクを少なくするためのphp.iniの設定
1. SQLインジェクション攻撃とその対策
1. オープンリダイレクトの脆弱性
1. PHP_CodeSniffer
:::

本書を読んだことで、**PHPに関する理解が深まった**と感じました！

もしも**基礎からPHPを学びたい**という方がいらっしゃいましたら、ぜひ本書を読んでみることをおすすめします！


ここまで読んでいただきありがとうございました。

それでは。