まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:php

2018/09/15 に開催された WordCamp Tokyo 2018 にスピーカーとして参加してきました:
WordPress meets IBM Watson ! 〜WordPress のコンテンツデータを IBM Watson に機械学習させてみよう〜

kimura-kei


セッションの中で話せたことだけでなく、その背景にあった部分も含めてブログにまとめました。


【WordPress 歴】
まず私自身の WordPress 歴を話しておきます。利用者という意味では結構早い段階から使っていたつもりでしたが、(WordPress をカスタマイズして使ってサービスを作る、という)開発者としては 2013 年からだと思います。この年に転職を経験しており、その転職先では WordPress を使ったサービス開発案件を何度も経験しました。業務で本格的に PHP を使うようになったのもこの頃からです。自分自身の印象として、WordPress は「サービスを高速開発するフレームワークの1つ」だと思っています。実際、テンプレートやプラグインの充実度が半端なく、ちょっとググればやりたいことを実現するプラグインが見つかるので、完成形に近いサービスをすぐに作ることができて感動しました。同時に WordPress のシステム内部についても興味を持つようになりました。当時はまだ REST API が標準ではなかったため、自分で MySQL テーブルの構造を調べて、データを効率よくインポート/エクスポートするにはどうすればよいか、を調べたり、ツールを作ったりしていました。今回発表させていただいた内容でもこの頃の知識が役立ちました。


【2度目の応募】
WordCamp でスピーカーとして登壇させていただいたのは初めてでしたが、スピーカーとしての応募は初めてではありません。WordCamp の存在を知ったのは 2013 年頃だったと思います。記憶が正しければこの頃(おそらく 2014 年)に一度スピーカーとして応募しています。少しビジネス寄りの利用を想定した内容だったのですが、この時は残念ながら登壇することはありませんでした。そしてこの時から「いつかはこの舞台でスピーチしたいなあ」という目標のようなものができたのでした。

そして今年、今回もどちらかというとビジネス寄りの内容だったと思いますが、よく言えば「流行りに乗っかる」形で人工知能と絡めたセッションのスピーチで応募し、念願だった登壇者に選んでいただきました。


【直前のトラブル】
今回のセッションは 15 分間の中で説明とライブデモを見せる、というものでした。このライブデモで実際に動いている所をお見せすることで理解をより具体的に深めていただく、という目的がありました。

ところがこのデモ環境にトラブルがありました。ちゃんと動く環境を(仮想環境で)用意して、そのイメージのバックアップもとって、何かあってもバックアップからリストアすれば大丈夫、と思っていました。ところがこの環境で使って、セッションが expire するまでは普通に使えるのですが、expire 後に何故かログインできなくなる、という問題が発生してしまいました。要は肝心のデモ環境に管理者権限でログインできなくなってしまう、という症状が出てしまったのでした。プラグインを紹介するデモなので、管理者コンソールに入れないのは致命傷でした。

判明したのがデモ直前だったこともあり、原因は未だによくわかっていないのですが、このトラブルに結構振り回されました。結論としては「セッションの直前にデモ環境をデモデータ含めて新規に構築する」という綱渡り的な対処で乗り切りました。冷や汗モノ・・・ (^^;


【セッションは無事に】
そんな綱渡りをしながらもセッションは無事に(3分オーバーでw)終えることができました。事前の練習では1分余らせて終える練習をしていたつもりが、本番になると調子に乗ってしまったのか、話す時間が多くなってしまったようです。

当日の資料の配布版はこちらで公開しています:


IBM Watson の NLC(Natural Language Classifier) というサービスを使って、WordPress の中に溜まったデータを Watson に学習させて、問い合わせすることに挑戦する、という内容を紹介させていただきました。全セッション中の最終セッションだったにもかかわらず多くの皆様に参加いただき、また終了後も多くの質問や感想をその場でいただくことができて、とても充実したセッションでした。この場をお借りしてお礼申し上げます。

セッション内でも触れましたが、機械学習の現場では「学習データ不足」が解決するべき課題となっています。WordPress のような広く使われている CMS のコンテンツデータを学習データとすることができればどんなに楽か・・・という思いもあってのセッションテーマおよびデモでした。同じような悩みを持っていたり、興味を持って参加いただけた皆さんの参考になれば嬉しいです。

また上述の通り、自分にとってはこの場でセッションすることがここ数年の目標の1つでした。とてもいい形で実現することができたと思っています。参加者の皆様、サポートスタッフの皆様、スポンサーの皆様、貴重な機会を本当にありがとうございました。

uXTscPOQ



無料のグループウェアを SaaS で提供していたサイボウズ Live が 2019 年4月15日をもってサービスを終了する、というアナウンスがありました:
サイボウズLiveサービス終了のお知らせ

個人的にも仲間内サークル活動の中で使っていたりしていたもので、このニュース自体はとても残念なものでした。が、終了まで1年半も猶予を持ったアナウンスであり、また(上記リンクによると)今後データのエクスポート(CSVファイル?機能?)などを提供する予定もあるらしいです。データを他のサービスに移行するための準備期間としては充分にあるようにも感じました。

データ移行に関しては時間的な猶予もあるので、しばらくは謹製のデータエクスポートが出されるのを待ってもいいとは思っています。が、個人的/技術的な興味もあって自分でも PHP で作ってみました。詳しくは後述しますが、もしご利用になる場合、現時点ではまだ不完全なものだと理解の上で使ってください。なおこのツールではデータを XML ファイルでエクスポートします(掲示板データなど、改行情報を含むデータは CSV に向かないと判断したので):
https://github.com/dotnsf/cbl_export/blob/master/cbl_export.php


実際に使うには、まず PHP 実行環境が必要です(自分自身がテストした範囲では PHP 5.3.3 でも動きました、かなり古いバージョンでも大丈夫だと思います):
(例 CentOS の場合)
# yum install php php-mbstring php-xml php-pear

加えてプログラムの中で OAuth などの外部ライブラリモジュールを使うため、これらのモジュールをあらかじめインストールしておいてください:
(例 pear を使う場合)
# pear install Net_URL2
# pear install HTTP_Request2
# pear install HTTP_OAuth

またこのツールを使う準備としてサイボウズ Developer Center のアカウントが必要です。同アカウントをお持ちでない場合はリンク先の「デベロッパー登録」ボタンから申請してアカウントを作成してください:
2017102901


アカウント作成後にログインし、「Myアプリケーション」の一覧で「アプリケーションを登録する」を選択し、今から動かすアプリケーションの登録を行います:
2017102902


アプリケーションを登録する際のアプリケーション名などは任意に設定いただいていいのですが、「アプリケーションの種類」は「クライアント」、そして「アクセスレベル」は「レベルZ」を選択する点に注意してください(ここを間違えると正しく動かなくなります。なおアプリケーション種類をクライアントにしないとレベルZのアクセスレベルは選択できないはずです):
2017102903


こうしてアプリケーションを登録し、登録後の画面に表示される Consumer Key と、Consumer Secret をメモしておきます(他人に教えてはいけません)。またアプリケーションの種類とアクセスレベルが正しく登録されていることを確認してください:
20171026



ここまでの準備ができたところで、改めて今回作成したエクスポートアプリを使います。こちらのサイトから git clone するなどして cbl_export.php をコピーします:
https://github.com/dotnsf/cbl_export/blob/master/cbl_export.php


cbl_export.php をテキストエディタで開き、一部を編集します:
<?php
//. cbl_export.php
//. Referer: https://developer.cybozulive.com/doc/current/#id1
//. For XAuth(Ones of Level Z)
$consumer_key           = '(CybozuLive Developer Center Consumer Key)';
$consumer_secret        = '(CybozuLive Developer Center Consumer Secret)';
$xauth_access_token_url = 'https://api.cybozulive.com/oauth/token';
 
$params = array(
    'x_auth_username' =--> '(CybozuLive Email)',
    'x_auth_password' => '(CybozuLive Password)',
    'x_auth_mode'     => 'client_auth',
);

:
:

上記の青字部分を編集します。$consumer_key には上記サイボウズ Developer Center でのアプリケーション登録時に確認した Consumer Key を、$consumer_secret には同 Consumer Secret を代入します。また $params 'x_auth_username' には自分のサイボウズ Live ログイン時のメールアドレスを、そして 'x_auth_password' には同パスワードを指定します。


ここまでの準備ができたら php コマンドで cbl_export.php を実行します:
# php -f cbl_export.php

実行が成功するとカレントディレクトリに自分のメールアドレスと同じ名前のフォルダと、自分がサイボウズ Live で所属しているグループごとの ID(X:XXXXX みたいなフォーマットです)のフォルダが作成され、各データがそれぞれのフォルダ内にエクスポートされます。例えば1つのグループにしか所属していない場合であれば、自分のメールアドレスのフォルダが1つと、そのグループのIDのフォルダが1つ生成されます:
2017102904
(↑自分が実行した場合の結果サンプル)


そして個人のデータはメールアドレスのフォルダ内に、要素毎に XML ファイルでエクスポートされています(現在の仕様ではスケジュール、チャット、ToDo、コネクションのデータがエクスポートされます):
2017102905


またグループ ID のフォルダには各グループ毎のメンバー、掲示板、イベント、ToDo、ファイル共有の情報が XML ファイルでエクスポートされています:
2017102906


なお、このツールを開発するにあたり、サイボウズ Live API を使っています。その仕様はこちらです:
サイボウズLive データ API ドキュメント


(↓2017/Oct/29 時点での制限事項)
上記のように一応動くものが作れたつもりなのですが、2017/Oct/29 時点ではファイルのダウンロード機能が正しく動いていません。例えばサイボウズ Live のファイル共有機能や掲示板の中でファイルを添付しているケースは珍しくないと思っています。その「どんなファイルが添付されているか?」といった情報は取得できて、その情報は現在取得できるファイルの中にも正しくエクスポートできているのですが、肝心のファイルそのもののダウンロードができていません。具体的にはこの API の実行時にだけ想定外のエラーになってしまい、まだその理由や解決策が分からずにいます:
https://developer.cybozulive.com/doc/current/pub/fileDownload.html


この部分については私のミスなのか、API 側の問題なのかの判別ができず、現在は問い合わせ中です。分かり次第に対応するつもりです。
(↑2017/Oct/29 時点での制限事項)


(↓2017/Nov/06 追記)
サイボウズより返答あり。サイボウズ側では問題なく動いているとのこと。むむむ・・ (--;
(↑2017/Nov/06 追記)


添付ファイルのダウンロード以外についてはある程度動くようになっているつもりです。早めのデータ移行を検討していたり、移行先候補へのテストを早めに実行したい場合に活用ください。

なおサイボウズから謹製のエクスポートデータやエクスポートツールが提供された場合は、(きっと面倒なアカウント登録なども不要になると思うので)そちらの利用をオススメします。 (^^;


(↓2017/Dec/19 追記)
kintone エバンジェリストでもある長井様( @akvabit)からコメントをいただき、現在ダウンロード API は画像ファイルのみが対象となっていることがわかりました。つまり画像以外のファイルは現時点では API ではダウンロードできないようです。
併せて長井様からのコメントを参考に PHP ファイルを改良しました。現在は「画像ファイルについてはダウンロードする(画像以外のファイルは無視する)」ような仕様にした上で動いています。
(↑2017/Dec/19 追記)

(↓2017/Dec/24 追記)
ファイルダウンロード機能ですが、動いていたように思えたのですが、やはり正しく動いていませんでした。ロジックを間違えていたせいで気付かなかったのですが、元のエラーが取れていませんでした(画像ファイルを対象にしても同じエラーが発生しています)。
(↑2017/Dec/24 追記)

データのマイグレーションなどで WordPress を使う場合、元データのユーザーデータごと移行するケースがあると思います。そんなユーザー情報を WordPress の機能で追加するのではなく、外部アプリケーションから直接データベースを操作して追加する方法を紹介します。

なお、今回紹介するケースでは WordPress 4.8.2 を使いました。また WordPress のテーブルプリフィクス値はデフォルトの 'wp_' であると仮定して以下を紹介します。異なる設定で利用している場合は適宜読み替えてください。加えて、今回紹介する PHP ファイルを MySQL サーバーとは異なるホストから実行する場合は、このファイル内で指定したユーザーが MySQL にリモートログインできるような設定があらかじめされている必要がある点をご注意ください。


まず WordPress を普通にセットアップすると、いくつかのテーブルが自動的に生成されます。その中でユーザー情報を管理するのは wp_users と wp_usermeta の2つです:


wp_users はその名の通り WordPress のユーザーテーブルとなっていて、1レコードが1ユーザーの情報を意味します。詳しくは後述しますが、単に「ユーザーを登録する」だけであればこのテーブルに1レコード追加するだけで事は足ります。

一方、wp_usermeta はユーザーの属性に関わる値を管理するテーブルです。例えばユーザーのニックネームや権限といった wp_users だけでは管理できない一部の情報や、システムが内部的に利用するセッション情報、最終アクション日時といった情報を wp_usermeta テーブルで管理します。以下では「管理画面にアクセスできる権限を持ったユーザーを追加する」例を紹介しますが、この場合は wp_usermeta も操作する必要があります。


まず wp_users テーブルの属性を desc コマンドで調べた結果がこちらです:
mysql> desc wp_users;
+---------------------+---------------------+------+-----+---------------------+----------------+
| Field               | Type                | Null | Key | Default             | Extra          |
+---------------------+---------------------+------+-----+---------------------+----------------+
| ID                  | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment |
| user_login          | varchar(60)         | NO   | MUL |                     |                |
| user_pass           | varchar(255)        | NO   |     |                     |                |
| user_nicename       | varchar(50)         | NO   | MUL |                     |                |
| user_email          | varchar(100)        | NO   | MUL |                     |                |
| user_url            | varchar(100)        | NO   |     |                     |                |
| user_registered     | datetime            | NO   |     | 0000-00-00 00:00:00 |                |
| user_activation_key | varchar(255)        | NO   |     |                     |                |
| user_status         | int(11)             | NO   |     | 0                   |                |
| display_name        | varchar(250)        | NO   |     |                     |                |
+---------------------+---------------------+------+-----+---------------------+----------------+
10 rows in set (0.01 sec)

プライマリキーは ID(auto_increment)で、他のフィールドは全て Not Null 属性です。ただ user_registered と user_status にはデフォルト値が設定されています。ということはこれらを除いた user_login, user_pass, user_nicename, user_email, user_url, user_activation_key, display_name を指定すればレコードは作れそうです。

というわけで、とりあえずは以下のような関数(createWpUser)およびファイル(wp-user-import.php(改良前))を PHP で作ってみました:
<?php

$wp_db_name = 'wpdb';            //. WordPress DB
$wp_db_host = 'localhost';       //. WordPress ホスト名
$wp_db_username = 'adminuser';   //. WordPress DB の管理者ユーザー名
$wp_db_password = 'adminpass';   //. WordPress DB の管理者パスワード
$wp_table_prefix = 'wp_';        //. WordPress DB のテーブルプリフィクス


function createWpUser( $u_email, $u_password ){
  global $wp_db_name, $wp_db_host, $wp_db_username, $wp_db_password, $wp_table_prefix;

  $pdo = new PDO( 'mysql:dbname='.$wp_db_name.';host='.$wp_db_host.';charset=utf8', $wp_db_username, $wp_db_password );
  if( $pdo != null ){
     $pdo->query( 'SET NAMES utf8' );

     $sql1 = 'insert into ' . $wp_table_prefix . 'users(user_login,user_pass,user_nicename,user_email,user_url,user_activation_key,display_name) values( :user_login, MD5(:user_pass), :user_nicename, :user_email, "", "", :display_name )';
     $stmt1 = $pdo->prepare( $sql1 );
     $stmt1->bindParam( ':user_login', $u_email );
     $stmt1->bindParam( ':user_pass', $u_password );
     $stmt1->bindParam( ':user_nicename', $u_email );
     $stmt1->bindParam( ':user_email', $u_email );
     $stmt1->bindParam( ':display_name', $u_email );
     $r1 = $stmt1->execute();
  }
}

?>

createWpUser 関数のパラメータはメールアドレスとパスワードの2つだけです。メールアドレスは user_login, user_nicename, user_email, display_name に設定します。パスワードは MySQL の MD5 関数を使ってハッシュ化したものを指定します。また user_url および user_activation_key には '' を指定して insert を実行しています。

このファイルを使って、1ユーザーを作成するサンプルがこちらです(test1.php):
<?php

require_once './wp-user-import.php';

$r1 = createWpUser( 'user1@xxx.com', 'P@ssw0rd' );  //. ID: user1@xxx.com , パスワード: P@ssword のユーザーを新規作成する

?>

試しにこの test1.php を実行すると、WordPress に user1@xxx.com という ID のユーザーが登録されます:
$ php -f test1.php


管理者権限でログインして管理ページを開くと、たしかに指定したユーザーが追加されていることが確認できます(この画面で「権限グループ」が「なし」になっていることがわかります):
2017103001


この時点で作成したユーザーは有効なので、指定した ID とパスワードでログインすることはできます:
2017103002


ただ管理機能については権限を一切持っていないユーザーなので、無理やり URL を指定して管理ページにアクセスしてもエラー画面となります:
2017103003


とりあえずはここまで。wp_users に1レコード追加するだけで(管理や投稿の権限を持たない)ユーザーを作ることができることがわかりました。とりあえずこの状態になれば、後から管理者がこれらのユーザーの権限を(手動で)変える、ということも可能ではあります。


次に管理者や投稿者の権限を持ったユーザーを同様に外部から作成する方法を紹介します。先程と同様に wp_usermeta テーブルの属性を同様に調べた結果がこちらです:
mysql> desc wp_usermeta;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| umeta_id   | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id    | bigint(20) unsigned | NO   | MUL | 0       |                |
| meta_key   | varchar(255)        | YES  | MUL | NULL    |                |
| meta_value | longtext            | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

umeta_id がプライマリキー(auto_increment)で、user_id は wp_users.ID を示す外部キーです。そしてこの user_id で指定したユーザーに対して、meta_key と meta_value で属性を加えることができるようになっています。

ユーザーの権限に関わる設定をするには、meta_key の値が 'wp_user_level' 及び 'wp_capabilities' となる2つのレコードを追加して指定することになります。具体的には対象のユーザーの権限に応じて、以下のような値を設定します:
ユーザー権限wp_user_level の設定値wp_capabilities の設定値
購読者0a:1:{s:10:"subscriber";b:1;}
寄稿者1a:1:{s:11:"contributor";s:1:"1";}
投稿者2~4a:1:{s:6:"author";b:1;}
編集者5~7a:1:{s:6:"editor";b:1;}
管理者8~10a:1:{s:13:"administrator";b:1;}


というわけで wp-user-import.php を少し改良します。createWpUser 関数のパラメータを2つから3つに変更し、3つ目のパラメータで上記の wp_user_level を指定するものとします:
<?php

$wp_db_name = 'wpdb';            //. WordPress DB
$wp_db_host = 'localhost';       //. WordPress ホスト名
$wp_db_username = 'adminuser';   //. WordPress DB の管理者ユーザー名
$wp_db_password = 'adminpass';   //. WordPress DB の管理者パスワード
$wp_table_prefix = 'wp_';        //. WordPress DB のテーブルプリフィクス


function createWpUser( $u_email, $u_password, $wp_user_level = 0 ){
  global $wp_db_name, $wp_db_host, $wp_db_username, $wp_db_password, $wp_table_prefix;

  $pdo = new PDO( 'mysql:dbname='.$wp_db_name.';host='.$wp_db_host.';charset=utf8', $wp_db_username, $wp_db_password );
  if( $pdo != null ){
    $pdo->query( 'SET NAMES utf8' );

    $sql1 = 'insert into ' . $wp_table_prefix . 'users(user_login,user_pass,user_nicename,user_email,user_url,user_activation_key,display_name) values( :user_login, MD5(:user_pass), :user_nicename, :user_email, "", "", :display_name )';
    $stmt1 = $pdo->prepare( $sql1 );
    $stmt1->bindParam( ':user_login', $u_email );
    $stmt1->bindParam( ':user_pass', $u_password );
    $stmt1->bindParam( ':user_nicename', $u_email );
    $stmt1->bindParam( ':user_email', $u_email );
    $stmt1->bindParam( ':display_name', $u_email );
    $r1 = $stmt1->execute();

    $user_id = null;
    $sql2 = 'select last_insert_id() as user_id from ' . $wp_table_prefix . 'users';
    $stmt2 = $pdo->query( $sql2 );
    if( $row = $stmt2->fetch( PDO::FETCH_ASSOC ) ){
      $user_id = $row['user_id'];
    }

    if( $user_id ){
      //. wp_user_level
      $sql3 = 'insert into ' . $wp_table_prefix . 'usermeta(user_id,meta_key,meta_value) values( :user_id, "wp_user_level", :wp_user_level )';
      $stmt3 = $pdo->prepare( $sql3 );
      $stmt3->bindParam( ':user_id', $user_id );
      $stmt3->bindParam( ':wp_user_level', $wp_user_level );
      $r3 = $stmt3->execute();

      //. wp_capabilities
      $wp_capabilities = null;
      if( $wp_user_level == 0 ){
        $wp_capabilities = 'a:1:{s:10:"subscriber";b:1;}';
      }else if( $wp_user_level == 1 ){
        $wp_capabilities = 'a:1:{s:11:"contributor";s:1:"1";}';
      }else if( 2 <= $wp_user_level && $wp_user_level <= 4 ){
        $wp_capabilities = 'a:1:{s:6:"author";b:1;}';
      }else if( 5 <= $wp_user_level && $wp_user_level <= 7 ){
        $wp_capabilities = 'a:1:{s:6:"editor";b:1;}';
      }else if( 8 <= $wp_user_level && $wp_user_level <= 10 ){
        $wp_capabilities = 'a:1:{s:13:"administrator";b:1;}';
      }
      if( $wp_capabilities != null ){
        $sql4 = 'insert into ' . $wp_table_prefix . 'usermeta(user_id,meta_key,meta_value) values( :user_id, "wp_capabilities", :wp_capabilities )';
        $stmt4 = $pdo->prepare( $sql4 );
        $stmt4->bindParam( ':user_id', $user_id );
        $stmt4->bindParam( ':wp_capabilities', $wp_capabilities );
        $r4 = $stmt4->execute();
      }
    }
  }
}
?>

createWpUser の3つ目のパラメータ(無指定時は 0)の値を上記の wp_user_level と見なして、wp_usermeta テーブルにユーザー権限に合わせた値を作成するように変更しました。また wp_usermeta テーブルにレコードを作成する際に wp_users.ID の値が必要になるので、MySQL の last_insert_id() 関数を使って作成したユーザーの ID を取得しています。

そして、例えば管理者権限を持ったユーザーを作成したい場合は以下(test2.php)のようなコードを用意します:
<?php

require_once './wp-user-import.php';

$r2 = createWpUser( 'user2@xxx.com', 'P@ssw0rd', 10 );  //. ID: user2@xxx.com , パスワード: P@ssword のユーザーを管理者権限で新規作成する

?>

試しにこの test2.php を実行すると、WordPress に user2@xxx.com という ID の管理者ユーザーが登録されます:
$ php -f test2.php

2017103004


このユーザーの ID とパスワードでログインすると、管理者ダッシュボードにアクセスして、管理者権限でのオペレーションが可能です:
2017103005



というわけで、PHP から WordPress の MySQL データベースを直接操作してユーザーを登録することができました!


(参考) WordPress Codex 日本語版:ユーザーの種類と権限

久しぶりに PHP を使っていて気付いたことをブログネタにしました。

最近の Web API/REST API は JSON フォーマットで結果を取得できるものが多いですが、中には XML のみサポートされているものもあります(RSS とかね)。そんな XML を PHP で扱う方法について調べました。
2017102700


まず、PHP には simplexml_load_file および simplexml_load_string という便利な関数が用意されています。これらは XML ファイル(のURL)や XML 文字列を指定して、PHP のオブジェクト化するというものです。例えば以下のような ATOM フィードフォーマットの XML ファイル(atom.xml)があったとします:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">
  <id>tag:phpxml/</id>
  <title>サンプルタイトル</title>
  <link rel="alternate" type="text/html" href="http://sample.xml.jp/feed/"/>
  <link rel="self" type="application/atom+xml" href="http://sample.xml.jp/feed/atom10.xml"/>
  <entry>
    <id>http://sample.xml.jp/test1.html</id>
    <title>紹介タイトルその1</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test1.html"/>
    <summary>紹介するエントリの1つです。</summary>
    <dc:updated>Thu, 26 Oct 2017 07:00:00 +0900</dc:updated>
  </entry>
  <entry>
    <id>http://sample.xml.jp/test2.html</id>
    <title>紹介タイトルその2</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test2.html"/>
    <summary>紹介するエントリの2つめです。</summary>
    <dc:updated>Fri, 27 Oct 2017 07:00:00 +0900</dc:updated>
  </entry>
</feed>

この atom.xml ファイルに対して、例えば以下のようなコード(test1.php)を用意して実行します:
<?php
$feed = simplexml_load_file( "atom.xml" ); echo "id: " . ( $feed->id ) . "\n"; echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){ echo "entry_title: " . ( $entry->title ) . "\n"; echo "link_url: " . ( $entry->link->attributes()->href ) . "\n"; }
?>

その実行結果(赤字部分)はこちら:
$ php -f test1.php

id: tag:phpxml/
title: サンプルタイトル
entry_title: 紹介タイトルその1
link_url: http://sample.xml.jp/test1.html
entry_title: 紹介タイトルその2
link_url: http://sample.xml.jp/test2.html

これだけ。simplexml_load_file 関数や simplexml_load_string 関数は XML ファイルや XML 文字列を簡単に PHP のオブジェクト化して、XML 内の各データや属性値にアクセスできるようになるものです。

※上記のように属性値は $entry->link->attributes()->属性名 で取得できます。


さてここまでは普通にできるのですが、実はこの方法では取得が難しいデータがあります。各 entry 内にある <dc:updated> という特殊な名前のデータです。 
      :
      :
    <title>紹介タイトルその1</title>
    <link rel="alternate" type="text/html" href="http://sample.xml.jp/test1.html"/>
    <summary>紹介するエントリの1つです。</summary>
    <dc:updated>Thu, 26 Oct 2017 07:00:00 +0900</dc:updated>
      :
      :

この部分が少し厄介なのです。test1.php と全く同じ要領でこんな test2.php ファイルを作ってみます:
<?php
$feed = simplexml_load_file( "atom.xml" ); echo "id: " . ( $feed->id ) . "\n"; echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){ echo "entry_title: " . ( $entry->title ) . "\n"; echo "link_url: " . ( $entry->link->attributes()->href ) . "\n"; echo "dc:updated: " . ( $entry->dc:updated ) . "\n"; }
?>

この test2.php を実行すると・・・ Parse error となってしまいます。PHP の文法ではここに ":" を記述してはいけないんですね。。
$ php -f test2.php

Parse error: syntax error, unexpected ':' in test2.php on line 8

なるほど。これでは文法的にNGということはわかった。が、問題はどうやって回避すればいいのか、です。simplexml_load_file 関数を使わずに別の方法で XML をパースする方法もありますが、それはそれで面倒だったりします。なんとか simplexml_load_file の便利な機能をそのままに回避できないか・・

そこでググって見つけたがこの方法でした。簡単にいうと
 (1) タグ(<から>まで)内の ":" を "_" とかに一括変換する
 (2) ただしプロトコル( http:// とか https:// とか)で使われている部分だけは元に戻す
という方法です。この方法で作り直したのがこちら(test3.php)です:
<?php
//. XML ファイルを文字列変数へ読み込む
$txt = file_get_contents( "atom.xml" );
//. <***:****> -> <***_****>
$txt  = preg_replace( "/<([^>]+?):(.+?)>/", "<$1_$2>", $txt );
//. プロトコルは元に戻す
$txt  = preg_replace( "/_\/\//", "://", $txt );

$feed = simplexml_load_string( $txt );
echo "id: " . ( $feed->id ) . "\n";
echo "title: " . ( $feed->title ) . "\n";
foreach( $feed->entry as $entry ){
  echo "entry_title: " . ( $entry->title ) . "\n";
  echo "link_url: " . ( $entry->link->attributes()->href ) . "\n";
  echo "dc:updated: " . ( $entry->dc_updated ) . "\n";
}
?>

青字部分ではまず普通に atom.xml ファイルを文字列として読み込み、上記 (1) および (2) の処理を行います。そしてその結果を simplexml_load_string 関数でオブジェクト化しました。このようにすると、各 entry 内の <dc:updated> 要素は <dc_updated> と変わっているので、そのまま扱えるようになります。

この test3.php を実行すると、期待通りの結果を得ることができました:
$ php -f test3.php

id: tag:phpxml/
title: サンプルタイトル
entry_title: 紹介タイトルその1
link_url: http://sample.xml.jp/test1.html
dc:updated: Thu, 26 Oct 2017 07:00:00 +0900
entry_title: 紹介タイトルその2
link_url: http://sample.xml.jp/test2.html
dc:updated: Fri, 27 Oct 2017 07:00:00 +0900

とりあえず動くことは動きました。でももうちょっと上手い方法がないもんかな。。

超有名なコンテンツ管理システムであるワードプレスは数多くのプラグインも提供されており、プラグインを組み合わせるだけで色々な目的のサイトが構築できてしまいます。

ふとした事情でクラウドファンディング的なサイトを試験的に用意する必要が生じたのですが、「ワードプレスだけでサクッとクラウドファンディングサイトが作れたりしないだろうか?」と思い立って調べてみました。結論としては「色々あるけど、とりあえずクラウドファンディングが作れるプラグインは見つけた」ので、調べて構築した内容を以下に紹介します。


まず、クラウドファンディング用のワードプレスプラグインは有料/無料あわせて数多く存在しています。その中で以下に紹介するのは IgnitionDeck という団体(?)から提供されている IDCF(IgnitionDeck CrowdFunding) (及び IDCF 対応のテーマ)です:
2017040812



IDCF をワードプレスに導入するにはワードプレス管理画面のプラグイン導入から "IgnitionDeck" を検索し、"IgnitionDeck Crowdfunding & Commerce" を見つけて「今すぐインストール」します:
2017040801


他のプラグイン同様に、インストール後「有効化」をクリックすると、このプラグインが有効になります:
2017040802


IDCF プラグインを有効にしても、この段階では IgnitionDeck 未登録のためまだ使えません。管理画面に "IgnitionDeck" というメニューが追加されているので、メニューから IDCF を選び、 "Activate Now" ボタンをクリックして登録画面に遷移します:
2017040803


登録といっても名前、メールアドレス、そしてパスワードを入力するだけです。簡単:
2017040804


登録が完了すると次に行うべき手順が紹介された画面が表示されます。↓下図の3ステップです:
2017040805


1ステップ目、(ここまでの作業でいつの間にか導入されていた)"500 Framework" テーマを有効にします:
2017040806


2ステップ目、IDCF の設定を行います(そのまま Save でも可):
2017040807


そして3ステップ目、というかクラウドファンディングサイトとしてのスタート地点です。クラウドファンディングのプロジェクトが開始できるようになったので、何か1つ追加してみます:
2017040808


プロジェクトを保存すると、ワードプレスのトップ画面のプロジェクト一覧から確認できるようになります:
2017040802


プロジェクトを選択して開くと、クラウドファンディングの説明や目標額が確認でき、支援表明もできるようになっていることが確認できます:
2017040803


IDCF には有料のオプションもあり、サポートや追加で使えるツールキットが用意されているようです。が、まだあまり調べていません。こちらは気が向いたらということで。。


軽く使ってみた限りでの感想ですが、管理画面が英語なのはともかくとして、目標額などを入力すると自動的に米ドル扱いになってしまうことに気付きました(要は金額のデータとして "100" と入力すると、自動的に「100 米ドル」扱いになってしまう)。これをカスタマイズするには IDC メニューの CURRENCY SETTINGS から "JPY" を選択して更新しておくことで変更できました:
2017040801


もう一点、ファンディングの支援レベル(例えばレベル1はステッカーのみ、レベル2は商品そのもの1つ、レベル3は商品を10個まとめて、・・・みたいなもの)を複数追加しようとしたのですが、うまくできませんでした。ググってみると、同じ現象で悩んでるっぽい人向けの FAQ っぽいページを見つけたのですが「会員専用ページです」みたいなメッセージから先に進めず、未だに原因やワークアラウンドが分かっていません。会員としてログインしてるはずなんだけどな・・・ もしかすると、ここが無料版の制限なのかもしれません:
2017040801


もう一点。実際にユーザーがこのプロジェクトを支援しようと "Support Now" して、"Next Step" に進もうとすると、、:
2017040901


こんな画面に推移します:
2017040902


ここで何らかの入力フォーム(project_purchase_form ?)が出て来る想定なんだろうけど、ここをどうすると表示できるようになるのか? カスタマイズするにはどうするのか? といった情報を探していたのですが、どうやらここから先は無料版では提供されていない機能のようでした:
Ignitiondeck does not redirect me to Purchase Page


というわけで、まだ IDCF のフル機能を試せたわけではないのですが、ここまでは超簡単に作れてしまったことも事実。便利なプラグインをまた1つ発見してしまった。。


このページのトップヘ