Help us understand the problem. What is going on with this article?

同時編集可能なアプリケーションを作る場合

More than 1 year has passed since last update.

本記事では、PHPで何かテキストを投稿するサイト(Blogやメモ帳など)を作る場合に同時編集をされたとき対応する方法がわからなかったので、記事にした

はじめに

本記事は、PHPで同時編集可能なアプリケーションを作成するときにどのようなアプローチで開発するのかについて検討する

2 way merge

2つのファイル間で差分の分析を行う仕組み
エラーが発生しやすくあまり利用されていない

3 way merge

こっちが本命の方ですが、Gitのマージなどにも使われている普及した手法である
3 way mergeの場合には変更前の元のファイルと変更後のファイル、現在の状態のファイルの三種類のファイルが必要である
これら3つのファイルを比較し、元のファイルを起点に差分を分析する
今回はこの方法を使って投稿型サービスに導入する為にPHPで実装してみる

導入

今回は、bircher/php-mergeというライブラリを使って実現していく
少しスター数が少ないのが気になるが、ソースコードを読むと、sebastian/diffという信頼の置けるライブラリの機能をほとんどの部分で使っているラッパーライブラリなので、不安なら自分で実装することもできるだろうと考える

以下で少しお試しのプログラムを書いてみる
- とりあえず、composerでbircher/php-mergeをインストールする
- autoloadを読み込む

composer require bircher/php-merge
3_way_merge.php
<?php
require_once __DIR__ . '/vendor/autoload.php';
$merger = new PhpMerge();

$original = <<<'EOD'
今日は
雨だけど
みんなで
代々木公園で
ピクニックをするために
外出の支度を始める

EOD;

$version1 = <<<'EOD'
今日は
晴れたけど
みんなで
代々木公園で
ピクニックをするのはちょっときついので、
家でゲームをする

EOD;

$version2 = <<<'EOD'
明日は
雨だけど
とりあえず、
支度しないと
代々木公園で
ピクニックをするために
外出の支度を始める

EOD;

try {
    $result = $merger->merge($original, $version1, $version2);
    print($result);
} catch (MergeException $exception) {
    print_r($exception->getConflicts());
}

上記のプログラムを実行すると以下のように出力される

明日は
晴れたけど
とりあえず、
支度しないと
代々木公園で
ピクニックをするのはちょっときついので、
家でゲームをする

見てわかる通り、どちらか片方で元のテキストから変更したものは変更が反映されている
改行した場合はその改行を抜かした部分で差分を比較している

試しに競合させてみる

conflict.php
<?php
require_once __DIR__ . '/vendor/autoload.php';
$merger = new PhpMerge();

$original = <<<'EOD'
今日は
雨だけど
みんなで
代々木公園で
ピクニックをするために
外出の支度を始める

EOD;

$version1 = <<<'EOD'
今日は
晴れたけど
みんなで
代々木公園で
ピクニックをするのはちょっときついので、
家でゲームをする

EOD;

$version2 = <<<'EOD'
明日は
雨だけど
とりあえず、
支度しないと
代々木公園で
ピクニックをするために
外出の支度を始めない

EOD;

try {
    $result = $merger->merge($original, $version1, $version2);
    print($result);
} catch (MergeException $exception) {
    print_r($exception->getConflicts());
}

このプログラムの結果は以下のようになる

Array
(
    [0] => PhpMerge\MergeConflict Object
        (
            [base:protected] => Array
                (
                    [0] => ピクニックをするために

                    [1] => 外出の支度を始める

                )

            [remote:protected] => Array
                (
                    [0] => ピクニックをするのはちょっときついので、

                    [1] => 家でゲームをする

                )

            [local:protected] => Array
                (
                    [0] => ピクニックをするために

                    [1] => 外出の支度を始めない

                )

            [baseLine:protected] => 4
            [mergedLine:protected] => 5
        )

)

baseLineとmergedLineが出力されている
これは、競合する1つ前の行の出力と、コンフリクトした行の出力している行数を示している
5行目が悪いということがエラーメッセージからわかる

ちなみに

PHPには元から3 way mergeができるextensionが用意されている(xdiff_string_merge3)
しかし、よほど使われていないのか、全くもって参考記事が見つからず、ドキュメントを読んでも、返り値が複数あるなど、少し使いにくい印象を受けた
PHPはPHP7から引数及び返り値の型指定ができるようになったので、できるだけ単一の型を返すメソッドを作った方が安定したアプリケーションを開発できる
よって、今回はsebastian/diffをベースにしたライブラリを使うことにした

終わりに

今回は共同編集可能なアプリケーションを作成するときにどうしたらいいかを軽く紹介した
調べてみると世の中では差分マージアルゴリズムに3 way mergeが広く使われていることがわかった
是非、ググる時間を減らせるような記事になっていることを祈る

参考

nocturnality
PHP / Kotlin / Python / Swift / JavaScript / TypeScript / GoLang
recode
Recode は個人開発者が集う Slack コミュニティです。エンジニア・デザイナー等の職種を問わず、多様性のあるコミュニティを目指しています。
http://hukumoto.pe-gawa.com/club/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away