WordPress Eklentisinde OpenAI API Kullanımı

WordPress ile bir proje geliştirirken “bunu yapay zekaya bağlasam harika olurdu” diye düşündüğünüz oldu mu? Ben bu fikri ilk kez bir müşteri için içerik üretim aracı geliştirirken yaşadım. O günden bu yana WordPress eklentilerine OpenAI entegrasyonu yapmak hem en çok zevk aldığım hem de en çok soru aldığım konulardan biri haline geldi. Bu yazıda sıfırdan başlayıp production’a hazır bir WordPress eklentisinde OpenAI API’yi nasıl kullanacağınızı adım adım anlatacağım.

Ortamı Hazırlamak

Başlamadan önce birkaç şeyin hazır olması gerekiyor. OpenAI hesabınızın aktif olması ve API key almanız şart. Bunun yanında local bir WordPress kurulumu da olmalı. Ben genellikle Local by Flywheel kullanıyorum ama Docker veya XAMPP de işinizi görür.

WordPress eklenti geliştirme için dosya yapısını şöyle kuruyorum:

mkdir -p /var/www/html/wp-content/plugins/oai-helper
cd /var/www/html/wp-content/plugins/oai-helper
touch oai-helper.php
mkdir -p includes assets/js assets/css

API key’inizi asla kod içine gömmemelisiniz. Bunu WordPress seçenekler tablosunda şifrelenmiş olarak saklamak en sağlıklısı. Ancak production ortamında wp-config.php içinde sabit olarak tanımlamak daha güvenli bir yaklaşım:

# wp-config.php dosyasına eklenecek satır
# nano /var/www/html/wp-config.php

define('OPENAI_API_KEY', 'sk-proj-xxxxxxxxxxxxxxxxxxxxxxxx');
define('OPENAI_ORG_ID', 'org-xxxxxxxxxxxxxxxx'); # opsiyonel

Bu yöntemle key dosya sisteminde kalır ve veritabanına gitmez. Git reponuza yanlışlıkla commit etme riskiniz de ortadan kalkar.

Temel Eklenti Yapısı

Ana eklenti dosyasını oluşturalım. Bu dosya WordPress’e eklentimizi tanıtır ve tüm bileşenleri yükler:

cat > /var/www/html/wp-content/plugins/oai-helper/oai-helper.php << 'EOF'
<?php
/**
 * Plugin Name: OAI Helper
 * Plugin URI: https://example.com
 * Description: OpenAI entegrasyonlu içerik asistanı
 * Version: 1.0.0
 * Author: Sysadmin Blog
 * License: GPL2
 */

if (!defined('ABSPATH')) {
    exit;
}

define('OAI_HELPER_VERSION', '1.0.0');
define('OAI_HELPER_PATH', plugin_dir_path(__FILE__));
define('OAI_HELPER_URL', plugin_dir_url(__FILE__));

// API key önce constant'tan, yoksa DB'den al
function oai_get_api_key() {
    if (defined('OPENAI_API_KEY')) {
        return OPENAI_API_KEY;
    }
    return get_option('oai_helper_api_key', '');
}

require_once OAI_HELPER_PATH . 'includes/class-oai-client.php';
require_once OAI_HELPER_PATH . 'includes/class-oai-admin.php';
require_once OAI_HELPER_PATH . 'includes/class-oai-shortcode.php';

register_activation_hook(__FILE__, 'oai_helper_activate');
function oai_helper_activate() {
    add_option('oai_helper_api_key', '');
    add_option('oai_helper_model', 'gpt-4o-mini');
    add_option('oai_helper_max_tokens', 1000);
}
EOF

API İstemci Sınıfı

Şimdi işin özüne gelelim. OpenAI ile iletişim kuracak sınıfı yazıyoruz. WordPress’te harici HTTP istekleri için wp_remote_post() kullanmak en doğrusu çünkü hem proxy ayarlarını hem de SSL sertifika sorunlarını otomatik olarak yönetiyor:

cat > /var/www/html/wp-content/plugins/oai-helper/includes/class-oai-client.php << 'EOF'
<?php

class OAI_Client {

    private $api_key;
    private $base_url = 'https://api.openai.com/v1';
    private $model;
    private $max_tokens;

    public function __construct() {
        $this->api_key    = oai_get_api_key();
        $this->model      = get_option('oai_helper_model', 'gpt-4o-mini');
        $this->max_tokens = (int) get_option('oai_helper_max_tokens', 1000);
    }

    public function chat_completion($messages, $options = []) {
        if (empty($this->api_key)) {
            return new WP_Error('no_api_key', 'API key tanımlı değil');
        }

        $body = array_merge([
            'model'       => $this->model,
            'messages'    => $messages,
            'max_tokens'  => $this->max_tokens,
            'temperature' => 0.7,
        ], $options);

        $response = wp_remote_post($this->base_url . '/chat/completions', [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type'  => 'application/json',
            ],
            'body'    => json_encode($body),
            'timeout' => 60,
        ]);

        if (is_wp_error($response)) {
            return $response;
        }

        $status = wp_remote_retrieve_response_code($response);
        $data   = json_decode(wp_remote_retrieve_body($response), true);

        if ($status !== 200) {
            $msg = $data['error']['message'] ?? 'Bilinmeyen API hatası';
            return new WP_Error('api_error', $msg, ['status' => $status]);
        }

        return $data['choices'][0]['message']['content'] ?? '';
    }

    public function generate_image($prompt, $size = '1024x1024') {
        $response = wp_remote_post($this->base_url . '/images/generations', [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type'  => 'application/json',
            ],
            'body' => json_encode([
                'model'  => 'dall-e-3',
                'prompt' => $prompt,
                'n'      => 1,
                'size'   => $size,
            ]),
            'timeout' => 90,
        ]);

        if (is_wp_error($response)) {
            return $response;
        }

        $data = json_decode(wp_remote_retrieve_body($response), true);
        return $data['data'][0]['url'] ?? new WP_Error('no_image', 'Görsel üretilemedi');
    }
}
EOF

Burada dikkat edilmesi gereken birkaç nokta var. timeout değerini 60 saniye veriyorum çünkü GPT-4 modelleri bazen uzun yanıtlar üretirken gecikebiliyor. DALL-E için ise 90 saniye gerekebiliyor. Varsayılan WordPress timeout olan 5 saniye ile bu işlemleri yapamazsınız.

Admin Paneli ve Ayarlar

Eklentinin yönetim arayüzünü oluşturalım. Buraya API key ayarı, model seçimi ve test aracı koyacağız:

cat > /var/www/html/wp-content/plugins/oai-helper/includes/class-oai-admin.php << 'EOF'
<?php

class OAI_Admin {

    public function __construct() {
        add_action('admin_menu', [$this, 'add_menu']);
        add_action('admin_init', [$this, 'register_settings']);
        add_action('wp_ajax_oai_test_connection', [$this, 'ajax_test_connection']);
        add_action('wp_ajax_oai_generate_content', [$this, 'ajax_generate_content']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
    }

    public function add_menu() {
        add_options_page(
            'OAI Helper Ayarları',
            'OAI Helper',
            'manage_options',
            'oai-helper',
            [$this, 'settings_page']
        );
    }

    public function register_settings() {
        register_setting('oai_helper_group', 'oai_helper_api_key', [
            'sanitize_callback' => 'sanitize_text_field',
        ]);
        register_setting('oai_helper_group', 'oai_helper_model', [
            'sanitize_callback' => 'sanitize_text_field',
        ]);
        register_setting('oai_helper_group', 'oai_helper_max_tokens', [
            'sanitize_callback' => 'absint',
        ]);
    }

    public function ajax_test_connection() {
        check_ajax_referer('oai_helper_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_die('Yetersiz yetki');
        }

        $client = new OAI_Client();
        $result = $client->chat_completion([
            ['role' => 'user', 'content' => 'Merhaba, bağlantı testi. Tek kelime yanıt ver.']
        ]);

        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        wp_send_json_success(['message' => 'Bağlantı başarılı: ' . $result]);
    }

    public function ajax_generate_content() {
        check_ajax_referer('oai_helper_nonce', 'nonce');

        if (!current_user_can('edit_posts')) {
            wp_die('Yetersiz yetki');
        }

        $prompt = sanitize_textarea_field($_POST['prompt'] ?? '');
        $type   = sanitize_text_field($_POST['content_type'] ?? 'general');

        if (empty($prompt)) {
            wp_send_json_error(['message' => 'Prompt boş olamaz']);
        }

        $system_prompts = [
            'blog'    => 'Sen deneyimli bir Türkçe blog yazarısın. SEO uyumlu, akıcı içerikler yazıyorsun.',
            'product' => 'Sen e-ticaret ürün açıklaması uzmanısın. Satışa yönlendiren, detaylı açıklamalar yazıyorsun.',
            'general' => 'Sen yardımcı bir asistansın. Türkçe yanıt veriyorsun.',
        ];

        $client = new OAI_Client();
        $result = $client->chat_completion([
            ['role' => 'system', 'content' => $system_prompts[$type] ?? $system_prompts['general']],
            ['role' => 'user', 'content' => $prompt],
        ]);

        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        wp_send_json_success(['content' => $result]);
    }

    public function enqueue_scripts($hook) {
        if ($hook !== 'settings_page_oai-helper') {
            return;
        }
        wp_enqueue_script(
            'oai-helper-admin',
            OAI_HELPER_URL . 'assets/js/admin.js',
            ['jquery'],
            OAI_HELPER_VERSION,
            true
        );
        wp_localize_script('oai-helper-admin', 'oaiHelper', [
            'nonce'   => wp_create_nonce('oai_helper_nonce'),
            'ajaxUrl' => admin_url('admin-ajax.php'),
        ]);
    }

    public function settings_page() {
        // Ayarlar sayfası HTML'i buraya gelecek
        include OAI_HELPER_PATH . 'includes/views/settings.php';
    }
}

new OAI_Admin();
EOF

Shortcode Sistemi ile Frontend Entegrasyonu

Eklentinin gerçek dünya kullanımında en pratik özellik shortcode desteği. Bir müşteri için geliştirdiğim senaryoda, ziyaretçilerin doğrudan sayfada AI destekli içerik oluşturmasını sağladım:

cat > /var/www/html/wp-content/plugins/oai-helper/includes/class-oai-shortcode.php << 'EOF'
<?php

class OAI_Shortcode {

    public function __construct() {
        add_shortcode('oai_chat', [$this, 'render_chat']);
        add_shortcode('oai_summary', [$this, 'render_summary']);
        add_action('wp_ajax_nopriv_oai_public_query', [$this, 'handle_public_query']);
        add_action('wp_ajax_oai_public_query', [$this, 'handle_public_query']);
        add_action('wp_enqueue_scripts', [$this, 'enqueue_frontend_scripts']);
    }

    public function render_chat($atts) {
        $atts = shortcode_atts([
            'placeholder' => 'Sorunuzu yazın...',
            'button_text' => 'Sor',
            'max_chars'   => 500,
        ], $atts);

        ob_start();
        ?>
        <div class="oai-chat-widget" data-maxchars="<?php echo esc_attr($atts['max_chars']); ?>">
            <textarea class="oai-input" placeholder="<?php echo esc_attr($atts['placeholder']); ?>"></textarea>
            <button class="oai-submit"><?php echo esc_html($atts['button_text']); ?></button>
            <div class="oai-response" style="display:none;"></div>
            <input type="hidden" class="oai-nonce" value="<?php echo wp_create_nonce('oai_public_nonce'); ?>">
        </div>
        <?php
        return ob_get_clean();
    }

    public function render_summary($atts) {
        $atts = shortcode_atts([
            'post_id' => get_the_ID(),
            'length'  => 'kısa',
        ], $atts);

        $post = get_post($atts['post_id']);
        if (!$post) {
            return '<p>İçerik bulunamadı.</p>';
        }

        $cache_key = 'oai_summary_' . $post->ID . '_' . $atts['length'];
        $cached    = get_transient($cache_key);

        if ($cached !== false) {
            return '<div class="oai-summary">' . wp_kses_post($cached) . '</div>';
        }

        $client  = new OAI_Client();
        $content = wp_strip_all_tags($post->post_content);
        $content = mb_substr($content, 0, 3000); // Token limitini aşmamak için

        $result = $client->chat_completion([
            ['role' => 'system', 'content' => 'Verilen makaleyi ' . $atts['length'] . ' bir şekilde Türkçe özetle.'],
            ['role' => 'user', 'content' => $content],
        ]);

        if (is_wp_error($result)) {
            return '<p>Özet oluşturulamadı.</p>';
        }

        // 24 saat önbellekle
        set_transient($cache_key, $result, DAY_IN_SECONDS);

        return '<div class="oai-summary">' . wp_kses_post($result) . '</div>';
    }

    public function handle_public_query() {
        check_ajax_referer('oai_public_nonce', 'nonce');

        // Rate limiting: aynı IP'den 1 dakikada 5 istek
        $ip       = sanitize_text_field($_SERVER['REMOTE_ADDR']);
        $rate_key = 'oai_rate_' . md5($ip);
        $count    = (int) get_transient($rate_key);

        if ($count >= 5) {
            wp_send_json_error(['message' => 'Çok fazla istek gönderdiniz. Lütfen bekleyin.']);
        }

        set_transient($rate_key, $count + 1, MINUTE_IN_SECONDS);

        $query  = sanitize_textarea_field($_POST['query'] ?? '');
        $client = new OAI_Client();
        $result = $client->chat_completion([
            ['role' => 'system', 'content' => 'Kısa ve öz Türkçe yanıtlar ver. Maksimum 3 paragraf.'],
            ['role' => 'user', 'content' => $query],
        ]);

        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        wp_send_json_success(['response' => $result]);
    }

    public function enqueue_frontend_scripts() {
        if (!has_shortcode(get_post()->post_content ?? '', 'oai_chat')) {
            return;
        }
        wp_enqueue_script(
            'oai-frontend',
            OAI_HELPER_URL . 'assets/js/frontend.js',
            ['jquery'],
            OAI_HELPER_VERSION,
            true
        );
    }
}

new OAI_Shortcode();
EOF

Frontend JavaScript

Shortcode’un çalışması için gerekli JS dosyasını oluşturalım:

cat > /var/www/html/wp-content/plugins/oai-helper/assets/js/frontend.js << 'EOF'
jQuery(document).ready(function($) {
    $('.oai-chat-widget').each(function() {
        var widget   = $(this);
        var maxChars = parseInt(widget.data('maxchars')) || 500;

        widget.find('.oai-submit').on('click', function() {
            var query = widget.find('.oai-input').val().trim();
            var nonce = widget.find('.oai-nonce').val();

            if (!query) {
                alert('Lütfen bir soru yazın.');
                return;
            }

            if (query.length > maxChars) {
                alert('Maksimum ' + maxChars + ' karakter girebilirsiniz.');
                return;
            }

            var btn = $(this);
            btn.prop('disabled', true).text('Yanıt bekleniyor...');
            widget.find('.oai-response').hide();

            $.ajax({
                url: oaiHelper.ajaxUrl,
                method: 'POST',
                data: {
                    action: 'oai_public_query',
                    nonce: nonce,
                    query: query,
                },
                success: function(res) {
                    if (res.success) {
                        widget.find('.oai-response')
                              .html('<p>' + res.data.response.replace(/n/g, '<br>') + '</p>')
                              .show();
                    } else {
                        widget.find('.oai-response')
                              .html('<p class="oai-error">' + res.data.message + '</p>')
                              .show();
                    }
                },
                error: function() {
                    widget.find('.oai-response')
                          .html('<p class="oai-error">Bir hata oluştu. Tekrar deneyin.</p>')
                          .show();
                },
                complete: function() {
                    btn.prop('disabled', false).text('Sor');
                }
            });
        });
    });
});
EOF

Maliyet Kontrolü ve Önbellekleme Stratejisi

Production ortamında en önemli konu OpenAI maliyetlerini kontrol altında tutmak. Aynı soruya defalarca API çağrısı yapmak hem yavaş hem de pahalı. İşte pratik bir önbellekleme yaklaşımı:

cat > /var/www/html/wp-content/plugins/oai-helper/includes/class-oai-cache.php << 'EOF'
<?php

class OAI_Cache {

    private static $instance = null;
    private $ttl;

    private function __construct() {
        $this->ttl = (int) get_option('oai_cache_ttl', HOUR_IN_SECONDS * 6);
    }

    public static function get_instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function get_or_generate($messages, $callable, $options = []) {
        $cache_key = 'oai_' . md5(json_encode($messages) . json_encode($options));
        $cached    = get_transient($cache_key);

        if ($cached !== false) {
            return $cached;
        }

        $result = call_user_func($callable);

        if (!is_wp_error($result)) {
            set_transient($cache_key, $result, $this->ttl);
        }

        return $result;
    }

    public function clear_all() {
        global $wpdb;
        $wpdb->query(
            "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_oai_%' 
             OR option_name LIKE '_transient_timeout_oai_%'"
        );
        return true;
    }

    public function get_usage_stats() {
        global $wpdb;
        $count = $wpdb->get_var(
            "SELECT COUNT(*) FROM {$wpdb->options} 
             WHERE option_name LIKE '_transient_oai_%' 
             AND option_name NOT LIKE '_transient_timeout_%'"
        );
        return [
            'cached_responses' => (int) $count,
            'estimated_savings' => (int) $count . ' API çağrısı önbellekten karşılandı',
        ];
    }
}
EOF

Hata Takibi ve Loglama

Canlı ortamda ne oluyor bilmeden sistemi yönetemezsiniz. Basit ama etkili bir log sistemi şart:

# WordPress debug logunu aktif et (wp-config.php)
# Bu satırları wp-config.php içine ekleyin

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

# Log dosyasını izlemek için
tail -f /var/www/html/wp-content/debug.log | grep -i "oai"

# Eklenti kendi log fonksiyonunu da kullanabilir
# PHP kodunda:
# if (WP_DEBUG) {
#     error_log('[OAI Helper] API yanıt süresi: ' . $duration . 'ms');
# }

Gerçek Dünya Senaryosu: Ürün Açıklaması Üreteci

Bir WooCommerce mağazası için geliştirdiğim özelliği paylaşayım. Ürün başlığını ve özelliklerini girince otomatik açıklama üretiyor:

# WooCommerce ürün sayfasına AI butonu eklemek için
# functions.php veya ayrı bir include dosyasına eklenebilir

cat >> /var/www/html/wp-content/plugins/oai-helper/includes/class-oai-woocommerce.php << 'EOF'
<?php

if (!class_exists('WooCommerce')) {
    return;
}

class OAI_WooCommerce {

    public function __construct() {
        add_action('woocommerce_product_options_general_product_data', [$this, 'add_ai_button']);
        add_action('wp_ajax_oai_generate_product_desc', [$this, 'generate_product_desc']);
    }

    public function add_ai_button() {
        echo '<div class="options_group">';
        echo '<p class="form-field"><label>AI Açıklama Üretici</label>';
        echo '<button type="button" id="oai-gen-desc" class="button">AI ile Açıklama Oluştur</button>';
        echo '<span class="description">Ürün başlığı ve özelliklerine göre açıklama üretir.</span>';
        echo '</p></div>';
    }

    public function generate_product_desc() {
        check_ajax_referer('oai_helper_nonce', 'nonce');

        if (!current_user_can('edit_products')) {
            wp_die('Yetersiz yetki');
        }

        $product_name = sanitize_text_field($_POST['product_name'] ?? '');
        $features     = sanitize_textarea_field($_POST['features'] ?? '');

        $client = new OAI_Client();
        $result = $client->chat_completion([
            [
                'role'    => 'system',
                'content' => 'Sen WooCommerce ürün açıklaması uzmanısın. SEO uyumlu, 
                              satış odaklı, HTML formatında Türkçe açıklamalar yazıyorsun. 
                              Madde listesi ve kalın yazı kullan.'
            ],
            [
                'role'    => 'user',
                'content' => "Ürün adı: {$product_name}nÖzellikler: {$features}nnDetaylı ürün açıklaması yaz."
            ],
        ], ['temperature' => 0.8]);

        if (is_wp_error($result)) {
            wp_send_json_error(['message' => $result->get_error_message()]);
        }

        wp_send_json_success(['description' => $result]);
    }
}

new OAI_WooCommerce();
EOF

Güvenlik Kontrol Listesi

Eklentiyi production’a almadan önce şu kontrolleri yapın:

  • Nonce doğrulaması: Her AJAX isteğinde check_ajax_referer() çağırın, atlamayın
  • Yetki kontrolü: current_user_can() ile kullanıcı yetkisini doğrulayın
  • Input sanitizasyonu: Kullanıcıdan gelen tüm veriler için sanitize_text_field() veya sanitize_textarea_field() kullanın
  • Output escape: Ekrana yazdırılan AI yanıtları için wp_kses_post() veya esc_html() kullanın
  • Rate limiting: Public endpoint’lerde mutlaka uygulayın, yoksa API maliyetiniz uçar
  • API key rotasyonu: Ayda bir API key yenileyin ve eski keyleri devre dışı bırakın
  • HTTPS zorunluluğu: API key içeren sayfalar kesinlikle HTTPS üzerinde olmalı

Performans ve Ölçeklendirme

Sitede trafik arttıkça OpenAI API gecikmeleri fark edilmeye başlar. Birkaç pratik öneri:

  • Object cache kullanın: Redis veya Memcached varsa WordPress transient yerine doğrudan obect cache tercih edin
  • Asenkron işlemler: Uzun süren içerik üretimlerini Action Scheduler veya wp-cron ile arka planda çalıştırın
  • Streaming API: GPT-4 streaming destekliyor, büyük yanıtlar için EventSource API ile stream açabilirsiniz ancak WordPress’te bunu yönetmek biraz daha karmaşık
  • Model seçimi: Her iş için GPT-4o kullanmak zorunda değilsiniz. Kısa özetler ve sınıflandırma için gpt-4o-mini çok daha hızlı ve ucuz

Sonuç

WordPress eklentisinde OpenAI entegrasyonu ilk bakışta karmaşık görünse de doğru mimariyle oldukça temiz bir yapı kurulabiliyor. En kritik nokta güvenlik: nonce, sanitizasyon ve rate limiting olmadan bu tür eklentileri production’a almayın. İkinci önemli konu maliyet kontrolü: transient önbellekleme ile gereksiz API çağrılarını ciddi ölçüde azaltabilirsiniz.

Bu yazıdaki kodları kendinize göre uyarlayabilirsiniz. Ben bu yapıyı birden fazla müşteri projesinde kullandım ve günde binlerce istek sorunsuz karşılıyor. Herhangi bir sorunda yorumlarda veya doğrudan yazabilirsiniz.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir