
PHP'de cURL ile Dış API Çağırma ve Hata Yönetimi
Dış servis çağrıları, bir uygulamanın denetimi dışındaki tek bölümüdür. Servis yavaşladığında ya da yanıt vermediğinde sorun kendi uygulamanıza yansır: istekler birikir, işlem havuzu dolar ve sayfa üretimi tamamen durur. Bu yüzden her dış çağrının zaman sınırı, hata davranışı ve başarısızlık planı önceden tanımlanmalıdır.
Bu bölümde cURL ile istek gönderme, zaman aşımı ayarları, hata ayrımı, yeniden deneme düzeni ve çağrıların test edilebilir hale getirilmesi ele alınıyor.
Temel istek
Varsayılan davranışta yanıt doğrudan çıktıya yazılır. Yanıtın değişkene alınması için ilgili seçenek açılmalıdır. Aşağıdaki örnek, en az ayarla çalışan bir okuma isteğidir.
$ch = curl_init('https://ornek.test/api/v1/urunler');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, // yanıt değişkene döner
CURLOPT_CONNECTTIMEOUT => 3, // bağlantı kurma sınırı
CURLOPT_TIMEOUT => 10, // toplam süre sınırı
CURLOPT_FOLLOWLOCATION => false, // yönlendirmeler denetimli ele alınır
CURLOPT_HTTPHEADER => ['Accept: application/json'],
]);
$govde = curl_exec($ch);
$kod = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$hata = curl_errno($ch);
curl_close($ch);Zaman aşımı ayarları isteğe bağlı değildir. Tanımlanmadığında istek, sunucu yanıt verene kadar bekler ve bu süre dakikalarla ölçülebilir. Bağlantı süresi ile toplam süre ayrı ayrı sınırlanmalıdır.
Hata türlerini ayırmak
İki farklı başarısızlık vardır ve karıştırılmamalıdır. Aktarım düzeyindeki hatalar sunucuya hiç ulaşılamadığını gösterir; bunlar yeniden denenebilir. Yanıt kodundan gelen hatalar ise sunucunun isteği anladığını ancak reddettiğini gösterir ve genellikle yeniden denemek işe yaramaz.
if ($hata !== CURLE_OK) {
// ağ, DNS, zaman aşımı: sonra tekrar denenebilir
throw new ServisUlasilamadi(curl_strerror($hata));
}
if ($kod === 429 || $kod >= 500) {
// sunucu geçici olarak yetişemiyor: beklenip tekrar denenir
throw new ServisMesgul("Yanıt kodu {$kod}");
}
if ($kod >= 400) {
// istek hatalı: aynı istek tekrar gönderilirse aynı sonuç alınır
throw new IstekReddedildi("Yanıt kodu {$kod}", $kod);
}Zaman aşımı süreleri, çağrılan servisin gerçek davranışına göre belirlenir. Ödeme sağlayıcısı gibi yavaş yanıt veren servislerde süre uzun tutulabilir, ancak bu durumda çağrının web isteği içinde değil arka planda yapılması düşünülmelidir. Kullanıcının on beş saniye bekletildiği bir akış, teknik olarak çalışsa bile kullanılabilir değildir.
Yanıt gövdesi de doğrulanmalıdır. Sunucunun 200 kodu döndürmesi, gövdenin beklenen yapıda olduğunu göstermez; çözümleme ve alan denetimi ayrıca yapılır.
Veri göndermek
$govde = json_encode($veri, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
$ch = curl_init('https://ornek.test/api/v1/siparisler');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $govde,
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json',
'Authorization: Bearer ' . $belirtec,
'Idempotency-Key: ' . $islemAnahtari,
],
]);Gönderilecek veri form biçimindeyse dizi doğrudan verilebilir; bu durumda içerik türü kendiliğinden ayarlanır. JSON gönderilirken ise metin olarak kodlanır ve içerik türü başlığı açıkça yazılır. İkisinin karıştırılması, sunucunun gövdeyi boş görmesine yol açan yaygın bir hatadır.
Yazma isteklerinde tekrar güvenliği önemlidir. Zaman aşımına uğrayan bir istek sunucuya ulaşmış olabilir; yeniden gönderildiğinde aynı işlem ikinci kez gerçekleşir. Servis destekliyorsa benzersiz bir işlem anahtarı gönderilir, desteklemiyorsa sonucun sorgulanabileceği bir yol bulunmalıdır.
Güvenlik ayarları
Sertifika doğrulaması hiçbir koşulda kapatılmaz. Doğrulamayı kapatmak, araya girme saldırılarına kapıyı açar ve şifreli bağlantının sağladığı güvencenin tamamını ortadan kaldırır. Geliştirme ortamında sertifika sorunu yaşanıyorsa çözüm, doğru sertifika paketini tanımlamaktır.
curl_setopt_array($ch, [
CURLOPT_SSL_VERIFYPEER => true, // varsayılan değer, kapatılmaz
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt',
]);Kullanıcıdan gelen bir adrese istek atılması gerekiyorsa, adresin iç ağ kaynaklarına yönlendirilmediği doğrulanmalıdır. Yönlendirmelerin izlenmesinin kapatılması ve hedef adresin çözümlenip denetlenmesi bu saldırıya karşı temel önlemdir.
Yeniden deneme düzeni
Geçici hatalarda hemen tekrar denemek, zaten zorlanan bir servisi daha da yorar. Bekleme süresi her denemede artırılır ve üzerine küçük bir rastgele sapma eklenir; böylece aynı anda başarısız olan istekler aynı anda tekrar denemez.
function yenidenDene(callable $islem, int $azami = 3): mixed
{
$deneme = 0;
while (true) {
try {
return $islem();
} catch (ServisUlasilamadi|ServisMesgul $e) {
if (++$deneme >= $azami) {
throw $e;
}
$bekleme = (2 ** $deneme) + random_int(0, 1000) / 1000;
usleep((int) ($bekleme * 1_000_000));
}
}
}Deneme sayısı sınırsız olmamalıdır. Belirli bir eşikten sonra çağrı durdurulur ve istek kuyruğa alınır ya da kullanıcıya sonucun daha sonra bildirileceği söylenir. Sürekli başarısız olan bir servise istek göndermeyi geçici olarak kesen devre kesici düzeni, bu davranışın yaygın adıdır.
Eş zamanlı istekler
Birbirinden bağımsız birkaç servise art arda istek göndermek, süreleri toplar. Üç servisin her biri bir saniye sürüyorsa toplam üç saniye beklenir. İstekler eş zamanlı gönderildiğinde ise toplam süre en yavaş isteğe iner.
$coklu = curl_multi_init();
$isteklar = [];
foreach ($adresler as $ad => $adres) {
$ch = curl_init($adres);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 8,
]);
curl_multi_add_handle($coklu, $ch);
$isteklar[$ad] = $ch;
}
do {
$durum = curl_multi_exec($coklu, $calisan);
if ($calisan) {
curl_multi_select($coklu);
}
} while ($calisan && $durum === CURLM_OK);
$yanitlar = [];
foreach ($isteklar as $ad => $ch) {
$yanitlar[$ad] = [
'govde' => curl_multi_getcontent($ch),
'kod' => curl_getinfo($ch, CURLINFO_RESPONSE_CODE),
];
curl_multi_remove_handle($coklu, $ch);
curl_close($ch);
}
curl_multi_close($coklu);Eş zamanlı çağrılarda toplam sınır da düşünülmelidir. Aynı anda açılan çok sayıda bağlantı, hem karşı servisi zorlar hem de kendi sunucunuzda kaynak tüketir; eş zamanlı istek sayısı makul bir değerle sınırlanır.
Eş zamanlı istekler yalnızca aralarında sıra bağımlılığı olmayan çağrılar için uygundur. Birinin sonucu diğerinin girdisi oluyorsa sıralı gitmek zorunludur; bu durumda kazanç, gereksiz çağrıları elemekten gelir.
Çağrıları yalıtmak
cURL çağrılarının iş mantığının içine dağılması iki soruna yol açar: aynı ayarlar tekrarlanır ve kod test edilemez hale gelir. Çağrılar bir arayüzün arkasına alındığında testlerde gerçek servis yerine hazır yanıt döndüren bir uygulama kullanılabilir.
interface HttpIstemci
{
public function gonder(string $yontem, string $adres, array $secenek = []): Yanit;
}
final class CurlIstemci implements HttpIstemci
{
public function __construct(
private readonly int $baglantiSaniye = 3,
private readonly int $toplamSaniye = 10,
) {}
public function gonder(string $yontem, string $adres, array $secenek = []): Yanit
{
// ortak ayarlar tek yerde toplanır
}
}
final class SahteIstemci implements HttpIstemci
{
public function __construct(private readonly Yanit $hazir) {}
public function gonder(string $yontem, string $adres, array $secenek = []): Yanit
{
return $this->hazir;
}
}Aynı yapı, istek ve yanıtların günlüğe yazılmasını da tek noktadan yönetmeyi sağlar. Kayıtlara gizli başlıkların yazılmaması için yetkilendirme başlıklarının maskelenmesi gerekir.
Kontrol listesi
- Her çağrıda bağlantı ve toplam süre sınırı tanımlı.
- Aktarım hatası ile yanıt kodu hatası ayrı ele alınıyor.
- Sertifika doğrulaması açık.
- Yazma isteklerinde tekrar güvenliği sağlanmış.
- Yeniden deneme sayısı sınırlı ve bekleme süresi artan biçimde.
- Çağrılar bir arayüz arkasında; testlerde sahte uygulama kullanılabiliyor.
- Günlüklerde yetkilendirme başlıkları maskeleniyor.
İlgili yazılar
PHP eğitim serisi
Bu yazı, temel sözdiziminden üretim ortamına kadar ilerleyen 25 bölümlük serinin bir parçası. Bölümler sırayla okunacak biçimde hazırlandı, ancak her biri tek başına da kullanılabilir.
Temeller
- Kurulum ve temel sözdizimi
- Döngüler ve akış kontrolü
- Diziler
- Fonksiyonlar
- String fonksiyonları
- Süper globaller
- Form işleme
- Dosya işlemleri
Dilin ayrıntıları
Nesne yönelimli PHP
- Sınıf ve nesne
- Kalıtım, arayüz ve trait
- Statik üyeler, sabitler ve enum
- Namespace, Composer ve autoload
Veri ve güvenlik
- PDO ile veritabanı işlemleri
- Oturum yönetimi ve kimlik doğrulama
- Güvenli çıktı ve XSS
- JSON ile veri değişimi
Dış dünya ve üretim
- cURL ile dış API çağırma (bu yazı)
- Kendi REST API'ni yazmak
- PHPUnit ile birim test
- Hata ayıklama ve performans ölçümü
- Üretime alma ve güvenlik
Yorumlar
İlk yorumu sen yap.
İlgili Yazılar

PHP Uygulamasını Üretime Almak: Yapılandırma ve Güvenlik
Geliştirme makinesinde çalışan kod ile üretimde güvenle çalışan kod arasındaki fark yapılandırmadır. Ayarlar, izinler, dağıtım akışı ve son kontroller.

PHP ile REST API Yazmak: Yönlendirme ve HTTP Durum Kodları
Bir API'nin kalitesi çoğunlukla ayrıntılarda belli olur: doğru durum kodu, tutarlı hata biçimi ve öngörülebilir adresler. Saf PHP ile uçtan uca kurulum.

PHPUnit ile Birim Test Yazmak: Kurulum, Sahte Nesneler ve Kapsam
Testin amacı hata bulmak değil, değişikliği güvenle yapabilmektir. Kurulum, ilk test, sahte nesneler ve test edilebilir kodun özellikleri.