PHP
PhantomJS

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

2018/03/26 追記

現在 Phantom JS の開発は止まっています

これからやるなら Headless Chrome を Node.js から操作する、というのが王道でしょうか。

この記事でわかること

  • 自分で書いた Phantom JS スクリプトを PHP から実行する方法
  • PHP から Phantom JS スクリプトにパラメータを渡す方法

概要

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

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

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

インストール

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

composer.json
    // ...
    "scripts": {
        "post-install-cmd": [
            "PhantomInstaller\\Installer::installPhantomJS"
        ],
        "post-update-cmd": [
            "PhantomInstaller\\Installer::installPhantomJS"
        ]
    },
    // ...
    "config": {
        "bin-dir": "bin"
    },
    // ...
bash
composer require "jonnyw/php-phantomjs:4.*"

任意のスクリプトを実行

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

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

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

<?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 メソッドを作るのが無難でしょう。

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

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 スクリプトでパラメータにアクセス

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

your_script.proc
[% autoescape false %]
{% autoescape false %}

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

{% endautoescape %}
[% endautoescape %]

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

your_script.proc
/* ...(略) */

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

/* ...(略) */

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

気をつけるべきこと

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

参考にしたURL