【PHP】名前空間とトレイトを同時に使用する場合

PHP初心者、初級者だと初めて聞く存在だと思いますが、PHPには名前空間、トレイトという仕組みも実装されています。

- [名前空間](https://qiita.com/7968/items/1e5c61128fa495358c1f)
- [トレイト](https://qiita.com/kazuhsat5/items/6ced7492daaddf1cd3d9)

では、今更ではあるのですが、この**名前空間とトレイトを同時に使う**場合（要は名前空間ごとに、同一名称のトレイトが存在する場合です。実務的な機会があるのかどうかはわかりませんが…）はどうすればいいのかを調査してみました。すると、色々と注意する部分があるようなので、備忘録としてプログラムと解説を置いておきます。

なお、本記事はオブジェクト指向を熟知しているPHP中級者以上を前提としています。また、名前空間、トレイトを知らない人は、参考記事を熟読しておいてください。

##実例
【要件定義】各都道府県の果物と野菜をそれぞれ調べたい。名前空間にはそれぞれfruits、vegitableと定義され、その中に各地方の名称となったトレイト(kanto、kinkiなど）があり、その中に各都道府県のメンバ（kantoならgunma()、tochigi()など）が用意されている。

つまり、このような状態です。トレイト名が同じために、名前空間で定義している状態になります。また、トレイト内も同じ名称のメンバが入っていますが、それぞれ用意されている値は異なっています。

```PHP:PHP
	namespace fruits
	{
		trait Kanto
		{
			public function tochigi(){
				$this -> fruits = "いちご";
			}
			
			public function gunma(){
				$this -> fruits = "りんご";
			}
		}
	}

	namespace vegitable
	{
		trait Kanto
		{
			public function tochigi(){
				$this -> vegi = "かんぴょう";
			}
			
			public function gunma(){
				$this -> vegi = "キャベツ";
			}
		}
	}
```

### メンバのエイリアス指定
ここで気をつけないといけないのは、トレイト名ならびにメンバ名が同一であるため、このままブラウザで表示しようとするとトレイトの規約違反で**コリジョンエラー**（同じトレイト名やメンバー名にしていると、何を呼び出していいのかプログラムで判断できない）を起こしてしまいます。そこで、各メンバに対しエイリアスを指定する必要があります。

→　[[PHP] こんなときどうする？trait編](https://qiita.com/sengoku/items/42ab8c850a2085b22d4e)

これを参考にして、以下のようにメンバ名を別名に定義します。
まずは、名前空間の外側を無名の名前空間で囲みます（名前空間の外側だとエラーになります）。
そして別名のメンバを必ずトレイト内のメンバと**オーバーライド**させるようにします。

→　[オーバーライドについて](http://www.objective-php.net/basic/override)

```PHP:PHP
	namespace
	{
		class Crops
		{
			//public $fruits;
			//public $vegi;
			//トレイト内で重複するメンバがないようにすること
			use fruits\kanto, vegitable\kanto{
				fruits\kanto::tochigi as f_tochigi;
				vegitable\kanto::tochigi as v_tochigi;
				fruits\kanto::gunma as f_gunma;
				vegitable\kanto::gunma as v_gunma;
			}
			//必ずメンバをオーバーライドさせること。独自に定義した別メンバ名だとエラーになる
			public function tochigi(){
				$this -> f_tochigi();
				$this -> v_tochigi();
			}
			
			public function gunma(){
				$this -> f_gunma();
				$this -> v_gunma();
			}
		}
	}
```

### エイリアスの定義
これだと、名前空間が煩雑なので、すっきりさせるためにエイリアスを定義します。

```PHP:PHP
	namespace
	{
        //名前空間のエイリアス定義はクラスの外側で行う
        use fruits\kanto as f_kanto;
        use vegitable\kanto as v_kanto;
		class Crops
		{
			//public $fruits;
			//public $vegi;
			//トレイト内で重複するメンバがないようにすること
			use f_kanto, v_kanto{
                f_kanto::tochigi as f_tochigi;
				v_kanto::tochigi as v_tocihi;
				f_kanto::gunma as f_gunma;
				v_kanto::gunma as v_gunma;
			}
			//必ずメンバをオーバーライドさせること
			public function tochigi(){
				$this -> f_tochigi();
				$this -> v_tochigi();
			}
			
			public function gunma(){
				$this -> f_gunma();
				$this -> v_gunma();
			}
		}
	}
```

### ゲッタの設定
値を引き出せるようにゲッタをクラス内に設定します。

```PHP:PHP
	namespace
	{
        //名前空間のエイリアス定義はクラスの外側で行う
        use fruits\kanto as f_kanto;
        use vegitable\kanto as v_kanto;
		class Crops
		{
			public $fruits;
			public $vegi;
			//トレイト内で重複するメンバがないようにすること
			use f_kanto, v_kanto{
                f_kanto::tochgi as f_tochigi;
				v_kanto::gunma as v_tochigi;
				f_kanto::tochigi as f_gunma;
				v_kanto::gunma as v_gunma;
			}
			//必ずメンバをオーバーライドさせること
			public function tochigi(){
				$this -> f_tochigi();
				$this -> v_tochigi();
			}
			
			public function gunma(){
				$this -> f_gunma();
				$this -> v_gunma();
			}
            //ゲッタの指定(共通でデータを抽出できる)
			public function getter(){
				$fruits = $this -> fruits;
				$vegi = $this -> vegi;
				return [$fruits,$vegi];
			}

		}
	}
```

### インスタンスからメンバの値を取り出す
あとはインスタンスを指定して、メンバにアクセスすればデータを取得できます。

```PHP:PHP
		$Crops = new Crops(); //インスタンス生成
		$Crops -> tochigi(); 
		list($fruits,$vegi) = $Crops -> getter();
		echo "【栃木】果物:{$fruits}、野菜:{$vegi}<br>";
		$Crops -> gunma();
		list($fruits,$vegi) = $Crops -> getter();
		echo "【群馬】果物:{$fruits}、野菜:{$vegi}<br>";
```

ブラウザなどでレビューをかけると

```
【栃木】果物:いちご、野菜:かんぴょう
【群馬】果物:りんご、野菜:キャベツ
```

このように表示させることができます。

## 全体の構造
備忘録として作成した原本プログラムはこのようになっています。トレイトの中はkinkiも入っており、メンバの中身もトレイトごとに違いますが、動作は全く同じです。また、トレイトやメンバの中をどんどん増やしていくことができます。その際には必ず逐一エイリアスも指定して、コリジョンを起こさないようにしてください。

```PHP:PHP
<?php
	namespace fruits
	{
		trait Kanto
		{
		    public function tochigi(){
			$this -> fruits = "いちご";
		    }

		    public function gunma(){
			$this -> fruits = "りんご";
		    }
		}
		
		trait Kinki
		{
			public function wakayama(){
				$this -> fruits = "うめ";
			}
			
			public function nara(){
				$this -> fruits = "かき";
			}
		}
	}
	
	namespace vegitable
	{
		trait Kanto
		{
		    public function tochigi(){
			$this -> vegi = "かんぴょう";
		    }

		    public function gunma(){
			$this -> vegi = "キャベツ";
		    }
		}
		
		trait Kinki
		{
			public function wakayama(){
				$this -> vegi = "しょうが";
			}
			
			public function nara(){
				$this -> vegi = "みょうが";
			}
		}
	}
	
	namespace
	{
		//エイリアスを指定する場合はクラスの外側で
		use  fruits\Kanto as f_kanto;
		use  vegitable\Kanto as v_kanto;
		use  fruits\Kinki as f_kinki;
		use  vegitable\Kinki as v_kinki;
		class Crops
		{
			public $fruits;
			public $vegi;
			//トレイト内で重複するメンバがないようにすること
			use f_kanto{
				f_kanto::tochigi as f_tochigi;
				f_kanto::gunma as f_gunma;
			}
			use v_kanto{
				v_kanto::tochigi as v_tochigi;
				v_kanto::gunma as v_gunma;
			}
			use f_kinki{
				f_kinki::wakayama as f_wakayama;
				f_kinki::nara as f_nara;
			}
			use v_kinki{
				v_kinki::wakayama as v_wakayama;
				v_kinki::nara as v_nara;
			}
			//必ずメンバをオーバーライドさせること
			public function tochigi(){
				$this -> f_tochigi();
				$this -> v_tochigi();
			}
			
			public function gunma(){
				$this -> f_gunma();
				$this -> v_gunma();
			}
			public function wakayama(){
				$this -> f_wakayama();
				$this -> v_wakayama();
			}
			
			public function nara(){
				$this -> f_nara();
				$this -> v_nara();
			}
			
			public function getter(){
				$fruits = $this -> fruits;
				$vegi = $this -> vegi;
				return [$fruits,$vegi];
			}
		}
				
		$Crops = new Crops();
		$Crops -> tochigi();
		list($fruits,$vegi) = $Crops -> getter();
		echo "【栃木】果物:{$fruits}、野菜:{$vegi}<br>";
		$Crops -> gunma();
		list($fruits,$vegi) = $Crops -> getter();
		echo "【群馬】果物:{$fruits}、野菜:{$vegi}<br>";
		$Crops -> wakayama();
		list($fruits,$vegi) = $Crops -> getter();
		echo "【和歌山】果物:{$fruits}、野菜:{$vegi}<br>";
		$Crops -> nara();
		list($fruits,$vegi) = $Crops -> getter();
		echo "【奈良】果物:{$fruits}、野菜:{$vegi}<br>";
	}
```
```
【栃木】果物:いちご、野菜:かんぴょう
【群馬】果物:りんご、野菜:キャベツ
【和歌山】果物:うめ、野菜:しょうが
【奈良】果物:かき、野菜:みょうが
```