Skip to content

プラグイン開発

プラグインは app/Plugin/<名前>/ に置きます。ディレクトリ名がプラグインの識別子になるので、パスカルケースで付けてください。

ディレクトリ構成

app/Plugin/Demo/
  Config/
    Info.php      プラグイン情報(必須)
    Config.php    設定の既定値
    Submit.js     管理画面の設定フォーム(旧形式 Submit.php も可)
  Hook/
    Main.php      フックの購読
    Lifecycle.php ライフサイクル(任意。Main.php に含めても可)
  Controller/
    Api.php       プラグイン独自のルート
  View/
    index.html    テンプレート

必須なのは Config/Info.php だけで、残りは必要に応じて追加します。

Config/Info.php

php
<?php
declare(strict_types=1);

use App\Consts\Plugin;

return [
    Plugin::NAME => 'デモプラグイン',
    Plugin::AUTHOR => 'あなたの名前',
    Plugin::WEB_SITE => 'https://example.com',
    Plugin::DESCRIPTION => '何をするプラグインかを 1 行で',
    Plugin::VERSION => '1.0.0'
];

Config/Config.php

設定の既定値です。STATUS は有効状態を表す慣習的なフィールドです。

php
<?php
declare(strict_types=1);

return [
    'STATUS' => '0',
    'api_key' => '',
    'enable_notify' => '1',
];

Config/Submit.js — 設定フォーム

管理画面の設定ダイアログの見た目は、このファイルが決めます。

形式が 2 つあり、Submit.js が優先されます。 両方存在する場合、Submit.jsSubmit.php を上書きします。

種類新形式(推奨)旧形式
プラグインConfig/Submit.jsConfig/Submit.php
決済プラグインConfig/Submit.jsConfig/Submit.php
テーマSubmit.js(テーマ直下。Config/ の中ではありません)Config インターフェースの SUBMIT 定数

テーマだけ置き場所が違います。Config/ に入れてもエラーにはならず、フォームが出てこないだけなので注意してください。

書き方

このファイルはフロント側で eval されるため、全体がひとつの式でなければなりません。素の配列が一番簡単です。

js
[
    {
        name: `${util.icon("fa-duotone fa-regular fa-gear")} 基本設定`,
        form: [
            {
                title: "加盟店 ID",
                name: "mch_id",
                type: "input",
                placeholder: "決済事業者から発行された加盟店 ID",
                required: true
            },
            {
                title: "通知を有効にする",
                name: "enable_notify",
                type: "switch",
                text: "有効"
            }
        ]
    },
    {
        name: "詳細設定",
        form: [
            { title: "タイムアウト", name: "timeout", type: "number", placeholder: "秒" }
        ]
    }
]

ヘルパー関数を書きたいときは IIFE で包みます。

js
(() => {
    const T = (s) => (typeof i18n === "function" ? i18n(s) : s);
    const cfg = (k, d = "") => (assign && assign[k] != null && assign[k] !== "" ? assign[k] : d);

    return [
        {
            name: T("基本設定"),
            form: [
                { title: T("API キー"), name: "api_key", type: "input", default: cfg("api_key") }
            ]
        }
    ];
})()

構造:グループとフィールド

Submit.php のフラットな配列と違い、Submit.jsグループ構造です。グループがダイアログのタブになり、name がタブのタイトル(util.icon() が使えます)、form がフィールドの配列です。

フィールドの種類

typeコントロール
input1 行テキスト
passwordパスワード
number数値
textarea複数行テキスト
selectドロップダウン
radioラジオボタン
checkboxチェックボックス
switchトグル
image画像アップロード
explain説明文のみ。保存されません
htmlHTML をそのまま挿入
custom自分で描画(下記参照)

スコープで使えるもの

変数用途
assign現在保存されている設定assign.api_key で読めます
utilヘルパー関数。よく使うのは util.icon()
i18n(s)翻訳関数。多言語対応のプラグイン向け
layuilayui のインスタンス

Submit.js は既定値を自動で埋めません。 Submit.php の場合はコアが Config.php の値を各フィールドの default に流し込みますが、.js の場合コアはファイルを文字列として読んでフロントに渡すだけなので、この処理は実行されません。現在値は assign から自分で取ってください。各プラグインが小さな cfg() を定義しているのはこのためです。

custom:自分で描画する

用意されている種類で表現できないものは custom で描きます。

js
{
    title: false,
    name: "gateway",
    type: "custom",
    complete: (form, dom) => {
        dom.html(`<a href="/plugin/Demo/panel" class="btn btn-sm btn-primary">パネルを開く</a>`);
    }
}

complete()繰り返し呼ばれます(タブの切り替えやフォームの再構築で発生)。中でタイマーやポーリングを始める場合は、前回分が自動的に無効になるようにしてください。そうしないとダイアログを閉じた後もバックグラウンドで回り続けます。

Submit.php はまだ使えますか

使えます。コアは両方を受け付け、リポジトリ内には .php を使っているプラグインも多数あります。

php
<?php
declare(strict_types=1);

return [
    ["title" => "API キー", "name" => "api_key", "type" => "input", "placeholder" => "API キーを入力してください"],
    ["title" => "説明", "name" => "explain", "type" => "explain", "placeholder" => "この文章は案内用で、保存されません"],
    ["title" => "通知を有効にする", "name" => "enable_notify", "type" => "switch", "text" => "有効"],
];

Submit.php はグループのないフラットなフィールド配列で、.js にはない fileeditorjson が使えます。単純なフォームならこれで十分です。タブ、条件付き表示、独自描画が必要になったら .js に移ってください。

設定に JSON 文字列を渡さないでください。 $_POST はグローバルにサニタイズされています。フォーム配列の形で送信してください。

フックの購読

php
<?php
declare(strict_types=1);

namespace App\Plugin\Demo\Hook;

use App\Controller\Base\View\UserPlugin;
use Kernel\Annotation\Hook;

class Main extends UserPlugin
{
    #[Hook(point: \App\Consts\Hook::USER_VIEW_FOOTER)]
    public function footer(): void
    {
        echo '<script>console.log("hello from Demo")</script>';
    }
}

基底クラスは用途で選びます。

基底クラス用途
App\Controller\Base\View\UserPluginストアフロント
App\Controller\Base\View\ManagePlugin管理画面

利用できるポイントの一覧はフック一覧にあります。

購読側は 16 進リテラル#[Hook(point: 0x2300)])で書いてください。定数を参照してはいけません。属性の引数はプラグイン有効化時に評価されるため、その定数を持たない古いコアでは Error になり、「START は実行済み、STATUS は未書き込み」という中途半端な状態でプラグインが止まります。

ライフサイクル

php
<?php
declare(strict_types=1);

namespace App\Plugin\Demo\Hook;

use Kernel\Annotation\Plugin;

class Lifecycle
{
    #[Plugin(state: Plugin::INSTALL)]
    public function install(): void
    {
        // インストール:テーブル作成
    }

    #[Plugin(state: Plugin::START)]
    public function start(): void
    {
        // 有効化のたびに
    }

    #[Plugin(state: Plugin::STOP)]
    public function stop(): void
    {
        // 停止時
    }

    #[Plugin(state: Plugin::UNINSTALL)]
    public function uninstall(): void
    {
        // アンインストール:データの後始末
    }

    #[Plugin(state: Plugin::UPGRADE)]
    public function upgrade(): void
    {
        // アップグレード
    }

    #[Plugin(state: Plugin::SAVE_CONFIG)]
    public function saveConfig(): void
    {
        // 管理画面で設定を保存した後
    }
}

state: という名前付き引数は省略できません。 #[Plugin(Plugin::INSTALL)] と位置引数で書くと何も起きません。プラグインは「有効化に成功」と表示されるのにテーブル作成は実行されず、以降すべての機能が「テーブルが存在しない」で失敗します。コアは $arguments['state'] を読んでいます。

プラグイン独自のページ

Controller/Api.php のメソッドはルートに対応します。

/plugin/<プラグイン名>/<コントローラ>/<メソッド>

たとえば app/Plugin/Demo/Controller/Api.phplogin() メソッドは次でアクセスできます。

/plugin/Demo/api/login

テンプレートの描画:

php
echo $this->render("タイトル", "index.html", ['key' => 'value']);

ストアフロントに UI を差し込むときの注意

プラグインがストアフロントに注入する HTML/CSS は、テーマ側のスタイルと衝突します。経験則が 2 つあります。

  1. CSS は必ず #自分のルートID .自分のクラス の形で書き、詳細度を上げること。裸のクラス名は使わない
  2. リセットは :where() で包み、テーマ側の要素を巻き込まないようにする

もう 1 つ:scrollIntoViewoverflow:hidden の祖先要素まで一緒にスクロールさせます。ストアフロントのモーダル内では注意してください。

パッケージングと公開

開発者センターからプラグインを提出すると、サーバー側でディレクトリ全体が自動的にパッケージ化されます。除外されるのは Config/Config.php だけです。

そのため提出前に大きなファイルを退避してください。バイナリ、runtime.log、キャッシュなどです。そうしないとパッケージが極端に大きくなります。

デバッグ

  • エラーはサイトルートの runtime.log に出ます
  • フックファイルを変更したら、プラグインを停止して再度有効化し、フック登録表を再構築します
  • テンプレートを変更したら、runtime/view/compile の中身を削除します

MIT ライセンスで公開