League\Flysystemを使用してFTP接続してみる


（2018/12/21）`env`関数から直接値を取得するのは避けた方がいいのでconfig経由で取得するようにしましょう

Laravel（Lumen）で[League\Flysystem](https://github.com/thephpleague/flysystem)を使ってFTP接続してみます。

# 準備

まず**``composer``**でインストール。

```
composer require league/flysystem
```

使う際は以下のようにFTP情報を**Adapter**に渡して**Filesystem**をインスタンス化してやればいいみたいです。

参考：[FTP Adapter - Flysystem](http://flysystem.thephpleague.com/adapter/ftp/)

```php
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Ftp as Adapter;

$filesystem = new Filesystem(new Adapter([
    'host' => 'ftp.example.com',
    'username' => 'username',
    'password' => 'password',

    /** optional config settings */
    'port' => 21,
    'root' => '/path/to/root',
    'passive' => true,
    'ssl' => true,
    'timeout' => 30,
]));
```

# 実際に実装した際のコード
「サーバーにあるファイルを取得しローカルにダウンロードする処理」の実装時のコードです。
  
本番とテストで使用するサーバーが異なるので直書きせず、**``.env``**に接続先の情報を記述


```.env
FILE_SYSTEM_HOST=
FILE_SYSTEM_USERNAME=
FILE_SYSTEM_PASSWORD=
FILE_SYSTEM_PORT=
FILE_SYSTEM_ROOT=
FILE_SYSTEM_PASSIVE=
FILE_SYSTEM_SSL=
FILE_SYSTEM_TIMEOUT=

STORAGE_DIRECTORY=
```


以下バッチ処理のClassです。
実行させる処理は**execute()**を想定しています。

```FileOperation.php
<?php

namespace App;

use Log;  // Log出さないなら不要
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Adapter\Ftp as Adapter;

class FileOperation
{
    /**
    * FTP接続情報
    * @see  __construct()
    */
    private $ftpConnection = [];

    /**
    * 保存先ディレクトリパス
    * @see  __construct()
    */
    private $storageDirectory;

    /**
    * ダウンロード対象ファイル名
    * @see  __construct()
    */
    private $downloadFiles = [];

    /**
    * 最大試行回数
    * @see  __construct()
    */
    private $maxTries;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        // この辺りの処理は場合によって異なるかもしれません・・・
        $this->ftpConnection = [
            'host'     => env('FILE_SYSTEM_HOST'),
            'username' => env('FILE_SYSTEM_USERNAME'),
            'password' => env('FILE_SYSTEM_PASSWORD'),
            'port'     => env('FILE_SYSTEM_PORT'),
            'root'     => env('FILE_SYSTEM_ROOT'),
            'ssl'      => env('FILE_SYSTEM_SSL'),
            'timeout'  => env('FILE_SYSTEM_TIMEOUT'),
        ];

        $this->storageDirectory = env('STORAGE_DIRECTORY');

        // ダウンロードするファイル名をここに記述する
        $this->downloadFiles = [
            'AAA.txt',
            'BBB.txt',
            'CCC.txt',
        ];

        $this->maxTries = 3;
    }

    /**
     * ファイル操作処理を実行する
     * 
     * @return bool
     */
    public function execute()
    {
        if ($this->download($this->ftpConnection) === false) {
            return false;
        }

        return true;
    }

    /**
     * ファイルのダウンロードを行う
     *
     * @param array $ftpConnection FTP接続情報
     * @return bool
     */
    public function download(array $ftpConnection)
    {
        Log::info('App\FileOperation:download() start');

        // エラー時、設定したトライ回数分処理を行う
        for ($i = 1; $i <= $this->maxTries; $i++) {
            try {
                // 接続先
                $remoteFilesystem = new Filesystem(new Adapter($ftpConnection));
                // ローカル
                $localFilesystem = new Filesystem(new Local($this->storageDirectory));

                // 設定したファイル数分取得処理を行う
                foreach ($this->downloadFiles as $downloadFile) {

                    // ファイル内容の読み込み
                    $content = $remoteFilesystem->read($downloadFile);

                    // 前回ファイルをアーカイブ
                    if ($localFilesystem->has($downloadFile) === true) {
                        $localFilesystem->rename($downloadFile, 'archive/' . $downloadFile);
                    }

                    // ローカルファイルに書き込み（ファイルがない場合は新規で作られる）
                    $result = $localFilesystem->write($downloadFile, $content);

                    Log::info('App\FileOperation:download() ' . $downloadFile . ' download processed. Result : ' . ($result === true ? 'SUCCESS' : 'FAILURE'));
        
                }

                Log::info('App\FileOperation:download() end');
        
                return $result;

            } catch (\Exception $e) {

                Log::error("App\FileOperation:download() ExceptionError(Try count " . $i . ")： \n" . $e);
                sleep(10);

            }
        }

        Log::error("App\FileOperation:download() Error (Max trial end.)");
        return false;
    }

}
```
  
一応さくらサーバーに接続してローカルにファイルをDLすることには成功しました。
（バッチ処理の作りとしてはどうなんでしょうかね・・・？いかんせん初めてなもので・・・）

## 参考

- [Flysystem - Multiple Filesystems, One API](http://flysystem.thephpleague.com/)
- [さまざまな環境で統一的にファイルを操作する [league/flysystem] - 超PHPerになろう](http://cho-phper.hateblo.jp/entry/2015/11/11/032710)
- [PHP の Flysystem の使い方まとめ | gotohayato](https://gotohayato.com/content/154)