WordPress で attachment のカスタムフィールドに作成される _wp_attachment_metadata を FileMaker で JSON に変換し、メディア管理に役立てます。
WordPress の管理、特にメディアの管理を FileMaker Pro で行う一連のお話です。Attachment(画像やメディア)のカスタムフィールド _wp_attachment_metadata の値を解析して情報を取得します。
_wp_attachment_metadata
_wp_attachment_metadata て、それ何やねん。はい。それはWordPress のメディアのカスタムフィールドです。
WordPress データベースのメインテーブルは wp_posts です。post_type フィールドに post だの page だの attachment だの書かれていて、これがつまり投稿だったりページだったりメディアだったりメニュー項目だったりします。post_type: attachment が添付メディアです。
attachment にもカスタムフィールド(postmeta)が当然ぶら下がっていて、alt や、/uploads 以下のファイルの場所などが作られています。
_wp_attachment_metadata の値にはメディアの情報が収められています。WordPress が自動作成するサイズ違いファイルの情報もあります。
詳しくは [WordPress] postmeta の _wp_attachment_metadata を解析
これを取得して管理に利用しましょう。
暗号めいたテキストがひしめいていますので、この値をJSONに変換していきます。
metadata の値を変換
_wp_attachment_metadata の値は、メディアの情報がシリアル化されたテキストです。a:6: とか s:29: とか暗号のようなラベルがくっついています。
シリアライズされたデータをJSONに変換する PHP や JavaScript の関数があるらしいですが、よく判らないので FileMaker のスクリプトでやります。
このスクリプトは、もしかすると汎用的にシリアライズされたテキストデータのJSON変換として使用できるかもしれませんが、あくまで _wp_attachment_metadata を変換するものと割り切ります。というのも、いろんなデータでテストしていないからです。
JSONに変換するスクリプト
事前に postmeta から _wp_attachment_metadata の値を取得しておきます。それを 変数$meta に収めたところから行きます。
スクリプトは以下になりましたが、他の言語と異なり、このテキストだけ示したところであまり役立つものでもありません(それにテスト途上なので結果をグローバル変数に書き出したままになってたりして、雑です。参考程度に)
#最初のループ ラベルを取り除く 変数を設定 [ $$meta; 値:Substitute ( $meta ; [ "{" ; "{" & ¶ ]; [ "}" ; ¶ & "}" & ¶ ]; [ ";" ; ";" & ¶ ] ) ] 変数を設定 [ $$meta; 値:AdjustLineBreaks ( $$meta ) ] 変数を設定 [ $c; 値:1 ] 変数を設定 [ $vc; 値:ValueCount ( $$meta ) ] Loop 変数を設定 [ $gv; 値:GetValue ( $$meta ; $c ) ] 変数を設定 [ $next; 値:GetValue ( $$meta ; $c + 1) ] If [ $gv = "}" ] #配列・連想配列の終わり If [ $array = 1 ] #現在 array = 1 なら $gv を "]" に変更 変数を設定 [ $gv; 値:"]" ] #配列関連の変数クリア 変数を設定 [ $array ] 変数を設定 [ $i ] End If #次のtype は key である 変数を設定 [ $type; 値:"key" ] #end mark If [ $c = $vc or $next = "}" ] Else 変数を設定 [ $gv; 値:$gv & "," ] End If Else If [ Left ( $gv ; 2 ) = "a:" ] #配列・連想配列の始まり 変数を設定 [ $i_vc; 値:GetValue ( Substitute ( $gv ; ":" ; ¶ ) ; 2 ) - 1 ] If [ $next = "i:0;" ] # 次が i:0; の場合はvalueのみの配列である。配列ON。i カウント開始。次のTYPE 変数を設定 [ $array; 値:1 ] 変数を設定 [ $gv; 値:"[" ] #次のTYPE は index 変数を設定 [ $type; 値:"index" ] 変数を設定 [ $i; 値:0 ] Else 変数を設定 [ $gv; 値:"{" ] #次のtype は key 変数を設定 [ $type; 値:"key" ] End If Else # { } 以外のすべて If [ $array = 1 ] #現在、配列である場合 If [ $type = "index" ] #この行 index。無視。次のTYPEは value 変数を設定 [ $gv ] 変数を設定 [ $type; 値:"value" ] Else If [ $type = "value" ] #value。次のTYPEは index 変数を設定 [ $type; 値:"index" ] #暗号ラベルを取り除く 変数を設定 [ $gv; 値:GetValue ( Substitute ( $gv ; ":" ; ¶ ) ; ValueCount ( Substitute ( $gv ; ":" ; ¶ ) ) ) ] #終端 変数を設定 [ $endMark; 値:If ( $i = $i_vc ; "" ; "," ) ] 変数を設定 [ $i; 値:$i + 1 ] End If Else #現在、配列でない場合 #暗号ラベルを取り除く 変数を設定 [ $gv; 値:Let ( [ gv = $gv ; pc = PatternCount ( gv ; ":" ); posi = If ( pc > 1 ; Position ( gv ; ":" ; 1 ; 2 ); Position ( gv ; ":" ; 1 ; 1 ) ); rep = Replace ( gv ; posi ; 1 ; ¶ ); gv2 = GetValue ( rep ; 2 ) ]; gv2)) ] If [ $type = "key" ] #終端 変数を設定 [ $endMark; 値:":" ] #次は value 変数を設定 [ $type; 値:"value" ] Else If [ $type = "value" ] #終端 変数を設定 [ $endMark; 値:If ( $next = "}" ; "" ; "," ) ] #次は key 変数を設定 [ $type; 値:"key" ] End If End If #$gv に終端処理 変数を設定 [ $gv; 値:Substitute ( $gv ; ";" ; $endMark ) ] End If 変数を設定 [ $$meta2; 値:List ( $$meta2 ; $gv ) ] 変数を設定 [ $c; 値:$c+1 ] Exit Loop If [ $c>$vc ] End Loop
スクリプトが何をしているのかをざっと流していきましょう。
具体例として次のような値を想定しておきます。こいつを処理します。
このような値です。
a:6:{s:5:"width";i:1200;s:6:"height";i:945;s:4:"file";s:29:"2024/05/testImage-artdump.jpg";s:8:"filesize";i:203766;s:5:"sizes";a:8:{s:6:"medium";a:5:{s:4:"file";s:29:"testImage-artdump-320x252.jpg";s:5:"width";i:320;s:6:"height";i:252;s:9:"mime-type";s:10:"image/jpeg";s:8:"filesize";i:18011;}s:5:"large";a:5:{s:4:"file";s:30:"testImage-artdump-1024x806.jpg";s:5:"width";i:1024;s:6:"height";i:806;s:9:"mime-type";s:10:"image/jpeg";s:8:"filesize";i:130730;}s:9:"thumbnail";a:5:{s:4:"file";s:29:"testImage-artdump-120x120.jpg";s:5:"width";i:120;s:6:"height";i:120;s:9:"mime-type";s:10:"image/jpeg";s:8:"filesize";i:7815;}s:4:"icon";a:5:{s:4:"file";s:27:"testImage-artdump-60x60.jpg";s:5:"width";i:60;s:6:"height";i:60;s:9:"mime-type";s:10:"image/jpeg";s:8:"filesize";i:5700;}s:12:"thumbnail@2x";a:5:{s:4:"file";s:29:"testImage-artdump-240x240.jpg";s:5:"width";i:240;s:6:"height";i:240;s:9:"mime-type";s:10:"image/jpeg";s:8:"filesize";i:14748;}s:9:"medium@2x";a:5:{s:4:"file";s:29:"testImage-artdump-640x504.jpg";s:5:"width";i:640;s:6:"height";i:504;s:9:"mime-type";s:10:"image/jpeg";s:8:"filesize";i:50334;}s:10:"middle_big";a:5:{s:4:"file";s:29:"testImage-artdump-740x583.jpg";s:5:"width";i:740;s:6:"height";i:583;s:9:"mime-type";s:10:"image/jpeg";s:8:"filesize";i:67719;}s:32:"twentyseventeen-thumbnail-avatar";a:5:{s:4:"file";s:29:"testImage-artdump-100x100.jpg";s:5:"width";i:100;s:6:"height";i:100;s:9:"mime-type";s:10:"image/jpeg";s:8:"filesize";i:7109;}}s:10:"image_meta";a:12:{s:8:"aperture";s:1:"0";s:6:"credit";s:0:"";s:6:"camera";s:0:"";s:7:"caption";s:0:"";s:17:"created_timestamp";s:1:"0";s:9:"copyright";s:0:"";s:12:"focal_length";s:1:"0";s:3:"iso";s:1:"0";s:13:"shutter_speed";s:1:"0";s:5:"title";s:22:"artdump-ogp-3-1200x630";s:11:"orientation";s:1:"0";s:8:"keywords";a:2:{i:0;s:7:"artDump";i:1;s:9:"testImage";}}}
1 改行テキストに変換
最初に、改行を入れてリスト状に変換します。改行を入れるのは以下の場所です。
1 開始波括弧 の後ろ
2 閉じ波括弧の前後
3 セミコロンの後ろ
Substitute ( $meta ; [ "{" ; "{" & ¶ ]; [ "}" ; ¶ & "}" & ¶ ]; [ ";" ; ";" & ¶ ] )
具体例のテキストデータは、以下のような改行テキストとなりました。
改行を入れることで何が書かれているか概ね理解できるようになりました。
a:6:{ s:5:"width"; i:1200; s:6:"height"; i:945; s:4:"file"; s:29:"2024/05/testImage-artdump.jpg"; s:8:"filesize"; i:203766; s:5:"sizes"; a:8:{ s:6:"medium"; a:5:{ s:4:"file"; s:29:"testImage-artdump-320x252.jpg"; s:5:"width"; i:320; s:6:"height"; i:252; s:9:"mime-type"; s:10:"image/jpeg"; s:8:"filesize"; i:18011; } s:5:"large"; a:5:{ s:4:"file"; s:30:"testImage-artdump-1024x806.jpg"; s:5:"width"; i:1024; s:6:"height"; i:806; s:9:"mime-type"; s:10:"image/jpeg"; s:8:"filesize"; i:130730; } s:9:"thumbnail"; a:5:{ s:4:"file"; s:29:"testImage-artdump-120x120.jpg"; s:5:"width"; i:120; s:6:"height"; i:120; s:9:"mime-type"; s:10:"image/jpeg"; s:8:"filesize"; i:7815; } s:4:"icon"; a:5:{ s:4:"file"; s:27:"testImage-artdump-60x60.jpg"; s:5:"width"; i:60; s:6:"height"; i:60; s:9:"mime-type"; s:10:"image/jpeg"; s:8:"filesize"; i:5700; } s:12:"thumbnail@2x"; a:5:{ s:4:"file"; s:29:"testImage-artdump-240x240.jpg"; s:5:"width"; i:240; s:6:"height"; i:240; s:9:"mime-type"; s:10:"image/jpeg"; s:8:"filesize"; i:14748; } s:9:"medium@2x"; a:5:{ s:4:"file"; s:29:"testImage-artdump-640x504.jpg"; s:5:"width"; i:640; s:6:"height"; i:504; s:9:"mime-type"; s:10:"image/jpeg"; s:8:"filesize"; i:50334; } s:10:"middle_big"; a:5:{ s:4:"file"; s:29:"testImage-artdump-740x583.jpg"; s:5:"width"; i:740; s:6:"height"; i:583; s:9:"mime-type"; s:10:"image/jpeg"; s:8:"filesize"; i:67719; } s:32:"twentyseventeen-thumbnail-avatar"; a:5:{ s:4:"file"; s:29:"testImage-artdump-100x100.jpg"; s:5:"width"; i:100; s:6:"height"; i:100; s:9:"mime-type"; s:10:"image/jpeg"; s:8:"filesize"; i:7109; } } s:10:"image_meta"; a:12:{ s:8:"aperture"; s:1:"0"; s:6:"credit"; s:0:""; s:6:"camera"; s:0:""; s:7:"caption"; s:0:""; s:17:"created_timestamp"; s:1:"0"; s:9:"copyright"; s:0:""; s:12:"focal_length"; s:1:"0"; s:3:"iso"; s:1:"0"; s:13:"shutter_speed"; s:1:"0"; s:5:"title"; s:22:"artdump-ogp-3-1200x630"; s:11:"orientation"; s:1:"0"; s:8:"keywords"; a:2:{ i:0; s:7:"artDump"; i:1; s:9:"testImage"; } } }
改行を入れただけで、そこそこ見て判るレベルになりました。目視で確認して手動でデータをコピーする程度の利用ならこれで十分かもしれません。でもまあ、実際には何万とあるattachmentを連続処理したいわけで、スクリプトは自分には必要です。
さて改行テキストを見て判るとおり、1行につき一つの項目があります。行の内容は、次のいずれかになります。
・配列開始の波括弧 {
・キー
・値
・配列終わりの閉じ波括弧 }
階層を持つデータもあります。
閉じ括弧以外のすべての行頭に、暗号ようなのラベルが付いています。この暗号を解析しつつ JSON 書式に直して新しいリストを作るのです。
改行されたテキストから1行ずつ取り出し、ループで処理していきます。
2 ループして行ごとに処理
最初にGetValue で行を抜き出し変数 $gv に収めます。これを処理していきます。
ついでに次の行も変数$nextに収めます。次の行が何であるかというのが大事だからです。実は「前の行の内容」も必要になるのですが、それは逆転の発想で、この行にいる間に「次の行の内容」を決定させます。
行の内容に応じて $gv を変形させ、書式をJSONに合わせます。
「この行」$gv が用意できたら、if で分岐しながら解析し整形していきます。この部分がスクリプトのメインで、詳細は章を改めます。
整形が完了すれば、行 $gv を新たな変数 $$meta2 にリストで追加していきます。(グローバル変数なのは作成途中に様子を見るためにそうしていた名残りにすぎません)$$meta2 が、JSON化の完成品です。
行を整形するループ内の処理
ループで「この行」$gv を処理します。最終的にやることは、内容に応じてJSON書式に整形することです。そのためには行の内容を知らないといけませんので、処理の中で内容を変数 $type にセットしていきます。
if で分岐しながら行を解析します。
- a: で始まる { の場合
- } の場合
- それ以外
それ以外がほとんどなわけですが、それ以外をさらに分岐して、内容がキーか値かによって整形したり次に来る内容 $type を決定したりします。
a: で始まる場合
if で left( $gv ; 2 ) = “a:” を条件にしています。
a: と数字が最初にある場合、配列が開始されることを示しています。a:6 なら、この後6個の項目が始まります。そして配列でありますから、a:x の直後に必ず開始の波括弧 { があります。
metadata 全体が配列(連想配列)ですから、1行目が a: で波括弧 { へと続くのは決定事項です。
それ以外に a: が出てきたら、一個下の階層の固まりということになります。その固まりが通常の連想配列(キーと値)なのか配列(値の羅列)なのかを見極め、配列だった場合にいろいろ仕込み作業を行います。
配列
さて普通の配列は [ ‘りんご’, ‘みかん’,’ざくろ’ ] のような形です。値が並んでるだけです。でも内部的には[0]からindexされています。metadata では、このindexが剥き出しになっていて、りんごみかんでいうと、こうなっております。
{i:0;'りんご';i:1;'みかん';i:2;'ざくろ'}
キーと値みたいに、インデックスと値のセットになってますね。
次が i:0 なら配列
次の行がもし i:0; であれば、それはindexであり配列であることが確定します。a: は配列の開始だから、次に来るのはインデックスの最初、即ち i:0; に決まっていますから。
確定したら仕込みを行います。
変数 $array を 1 にセット。配列にいますよーと記憶させます。配列開始の次に来るのは index と決まっています。変数 $type に “index” とセットします。ついでに index を数えましょう。変数 $i に 0 をセットしておきます。
この行 $gv は「 a:x{ 」という形です。これを変更します。もし配列が確定していれば、波括弧 { を 角括弧 [ に変更します。
次が i:0 ではない
さて次の行が i:0; でなかった場合、それは通常の連想配列(キーと値のセット)が開始されるということです。ということは、次の行の内容は必ずキーです。$type に “key” をセットします。
この行 $gv は「 a:x{ 」という形です。配列ではないので、$gv を { に変更します。
a: で始まる「配列の開始」行は、最終的に ただの { またはただの [ に変更されました。
閉じ波括弧の場合
行を抜き出した変数 $gv が } だった場合、それは配列の終わりを意味します。
$array が 1 なら
もし変数 $array が 1 ならこれまで配列だったのであり、開始が [ に変更されているので、こっちは ] に変更します。
配列が終わるのだから $array と $i の値を空にして変数を消します。
$array が 1 でないなら
$array が 1 でなければ、通常の連想配列の終わりです。波括弧 } のまま変更なしです。
続きがあれば最後にカンマを追加
閉じ括弧にはもう一つやっておくことがあります。
もしこれが最後の行なら } あるいは ] のままで良いですが、そうでないなら以降も続きがあります。続きがあるときは終点にカンマを加えなければなりません。が、もし次の行も閉じ括弧ならカンマを付けてはいけません。
最後の行でもない、次の行が } ではない場合にカンマ追加します。
波括弧以外の場合
この行 $gv が波括弧ではない場合です。ほとんどこれですね。ここからさらに条件分岐します。
$array が 1 なら
もし $array が 1 なら、現在の行は配列の中にいて、変数 $type に内容が入っている筈です。少なくとも $type には “index” がセットされています。配列の開始時にそうしていますから。
$type が index なら
$type が “index” なら変数 $i に数字が入っていて、この行「 i:x; 」の x は $i と等しいはずです。
index なら $gv を消します。この行を使いません。そして、ここがindexなんだから次の行の内容は必ず「値」であることが確定しています。$type に “value” をセットしておきます。
$type が value なら
$type が “value” ならそれは値です。冒頭は s: か i: か b: でしょう。あるいは a: である可能性もなくはないですが、ないです。wp_attachment_metada では配列の中にさらに配列が来ることはないはずです(もしあったらごめん。全部作り直しや💦 )
s: か i: かわからないが取りあえず行頭の暗号を取り除きます。: で改行し最後の行を抜き出します。
行が「 s:8:”pict_239″; 」なら「 “pict_239”; 」という形になります。
value の場合、最後の ; を変更するためにさらに分岐します。
次が } なら
ここで次の行を見ます。もし次の行が } なら、今の行は配列の最後の値です。これが配列の最後の値なら、次に来る内容は括弧を除くと必ずキーになる筈です。$type に “key” をセットします。
または、$i でインデックスをカウントしているので、配列開始の a:x のxを保存しておいて、それと $i が一致すれば最後の行と判断できます(スクリプトではこっちの判断を利用しています)
配列の最後なので、この行の最後はカンマもなにもつけません。
次が } でないなら
次が } でないなら $array が継続しており、まだ配列の中にいます。そうなると value 行の次の行は必ず index です。$type に “index” をセットします。
配列にいて次も継続するなら、最後にカンマを付けます。
$array ではない場合
$array が 1 でない場合です。この行 $gv は、キーか値かのいずれかですが、多分、$type に内容が入っている筈です。
その判断より前に、最初に行頭の暗号部分を消しておきます。コロン : で改行して最後の行を取得します。
!コロンでの改行に注意!
・・・コロンで改行すれば良いと、簡単に考えていたこともありました。でも違った。JSONの構文エラーになることがあり、原因を見つけるのにめちゃ苦労しました。ここにミスが潜んでいると思わなかったからです。
コロン “:” で迂闊に改行してはなりません。なぜなら、値に「 “0:14″」という時間の書式があり得るからです。コロンで改行したら結果が「 14″」になってしまいます!こんなのではエラーになります。
s:4: とか i: とか、この冒頭部分を消して中身だけにしたいとき、どうしましょう。あれこれドツボにハマりながらも最終的に得た回答はこうでした。
Let ( [ gv = $gv ; pc = PatternCount ( gv ; ":" ); posi = If ( pc > 1 ; Position ( gv ; ":" ; 1 ; 2 ); Position ( gv ; ":" ; 1 ; 1 ) ); rep = Replace ( gv ; posi ; 1 ; ¶ ); gv2 = GetValue ( rep ; 2 ) ]; gv2)
何をやってるかというと、まずコロンを数えます。暗号部分は、テキストの場合は s:14: 、数字の場合は i: ですね。つまり暗号としてのコロンの数は、最大2個、最小1個です。
ここに着目して、コロンが 1個の場合は1、1個より多い場合は2と判断します。時刻表示みたいのがあればコロンは3個になりますが、3個目は暗号部分ではないことが確実なので2個として勘定するんです。
そこで改行して最後の行をゲットします。
$type が “key” なら
$type が key なら、最後のセミコロン ; をコロン : に置換します。これがキーのお約束です。そして次の行の内容は必ず値ですから $type に “value” を入れておきます。
次の行に配列 a:x{ が来る可能性もありますが、もしそれが来たら分岐でそっちの処理が始まるので関係ないです。
$type が “value” なら
$type が value なら、次の行はキーです。$type に “key” を入れます。次の行は必ずしも key とは限らないのですが、問題ありません。
行最後の ; を処理します。これまでと同じく、次の行を見て、} なら配列の終わりですから何も付けずに ; を消すだけです。終わりでないならカンマに変更します。
この行を新しい変数のリストに加える
$gv が整形されたのでこれを新しい変数にリストで加えます。例えば $meta2 に List 関数で加えていきます。
List ( $$meta2 ; $gv )
ループをすべて回しきると、$$meta2 は完成したJSON です。フィールドに保存しておきましょう。
具体例のJSONはこうなりました。
{ "file" : "2024/05/testImage-artdump.jpg", "filesize" : 203766, "height" : 945, "image_meta" : { "aperture" : "0", "camera" : null, "caption" : null, "copyright" : null, "created_timestamp" : "0", "credit" : null, "focal_length" : "0", "iso" : "0", "keywords" : [ "artDump", "testImage" ], "orientation" : "0", "shutter_speed" : "0", "title" : "artdump-ogp-3-1200x630" }, "sizes" : { "icon" : { "file" : "testImage-artdump-60x60.jpg", "filesize" : 5700, "height" : 60, "mime-type" : "image/jpeg", "width" : 60 }, "large" : { "file" : "testImage-artdump-1024x806.jpg", "filesize" : 130730, "height" : 806, "mime-type" : "image/jpeg", "width" : 1024 }, "medium" : { "file" : "testImage-artdump-320x252.jpg", "filesize" : 18011, "height" : 252, "mime-type" : "image/jpeg", "width" : 320 }, "medium@2x" : { "file" : "testImage-artdump-640x504.jpg", "filesize" : 50334, "height" : 504, "mime-type" : "image/jpeg", "width" : 640 }, "middle_big" : { "file" : "testImage-artdump-740x583.jpg", "filesize" : 67719, "height" : 583, "mime-type" : "image/jpeg", "width" : 740 }, "thumbnail" : { "file" : "testImage-artdump-120x120.jpg", "filesize" : 7815, "height" : 120, "mime-type" : "image/jpeg", "width" : 120 }, "thumbnail@2x" : { "file" : "testImage-artdump-240x240.jpg", "filesize" : 14748, "height" : 240, "mime-type" : "image/jpeg", "width" : 240 }, "twentyseventeen-thumbnail-avatar" : { "file" : "testImage-artdump-100x100.jpg", "filesize" : 7109, "height" : 100, "mime-type" : "image/jpeg", "width" : 100 } }, "width" : 1200 }
データの取り出し方
JSON として保存したデータを取り出します。
width や height といったキーを指定して取り出すには JsonGetElement を使います。
JsonGetElement ( json ; "width" ) JsonGetElement ( json ; "height" )
アップしたメディアのサイズをさくっと取得できます。
sizes というのがあって、これは WordPress で設定するメディアサイズです。サムネイル、中サイズ、大サイズって指定しますよね。あれです。カスタムしてサイズを追加している人もおられましょう。それがリストされます。
どんな名前のサイズが作られているかを確認するには、JsonListKeys を使えば良いですね。
JsonListKeys ( json ; "sizes" )
具体例のメディアでは次のように返ってきました。
icon large medium medium@2x middle_big thumbnail thumbnail@2x twentyseventeen-thumbnail-avatar
この値と、実際に function や設定画面で設定したサイズに違いが生じていると、ブラウザ上で画像が上手く表示されないかもしれません。そういうエラー探しに使えるかもしれません。
メタデータというだけあってメタデータもいくつか取得できます。キーワードもあるので、アップした画像にうっかりキーワードを残したままかどうかも簡単に見極められます。
JSONGetElement ( localData::json ; "image_meta.keywords")
メタデータは “image_meta” 以下にあります。
そんなわけで、attachment の _wp_attachment_metadata を JSON に変換するスクリプトでした。