BEAR.SundayでDIをレンタルサーバーで試してみる

はじめに
-----------------------------------


前回は、クイックスタートを動かすことができたので、
今回はBEAR Serverの特徴の１つ、DIを試してみます。
DIにはたくさんの種類がありますが、今回は基本の「コンストラクタインジェクション」を試してみます。

* 前回の記事
  [BEAR.Sunday クイックスタートをレンタルサーバーではじめる方法](https://qiita.com/chiitake/items/384ecf455da48be467af)

ゴール
-----------------------------------
DIされたPersonManagerクラスにタロウさんとマイケルの二人の情報を登録し、
listメソッドで登録されている一覧をJSONで返すという単純な例です。


今回作る成果物をレンタルサーバーXServer上にアップロードし、

```
  http://{あなたのドメイン}/foo/
```
ターゲットURLにアクセスすると
  



**JSONレスポンス**


```json:
  {
    "list": [
        {
            "name": "Taro",
            "country": "JP"
        },
        {
            "name": "Michale",
            "country": "US"
        }
    ],
    "_links": {
        "self": {
            "href": "/index"
        }
    }
  }
```


が表示されることがゴールになります。



登場クラス
-----------------------------------
* 人材データを保存するPersonクラス
* 人材管理用クラスのPersonManagerクラス
* DIで使用するためのPersonManagerIntarfaceインターフェース



公式 DIの説明
-----------------------------------
https://bearsunday.github.io/manuals/1.0/ja/di.html



ローカル開発環境
-----------------------------------
前回と同様 Windowsにて開発

* Windows7 CLI環境
* PHP 7.2.5 (cli) (built: Apr 25 2018 02:20:05) ( ZTS MSVC15 (Visual C++ 2017) x64 )
* Composer version 1.6.4 2018-04-13 12:04:24



サーバー環境の準備 (xserverの場合)
-----------------------------------


* 必要なもの
* SSHでの接続
* FTPでの接続

 :information_source:   **参考： XServerの標準パス例**

```　
   * [ドメインルート]
  
   * URL　
      - http://{あなたのドメイン名}/
　　
   * サーバードメインルートパス:（ウェブ非公開フォルダ）
      - /home/{ユーザー名}/{あなたのドメイン名}/
　　
   * ウェブ公開パス
     - /home/{ユーザー名}/{あなたのドメイン名}/public_html/
```

BEAR.Sundayのプロジェクトの作成
-----------------------------------

```
   cd C:\bear_di (作業フォルダ。環境によって変えてください。)
   C:\bear_di>composer create-project -n bear/skeleton MyVendor.DiProject
```

　数分でインストール完了します。



CLI（コマンドプロンプト）で動作確認
-----------------------------------
```
  C:\bear_di\MyVendor.DiProject\> php bootstrap\web.php get /
```

以下のようなレスポンスが帰ってくればOKです

``` json:

  200 OK
  content-type: application/hal+json

   {
       "greeting": "Hello BEAR.Sunday",
       "_links": {
           "self": {
               "href": "/index"
           }
       }
   }
```


作業の手順
-----------------------------------
* Interfaceを作る
	+ PersonManagerInterface

* classを作る
	+ PersonManager
	+ Person

* AppModuleにDIを書く

* ページリソースの Index.phpに実装する
        + 二人の情報をPersonManngerに登録して、一覧を帰り値とする

* ローカルでテストを行う
* サーバーにアップロード
* シンボリックリンクを貼る
* ブラウザで表示


srcフォルダのファイルツリー
-----------------------------------
![tree.png](https://qiita-image-store.s3.amazonaws.com/0/28269/4349905b-20db-807c-a40b-150b04c3007e.png)

Interfaceを作る
-----------------------------------
 :information_source: **ポイント**
```
   srcフォルダへのnamespageはデフォルトでは、MyVendor\MyProject になる
```



```php
<?php
namespace MyVendor\MyProject;

interface PersonManagerInterface {
	public function register(Person $p);
	public function list();
}
```


classを作る
-----------------------------------

**src\\PersonManager.php**

```php
<?php
namespace MyVendor\MyProject;

class PersonManager implements PersonManagerInterface {
	// 登録ユーザーの保存用
	private $personList = [];

	// ユーザーを登録する
	public function register(Person $p) {
		array_push($this->personList, $p);
	}

	// ユーザー一覧を返す
	public function list() {
		return $this->personList;
	}
}
```


**src\\Person.php**

```php
<?php
namespace MyVendor\MyProject;

class Person {
	public $name;
	public $country;
	
	static function create($name, $country) {
		$p = new Person();
		$p->name = $name;
		$p->country = $country;
		return $p;
	}
}
```


依存を受け取る箇所をPageリソースのIndex.phpに記述する
------------------------------------------------------
**src\\Resource\\Page\\Index.php**

```:php
<?php
namespace MyVendor\MyProject\Resource\Page;

use	Ray\Di\Inject;	// 追加した
use Ray\Di\Named;	// 追加した
use BEAR\Resource\ResourceObject;

// 作成したPerson, PersonManagerを読み込む
use MyVendor\MyProject\Person;
use MyVendor\MyProject\PersonManager;
use MyVendor\MyProject\PersonManagerInterface;

class Index extends ResourceObject
{
	private $pm;	// PersonManagerInterface

	/**
	 * コンストラクタインジェクション
	 */
	function __construct(PersonManagerInterface $pm) {
		$this->pm = $pm;
	}

    public function onGet(string $name = 'BEAR.Sunday') : ResourceObject
    {
		if ($this->pm == null) {
			$this->body = [
				'error' => "error: pm is null. It is not DI inject."
			];
			return $this;
		}

		$taro = Person::create("Taro", "JP");
		$michale = Person::create("Michale", "US");

		// 二人追加する
		$this->pm->register($taro);
		$this->pm->register($michale);

		// 一覧を取得
		$list = $this->pm->list();

        $this->body = [
            'list' => $list
        ];

        return $this;
    }
}
```

AppModule.phpに束縛を追加する
------------------------------

今回は、**リンク束縛** を使います。

**書式は、**

```
  $this->bind($interface)->to($class);
```


公式には「 **最も基本の束縛** です」と書かれていますが、
この基本とは「インターフェイスとクラスを束縛するので”基本” by Koriymさん」とのことです。
（※ ただ例えば文字列の場合はインターフェイスがないので使えません。）


:information_source:   **束縛とは？**

```
   インジェクタの仕事はオブジェクトグラフを作成することです。
   型を指定してインスタンスを要求し、依存関係を解決し、すべてを結びつけます。
   依存関係の解決方法を指定するにはバインディングを設定します。
   
   公式HPより引用
   https://bearsunday.github.io/manuals/1.0/ja/di.html
```



**それでは、AppModule.phpに束縛(bind部分)を追加します**


**src\\Module\\AppModule.php**

```:php
<?php
namespace MyVendor\MyProject\Module;

use BEAR\Package\PackageModule;
use josegonzalez\Dotenv\Loader;
use Ray\Di\AbstractModule;
use Ray\Di\Di\Named;

use MyVendor\MyProject\PersonManagerInterface;
use MyVendor\MyProject\PersonManager;
class AppModule extends AbstractModule
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
    	// 追加した
        $pm = new PersonManager();

        // 追加した「リンク束縛」
        $this->bind(\MyVendor\MyProject\PersonManagerInterface::class)
             ->to(PersonManager::class);

		// 最初から記述されている部分
        $appDir = dirname(__DIR__, 2);
        (new Loader($appDir . '/.env'))->parse()->toEnv();
        $this->install(new PackageModule);
    }
}

```

AppModule.php内では、 **リンク束縛** を

```
  $this->bind(\MyVendor\MyProject\PersonManagerInterface::class)
             ->to(PersonManager::class);
```

のように記述しています。
  
以上で準備が完了です。


PHPUnitによるテストを行う
------------------------------

BEAR.Sundayのスケルトンでは **ローカル上** でも **PHPUnit** が使えるようになっているので、
以下のようにコマンドを入力することでテストが行えます。

```
  composer test
```


このままではエラーが出ますので、**IndexTest.php** に期待するテストコードを記述します。

**tests\\Resource\\Page\\IndexTest.php**

```:php
<?php
namespace MyVendor\MyProject\Resource\Page;

use BEAR\Package\AppInjector;
use BEAR\Resource\ResourceInterface;
use BEAR\Resource\ResourceObject;
use PHPUnit\Framework\TestCase;

class IndexTest extends TestCase
{
    /**
     * @var ResourceInterface
     */
    private $resource;

    protected function setUp()
    {
        $this->resource = (new AppInjector('MyVendor\MyProject', 'app'))->getInstance(ResourceInterface::class);
    }

    public function testOnGet()
    {
        $ro = $this->resource->uri('page://self/index')(['name' => 'BEAR.Sunday']);
        /* @var ResourceObject $ro  */
        $this->assertSame(200, $ro->code);

        // リストを取り出す
        $list = $ro->body['list'];
        $person1 = $list[0];
        $person2 = $list[1];

        // テスト
        $this->assertSame('Taro', $person1->name);
        $this->assertSame('JP', $person1->country);
        $this->assertSame('Michale', $person2->name);
        $this->assertSame('US', $person2->country);
    }
}
```

上記のように期待するテストを追加したあと、再度composer testを実行します。

```
  composer test
```

![test_ok.png](https://qiita-image-store.s3.amazonaws.com/0/28269/bd0e9466-9295-723b-6e22-6870b125a10d.png)


このように OK がでれば完了です。

これでサーバーへアップロードする準備ができました。
			
			
MyVendor.DiProjectをzipファイルにまとめる（ローカル側）
-------------------------------------------------------

```
  好きなツールを使って、MyVendor.DiProjectフォルダごとzipに圧縮
  MyVendor.DiProject.zipができる
```


サーバー側のアップロード先フォルダ「app_bear」の作成
----------------------------------------------------
 :warning: **[注意ポイント]**

```
  アップロードするコードなどは ウェブ非公開のフォルダ に保存すること
```

・ウェブ 非公開フォルダ のドメインルートパスに、「app_bear」フォルダを作成

```
  $cd /home/{ユーザー名}/{あなたのドメイン名}/
  $mkdir app_bear
```

作成したプロジェクトをサーバーのapp_bearフォルダへ転送
-------------------------------------------------------

```
  転送先　/home/{ユーザー名}/{あなたのドメイン名}/app_bear/
```


SCPやGit、FTPなどを使ってapp_bearフォルダへ転送します。

 :warning: **注意** 

```
   XServerの場合、 GitやSCPは正式にサポートされていない ので :warning: 自己責任 :warning: で行ってください。
   
   私自身はGITリポジトリの作成やSCP接続でのアップロードはネットを参考に使用することはできましたが、
   :warning: 何か問題が発生してホームページが表示されない等のトラブルがあってもXServerのサポートを受けることができません。:warning:

   XServerで利用する場合には、下記の「FTPで転送したい場合」を試すか、
   GitやSCPを使いたい方は、素直にGitやSCPサポートを受けられるサーバーを利用することをお勧めします。
```

:information_source:　**メモ**

```
   FTPで転送したい場合
   
   FTPの場合、転送するファイル数が多いので、ローカル側でZIP圧縮したファイルを送り、サーバー側で展開すると効率的です。
```


   * サーバーへSSH接続後、

```
     cd /home/{ユーザー名}/{あなたのドメイン名}/app_bear
     unzip MyVendor.DiProject.zip
```

   展開すると **MyVendor.DiProjectフォルダが作成され**、
   そのなかに展開されたファイルが

```
     /home/{ユーザー名}/{あなたのドメイン名}/app_bear/MyVendor.DiProject
```
  
   にファイルが設置されます。
   

web公開フォルダに、「foo」ディレクトリを作る
--------------------------------------------

```
  cd /home/{ユーザー名}/{あなたのドメイン名}/public_html/
  mkdir foo
  cd foo
```


XServer用のbootstrapを作成する
------------------------------------------------------------------------------------------
通常、web公開フォルダをドメインルートにした場合はこの作業は必要ありませんが、

```
  http://{あなたのドメイン名}/foo/
```
  
のように **サブディレクトリ内** で動かす場合には、

サーバーごとのPHPのREQUEST_URIに合わせて、bootstrapファイルを作ると便利です。

BEAR.Sundayには、api用のbootstrapである「api.php」と、webページ用の「web.php」が用意されています。

今回はページリソースのIndex.phpに実装しているので、web.phpをベースにして、xserver.phpを作成します。

```
  cd /home/{ユーザー名}/{あなたのドメイン名}/app_bear/MyVendor.DiProject/bootstrap
  cp web.php xserver.php
```


**次に、このファイルを修正します。**

XServerの場合は、

```
  http://{あなたのドメイン名}/foo/
```
  
にアクセスすると
**[REQUEST_URI] => /foo/**
と設定されるので、xserver.php内で、以下のようにします。

```:php
<?php
$_SERVER["REQUEST_URI"] = "/";
$context = PHP_SAPI === 'cli' ? 'cli-hal-app' : 'hal-app';
require __DIR__ . '/bootstrap.php';
```

2行目のここがポイントです。
**$_SERVER["REQUEST_URI"] = "/";**

これを記述することによって、/fooディレクトリをサービスの起点に変更できます。

```
ユーザーによっては / ではいけない場合があるかもしれないので、その場合は調整してください。
```


BEAR.Sundayのpublicフォルダ内にある 「.htaccess」と「index.php」にシンボリックリンクを貼る
------------------------------------------------------------------------------------------

```
  ln -s /home/{ユーザー名}/{あなたのドメイン名}/app_bear/MyVendor.DiProject/public/.htaccess .
```
  
```
  ln -s /home/{ユーザー名}/{あなたのドメイン名}/app_bear/MyVendor.DiProject/bootstrap/xserver.php index.php
```


ブラウザでアクセス
------------------
```
  http://{あなたのドメイン名}/foo/hello
```

表示が下記のようになれば成功です。


**JSONレスポンス**

```json:
  {
    "list": [
        {
            "name": "Taro",
            "country": "JP"
        },
        {
            "name": "Michale",
            "country": "US"
        }
    ],
    "_links": {
        "self": {
            "href": "/index"
        }
    }
  }
```

おつかれさまでした。
-------------------

以上で終了です。

これでBEAR.SundayのDIの基本的な **リンク束縛**を試すことが出来ました。
DIにはこのほかにもセッターインジェクションなどたくさんの例がありますので
公式ページを参考にしてみてください。

   **公式HP DI**
   
   https://bearsunday.github.io/manuals/1.0/ja/di.html




なぜDIを使うの？
----------------

なぜDIを使うのかというと、サービスクラスの **疎結合** を目的にしています。
サービスの実装をコード内で new してしまうと（密結合と呼ばれる）、使用するサービスを変更したいときには、
使用しているソースコードの変更が必要になります。

```
  DIを使うことで、使用する実装を注入することができるので、結合度がゆるくなります。
```

**これがDIの利点だと思います。**


DIについては下記のリンクが参考になると思います。

**「なぜDI(依存性注入)が必要なのか？」についてGoogleが解説しているページを翻訳した**
　https://qiita.com/mizunowanko/items/53eed059fc044c5aa5dc


最後に
------

　BEAR.Sundayの開発者さまのみなさま、特に今回も直接サポートしていただいたKoriymさんに感謝を申し上げます。

　**XServerなどのレンタルサーバーでも、BEAR.Sundayがどんどん使われること願って。**