PHPレガシーシステムにTypeScriptを導入を考える

一回これでやってみたが、まぁまぁいけそうだ。

ヾ(・ω<)ﾉ"　三三三●　ⅱⅲ コロコロ♪

----
------------------- ↓ 余談はここから ↓-------------------
PHPのシステムを扱っていて、
JavaScriptは切っても切れない関係にあるが、
JavaScriptで記述中にあれこれ気を遣わなきゃならない事態に陥る。

真っ先に考えるのはIE対応。
IEのバージョンがいくつまでサポートするかによって、
使えるコードが変わってくる。

そして、コードが長くなってくると信頼性が著しく落ちてくる。

(･ω･) これほんまに動くんやろか・・・

ブラウザ上のテスト、E2Eテストを「やってみないと」わからないのはちょっと辛いので、
事前にコードの信頼性を担保する方法が欲しい。

そこで、JavaScript自体はジェネレーターに出力させて、
コードはTypeScriptで書くのはどうだろうという考えに至った。

[TypeScript](https://www.typescriptlang.org)

> TypeScript is a language for application-scale JavaScript. TypeScript adds optional types to JavaScript that support tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the playground, and stay up to date via our blog and Twitter account.

JavaScriptのES6に型を追加したような言語。
CoffeeScriptのような暗号文字と違って読みやすくなっている。
ここから出力されたJavaScriptをPHP側で取り込む流れでいけそうだ。

いくらレガシーシステムといってもフレームワークを使っていないほど古い物は見かけなくなっている。
（ないとはいわないが）
大体がMVCになっているので、
ViewからJSファイルを直接呼び出せばいいが、
TypeScript側のコンパイル（トランスパイルか）の都合で、
以下のようなPHPの出力文字列が邪魔になる。

```
let param = '<?= $abc; ?>'
const json = <?php echo json_encode($json); ?>
```

これらを解決する方法はあるか考えてみたが、
なんとかなったので記録しておく。

----
------------------- ↓ 本題はここから ↓-------------------


### 全体構成
PHP側のViewからTypescriptが出力したJSをどういう構成で組み込むのか。
前書きで書いたようにPHPの出力コードが邪魔でTypeScriptのコードに直接PHPコードを書くことができない。
そこで、PHPとTypeScriptの間に一枚コードを挟む形にする。

PHP側ではこういう具合

```view.php
┌────────────────────────
│ view.php
│┌───────────────
││phpparam.php
││
│└──────────────
│┌───────────────
││typescript.js
││
│└───────────────
└────────────────────────
```

#### view.php
View本体ソースはこんな感じ。

```php:view.php
<html>
<body>
 ・・・・・
<script>
    var exports = {}; // commonjs出力緩衝用
    var userClass = {};
  <?php include('phpparam.php'); // viewの読み込みは各FWに依存 ?>

    (function(){
  <?php echo file_get_contents('typescript.js'); // ファイルをそのまま出力 ?>
        userClass = new User(phpparams.userId);
    }());
</script>

 </body>
</html>
```

#### typescript.ts

TypeScript側ではこういう具合
phpparam.phpの出力をそのままコピーしたphpparam.tsを用意し、
それをreferenceとして読み込めばViewの状態をエミュレートできる。

```typescript.ts
┌────────────────────────
│ /// <reference path="phpparam.ts"/>
│
│
└────────────────────────
```

typescriptの本体はこんな感じ。

```typescript:typescript.ts
/// <reference path="phpparams.ts"/>
export default class User{
    private userId:number;
    constructor(userId:number){
        this.userId = userId;
    }
    public getUser(){
        return this.userId;
    }
}
```

#### phpparam.php
PHPとJSを橋渡しする緩衝用のViewはこんな感じ。
(escapeがどうとか細かいことはしらん)

```php:phpparam.php
var phpparam = {
  userId: '<?php echo $userId; ?>',
  json: <?php json_encode($json); ?>,
}
```

```typescript:phpparam.ts
var phpparams = {
    userId: '201843',
    json: {"abc": "def"},
};
```

パラメータが増えたときはphpparams.phpにパラメータを足し、
実行結果をphpparam.tsに転記する。
phpparams.ts自体は実行過程に登場しないので、
そこまでナーバスにならなくてもよい。



### 結果
typescriptをcommonjs方式でコンパイルしてjsファイルを作成すれば最初のPHPのViewが綺麗に動作する。

```html
<html>
<body>
 ・・・・・
<script>
    var exports = {};
    var userClass = {};
    var phpparams = {
        userId: '201843',
        json: { "abc": "def" },
    };
    (function(){
// ****** Typescriptの箇所 ↓ *****
        "use strict";
        exports.__esModule = true;
        var User = (function () {
            function User(userId) {
                this.userId = userId;
            }
            User.prototype.getUser = function () {
                return this.userId;
            };
            return User;
        }());
        exports["default"] = User;
// ****** Typescriptの箇所 ↑ *****
        userClass = new User(phpparams.userId);
    }());
</script>
</body>
</html>
```

ジェネレータに適したJSにする方法は他にもあったかもしれないが、
いったんはこれで進めよう。

----
------------------- ↓ 後書はここから ↓-------------------



### IE対応

JavaScriptで頑張るときはどうしても考えなきゃならないのがIE対応。
最近はIE11を対応すればおｋ。という流れになっている。
TypeScriptの出力では完璧にIEに対応はできないので、
その辺りを補足。

#### Promise
ES6(ES2015)で登場し、非同期制御をコントロールするためのオブジェクト。
記述が簡単になるので非常に便利。
ただ、当然のようにIEには対応していない。

TypeScript上でasync/awaitを記述すると
コードのどこか、あるいは呼び出されるコードにPromiseが必要になる。

IEでPromiseを使うにはPolyfillと言われる過去のブラウザに対応するためのソリューションがある。
ただ、結構重たい動作をするので、
IEの時だけ呼び出したい。

```html
<script>
window.Promise || document.write('<script src="https://www.promisejs.org/polyfills/promise-done-7.0.4.min.js"><'+'/script>');
</script>
```

またはPromiseを代替するライブラリを使う。

```html
<script src="//cdn.jsdelivr.net/npm/bluebird@3.5.5/js/browser/bluebird.min.js"></script>
```

### try - catch - finally

個人的にも意外だったのがtry-catchにIEが対応していること。
JavaScriptはその性質上エラーが発生するとその後の動作がすべて止まる。
ちょっとした取り違いでサイト全体を停止させてしまうこともあるので、
念のため全体にtry-catchで括っておくのも手かもしれない。