---
# vim: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab:
layout: "@/layouts/MarkdownLayout.astro"
---

import Toc from "../../components/Toc.astro";
import ExampleWrapper from "@/components/ExampleWrapper.astro";
import RenderFile from "@/components/RenderFile";
import RenderHtml from "@/components/RenderHtml";
import Details from "@/components/Details.astro";

export const title = "オブジェクト指向とクラス";

# {title}

## TOC

## 大事な用語

- Class (クラス)
- インスタンス
- 継承

## データのまとまり

プログラムを組むとき、データのまとまりを扱うことがよくあります。

- 設定ファイル
- データベースから取得したデータ
- 入力フォームから送られてきたデータ
- ...etc

この中でも入力フォームは、開発者自身であってもどのような値が入ってくるかわからなくなることが多いです。

このようなフォームがあったとしても、

<RenderFile path="public/sample/tips/object-oriented/form.html" />

データを受け取る側は、どのような値が入ってきているのかわかりません。  
これを知るために、とりあえず変数の中身を表示する処理を書いてみたり、

```php file=public/sample/tips/object-oriented/confirm.php
```

<br />

もととなるHTMLの`name`属性を直接確認してみたりすることもあるでしょう。

<Details summary="HTMLを見る">

```php file=public/sample/tips/object-oriented/form.html collapse={1-153, 179-183} /name="[a-zA-Z0-9]+"/
```

</Details>

<br />

これらの確認を経て、送られてくるデータには以下のものがあるとはじめてわかります。

- name
- gender
- message

しかし、予めどのようなデータが入ってくるのかを把握したいものです。

今回の例のような入力フォームであれば、HTMLの変更によって意図しないデータが入ってくる可能性もありますし、そもそもHTMLを見ないとどのようなデータが入ってくるのかわからないのは、あまり良い状態とは言えません。

これを解決するために、あるデータの集まりに対して、具体的になにがあるのかを説明するようなアプローチが存在します。  
その最も原始的な方法が、 `構造体(struct)` や `クラス(class)` を使うことです。

これはHTMLから送られてくるデータ全体をひとつのまとまりとして扱う考え方です。  
この構造の中に、具体的に何が入ってくるのかを定義することができます。

```php
<?php
/**
 * HTMLから送られてくるデータの構造を定義するクラス
 */
class FormField {
    public $name;
    public $gender;
    public $message;
}
```

次の章では、クラスの使い方について説明していきます。

## クラスの使い方

### 構造を定義する (メンバー変数)

[データのまとまり](#データのまとまり) で示したように、そのまとまりに対して具体的にどのようなデータが入ってくるのかを定義することができます。

```php
<?php

class FormField {
    public $name;
    public $gender;
    public $message;
}
```

このクラスは通常の型と同じように、変数の型として使うことができます。

ただしクラスは通常の型と違い、`new` というキーワードを使って変数に代入する必要があります。

```php collapse={2-8}
<?php

class FormField {
    public $name;
    public $gender;
    public $message;
}

// $変数 = new クラス名();
$formField = new FormField();
```

ところで通常の型は代入した時点で、その型の値が入ってきます。

```php
<?php
// numberという型で、値は123
$data = 123;

// stringという型で、値は"hello"
$data = "hello";
```

NOTE: うまくインスタンスと呼ばれるという説明を組み込む

一方でクラスは、`new` というキーワードを使って代入した時点では空の入れものだけが入ってきます。  
ここに実際のデータは含まれていません。

この入れものに対して、データを入れる処理を書く必要があります。

```php collapse={2-8}
<?php

class FormField {
    public $name;
    public $gender;
    public $message;
}

// $変数 = new クラス名();
$formField = new FormField();

var_dump($formField);
// この時点では、空の値であることを示すNULLが入っている
// object(FormField)#1 (3) {
//   ["name"]=>
//   NULL
//  ["gender"]=>
//   NULL
//   ["message"]=>
//   NULL
// }

$formField->name = $_POST["name"];
$formField->gender = $_POST["gender"];
$formField->message = $_POST["message"];

var_dump($formField);
// object(FormField)#1 (3) {
//   ["name"]=>
//   string(5) "Alice"
//   ["gender"]=>
//   string(3) "woman"
//   ["message"]=>
//   string(11) "Hello world"
// }
```

### 振る舞いを定義する (メソッド / 関数)

### 特別なメソッド

### 敢えてアクセス制限をかける (アクセス修飾子)

### 派生を作る (継承)

## よく見るコード例

---

```c
#include <stdio.h>

typedef char* str;

struct User {
  str name;
  int age;
};

int main() {
  User me = {
    .name = "Alice",
    .age = 30
  };

  printf("name: %s\n", me.name);
  // name: Alice

  printf("age: %d\n", me.age);
  // age: 30

  return 0;
}
```

```php
<?php
class User {
  public $name;
  public $age;
}

$user = new User();

$user->name = "Alice";
$user->age = 30;

echo $user->name . PHP_EOL;
// Alice

echo $user->age . PHP_EOL;
// 30
```

```php
<?php
class User {
  public $name;
  public $age;

  public function getName() {
    return $this->name;
  }
}

$user = new User();

$user->name = "Alice";
$user->age = 30;

echo $user->name . PHP_EOL;
echo $user->getName() . PHP_EOL;
// Alice

echo $user->age . PHP_EOL;
// 30
```

```php
<?php
class User {
  public $name;
  public $age;

  public function setName($name) {

    if (!is_string($name)) {
      throw new Exception("name must be a string");
    }

    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }
}

$user = new User();

$user->name = 9817298471982;  // 文字じゃねぇ
if (!is_string($user->name)) {
  throw new Exception("name must be a string");
}

$user->setName("Alice");
// ok

$user->setName(123);
// Exception: name must be a string

$user->age = 30;

echo $user->name . PHP_EOL;
echo $user->getName() . PHP_EOL;
// Alice

echo $user->age . PHP_EOL;
// 30
```

```php
<?php
class User {
  // private $name;
  protected $name;

  public $age;

  public function setName($name) {

    if (!is_string($name)) {
      throw new Exception("name must be a string");
    }

    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }
}

$user = new User();

$user->name = 9817298471982;
if (!is_string($user->name)) {
  throw new Exception("name must be a string");
}

$user->setName("Alice");
// ok

$user->setName(123);
// Exception: name must be a string

$user->age = 30;

echo $user->name . PHP_EOL;
echo $user->getName() . PHP_EOL;
// Alice

echo $user->age . PHP_EOL;
// 30
```

constructor コンストラクタ
static

```php
<?php
class User {

  protected $id;
  protected $name;
  protected $age;

  public function __constructor($name) {
    echo "newされました";

    $this->name = $name;
    $this->age = 0;
  }

  public static function findUser($id) {

    // $name を使ってDBから取ってくる

    $data = [
      "name" => "bob",
      "age" => 35,
    ];

    $instance = new User($data["name"]);
    $instance->age = $data["age"];

    return $instance;
  }

  public function setName($name) {

    if (!is_string($name)) {
      throw new Exception("name must be a string");
    }

    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }
}

$user = new User("Alice");

$bob = User::findUser(1);

$user->name = 9817298471982;
if (!is_string($user->name)) {
  throw new Exception("name must be a string");
}

$user->setName("Alice");
// ok

$user->setName(123);
// Exception: name must be a string

$user->age = 30;

echo $user->name . PHP_EOL;
echo $user->getName() . PHP_EOL;
// Alice

echo $user->age . PHP_EOL;
// 30
```

```php
<?php

class User {
  protected $id;
  protected $user;
  protected $age;

  public function __constructor($name) {
    echo "私は {$name} です";
    $this->name = $name;
  }

  public function setName($name) {

    if (!is_string($name)) {
      throw new Exception("name must be a string");
    }

    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }
}

$user1 = new User("Alice");
// 私は Alice です



class ParttimeUser extends User {
  public function __constructor($name) {
    echo "吾輩は {$name} です";
    $this->name = $name;
  }
}

$user2 = new ParttimeUser("Bob");
// 吾輩は Bob です

$user2->setName("George");
```

スマホでボリュームを上げ下げしたいとき、ボタンを押させる。
中身に触れさせて、この端子をさわってね みたいなことはさせない。
