Konu

PHP ve Laravel'de OG Görselleri

Bu kılavuz, FastOG ile PHP ve Laravel'de OG görsellerini kapsar — backend'inizde bir HMAC URL'si imzalayın, meta etiketine ekleyin ve blog yazılarınız otomatik olarak dinamik Open Graph görselleri kazanır. Ücretsiz hesap ile abonelik tabanlıdır (Dynamic API planını seçin; API anahtarınızı panelde oluşturun); bağlamadan önce bir şablonu önizlemek için Ücretsiz OG Görsel Test Aracı'nı deneyin.

PHP ve Laravel'de HMAC imzalı OG görsel URL'lerini uygulamak için eksiksiz kılavuz.

Hızlı Başlangıç

1. İmzalayıcı servisini oluşturun

php
// app/Services/QuickogSigner.php
namespace App\Services;

class QuickogSigner
{
    public function generateSignedOgUrl(array $params, array $options = []): string
    {
        $baseUrl = $options['base_url'] ?? config('services.fastog.base_url', 'https://fastog.com/api/v1/og');
        $apiKey = $options['api_key'] ?? config('services.fastog.api_key');
        $hmacSecret = $options['hmac_secret'] ?? config('services.fastog.hmac_secret');

        if (!$apiKey || !$hmacSecret) {
            throw new \RuntimeException('Missing FASTOG_API_KEY or FASTOG_HMAC_SECRET');
        }

        // Canonical query: every param except `s`, including `key`
        $all = array_merge($params, ['key' => $apiKey]);

        $pairs = [];
        foreach ($all as $k => $v) {
            $values = is_array($v) ? $v : [$v];
            sort($values, SORT_STRING);
            foreach ($values as $vv) {
                $pairs[] = rawurlencode((string) $k) . '=' . rawurlencode((string) $vv);
            }
        }
        sort($pairs, SORT_STRING);
        $canonical = implode('&', $pairs);
        $signature = hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret);

        return $baseUrl . '?' . http_build_query(array_merge($all, ['s' => $signature]), '', '&', PHP_QUERY_RFC3986);
    }
}

Neden rawurlencode? Kanonik sorgu, RFC-3986'ya göre yüzde kodlamalıdır (boşluklar → %20, asla + değil). rawurlencode bununla tam olarak eşleşir — urlencode eşleşmez (+ üretir), bu da imzayı bozar ve invalid_signature döndürür. PHP_QUERY_RFC3986 değerini http_build_query'a iletmek, son URL'yi aynı kodlamada tutar.

2. Yapılandırma

php
// config/services.php
return [
    // ...
    'fastog' => [
        'api_key' => env('FASTOG_API_KEY'),
        'hmac_secret' => env('FASTOG_HMAC_SECRET'),
        'base_url' => env('FASTOG_BASE_URL', 'https://fastog.com/api/v1/og'),
    ],
];
bash
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here

3. Singleton olarak kaydedin

php
// app/Providers/AppServiceProvider.php
use App\Services\QuickogSigner;

public function register(): void
{
    $this->app->singleton(QuickogSigner::class);
}

Laravel Blade

blade
{{-- resources/views/blog/show.blade.php --}}
@extends('layouts.app')

@section('head')
    <meta property="og:image" content="{{ $ogImageUrl }}" />
    <meta property="og:title" content="{{ $post->title }}" />
    <meta property="og:description" content="{{ $post->excerpt }}" />
@endsection

@section('content')
    <article>
        <h1>{{ $post->title }}</h1>
        <p>{{ $post->excerpt }}</p>
    </article>
@endsection

Denetleyici (Controller)

php
// app/Http/Controllers/BlogController.php
namespace App\Http\Controllers;

use App\Services\QuickogSigner;
use App\Models\Post;

class BlogController extends Controller
{
    public function show(Post $post, QuickogSigner $signer)
    {
        $ogImageUrl = $signer->generateSignedOgUrl([
            'template' => 'blog',
            'title' => $post->title,
            'subtitle' => $post->excerpt,
            'author' => $post->author->name,
        ]);

        return view('blog.show', compact('post', 'ogImageUrl'));
    }
}

API Yolu

php
// routes/api.php
use App\Services\QuickogSigner;
use Illuminate\Http\Request;

Route::get('/og', function (Request $request, QuickogSigner $signer) {
    $validated = $request->validate([
        'title' => 'required|string',
        'template' => 'nullable|string',
    ]);

    $ogImageUrl = $signer->generateSignedOgUrl([
        'template' => $validated['template'] ?? 'default',
        'title' => $validated['title'],
    ]);

    return response()->json(['ogImageUrl' => $ogImageUrl]);
});

E-Ticaret Örneği

php
// app/Http/Controllers/ProductController.php
public function show(Product $product, QuickogSigner $signer)
{
    $ogImageUrl = $signer->generateSignedOgUrl([
        'template' => 'ecommerce',
        'title' => $product->name,
        'price' => $product->formatted_price,
        'image' => $product->image_url,
    ]);

    return view('products.show', compact('product', 'ogImageUrl'));
}

Blog Yazısı Örneği

php
// app/Http/Controllers/ProfileController.php
public function show(User $user, QuickogSigner $signer)
{
    $ogImageUrl = $signer->generateSignedOgUrl([
        'template' => 'blog',
        'title' => $user->name,
        'subtitle' => $user->bio,
        'author' => $user->name,
    ]);

    return view('profile.show', compact('user', 'ogImageUrl'));
}

Düz PHP (Framework Yok)

php
// lib/fastog-signer.php
function generateSignedOgUrl(array $params, array $options = []): string
{
    $baseUrl = $options['base_url'] ?? 'https://fastog.com/api/v1/og';
    $apiKey = $options['api_key'] ?? $_ENV['FASTOG_API_KEY'];
    $hmacSecret = $options['hmac_secret'] ?? $_ENV['FASTOG_HMAC_SECRET'];

    $all = array_merge($params, ['key' => $apiKey]);

    $pairs = [];
    foreach ($all as $k => $v) {
        $values = is_array($v) ? $v : [$v];
        sort($values, SORT_STRING);
        foreach ($values as $vv) {
            $pairs[] = rawurlencode((string) $k) . '=' . rawurlencode((string) $vv);
        }
    }
    sort($pairs, SORT_STRING);
    $canonical = implode('&', $pairs);
    $signature = hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret);

    return $baseUrl . '?' . http_build_query(array_merge($all, ['s' => $signature]), '', '&', PHP_QUERY_RFC3986);
}
php
// index.php
require_once 'lib/fastog-signer.php';

$ogUrl = generateSignedOgUrl([
    'template' => 'blog',
    'title' => 'My Blog Post',
]);
?>
<!DOCTYPE html>
<html>
<head>
    <meta property="og:image" content="<?= htmlspecialchars($ogUrl) ?>" />
    <meta property="og:title" content="My Blog Post" />
</head>
<body>
    <h1>My Blog Post</h1>
</body>
</html>

Sık Karşılaşılan Hatalar

Yanlış Mesajın İmzalanması

❌ Yanlış: hash_hmac('sha256', 'GET/api/v1/og' . $canonical, $hmacSecret) — yeni satır ayracı eksik. ✅ Doğru: hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret) — yöntem + yol, bir yeni satır ve ardından kanonik sorgu.

key Değerinin İmzadan Çıkarılması

❌ Yanlış: yalnızca içerik parametrelerinizi imzalamak ve key değerini sonradan URL'ye eklemek. ✅ Doğru: key, kanonik sorgu oluşturulmadan önce parametrelere dahil edilir ve son URL'de görünür.

Tekrarlanan Parametreler (Diziler)

Dizi değerlerini generateSignedOgUrl işlevine iletin ve URL'yi http_build_query yazsın: köşeli parantezli anahtarlar (features[0]=a&features[1]=b) üretirken, kanonik imza düz tekrarlanan anahtarları (features=a&features=b) kullanır. URL'de features=a&features=b değerini elle oluşturmayın — PHP tekrarlanan anahtarları bir skaler değere dönüştürür (son değer kazanır), bu nedenle sunucu farklı bir kanonik yeniden oluşturur ve invalid_signature ile reddeder.

401 missing_signature / invalid_signature

missing_signature (401), URL'de key veya s eksik olduğu anlamına gelir. invalid_signature (401), kanoniğin eşleşmediği anlamına gelir — imzalanan mesajdaki yeni satırı, her iki tarafta da aynı hmac_secret değerini, her anahtar ve değer için rawurlencode (urlencode değil) kullanıldığını ve URL'de tekrarlanan parametreler için köşeli parantezli anahtarların kullanıldığını doğrulayın.

HMAC Gizli Anahtarının Açığa Çıkarılması

FASTOG_HMAC_SECRET değerini asla ön uç JavaScript'inde veya genel yapılandırma dosyalarında açığa çıkarmayın.

Tam API Referansı

Ücretsiz deneyin — kayıt gerekmez

Dinamik OG görsellerini saniyeler içinde önizleyin ve test edin.

Ücretsiz OG Görsel Test Aracını Aç

A BeCaSimulations site