Silex-SkeletonをSymfony4+Flexに移行する

Silexが2018年6月にEOLになることが発表されました。

SilexのEOLは非常に残念ですが、Symfony4+Flexにより、Symfonyが小さなアプリから大規模なシステムまでカバーできるようになったため、Silexはその役割を終えた、ということなのだろうなと考えています。

今回は、[Silex-Skeleton](https://github.com/silexphp/Silex-Skeleton)をSymfony4+Flexの構成に移行してみたので、その手順を紹介したいと思います。

# 前提

- [Silex-Skeleton](https://github.com/silexphp/Silex-Skeleton)をSymfony4+Flexの構成に移行します
- PHP7.1.3以上の環境が必要です

# 環境のセットアップ

まずはSilex-Skeltonをcloneし、動かすまで。

```shell
$ git clone https://github.com/silexphp/Silex-Skeleton.git
$ cd Silex-Skeleton 
$ composer install
$ composer run
> echo 'Started web server on http://localhost:8888'
Started web server on http://localhost:8888
> php -S localhost:8888 -t web
```

http://localhost:8888 にアクセスし、`Welcome to your new Silex Application!`が表示されればOKです。単純な表示確認なので、確認できればCTRL+Cでサーバを止めてもらって構いません。

# ディレクトリ構成

Symfony4+Flexのディレクトリ構成は以下のディレクトリ構成にする必要があります。

```
symfony+flex/
├── assets/
├── bin/
│   └── console
├── config/
│   ├── bundles.php
│   ├── packages/
│   ├── routes.yaml
│   └── services.yaml
├── public/
│   └── index.php
├── src/
│   ├── ...
│   └── Kernel.php
├── templates/
├── tests/
├── translations/
├── var/
└── vendor/
```

Silex-Skeltonのディレクトリ構成を見てみると、以下のようになっています。

```
silex-skelton/
├── bin/
│   └── console
├── config/
│   ├── dev.php
│   └── prd.php
├── web/
│   └── index.php
├── src/
│   ├── app.php
│   ├── console.php
│   └── controllers.php
├── templates/
├── tests/
├── var/
└── web/
    └── index.php
```
`web`が`public`に変わっている以外は、それほど違いはありません。いったんはこのままで進めます。

# composer.jsonのマージ

[Upgrading Existing Applications to Flex](https://symfony.com/doc/current/setup/flex.html#upgrading-existing-applications-to-flex)にある通り、ディレクトリ構成はほぼ同じなので、あとは依存関係をあわせていく必要があります。

Silex-Skeletonのcomposer.jonとSymfony+Flexのcomposer.jsonをマージします。
Symfony+Flexのcomposer.jsonは`composer create-project symfony/skeleton xxx`とかで作っておくとわかりやすいです。

composer.jsonをがっちゃんこしたのが、以下になります。

```diff
// composer.json

-        "php": ">=5.5.9",
+        "php": "^7.1.3",
+        "ext-iconv": "*",
+        "symfony/console": "^4.0",
+        "symfony/flex": "^1.0",
+        "symfony/framework-bundle": "^4.0",
+        "symfony/lts": "^4@dev",
+        "symfony/yaml": "^4.0",
         "silex/silex": "~2.0",
         "silex/web-profiler": "~2.0",
         "symfony/asset": "~2.8|^3.0",
         "symfony/browser-kit": "~2.8|^3.0",
         "symfony/class-loader": "~2.8|^3.0",
         "symfony/config": "~2.8|^3.0",
-        "symfony/console": "~2.8|^3.0",
         "symfony/css-selector": "~2.8|^3.0",
         "symfony/debug": "~2.8|^3.0",
         "symfony/finder": "~2.8|^3.0",
@@ -23,8 +28,18 @@
         "symfony/twig-bridge": "~2.8|^3.0",
         "symfony/validator": "~2.8|^3.0"
     },
+    "require-dev": {
+        "symfony/dotenv": "^4.0"
+    },
     "autoload": {
-        "psr-0": { "": "src/" }
+        "psr-4": {
+            "App\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "App\\Tests\\": "tests/"
+        }
     },
```

ここから、`composer update --dev`すると、いくつかのファイルやディレクトリが生成されます。

```shell
// composer update後のgit status

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   .gitignore
	modified:   composer.json

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.env.dist
	composer.lock
	config/bundles.php
	config/packages/
	config/routes.yaml
	config/routes/
	config/services.yaml
	flex/
	public/
	src/Controller/
	src/Kernel.php
	symfony.lock
	translations/
```

Symfony+Flexで必要なディレクトリやファイルが生成されているのがわかります。

この状態で、`php -S localhost:8000 -t public`し、http://localhost:8000/ にアクセスしてみましょう。※ドキュメントルートは、webではなくpublicを指定します。

多分こんなエラーが表示されると思います。

```
FileLoaderLoadException
The autoloader expected class "App\app" to be defined in file "/Users/xxx/repos/Silex-Skeleton/vendor/composer/../../src/app.php". The file was found but the class was not in it, the class name or namespace probably has a typo in /Users/xxx/repos/Silex-Skeleton/config/services.yaml (which is loaded in resource "/Users/xxx/repos/Silex-Skeleton/config/services.yaml").
```

これは、Symfonyがsrc以下のファイルをすべてコンテナに登録しようとしており、その際にapp.phpでエラーになっています。

`config/services.yaml`を編集して除外しておきます。

```diff
// config/services.yaml
    App\:
        resource: '../src/*'
-       exclude:'../src/{Entity,Migrations,Tests}'
+       exclude:'../src/{Entity,Migrations,Tests,app.php,console.php,controllers.php}'

```

# app.phpの移行

ここから、Silexのアプリケーションを移行していきます。
Silexのエンドポイントである`web/index.php`をみると、

```php
// web/index.php
$app = require __DIR__.'/../src/app.php';
require __DIR__.'/../config/prod.php';
require __DIR__.'/../src/controllers.php';
$app->run();
```

のようになっています。

`src/app.php`では、いくつかのサービスプロバイダが有効になっています。

```php
// src/app.php
$app->register(new ServiceControllerServiceProvider());
$app->register(new AssetServiceProvider());
$app->register(new TwigServiceProvider());
$app->register(new HttpFragmentServiceProvider());
```

サービスプロバイダはSymfonyでは利用できないため、バンドル等に移行していく必要がありますが、ここで必要なのはTwigServiceProviderのみです。それ以外は、FrameworkBundleに含まれているため、特に何もする必要はありません。

TwigServiceProviderに対応するTwigBundleをインストールします。

`$ composer req twig`でインストールします。

`config/prd.php`をみると、

```php
// config/prd.php
$app['twig.path'] = array(__DIR__.'/../templates');
$app['twig.options'] = array('cache' => __DIR__.'/../var/cache/twig');
```

のように定義されていますが、`composer req twig`で、config/twig.yamlが生成されており、twigのパスなどはすでに設定されている状態になっています。

```yaml
twig:
    paths: ['%kernel.project_dir%/templates']
    debug: '%kernel.debug%'
    strict_variables: '%kernel.debug%'
```

ここで、http://localhost:8000/ にアクセすると、やはりエラーになりますが、`NotFoundHttpException`にエラー内容が変わっていると思います。※あとついでにエラー画面もSilexのものよりリッチになっています。

# コントローラの移行

Silexのコントローラは、src/controllers.phpに定義されています。

```php
// src/controlelrs.php
$app->get('/', function () use ($app) {
    return $app['twig']->render('index.html.twig', array());
})
->bind('homepage')
;
```

Symfonyの場合、コントローラは`src/Controller/`以下にコントローラのクラスを配置します。
コントローラはコマンドで生成するのが楽なので、以下のように`MakerBundle`をインストールします。

`$ composer req annotations make`

※MakerBundleがSensioExtraFrameworkBundleに依存するため、`annotations`で合わせてインストールしています。

`$ bin/console make:controller`でコントローラを作成します。

```
$ bin/console make:controller
Choose a name for your controller class (e.g. DeliciousGnomeController):
 > IndexController

 created: src/Controller/IndexController.php
```

作成されたIndexControllerをSilexのコントローラに合わせ、以下のように編集します。

```diff
// src/Contoroller/IndexController.php

 class IndexController extends Controller
 {
     /**
-     * @Route("/index", name="index")
+     * @Route("/", name="homepage")
      */
     public function index()
     {
         // replace this line with your own code!
-        return $this->render('@Maker/demoPage.html.twig', [ 'path' => str_replace($this->getParameter('kernel.project_dir').'/', '', __FILE__) ]);
+        return $this->render('index.html.twig');
     }
 }

```

http:localhost:8000 にアクセスし、`Welcome to your new Silex Application!`が表示されれば成功です。

もし更新されない場合は、キャッシュされている場合もあるので`$ bin/console cache:clear`でキャッシュをクリアしてみてください。

以上で、Silex-SkeletonをSymfony4+Flexの構成で動かすまでができました。

ただ、Silex-Skeletonはほんとに最小構成なので、実際に運用されているシステムの場合は、他にも色々移行する必要があるかと思います。

無理にSilexのまま移植していくよりかは、SymfonyのSkeletonの上に載せ替えていくほうが楽かな、と思いました。

次項で各種サービスプロバイダの移行先バンドル・ライブラリを調べてみたので、参考にしてみてください。

# 各種サービスプロバイダの移行

Silex-Skeletonは必要最小限の構成のため、各種サービスプロバイダを利用している場合は、それぞれSymfonyの各バンドルやライブラリへ移行する必要があります。

[Silexの公式で紹介されているサービスプロバイダ](https://silex.symfony.com/doc/2.0/providers.html)の移行先バンドルは以下になります。

## AssetServiceProvider
[FrameworkBundle](https://symfony.com/doc/current/reference/configuration/framework.html)に含まれているため、バンドルのインストールは不要です。

`composer req asset` で[asset component](https://symfony.com/doc/current/components/asset.html)をインストールすれば利用できます。

## CsrfServiceProvider
[FrameworkBundle](https://symfony.com/doc/current/reference/configuration/framework.html)に含まれているため、バンドルのインストールは不要です。

`composer req symfony/security-csrf`で[security-csrf](https://symfony.com/doc/current/components/security.html)をインストールすれば利用できます。

## DoctrineServiceProvider
[Doctrine Bundle](https://symfony.com/doc/master/bundles/DoctrineBundle/index.html)をインストールします。

`composer req doctrine`でインストールできます。

## FormServiceProvider
[Form Component](http://symfony.com/doc/current/forms.html)をインストールします。

`composer req form`でインストールできます。

## HttpCacheServiceProvider
[FrameworkBundle](https://symfony.com/doc/current/reference/configuration/framework.html)に含まれているため、バンドルのインストールは不要です。

## HttpFragmentServiceProvider
[FrameworkBundle](https://symfony.com/doc/current/reference/configuration/framework.html)に含まれているため、バンドルのインストールは不要です。

## LocaleServiceProvider
[FrameworkBundle](https://symfony.com/doc/current/reference/configuration/framework.html)に含まれているため、バンドルのインストールは不要です。

## MonologServiceProvider
[Monolog Bundle](https://symfony.com/doc/current/logging.html)をインストールします。

`composer req monolog`でインストールできます。

## RememberMeServiceProvider
[FrameworkBundle](https://symfony.com/doc/current/reference/configuration/framework.html)に含まれているため、バンドルのインストールは不要です。

## SecurityServiceProvider
[SecurityBundle](https://symfony.com/doc/current/security.html)をインストールします。

`composer req security`でインストールできます。

## SerializerServiceProvider

[serializer component](https://symfony.com/doc/current/components/serializer.html)をインストールします。

`composer req symfony/serializer`でインストールできます。

## ServiceControllerServiceProvider
[FrameworkBundle](https://symfony.com/doc/current/reference/configuration/framework.html)に含まれているため、バンドルのインストールは不要です。

## SessionServiceProvider
[FrameworkBundle](https://symfony.com/doc/current/reference/configuration/framework.html)に含まれているため、バンドルのインストールは不要です。

## SwiftmailerServiceProvider
[SwiftmailerBundle](https://symfony.com/doc/current/reference/configuration/swiftmailer.html)をインストールします。

`composer req mail`でインストールできます。

## TranslationServiceProvider

[translation component](http://symfony.com/doc/current/components/translation.html)をインストールします。

`composer req symfony/translation`でインストールできます。

## TwigServiceProvider
[TwigBundle](http://symfony.com/doc/current/reference/configuration/twig.html)をインストールします。

`composer req twig`でインストールできます。

## ValidatorServiceProvider

[validator comoponent](https://symfony.com/doc/current/validation.html)をインストールします。

`composer req symfony/validator`でインストールできます。

## VarDumperServiceProvider

[DebugBundle](https://symfony.com/doc/current/reference/configuration/debug.html)をインストールします。

`composer req debug-pack`でインストールできます。

以上です。

Symfonyを触り始めてまだ2〜3週間程度なので、移行先のバンドルやライブラリについては誤りがあるかもしれません。もし間違いなどありましたらご指摘いただけるとありがたいです。