PHP’de Fonksiyonu Günde 1 Kez Çalıştırmanın Kesin Yolu [2026]

Bir PHP fonksiyonunu “günde bir kez” çalıştırmak istiyorsan, en büyük sorun kodu yazmak değil; tam olarak bir kez çalıştığını garanti etmektir. Aynı gün içinde iki tetikleme, farklı saat dilimi yüzünden kaçan çalıştırma ya da yoğun trafikte yarış durumu gibi hatalar, basit görünen bu işi üretimde riskli hale getirir. Kendi tecrübemle söyleyebilirim ki, bu konuda en güvenli yaklaşım tek başına if kontrolü yazmak değil; cron, kalıcı kayıt ve kilit mekanizmasını birlikte kullanmaktır. Bu yazıda, PHP’de fonksiyonu günde 1 kez çalıştırmanın en sağlam yolunu adım adım anlatacağım.

Önce problemi doğru tanımla: “Günde 1 kez” ne demek?

“Günde 1 kez” ifadesi ilk bakışta basit görünür ama teknik tarafta net tanım ister. Burada üç ayrı anlam ortaya çıkar:

1. Her 24 saatte bir çalışsın
2. Takvim gününde sadece bir kez çalışsın
3. Belirli bir saatten sonra, o gün için tek sefer çalışsın

Bu ayrım kritik önem taşır çünkü çözüm mimarisi buna göre değişir. Örneğin saat 23:59’da çalışan bir fonksiyon, 2 dakika sonra yeni güne girince tekrar tetiklenebilir. Eğer senin iş mantığın “takvim gününde yalnızca bir kez” ise bu davranış doğru kabul edilir. Ama “en az 24 saat ara olsun” diyorsan bu yanlıştır.

PHP tarafında en yaygın hata, sadece son çalışma zamanını zaman damgası olarak tutup kaba bir karşılaştırma yapmaktır. Bu yöntem küçük projede iş görebilir ama sunucu saati, timezone, manuel tetikleme ve paralel isteklerde açık verir. Yıllar süren sunucu tarafı iş akışı takibim gösteriyor ki, “günde bir kez” kuralı için asıl güvence kod satırından değil, sistem tasarımından gelir.

Google SRE ve klasik job scheduling yaklaşımı da aynı çizgidedir: görev zamanlamasında idempotency, locking ve durable state temel taşlardır. Dağıtık sistemlerde görevlerin birden fazla kez tetiklenebilmesi normal kabul edilir; kritik olan, işin tekrar çalışsa bile ikinci kez etki üretmemesidir. Bu ilke PHP tarafında da birebir geçerlidir.

Kesin çözüm: Cron artı veritabanı kaydı artı kilit mekanizması

PHP’de bir fonksiyonu günde 1 kez çalıştırmanın en güvenli yolu şu üç parçayı birlikte kullanmaktır:

1. Görevi cron ile düzenli tetikle
2. O gün çalışıp çalışmadığını veritabanında kontrol et
3. Aynı anda iki süreç başlamasın diye kilit kullan

Tek başına cron yeterli değildir. Cron, görevi çağırır ama “bugün zaten çalıştı mı?” sorusunu cevaplamaz. Tek başına veritabanı kaydı da yeterli değildir çünkü aynı anda gelen iki süreç aynı kontrolü geçebilir. Tek başına lock da yetmez çünkü sunucu yeniden başlarsa geçmiş bilgi kaybolabilir. Bu yüzden üçlü kombinasyon gerekir.

Neden cron en doğru tetikleyici?

Web isteğine bağlı çalışan çözümler güven vermez. Çünkü düşük trafikli bir sitede ilgili sayfa hiç açılmazsa fonksiyon da çalışmaz. WordPress dünyasında WP-Cron bu yüzden sık eleştiri alır; resmi dokümantasyonda da gerçek sistem cron’unun daha güvenilir olduğu açıkça önerilir. Gerçek cron, işletim sistemi düzeyinde zamanlama yaptığı için trafik bağımsız çalışır.

Örnek cron mantığı şu şekildedir:

– Her gün 00:05’te PHP scriptini çağır
– Script içinde “bugün çalıştı mı” kontrolü yap
– Çalışmadıysa fonksiyonu yürüt
– Başarılıysa o günün kaydını veritabanına yaz

00:00 yerine 00:05 seçmek de iyi bir pratiktir. Saat geçişi, log rotasyonu, yedekleme ya da sunucu yükü yüzünden tam gece yarısı bazı sistemlerde küçük sapmalar oluşabilir.

Neden veritabanı kaydı şart?

Kalıcı kayıt, işin gerçekten hangi gün çalıştığını kanıtlar. Bellekte tutulan değişken ya da dosya tabanlı geçici çözüm çoğu üretim ortamında kırılgandır. Veritabanı ise yeniden başlatma, deploy ve process çökmesi gibi durumlarda bilgiyi korur.

En sade yapı şu olabilir:

Tablo adı: dailyjobs

Alanlar:
– id
– job
name
– rundate
– created
at

Burada jobname örneğin dailyreport olabilir, rundate ise 2026-05-13 gibi tarih formatında tutulur. Bu iki alan için unique index tanımlarsan aynı iş aynı gün için ikinci kez eklenemez. Asıl güven burada başlar.

MySQL ve MariaDB tarafında unique constraint, yarış durumlarını engellemede çok güçlü bir araçtır. Uygulama katmanındaki if kontrolüne göre daha güvenlidir çünkü kararı veritabanı motoru verir.

Neden kilit mekanizması kullanmalısın?

Şu senaryoyu düşün:

– Cron scripti başladı
– Aynı anda manuel tetikleme geldi
– İki süreç de veritabanında “bugün kayıt yok” dedi
– İkisi de fonksiyonu çalıştırdı

İşte buna race condition denir. Özellikle yüksek trafikli uygulamalarda sık görülür. Bu problemi çözmek için kilit kullanırsın. MySQL kullanıyorsan GETLOCK pratik bir tercihtir. Redis varsa dağıtık kilit kurabilirsin. Tek sunucuda flock da iş görür.

Basit mantık şöyledir:

– Script başında lock almaya çalış
– Lock alınamazsa çık
– Lock alındıysa veritabanını kontrol et
– Kayıt yoksa fonksiyonu çalıştır
– Başarıdan sonra kaydı ekle
– Lock’u bırak

Bu yaklaşım, eşzamanlı tetiklemelerde çakışmayı ciddi biçimde azaltır.

Adım adım uygulanabilir mimari ve örnek PHP kodu

Şimdi bunu üretimde kullanılabilecek sade bir yapıya dönüştürelim. Buradaki amaç tek dosyada sihir yapmak değil; denetlenebilir, loglanabilir ve yeniden çalıştırılabilir bir akış kurmaktır.

1. Veritabanı tablosunu oluştur

MySQL örneği:

“`sql
CREATE TABLE dailyjobs (
id INT AUTO
INCREMENT PRIMARY KEY,
jobname VARCHAR(100) NOT NULL,
run
date DATE NOT NULL,
createdat DATETIME NOT NULL DEFAULT CURRENTTIMESTAMP,
UNIQUE KEY uniqjobdate (jobname, rundate)
);
“`

Bu tablo sana iki şey sağlar:
– İşin hangi gün çalıştığını kayıt altına alır
– Aynı işin aynı gün ikinci kez eklenmesini teknik olarak engeller

2. Saat dilimini sabitle

Timezone konusu sandığından daha kritik. PHP’nin varsayılan timezone ayarı ile veritabanı timezone ayarı farklıysa aynı gün içinde iki farklı tarih algısı oluşabilir.

PHP tarafında açıkça tanımla:

“`php
datedefaulttimezoneset(‘Europe/Istanbul’);
“`

Ayrıca MySQL oturumu için de timezone sabitlemek iyi olur. Özellikle UTC çalışan sunucularda uygulama mantığın yerel saate bağlıysa bu adımı atlama.

3. İş fonksiyonunu idempotent tasarla

İdempotent demek, aynı iş ikinci kez koşarsa bile veri bozulmaması demektir. Bu yaklaşım büyük sistemlerin temelidir. AWS, Google Cloud ve ödeme altyapıları bu prensibi sıkça vurgular çünkü ağ hataları ve tekrar denemeler doğaldır.

Örneğin günlük rapor üretiyorsan:
– Var olan raporu ezmeden önce tarih bazlı kontrol koy
– Aynı kullanıcıya ikinci kez aynı ödülü tanımlama
– Tekrar çalışırsa ek kayıt patlaması yaratma

İş mantığın ikinci tetiklemeyi tolere ederse sistemin çok daha sağlam olur.

4. Lock al, kontrol et, çalıştır, kaydet

Aşağıdaki örnek PDO ve MySQL named lock mantığı kullanır:

“`php
<?php

datedefaulttimezoneset(‘Europe/Istanbul’);

$pdo = new PDO(
‘mysql:host=127.0.0.1;dbname=app;charset=utf8mb4’,
‘user’,
‘pass’,
[
PDO::ATTRERRMODE => PDO::ERRMODEEXCEPTION,
] );

$jobName = ‘dailyreport’;
$today = date(‘Y-m-d’);

function runDailyFunction(PDO $pdo, string $today): void
{
// Asıl iş mantığı burada
// Örnek: rapor üret, cache yenile, günlük özet hesapla
file
putcontents(DIR . ‘/daily.log’, “Calisti: {$today}n”, FILEAPPEND);
}

try {
$stmt = $pdo->query(“SELECT GETLOCK(‘dailyreportlock’, 10) AS lockstatus”);
$lockStatus = (int) $stmt->fetch(PDO::FETCHASSOC)[‘lockstatus’];

if ($lockStatus !== 1) {
exit(“Lock alinamadi, cikiliyor.n”);
}

$check = $pdo->prepare(“SELECT 1 FROM dailyjobs WHERE jobname = ? AND rundate = ? LIMIT 1″);
$check->execute([$jobName, $today]);

if ($check->fetch()) {
$pdo->query(“SELECT RELEASE
LOCK(‘dailyreportlock’)”);
exit(“Bugun zaten calisti.n”);
}

$pdo->beginTransaction();

runDailyFunction($pdo, $today);

$insert = $pdo->prepare(“INSERT INTO dailyjobs (jobname, rundate) VALUES (?, ?)”);
$insert->execute([$jobName, $today]);

$pdo->commit();

$pdo->query(“SELECT RELEASELOCK(‘dailyreportlock’)”);
echo “Basariyla calisti.n”;

} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}

try {
$pdo->query(“SELECT RELEASELOCK(‘dailyreportlock’)”);
} catch (Throwable $ignored) {
}

errorlog($e->getMessage());
exit(“Hata olustu.n”);
}
“`

Bu yapının güçlü yanları:
– Aynı anda iki süreç başladığında lock koruma sağlar
– Veritabanı kaydı tekrar çalışmayı engeller
– Transaction, iş ve kayıt adımını daha tutarlı hale getirir

Burada kritik bir nüans var: Eğer asıl iş çok uzun sürüyorsa, transaction’ı gereğinden uzun açık tutma. Bazı durumlarda önce “başladı” durumu, sonra “tamamlandı” durumu yazmak daha doğru olur. Büyük veri işlerinde bu yaklaşım daha sağlıklıdır.

5. Cron satırını ekle

Linux cron örneği:

“`bash
5 0 * /usr/bin/php /var/www/app/daily-job.php >> /var/log/daily-job.log 2>&1
“`

Bu satır her gün 00:05’te scripti çalıştırır ve çıktıyı log dosyasına yazar. Log tutmak lüks değil, zorunluluktur. Bir işin çalıştığını tahmin etmek yerine logdan doğrulamak istersin.

Cron doğruluğu konusunda tarihsel veri de net konuşur. Unix cron onlarca yıldır sistem yöneticilerinin temel zamanlayıcısı olarak kullanılır. Bu kadar yaygın kabul görmesinin nedeni basitliği kadar öngörülebilir davranmasıdır.

Hangi yöntem neden zayıf kalır?

Piyasada sık görülen ama üretimde açık bırakan çözümler var. Bunları bilmen işini hızlandırır.

Bu yaklaşım kullanıcı bazlıdır, sunucu bazlı görev mantığına uymaz. Kullanıcı tarayıcıyı değiştirirse ya da çerez silerse kontrol bozulur. Arka plan işi için uygun değildir.

Sadece dosyaya tarih yazmak

Tek sunucuda küçük bir script için işe yarayabilir. Ama dosya izinleri, deploy sırasında silinme, NFS senkronizasyonu ve çoklu sunucu yapısında tutarsızlık çıkarır. Küçük projede geçici çözüm olabilir ama “kesin yol” değildir.

Sadece if date kontrolü

Kod içinde “son çalışmadan beri 86400 saniye geçti mi” mantığı yazmak yaygın bir kısa yoldur. Ama yaz saati değişimi, timezone farkı ve restart sonrası veri kaybı bu yöntemi kırar. Takvim gününe göre çalışan işler için de yanlış sonuç üretebilir.

Sadece cron, kontrol yok

Cron düzenli çağrı yapar ama tekrar tetiklemeyi engellemez. Eğer biri scripti elle çalıştırırsa ya da cron iki kez eklenirse iş iki kez yürür.

Yararlı Bitki üzerinde teknik içerik hazırlarken özellikle bu ayrımı sık vurguluyorum: tetikleme ve güvence aynı şey değildir. Bir sistem işi başlatabilir, ama sadece iyi tasarlanmış kayıt ve kilit düzeni o işi güvenli hale getirir.

Üretimde hata payını azaltan sahadan notlar

Kendi tecrübemle söyleyebilirim ki, geliştiricilerin çoğu “çalışıyor” ile “garanti veriyor” arasındaki farkı ilk başta hafife alıyor. Yerel ortamda sorunsuz görünen bir script, üretimde aynı anda iki tetikleme alınca beklenmedik kayıtlar üretebiliyor. Bu yüzden aşağıdaki noktaları özellikle öneriyorum.

1. Job adı sabit ve anlamlı olsun
dailycleanup, dailyreport, dailyreward gibi açık bir isim kullan. Log ve veritabanı kaydı okunur hale gelir.

2. Tarihi string yerine DATE olarak sakla
2026-05-13 formatı hem sorguda netlik sağlar hem de indeks performansını iyileştirir.

3. Başarısız denemeleri de logla
Sadece başarı kaydı yetmez. Lock alınamadı, hata oluştu, duplicate giriş denendi gibi olayları da yaz.

4. Manuel yeniden çalıştırma stratejisi tanımla
Bazen o günkü işi yeniden koşturmak istersin. Bunun için ya ilgili kaydı silersin ya da force parametresi eklersin. Ama bu kapıyı kontrollü aç.

5. İzleme koy
Cron’un çağrıldığı ama işin patladığı senaryolar çok olur. En azından log takibi, tercihen health check ya da bildirim sistemi kur.

6. Fonksiyonu kısa tut
Uzun süren işler için kuyruk yapısı ya da parçalı görev mantığı daha güvenlidir. Tek script içinde her şeyi yığma.

7. Sunucu saatini izle
NTP senkronizasyonu bozuk sunucuda günlük görev mantığı da bozulur. Sistem saatinin doğruluğu temel gerekliliktir.

Bu noktada bir teknik kanıt daha önemli: Google’ın Site Reliability yaklaşımı ve dağıtık görev prensipleri, zamanlanmış işlerde gözlemlenebilirlik ve tekrar güvenliği kavramlarını sürekli öne çıkarır. Yani yalnızca “iş çalıştı” demek yetmez; ne zaman çalıştı, kaç kez denendi, hata aldı mı, yeniden denendi mi sorularını da cevaplamalısın.

Ne zaman Redis, ne zaman veritabanı, ne zaman sistem aracı kullanmalısın?

Her proje aynı ölçekte değildir. Doğru araç, uygulamanın mimarisine göre değişir.

Küçük ve orta ölçekli PHP projeleri

Cron artı MySQL unique key artı GETLOCK çoğu senaryo için yeterlidir. Uygulaması kolaydır, bakım maliyeti düşüktür.

Çoklu sunucu ya da konteyner mimarisi

Birden fazla instance aynı işi tetikleyebiliyorsa Redis lock ya da merkezi job runner daha iyi seçim olur. Kubernetes CronJob gibi araçlar da değerlendirilebilir. Yine de uygulama içinde “bugün çalıştı mı” kaydı tutmayı bırakma.

Kritik finansal ya da raporlama işleri

Bu seviyede sadece günlük kayıt yetmez. Job status tablosu kur:
– pending
– running
– success
– failed

Böylece yarıda kalan işleri ayıklarsın. Finans, faturalama, ödül tanımlama gibi alanlarda bu disiplin fark yaratır.

Yararlı Bitki gibi kaynaklarda teknik rehber arayanların çoğu önce kısa çözüm arıyor. Benim tavsiyem şu: eğer iş veri üretiyor, para etkiliyor ya da rapor besliyorsa kısa çözüm yerine izlenebilir çözüm kur.

Sıkça Sorulan Sorular

PHP’de fonksiyonu sadece ziyaret olunca çalıştırmak doğru mu?

Hayır. Trafik yoksa fonksiyon hiç çalışmaz. Günlük görevler için sistem cron daha güvenlidir.

Günde 1 kez için sadece cron yeterli olur mu?

Yetmez. Cron sadece çağırır. Tekrar çalışmayı engellemek için veritabanı kaydı ya da benzer bir kalıcı kontrol gerekir.

Unique key tek başına yeterli mi?

Birçok durumda güçlü koruma sağlar ama uzun süren işlerde lock ile birlikte kullanmak daha emniyetlidir.

GET_LOCK yerine dosya kilidi kullanabilir miyim?

Tek sunucuda evet. Çoklu sunucu yapısında merkezi kilit daha doğru seçim olur.

86400 saniye kontrolü neden risklidir?

Çünkü takvim günü mantığıyla birebir örtüşmez. Saat farkı, timezone ve yaz saati değişimi sorun çıkarabilir.

İş başarısız olursa aynı gün yeniden çalıştırmalı mıyım?

Evet, ama bunu bilinçli yapmalısın. Başarısız ve başarılı durumları ayrı kaydet ki yeniden tetikleme kararını net verebilesin.

En güvenli veri tipi tarih için hangisi?

Takvim günü bazlı işlerde DATE alanı en temiz çözümdür. Son çalışma anını izlemek istersen ayrıca DATETIME tutabilirsin.

Eğer bu yapıyı kendi projene uyarlayacaksan önce işinin “takvim günü mü, 24 saat kuralı mı” istediğini netleştir, sonra cron artı unique key artı lock düzenini kur. İstersen kullandığın altyapıyı yaz; Apache, Nginx, cPanel, Laravel ya da düz PHP fark etmeksizin senin senaryona uygun örnek akışı çıkarayım.