PHP の画像処理コードは、ほとんどがレイアウトのコードです。GD や Imagick では、デザイン上の判断がすべて関数呼び出しになります。
$image = imagecreatetruecolor(1200, 630);
$blue = imagecolorallocate($image, 37, 99, 235);
imagefilledrectangle($image, 0, 0, 1200, 630, $blue);
imagettftext($image, 48, 0, 80, 300, $white, 'Inter-Bold.ttf', 'Summer sale: 30% off');
最初のうちは問題なく動きます。ところが、見出しを 2 行にしたい、価格に影をつけたい、隅に QR コードを置きたいと言われた時点で、デザインが PHP の中に入り込んでしまいます。細かな調整のたびにコードを変更してデプロイすることになります。
「自分で描く」と「ヘッドレスブラウザを大量に動かす」の間には、もうひとつ選択肢があります。レイアウトはテンプレートに置いておき、PHP からは画像生成 APIにデータだけを送る方法です。
API を使った場合の流れ
- エディタでバナーを一度デザインします。ギャラリーのテンプレートを複製してもかまいません。
- あとで差し替えたいレイヤーに、
headline、price、photoのような名前をつけます。 - テンプレート ID と値を API に送ります。レスポンスが完成した画像です。
フォント、余白、グラデーション、影、テーブル、QR コードはテンプレート側に残ります。デザイナーは PHP に触れずにこれらを変更でき、PHP はデータの受け渡しという得意な仕事に専念できます。
必要なもの
- API キー。 ダッシュボード → APIキー で作成し、サーバー上で管理します。データベースのパスワードと同じように扱ってください。
- テンプレート ID。 デザインしたテンプレートを開いて、その ID をコピーします。
tpl_kd93mzのような形です。 - レイヤー名。 エディタでレイヤーにつけた名前が、そのままリクエストで使う名前になります。大文字小文字も含めて一致させてください。
方法 1:素の cURL
依存パッケージは不要で、どの PHP ホスティングでも動きます。拡張機能のインストールに苦労する環境でも使えます。
<?php
$payload = [
'apiKey' => getenv('BANNERIFY_API_KEY'),
'templateId' => 'tpl_kd93mz',
'format' => 'png',
'modifications' => [
['name' => 'headline', 'text' => 'Summer sale: 30% off'],
['name' => 'price', 'text' => '$149.00'],
['name' => 'photo', 'src' => 'https://example.com/sneaker.jpg'],
],
];
$ch = curl_init('https://api.bannerify.co/v1/templates/createImage');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_TIMEOUT => 30,
]);
$image = curl_exec($ch);
if ($image === false) {
throw new RuntimeException('Transport error: ' . curl_error($ch));
}
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("Bannerify returned {$status}: " . substr($image, 0, 200));
}
file_put_contents(__DIR__ . '/banner.png', $image);
つまずきやすい点が 2 つあります。
- レスポンスの本文は画像そのものです。 base64 が入った JSON ではありません。そのままファイルに書き込むか、ブラウザにストリーミングしてください。
- エラーは 4xx ステータスの JSON で返ります。 上のコードが保存の前にステータスコードを確認しているのは、そのためです。
方法 2:公式の PHP SDK
リクエストを自分で組み立てたくない場合は、SDK をインストールします。
composer require bannerify/bannerify
SDK は result か error のどちらか一方だけを返します。失敗時の処理を書き忘れる心配がありません。
<?php
use Bannerify\Bannerify\BannerifyClient;
$client = new BannerifyClient(getenv('BANNERIFY_API_KEY'));
$result = $client->createImage('tpl_kd93mz', [
'modifications' => [
['name' => 'headline', 'text' => $product['name']],
['name' => 'price', 'text' => '$' . number_format($product['price'], 2)],
['name' => 'photo', 'src' => $product['image_url']],
],
]);
if (isset($result['error'])) {
error_log('Render failed: ' . $result['error']['message']);
return null;
}
$path = __DIR__ . "/banners/{$product['sku']}.png";
file_put_contents($path, $result['result']);
メソッドの一覧とオプションは、PHP SDK のドキュメントにあります。
実際のデータを送る
modifications の配列を手で組み立てる必要はありません。API は、キーをレイヤー名にしたオブジェクト形式の省略記法も受け付けます。
'modifications' => [
'headline' => $product['name'],
'price' => '$' . number_format($product['price'], 2),
'photo' => ['src' => $product['image_url']],
'qr' => ['qrcode' => $product['checkout_url']],
],
単純な値はテキストとして扱われます。配列をネストすると、それぞれのフィールドがそのまま渡されます。画像、QR コード、バーコード、グラフ、テーブルにはこの形を使います。
レイヤーごとに送れるフィールドは次のとおりです。
textはテキストレイヤーの内容を置き換えますcolorはレイヤーの色を上書きします(例:#FF0000)srcは画像レイヤーを差し替えます。外部から取得できる HTTPS の URL を指定しますqrcodeとbarcodeは、リンク、SKU、追跡番号をエンコードしますrowsとcolumnsはグラフとテーブルにデータを入れますvisibleは、その 1 回の生成に限ってレイヤーを非表示にしますstarは評価の値を設定します
1 つのレイヤーには 1 つのフィールドを、名前を指定して送ります。同じデータを 2 つのレイヤーに入れたい場合は、2 回送ってください。
バッチ処理とキャッシュ
1,000 枚の画像を生成するのも、特別なことではなく、ただのループです。
foreach ($products as $product) {
$payload = buildPayload($product);
$cacheKey = hash('xxh128', json_encode($payload));
$path = __DIR__ . "/cache/{$cacheKey}.png";
if (file_exists($path)) {
continue;
}
file_put_contents($path, renderBanner($payload));
}
ループは上限を決め(同時リクエストは数件まで)、キャッシュのキーにはペイロードを使います。そうすれば、同じリクエストに二度支払うことはありません。生成は同期的に行われるので、ジョブキューをポーリングせず、同じリクエストの中でファイルを受け取れます。
ファイルをまったく保存したくない場合は、代わりに createStoredImage を使います。生成した画像をアップロードして、ホスティングされた URL を返します。
{ "url": "https://cdn.bannerify.co/..." }
OG画像、メールのヘッダー、そのほかファイルより URL のほうが扱いやすい場面では、この方法が便利です。
フォーマットとサイズ
formatにはpng(デフォルト)、jpeg、webpを指定できます。- PDF が必要な場合は、同じテンプレートを
createPdfで PDF にできます。詳しくは PDF 生成 API のページを、PHP のコードは請求書 PDF のガイドをご覧ください。 - キャンバスのサイズはリクエストではなくテンプレートで決まります。必要なサイズごとにテンプレートを 1 つ用意してください。リンクプレビューなら 1200x630、フィードなら 1080x1080、ストーリーズなら 1080x1920 です。
- 写真を
srcで送る場合、レンダラーがその URL を取得しに行くため、公開されている必要があります。ローカルのファイルやプライベートなバケットでは失敗します。
よくある間違い
- API キーをブラウザの JavaScript やモバイルアプリに入れてしまう。API キーはサーバーに置いてください。
- 何も変わっていないのに画像を作り直す。ペイロードのハッシュでキャッシュしてください。
- 1 回の Web リクエストの中で数千枚の画像を生成する。数十枚なら動きますが、数千枚ならキューを使ってください。
- レスポンスが JSON だと思い込む。stored エンドポイントを使う場合を除き、レスポンスはファイルそのものです。
料金
無料プランには、月 100 回(1 日 10 回まで)の生成、テンプレート 1 つ、1GB の画像保存容量が含まれています。実際のアプリに組み込んで試すには十分です。Automate は月 $29 から始まり、3,000 回の生成とテンプレート無制限が含まれます。詳しくは料金をご覧ください。
いま PHP で画像を生成しているなら、無料プランで始めて、まず 1 つのテンプレートを GD から移してみてください。レイアウトが崩れずに最初の画像が返ってきたとき、多くの人は位置調整のコードを書くのをやめます。