PHP オブジェクト クラス

## はじめに
PHPのオブジェクトです。何年か前にpythonで少しやった気がします。自分用のまとめなので間違いや自分が完全に理解しているところに関しては省略して書いていることもあります。ご了承お願いします。

## クラスの定義
クラスはclass クラス名で定義します。
また、クラス名は大文字スタートです、
```php
class Client{
}
```

プロパティとメソッド、定数を定義していきます。
```php
class Client{
    public $number;
    public $name;
    public const VERSION = 1.0;

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}
```
このthisってのがインスタンスのことって考えると覚えやすかったりした気がしてきたりします。
また、以下のように記述することでプロパティの型の指定が可能です。普通の書き方と少し違うのでそこは注意です。
```php
$price = (float)12500; //変数宣言時に型を指定する

class Client{
    public string $number; //クラスでのプロパティ宣言時に型を指定する
    public $name;

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}
```

## インスタンスを作ってみます
インスタンスを作るにはインスタンス名 = new クラス名です。
```php
$onlineClient = new Client();
```
インスタンスに情報を書き込んでいきます。
```php
$onlineClient->$number = 1;
$onlineClient->$name = 'keia';
```
インスタンスの型はクラス名です。上記のインスタンスの型はClientです。あまりしっくりこないのですがインスタンスがなにか考えたらわかるかもしれません。これはintとかstringじゃないです。クラスを元に作られるものです。

## インスタンスを作成した時に実行する__construct()
インスタンスを作成した時に実行する関数があります。ちなみに関数名を__construct()以外にすると実行されません。
```php
class Client{
    public $number;
    public $name;

    public function __construct($number, $name){
        $this->number = $number;
        $this->name = $name;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}
```


__constructを定義しておけばインスタンスを作成すると同時に情報を書き込むことも可能です。
```php
$onlineClient[0] = new Client(1, 'keia');
$onlineClient[1] = new Client(2) //引数が1つしか記述されていないのでエラー
```
インスタンスを作成しました。インスタンスを作成する時に仮引数？にnumberとnameに入れたい値を記述します。
そうすると__construct()が実行されインスタンスのプロパティのnumberとnameに仮引数の値が入ります。

また、同じ情報を扱うのにいちいち変数名変えるのっておかしいですよね...
```php
$onlineClient = []; //配列を作成
$onlineClient[0] = new Client(1, 'keia'); //インスタンスの作成
```
配列にすることで今後データを追加する時も便利ですね。

## ブレイクタイム
__construct()あたりが迷いどころでしょうか、そこ理解できてるなら他で理解できないところはないと思います。
ゆっくり体に落とし込んでいきましょう。

## publicとprivate
```php
class Client{
    public $number;
    public $name;

    public function __construct($number, $name){
        $this->number = $number;
        $this->name = $name;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}

$onlineClient = [];
$onlineClient[0] = new Client(1, 'keia'); 
$onlineClient[9]->number++; 
```
顧客番号を1プラスするコードを書きました。外部から書き換えられたらまずい値もあると思います。
そのような時はプロパティの宣言を行うときにprivateと記述すると外部から値の書き換えが不可能になります。

```php
class Client{
    private $number;
    public $name;

    public function __construct($number, $name){
        $this->number = $number;
        $this->name = $name;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}

$onlineClient = [];
$onlineClient[0] = new Client(1, 'keia'); 
$onlineClient[0]->number++; 
```
```php
Fatal error: Uncaught Error: Cannot access private property Client::$number in /home/dotinstall/main.php:21
Stack trace:
#0 {main}
  thrown in /home/main.php on line 16
```

## staticを使ってみる
インスタンスが何個生成されたかなど、クラスに基づいたプロパティを作成することも可能です。
クラス自身のプロパティの作成を行う時はプロパティの前にstaticをつけます
```php
class Client{
    private $number;
    public $name;
    public static $count; //インスタンスの作成した数を保持するプロパティ

    public function __construct($number, $name){
        $this->number = $number;
        $this->name = $name;
        self::$count++; //クラスに基づくものなのでself::プロパティ名と記述する
    }

    public static function info(){
        printf('インスタンス数->%d' . PHP_EOL, self::$count);
    }
}

Client::info(); //クラス名::メソッドで実行可能
```

## クラスの継承
新しいクラスを作るときこれまで作ったクラスの継承が可能です。
```php
class Client{
    private $number; //privateで宣言している
    public $name;
    public static $count;

    public function __construct($number, $name){
        $this->number = $number;
        $this->name = $name;
        self::$count++;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}

class OfflineClient extends Client{
    public function __construct($number, $name, $area){
        parent::__construct($number, $name); //親クラスの__constructを実行
        $this->area = $area;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s、エリア->%s' . PHP_EOL, $this->number, $this->name, $this->area);
    }
}

$offlineClient = [];
$offlineClient[0] = new OfflineClient(1, 'keia', '東京');
$offlineClient[0]->info();
```

このように記述します。ですが、エラーが発生します。
親クラスで宣言した$numberプロパティがprivateで宣言してあるので子クラスから扱うことができません。
子クラスからでも扱うことができるように以下のように記述します。
```php
protected $number;
```
また少し似た話ですが、子クラスで親クラスのメソッドの書き換え(オーバーライド)を行いたくない時は以下のように記述します。
```php
final public function info(){
    printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
}
```
## 継承したメソッド
子クラスでinfoメソッドがオーバーライドされています。ですが、誰かが間違えて子クラスのinfoメソッドを削除してしまいました。
その状態で子クラスから生成されたインスタンスでinfoメソッドを実行してもエラーにはなりません。理由は親クラスでinfoメソッドを定義しているからです。

もしかしたらそのような状態は都合が悪いかもしれません。なのでinfoメソッドはちゃんと子クラスで定義してねというルールを作れます。

```php
abstract class BaseClient{
    abstract public function info(); //infoメソッドの定義ルールの作成
}

class Client extends BaseClient{ //abstract classを継承させる必要がある
    protected $number;
    public $name;
    public static $count;

    public function __construct($number, $name){
        $this->number = $number;
        $this->name = $name;
        self::$count++;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}

class OfflineClient extends Client{
    public function __construct($number, $name, $area){
        parent::__construct($number, $name); //親クラスの__constructを実行
        $this->area = $area;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s、エリア->%s' . PHP_EOL, $this->number, $this->name, $this->area);
    }
}

$offlineClient = [];
$offlineClient[0] = new OfflineClient(1, 'keia', '東京');
$offlineClient[0]->info();
```
abstractを使用することで子クラスごとにメソッドを定義するルールが作れます。abstractを親クラスに継承させるのを忘れないように。

## インターフェイス
上記で子クラスごとにメソッドを定義するルールが作れました。しかしまた問題が発生しました。
子クラスが3つあるとします。親クラスにNichiメソッドというものがあり、子クラスのうち２つにそのNichiメソッドが必要です。

上記のようにabstractを使用するとNichiメソッドが必要ないクラスまで定義しないといけなくなります。また、PHPは複数クラスを継承できないのでNichiメソッドが必要な子クラスに別でNichiメソッドがある親クラスを継承させるのも無理です。(親クラスと子クラスはコードを書いている人が別なのでできるだけ継承を使いたいという設定)

そのような時はインターフェースを使います。
```php
<?php
interface NichiInterface{
    public function Nichi();
}

abstract class BaseClient{
    abstract public function info(); //infoメソッドの定義ルールの作成
}

class Client extends BaseClient{ //abstract classを継承させる必要がある
    protected $number;
    public $name;
    public static $count;

    public function __construct($number, $name){
        $this->number = $number;
        $this->name = $name;
        self::$count++;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}

class OfflineClient extends Client{
    public function __construct($number, $name, $area){
        parent::__construct($number, $name); //親クラスの__constructを実行
        $this->area = $area;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s、エリア->%s' . PHP_EOL, $this->number, $this->name, $this->area);
    }

    public function Nichi(){
        $this->Nichi++;
    }
}

$offlineClient = [];
$offlineClient[0] = new OfflineClient(1, 'keia', '東京');
$offlineClient[0]->info();
```
3つも子クラスがあるわけではないですが。。。おなじインターフェースを使うと同じ型にもなります。

## トレイトで共通コードを書こう
上記のように3つのうち2つが同じコードになります。子クラスごとにコードを書いていたら大変です。なのでトレイトを使ってスマートにかけます。

```php
interface NichiInterface{
    public function Nichi();
}

trait NichiTrait{
    private $nichi = 0;
    public function Nich() {
        $this->nichi++;
    }
}

abstract class BaseClient{
    abstract public function info(); //infoメソッドの定義ルールの作成
}

class Client extends BaseClient{ //abstract classを継承させる必要がある
    protected $number; 
    public $name;
    public static $count; 

    public function __construct($number, $name){
        $this->number = $number;
        $this->name = $name;
        self::$count++; 
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s' . PHP_EOL, $this->number, $this->name);
    }
}

class OfflineClient extends Client{
    use NichiTrait; //トレイトを有効化する
    
    public function __construct($number, $name, $area){
        parent::__construct($number, $name); //親クラスの__constructを実行
        $this->area = $area;
    }

    public function info(){
        printf('顧客番号->%d、顧客名->%s、エリア->%s' . PHP_EOL, $this->number, $this->name, $this->area);
    }
}

$offlineClient = [];
$offlineClient[0] = new OfflineClient(1, 'keia', '東京');
$offlineClient[0]->info();
```
PHPは子クラスに対して複数の親クラスを継承できないのでかなり重要ですね。
また、クラスとは違って型はないです。