僕なりの PHP 備忘録

# [言語リファレンス](https://www.php.net/manual/ja/langref.php)



## [クラスとオブジェクト](https://www.php.net/manual/ja/language.oop5.php)



###  [コンストラクタとデストラクタ](https://www.php.net/manual/ja/language.oop5.decon.php)

```php
<?php
class BaseClass {
    function __construct() {
        print "In BaseClass constructor\n";
    }
}

class SubClass extends BaseClass {
    function __construct() {
        parent::__construct();
        print "In SubClass constructor\n";
    }
}

class OtherSubClass extends BaseClass {
    // BaseClass のコンストラクタを継承します
}

// In BaseClass constructor
$obj = new BaseClass();

// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();

// In BaseClass constructor
$obj = new OtherSubClass();
?>
```



```php
<?php
class Point {
    protected int $x;
    protected int $y;

    public function __construct(int $x, int $y = 0) {
        $this->x = $x;
        $this->y = $y;
    }
}

// 引数を両方渡す
$p1 = new Point(4, 5);
// 必須の引数のみを渡す。$y はデフォルト値0になります。
$p2 = new Point(4);
// 名前付き引数(PHP 8.0 以降):
$p3 = new Point(y: 5, x: 4);
?>
```



```php
<?php
class Point {
    public function __construct(protected int $x, protected int $y = 0) {
    }
}
```

- コンストラクタのプロモーション（昇格）という。



```php
<?php
class Product {

    private ?int $id;
    private ?string $name;

    private function __construct(?int $id = null, ?string $name = null) {
        $this->id = $id;
        $this->name = $name;
    }

    public static function fromBasicData(int $id, string $name): static {
        $new = new static($id, $name);
        return $new;
    }

    public static function fromJson(string $json): static {
        $data = json_decode($json, true);
        return new static($data['id'], $data['name']);
    }

    public static function fromXml(string $xml): static {
        // Custom logic here.
        $data = convert_xml_to_array($xml);
        $new = new static();
        $new->id = $data['id'];
        $new->name = $data['name'];
        return $new;
    }
}

$p1 = Product::fromBasicData(5, 'Widget');
$p2 = Product::fromJson($some_json_string);
$p3 = Product::fromXml($some_xml_string);
```

- 異なる入力を使い、 異なるやり方でオブジェクトを生成させたい。
-  その場合に上記がおすすめ。
- staticメソッドをコンストラクタのラッパーとして使う。





# 配列には旅をさせない

> https://speakerdeck.com/uzulla/throw-away-all-php-array-now?slide=1



## 重要なこと

不完全なインスタンスを作らない。

- オブジェクトが「生まれる」場所を集約する。
- そのために、「生まれる」場所にたどり着くまで、引数で連れまわす。

namespaceを使う。

- 「良い名前」より「違う名前」をつける意識
- 名前は「特性・役割」ではなく、「実現する機能」で切る
    - `Request\UserPointDto`ではなく、`UserPoint\RequestDto`
    - 前者は`UserPoint`という特性・役割になっていて、後者は`Request`を返すよという機能になっている

ライブラリはWrapする。

- ライブラリには配列でオプションを渡したり、受け取ったりする仕様のものがある。
- Wrapperは、愚直に詰め替えを書いて良い。
- ライブラリを満足させるために設計しない方が良い。

OOPとか考えない。

- ファイル数やコード行数が増えるのは仕方ない。その代わり、初心者に書きやすく、ルールにしやすく、静的解析が効くのでリファクタリングしやすい。
- 構造化プログラミングもどきで十分
- 素朴かつ疎かにする

静的解析に頼ろう。



## DTO

### どんなもであるか

Data Transfer Object の略。

データを転送するためのクラスのこと。

配列より厳格・静的・透明性が高い・型の名がある・静的解析と相性〇



### 定義方法

初めの段階でプロパティを決め切っちゃう。

- 名前は、`UserStruct`や`UserDto`等にする

- 使われ**うる**プロパティをもつ
    - 不要なフィールドは持たない
    - 足りなければ足す
    - 実行時の追加は許さない
- getter/setterは無くてよい

```php
class UserDto {
    public function __construct(
    	public int $id,
        public string $name,
        public int $lastLoginAdEpoch,
        public bool $isLongTimeMissed = false,
        public bool $isAdmin = false,
    ){}
}
```



### 使い方

データを取得したら、すぐにDTOにする。

```php
function getUser(int $id): UserDto
{
    $pdo = new PDO('sqlite::memory:');
    // 省略
    $stmt->execute();
    
    return new UserDto(...$stmt->fetch(PDO::FETCH_ASSOC));
}
```

- 引数にPHPの配列はやめて、何を渡しているか明示する
- 返値が`UserDto`であることを明示する
- ただし、nullの場合もあるので例外処理はする。例外処理が面倒な場合は返値にnullを許容するのもあり。例外処理の方が、安全よりであることを忘れない。



fetch all の例

```php
# foreach例
$list = [];
while(false !== $row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    $list[] = new UserDto(...$row);
}
return $list;
// or
return $stmt->fetch(PDO::FETCH_CLASS, UserDto::class);

# array_map例
return array_map(
	fn(array $row) => new UserDto(...$row),
    $stmt->fetchAll(PDO::FETCH_ASSOC)
);
//or
return $stmt->fetchAll(PDO::FETCH_CLASS, UserDto::class);
```



### 注意

`getUser()`のスコープ内部で、配列が役割を終えていることが大切＝旅させない。

`UserDto`のプロパティはテーブルのカラムと一致しなくても良い。

1つの「データソース」が、複数の異なる形状の DTO にマップされても良い。`UserPublicDto`や`UserInternalDto`等の派生をつくるのもあり。

ファクトリで作ってもよいが、DTOを1つにしたら意味がない。またデフォルト値は動的な余地を残すので避ける。





# バージョンの確認方法

- 既にインストールされているバージョンを表示し、そこから選択できる

    ```bash
    $ sudo update-alternatives --config php
    ```



# 設定ファイル（`php.ini`）について

## 画像アップロードの際に影響を与える設定値

- `upload_max_filesize`
    - 単一のファイルの最大アップロードサイズを定義する。これ以上のサイズのファイルはアップロードできない。
- `post_max_size`
    - POSTリクエストを通じて送信できるデータの最大量を定義する。この設定は `upload_max_filesize` よりも大きい値に設定する必要がある。