PHP Phantom JS で任意のスクリプトを走らせる

## 2018/03/26 追記

現在 [Phantom JS の開発は止まっています](https://github.com/ariya/phantomjs/issues/15344)。

これからやるなら [Headless Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome?hl=ja) を Node.js から操作する、というのが王道でしょうか。

## この記事でわかること
 * 自分で書いた Phantom JS スクリプトを PHP から実行する方法
 * PHP から Phantom JS スクリプトにパラメータを渡す方法

## 概要
[PHP Phantom JS](http://jonnnnyw.github.io/php-phantomjs/) ではスクリプトのテンプレートが予め準備されており、「URL指定で特定のページにアクセスし、特定の要素を取り出す」のような用途ではパラメータを渡すだけで簡単に目的を実現できます。

その一方で、自分で作成した [Phantom JS](http://phantomjs.org/) スクリプトを実行したり、PHP からパラメータを渡したりする仕組みも備えており、割と自由自在に使うことができます。

この記事ではその方法を説明します。

## インストール

公式ページの記述に従って、Composer を使ってインストールします。
一緒に Phantom JS 自体の実行ファイルもついてきます。

```json:composer.json
    // ...
    "scripts": {
        "post-install-cmd": [
            "PhantomInstaller\\Installer::installPhantomJS"
        ],
        "post-update-cmd": [
            "PhantomInstaller\\Installer::installPhantomJS"
        ]
    },
    // ...
    "config": {
        "bin-dir": "bin"
    },
    // ...
```


```bash:bash
composer require "jonnyw/php-phantomjs:4.*"
```


## 任意のスクリプトを実行

実行したいスクリプト（ .js ）の拡張子を .proc に変えておきます。
また、実行権限を付与します。

```bash:bash
cp your_script.js /path/to/directory/your_script.proc
chmod 755 /path/to/directory/your_script.proc
```

以下のように PHP から Phantom JS を実行することができます。

```php
<?php

use JonnyW\PhantomJs\Client;
use JonnyW\PhantomJs\DependencyInjection\ServiceContainer;

// スクリプトファイル（.proc）が設置してあるディレクトリ
$location = '/path/to/directory';

$serviceContainer = ServiceContainer::getInstance();
$procedureLoader = $serviceContainer->get('procedure_loader_factory')->createProcedureLoader($location);

$client = Client::getInstance();

// スクリプトファイル名を指定（拡張子は除く）
$client->setProcedure('your_script');
$client->getProcedureLoader()->addLoader($procedureLoader);

$request = $client->getMessageFactory()->createRequest();
$response = $client->getMessageFactory()->createResponse();

// スクリプト実行
$client->send($request, $response);

// 結果を取得（Phantom JS の標準出力）
$result = $response->getContent();
```


### PHP からパラメータを渡す

PHP から Phantom JS スクリプトにパラメータを渡したい場合、まず `JonnyW\PhantomJs\Http\Request` を継承したクラスを作成します。
※公式ドキュメントでは `JonnyW\PhantomJs\Messages\Request` を継承するように書かれていますが、`JonnyW\PhantomJs\Http\Request` を継承しましょう。

パラメータを設定する際には getter に対応した setter メソッドを作るのが無難でしょう。

```php:CustomRequest.php
<?php

use JonnyW\PhantomJs\Http\Request;

class CustomRequest extends Request
{
    // パラメータを格納するプロパティ
    protected $hoge;

    // パラメータを設定するメソッド
    public function setHoge($hoge)
    {
        $this->hoge = $hoge;
    }

    // パラメータにアクセスするメソッド（スクリプト側で呼び出す）
    public function getHoge()
    {
       return $this->hoge;
    }
}
```

上記クラスを作成後、先程の PHP ソースを次のように書き換えます。

```php
<?php

use JonnyW\PhantomJs\Client;
use JonnyW\PhantomJs\DependencyInjection\ServiceContainer;

// スクリプトファイル（.proc）が設置してあるディレクトリ
$location = '/path/to/directory';

$serviceContainer = ServiceContainer::getInstance();
$procedureLoader = $serviceContainer->get('procedure_loader_factory')->createProcedureLoader($location);

$client = Client::getInstance();

// スクリプトファイル名を指定（拡張子は除く）
$client->setProcedure('your_script');
$client->getProcedureLoader()->addLoader($procedureLoader);

// createRequest() の代わりに CustomRequest インスタンスを生成
$request = new CustomRequest();
// hoge パラメータに 1 を設定
$request->setHoge(1);

$response = $client->getMessageFactory()->createResponse();

// スクリプト実行
$client->send($request, $response);

// 結果を取得（Phantom JS の標準出力）
$result = $response->getContent();
```

これで、Phantom JS スクリプトからパラメータにアクセスする準備が整いました。

### Phantom JS スクリプトでパラメータにアクセス

スクリプトファイルの先頭と末尾に次の記述を追加します。

```js:your_script.proc
[% autoescape false %]
{% autoescape false %}

/* ... スクリプト本体 */

{% endautoescape %}
[% endautoescape %]
```

スクリプトでパラメータにアクセスしたい部分で次のように記述します。

```js:your_script.proc
/* ...（略） */

var hoge = {{ input.getHoge() }};

/* ...（略） */
```

`{{ }}`の中身がそのまま入れ替わる形なので、`hoge` パラメータが文字列であるなら `var hoge = '{{ input.getHoge() }}';` のように書く必要があります。


## 気をつけるべきこと

* スクリプトは、エラーが起きても `phantom.exit()` が呼ばれるようにしておく必要がある
  - そうしないといつまでたってもスクリプトの実行が終了しない
* スクリプトファイルのコメントは`/* */` 形式にしないといけない
  - `//` 形式のコメントがあると構文エラーになる模様


## 参考にしたURL
* http://jonnnnyw.github.io/php-phantomjs/4.0/4-custom-scripts/
* http://tech.quartetcom.co.jp/2016/04/07/php-phantomjs/