言語リファレンス
クラスとオブジェクト
コンストラクタとデストラクタ
<?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
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
class Point {
public function __construct(protected int $x, protected int $y = 0) {
}
}
- コンストラクタのプロモーション(昇格)という。
<?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は無くてよい
class UserDto {
public function __construct(
public int $id,
public string $name,
public int $lastLoginAdEpoch,
public bool $isLongTimeMissed = false,
public bool $isAdmin = false,
){}
}
使い方
データを取得したら、すぐにDTOにする。
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 の例
# 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つにしたら意味がない。またデフォルト値は動的な余地を残すので避ける。
バージョンの確認方法
-
既にインストールされているバージョンを表示し、そこから選択できる
$ sudo update-alternatives --config php
設定ファイル(php.ini
)について
画像アップロードの際に影響を与える設定値
-
upload_max_filesize
- 単一のファイルの最大アップロードサイズを定義する。これ以上のサイズのファイルはアップロードできない。
-
post_max_size
- POSTリクエストを通じて送信できるデータの最大量を定義する。この設定は
upload_max_filesize
よりも大きい値に設定する必要がある。
- POSTリクエストを通じて送信できるデータの最大量を定義する。この設定は