PHP5技術者認定試験（上級）に学ぶ、深入りすると面倒なオブジェクトの話(4)

PHP5技術者認定試験（上級）に学ぶ、実はよく知らないPHPの仕様

# PHP5技術者認定試験（上級）

資格試験に向けて勉強中です
勉強していて、PHPについて実は知らなかった／勘違いしていたことを記載します。

今回はオブジェクトです。 予め断っておきますが、 **PHP5の話** です。


## オブジェクト

### キーワード

説明は（個人的に不要なため）略します

- オブジェクト指向プログラミング
- オブジェクトの **カプセル化**
- **class** キーワードによる **クラス定義**
- **プロパティ** と **メソッド**
- **new** キーワードによる **インスタンスの作成**
- アクセス修飾子 **public** , **protected** , **private**
- **function** キーワードによるメソッドの定義
- プロパティの呼び出し `$インスタンス変数名->プロパティ名`
- メソッドの呼び出し `$インスタンス変数名->メソッド名`
- 内部からの呼び出し `&this`


## オブジェクトの生成

- コンストラクタ引数を持たないなら、 **()は省略可能**

```php
$m1 = new Mail; // ()を省略したインスタンス生成
```

- new に続けて クラス名を **シングルクォートで括る** とエラーになる
	- ダブルクオーテーションだといけるのかな
- クラス名（文字列）を格納した変数を利用して、インスタンス生成可能 （可変変数）

## クラス名

- 大文字・小文字は区別されない
- 予約済みのクラスを新しく定義するとエラーになる （stdClassなど）
- アンダースコア２つ( __construct() など ) で始まるメソッドを独自に作るのは非推奨
	- 禁止されているわけではない

## クラスのプロパティ

- **デフォルト値** の指定が可能、指定しない場合 **NULL** となる
- デフォルト値には単純な定数を指定可能
	- 指定可能
		- 整数
		- 文字列（変数の展開を伴わない）
		- ヒアドキュメント（同上）
		- NowDoc
		- array()
	- 指定不可
		- 変数
		- 変数の展開を伴う文字列
		- 式
		- 関数
		- new キーワード
- 宣言していないプロパティへの代入は **可能**
- 未定義のプロパティを読み込むと **エラー** (Notice)
- __get(), __set()で未定義プロパティ操作時の挙動を設定可能

## 静的プロパティ

- アクセス
	- `self::$プロパティ名`
	- `static::$プロパティ名` (遅延静的束縛 PHP5.3以降)
- 初期化
	- 式で初期化することはできない
	- 以下も式なのでダメ

```php
public static $a;
public static $b = self::$a; // エラー
```

## 静的メソッド

- 呼び出し
	- `self::$メソッド名`
	- `static::$メソッド名` (遅延静的束縛 PHP5.3以降)

## オブジェクトの代入・コピー

- オブジェクトは **バンドル** で扱われます
- コピーする場合は `clone()`
	- `__clone()` メソッドを定義すると、 コピーされた **後に** 自動的に呼び出される


## アクセス修飾子

- `var $変数名 ` は public指定と同じ（PHP4互換のため残されている）
- 親クラスで `private $a` を定義し、子クラスで `$this->a = 10` とした場合、 `$a` は２つ存在することになる。

> `$a` は２つ存在する

（これマジで仕様なの…？バグじゃないの…？）

## クラス定数

- 定義 `const TEISU = "1.0";`
- 呼び出し
	- `self::TESIU;` 
	- `static::TESIU;` (PHP5.3以降)
	- `クラス名::TEISU;`

## 継承

- `extend` に続けてクラス名。 **多重継承は不可**

### オーバーライド

- 子クラスで親と同じ関数名を定義すると上書きされる
- 子クラスから親クラスのメソッドを明示的に呼び出す `parent::メソッド名()`
- 親クラスから親クラスのメソッドを明示的に呼び出す `self::メソッド名()`

### finalキーワード

- オーバーライド不可
- メソッドのみ、プロパティにfinalは **記述できない**

## 抽象クラス

- 抽象メソッドを一つでも含むなら 抽象クラスにする必要がある
- virtualキーワードはPHPに無い

## インターフェース

- インターフェースのメソッドにabstructは記載しないが、抽象メソッドとして扱われる
- インターフェースはインターフェースを継承可能。 **interface SubItf exteds SuperItf**
	- 継承するときは、同じメソッド名があってはいけない
- インターフェースにプロパティは定義できない
- インターフェースに **const定数は定義できる**

## instanceof

- インターフェースも指定・確認可能

## タイプヒンディング

引数のクラス・インターフェースを指定

- array の指定も可能
- int や string は不可
- 違反した場合 **キャッチ可能な致命的エラー** が発生する

## コンストラクタ・デストラクタ

- `__construct()` , `__destruct()` で定義する
	- `__construct()` が **定義されていない場合** クラス名と同名のメソッドがあればコンストラクタとして実行される（過去との互換）
	- 引数を持つデストラクタを定義すると致命的エラー
- 継承時の動作
	- 子クラスで定義されていない場合 親クラスのコンストラクタ・デストラクタが実行される
	- 子クラスで定義されている場合、親クラスの（同上）は暗黙的には **呼び出されない。**


## クラスの調査

|関数名|機能|
|---|---|
|`class_exsists($class_name, $autoload = true)`|クラスが存在するかチェック、（２パラがTrue）存在しない場合__autoload()を呼び出す|
|`get_declared_classes()`|定義されたすべてのクラス名を配列で返す|
|`get_class_methods($classname)`|全メソッド名を配列で取得（ **アクセス可能のみ** ）（ **静的メソッド含む** ）|
|`get_class_vers($class_name)`|全プロパティ名とデフォルト値を連想配列で取得（keyがプロパティ名）|
|`get_parent_class($obj)`|親クラス名を返す。なければfalse|
|`is_object($var)`|オブジェクトかどうか|
|`get_class($object)`|クラス名を取得|
|`method_exsists($obj, $method_name)`|メソッドが存在するかどうか|
|`get_object_vars($obj)`|アクセス可能なプロパティとその値を返す（keyがプロパティ名）（ **静的プロパティ含まず** ）|


## シリアライズ

**シリアライズ** とは オブジェクトを **バイトストリームに変換** することです。


|関数名|機能|
|---|---|
|serialize($val)|シリアライズされた文字列を返す|
|unserialize($str)|復元|

- __sleep()
	- 定義されている場合、シリアライズ直前に呼び出される
	- __sleepメソッドは、オブジェクトのすべての変数の名前を配列で返してください。
- __wakeup()
	- 復元の直後に呼び出される

sleepとwakeupは試験に出るよ


## 終わりに

要チェック項目

- [PHP 遅延静的束縛 (Late Static Bindings)](http://php.net/manual/ja/language.oop5.late-static-bindings.php)

他の投稿

- [第1回 関数](http://qiita.com/tmak_tsukamoto/items/84f7d62f6106fad5fd23)
- [第2回 文字列](http://qiita.com/tmak_tsukamoto/items/41673ab2d6fedeef0980)
- [第3回 配列](http://qiita.com/tmak_tsukamoto/items/2eb6d10e83b6c00981c9)

いいね！いただけると勉強が捗ります