Warning: Undefined variable $x uyarısı, değer atanmamış bir değişkeni okuduğunuzda; Warning: Undefined array key "ad" dizide olmayan bir anahtara eriştiğinizde; Warning: Undefined property: Sinif::$alan ise nesnede bulunmayan bir özelliği okuduğunuzda çıkar. Üçünde de PHP okunan değeri null kabul eder ve betik çalışmaya devam eder. Kalıcı çözüm değeri kullanmadan önce varlığını kontrol etmektir: isset(), null birleştirme operatörü ?? veya array_key_exists().

Bu uyarılar PHP 8.0 öncesinde daha hafif bir seviye olan "Notice" idi; PHP 8.0 Backward Incompatible Changes sayfasına göre PHP 8.0 ile "Warning" seviyesine yükseltildi. Bu yüzden eski bir projeyi yeni sürüme taşıdığınızda daha önce görmediğiniz uyarılar çıkabilir. Aşağıda her uyarıyı gerçek mesajıyla gösteriyor, hangi aracın hangi durumda doğru olduğunu karşılaştırıyoruz. Örnekler PHP 8.5.12 ile çalıştırıldı. Serinin geneli için PHP öğrenme rehberine bakabilirsiniz.

Undefined variable: tanımsız değişken

Undefined variable: tanımsız değişken

PHP'de değişken bildirmek zorunlu değildir, ilk atamayla değişken oluşur. Ancak hiç atanmamış bir değişkeni okursanız kılavuza göre PHP E_WARNING üretir ve değişkenin değerini null kabul eder. Her okuma ayrı bir uyarıdır:

php
<?php
echo $mesaj;
var_dump($mesaj);
echo 'Betik devam etti', PHP_EOL;
/* çıktı:
Warning: Undefined variable $mesaj in /yol/ornek.php on line 2

Warning: Undefined variable $mesaj in /yol/ornek.php on line 3
NULL
Betik devam etti
*/

Uyarının en sık nedenleri şunlardır: değişken adında yazım hatası ($kullanici yerine $kulanici), yalnızca bir if dalında atanıp dalın dışında kullanılan değişken, döngü hiç dönmediği için oluşmayan değişken ve en önemlisi fonksiyon kapsamı.

Bir istisna var: tanımsız bir değişkene [] ile eleman eklemek uyarı üretmez. Kılavuz bunu "autovivification" (kendiliğinden oluşturma) olarak adlandırır; PHP yeni bir dizi oluşturur.

php
<?php
$liste[] = 'ilk';
var_dump($liste);
/* çıktı:
array(1) {
  [0]=>
  string(3) "ilk"
}
*/

Bu davranış uyarı vermese de okunabilirlik açısından $liste = []; ile diziyi önce açıkça oluşturmanız önerilir. Kılavuz da özellikle bir dosya başka bir dosyaya dahil edildiğinde, aynı adlı değişkenin varsayılan değerine güvenmenin sorun çıkarabileceği konusunda uyarır.

Fonksiyon içinde dışarıdaki değişkeni kullanmak

Yeni başlayanların en çok yaşadığı durum budur. PHP'de fonksiyonların kendi yerel kapsamı vardır; dosyanın en üstünde tanımladığınız değişken, fonksiyonun içinden otomatik olarak görünmez.

php
<?php
$siteAdi = 'keydal';

function baslik(): string
{
    return 'Hoş geldiniz: ' . $siteAdi;
}

echo baslik(), PHP_EOL;
/* çıktı:
Warning: Undefined variable $siteAdi in /yol/ornek.php on line 6
Hoş geldiniz:
*/

Doğru çözüm değeri parametre olarak geçirmektir: function baslik(string $siteAdi). global anahtar kelimesi de çalışır, ama fonksiyonu dışarıdaki bir isme bağımlı yaptığı için test etmeyi zorlaştırır. Closure kullanıyorsanız değişkeni use ($siteAdi) ile yakalayabilir ya da dış kapsamı otomatik yakalayan arrow function (fn) kullanabilirsiniz. Kapsam kurallarının tamamı PHP değişkenler ve kapsam, closure tarafı anonim fonksiyonlar ve arrow function yazısında.

Undefined array key: dizide olmayan anahtar

Bir dizide bulunmayan anahtarı okuduğunuzda uyarı, anahtarın tipine göre biraz farklı görünür: metin anahtarlar tırnak içinde, sayısal anahtarlar tırnaksız yazılır. Değer yine null kabul edilir, echo boş metin basar.

php
<?php
$kullanici = ['ad' => 'Elif', 'rol' => 'editör'];
echo $kullanici['eposta'], PHP_EOL;

$renkler = ['kırmızı', 'yeşil'];
echo $renkler[2], PHP_EOL;
/* çıktı:
Warning: Undefined array key "eposta" in /yol/ornek.php on line 3

Warning: Undefined array key 2 in /yol/ornek.php on line 6
*/

İkinci örnek klasik bir sınır hatasıdır: iki elemanlı dizinin indeksleri 0 ve 1'dir. Döngülerde $i <= count($dizi) yazmak bu uyarının çok yaygın bir kaynağıdır; $i < count($dizi) ya da doğrudan foreach kullanın (PHP döngüleri).

Form verisi, JSON çıktısı ya da veritabanı satırı gibi dış kaynaklardan gelen dizilerde hangi anahtarların bulunacağı garanti değildir. Bu dizilerde her okumayı bir varsayılan değerle yapmak alışkanlık hâline gelmelidir:

php
<?php
$satirlar = [
    ['ad' => 'Kalem', 'adet' => 3],
    ['ad' => 'Defter'],
];

foreach ($satirlar as $s) {
    $adet = $s['adet'] ?? 0;
    echo $s['ad'], ': ', $adet, PHP_EOL;
}
/* çıktı:
Kalem: 3
Defter: 0
*/

Trying to access array offset on null

Bu uyarı, dizi olmayan bir değere dizi gibi eriştiğinizde çıkar. En sık görülen kaynağı, sonuç bulamayınca null ya da false döndüren fonksiyonlardır: veritabanından satır getiren bir fonksiyon, array_find() veya json_decode() gibi. Mesajın sonundaki kelime, eriştiğiniz değerin tipini söyler.

php
<?php
$sonuc = null;
echo $sonuc['ad'], PHP_EOL;

$sayi = 42;
echo $sayi['ad'], PHP_EOL;
/* çıktı:
Warning: Trying to access array offset on null in /yol/ornek.php on line 3

Warning: Trying to access array offset on int in /yol/ornek.php on line 6
*/

Çözüm, fonksiyonun sonucunu kullanmadan önce kontrol etmektir: if ($satir === null) { ... } ya da kısaca $satir['ad'] ?? 'bilinmiyor'. ?? operatörü sol taraf null olduğunda da uyarı üretmeden varsayılan değere geçer.

Undefined property: nesnede olmayan özellik

Nesnede bildirilmemiş bir özelliği okumak da aynı mantıkla uyarı üretir. Mesaj bu kez sınıf adını da içerir:

php
<?php
class Uye
{
    public string $ad = 'Mert';
}

$u = new Uye();
echo $u->yas, PHP_EOL;
/* çıktı:
Warning: Undefined property: Uye::$yas in /yol/ornek.php on line 8
*/

Okumanın tersi, yani bildirilmemiş bir özelliğe yazmak ise farklı bir konudur. PHP 8.2'den beri bu "dinamik özellik" oluşturma eskimiş sayılır. PHP 8.2 Deprecated Features sayfası, özelliği sınıfta bildirmeyi tercih edilen çözüm olarak gösterir; gerçekten gerekiyorsa sınıfa #[\AllowDynamicProperties] özniteliği eklenebilir. stdClass bu kuraldan muaftır.

php
<?php
class Urun {}

$u = new Urun();
$u->fiyat = 100;
echo $u->fiyat, PHP_EOL;
/* çıktı:
Deprecated: Creation of dynamic property Urun::$fiyat is deprecated in /yol/ornek.php on line 5
100
*/

İç içe nesnelerde ara adımlardan biri null olabiliyorsa nullsafe operatörü ?-> kullanılabilir: zincirdeki değer null ise ifade uyarı ya da hata üretmeden null döndürür. Özelliğin sınıfta bildirilip bildirilmediğini (değerinden bağımsız olarak) öğrenmek için property_exists() vardır.

php
<?php
class Adres
{
    public function __construct(public string $sehir) {}
}

class Musteri
{
    public ?Adres $adres = null;
}

$m = new Musteri();
var_dump($m->adres?->sehir);           // çıktı: NULL
var_dump(isset($m->adres->sehir));     // çıktı: bool(false)
var_dump(property_exists($m, 'adres')); // çıktı: bool(true)

Sınıf ve özellik bildirimi için PHP sınıf ve nesne, __get() ile tanımsız özellik okumayı kontrollü hâle getirmek için sihirli metotlar yazısına bakabilirsiniz.

isset, ??, array_key_exists ve empty: hangisi ne zaman?

isset, ??, array_key_exists ve empty: hangisi ne zaman? isset($a[k]), $a[k] ?? v, array_key_exists(k, $a), empty($a[k])

Dört araç da uyarıyı önler ama aynı soruyu sormaz. Kılavuza göre isset() bir değişkenin tanımlı ve null dışında bir değere sahip olup olmadığını söyler; birden fazla argüman alırsa yalnızca hepsi tanımlıysa true döner. array_key_exists() ise yalnızca anahtarın var olup olmadığına bakar, değeri null olsa bile true döndürür. Aradaki farkı değeri null olan bir anahtarda net görürsünüz:

php
<?php
$ayar = ['tema' => null, 'dil' => 'tr'];

var_dump(isset($ayar['tema']));             // çıktı: bool(false)
var_dump(array_key_exists('tema', $ayar));  // çıktı: bool(true)
var_dump($ayar['tema'] ?? 'varsayılan');    // çıktı: string(11) "varsayılan"
var_dump(isset($ayar['dil'], $ayar['yok'])); // çıktı: bool(false)
var_dump(empty($ayar['yok']));              // çıktı: bool(true)
AraçTanımsızsaDeğer null iseDeğer 0 veya boş metin iseUyarı üretir mi
isset($a[k])falsefalsetrueHayır
$a[k] ?? vvvDeğerin kendisiHayır
array_key_exists(k, $a)falsetruetrueHayır
empty($a[k])truetruetrueHayır
$a[k] (doğrudan)null + WarningnullDeğerin kendisiEvet

Genel kural şöyle: varsayılan değer vermek istiyorsanız ??, "bu anahtar gerçekten var mı" sorusu önemliyse (örneğin null anlamlı bir değerse) array_key_exists() kullanın. empty() dikkatli kullanılmalıdır: 0, "0", boş metin ve boş dizi de "boş" sayılır, bu yüzden "stok 0" gibi geçerli değerleri yanlışlıkla eler. Bu dönüşüm kuralları PHP eşitlik ve tip dönüşümü yazısında.

Superglobal'ler ve ??= ile varsayılan değer

$_GET, $_POST ve $_COOKIE içinde hangi anahtarların bulunacağını ziyaretçi belirler. Bu yüzden bu dizilere asla doğrudan erişmeyin. Null birleştirme operatörü burada en kısa ve en okunur çözümdür. PHP 7.4'ten beri bulunan ??= ise "değer yoksa ata" işini tek adımda yapar; sayaç ve önbellek dizilerinde çok kullanışlıdır.

php
<?php
$_GET = [];   // CLI ortamında boş; örnek için açıkça boş bırakıldı
$sayfa = (int) ($_GET['sayfa'] ?? 1);
echo $sayfa, PHP_EOL; // çıktı: 1

$sayac = [];
$sayac['ziyaret'] ??= 0;
$sayac['ziyaret']++;
print_r($sayac);
/* çıktı:
Array
(
    [ziyaret] => 1
)
*/

Metin indeksi: Uninitialized string offset

Bir metnin karakterlerine dizi sözdizimiyle erişebilirsiniz. Metnin uzunluğunu aşan bir indeks okursanız farklı bir uyarı alırsınız:

php
<?php
$kod = 'TR';
echo $kod[5], PHP_EOL;
echo 'son', PHP_EOL;
/* çıktı:
Warning: Uninitialized string offset 5 in /yol/ornek.php on line 3

son
*/

Bu indeksler bayt bazlıdır; Türkçe karakterli metinlerde $metin[0] çok baytlı bir harfin yalnızca ilk baytını verir. Karakter bazlı erişim için mb_substr() kullanın: PHP mbstring ve Türkçe karakter.

PHP 8.5: null dizi anahtarı eskimiş sayıldı

PHP 8.5 ve sonrasında null değerini dizi anahtarı olarak kullanmak ya da array_key_exists() fonksiyonuna anahtar olarak vermek eskimiş sayılıyor. array_key_exists() sayfasının değişiklik kaydı yerine boş metin kullanılmasını önerir. Bu nedenle anahtarı bir değişkenden alıyorsanız ve o değişken tanımsız olabiliyorsa (tanımsız değişken null sayıldığı için) PHP 8.5'te iki uyarıyı birden görürsünüz. Üstelik ?? burada sizi korumaz:

php
<?php
$a = ['x' => 1];
echo $a[$anahtar] ?? 'yok', PHP_EOL;
/* çıktı:
Warning: Undefined variable $anahtar in /yol/ornek.php on line 3

Deprecated: Using null as an array offset is deprecated, use an empty string instead in /yol/ornek.php on line 3
yok
*/

Sebep şudur: ?? dizi erişiminin sonucunu korur, ama köşeli parantezin içindeki anahtar ifadesi yine normal biçimde değerlendirilir. Çözüm anahtarı kullanmadan önce güvenceye almaktır, örneğin $anahtar = $_GET['k'] ?? ''; ile. Bu davranış PHP 8.5 gerektirir; eski sürümlerde yalnızca tanımsız değişken uyarısı görünür.

Neden @ ile susturmamalısınız?

@$dizi['anahtar'] yazımı uyarıyı gizler ama nedenini ortadan kaldırmaz: değer yine null olur ve hata bir sonraki adımda, çok daha zor anlaşılır biçimde ortaya çıkar. Ayrıca kendi hata işleyicinizi kullanıyorsanız @ operatörünün etkisini doğru ele almanız gerekir. Uyarıyı günlükte görmek, kodda nerede bir varsayımın yanlış olduğunu bulmanın en hızlı yoludur. Hata seviyeleri ve @ operatörünün ayrıntıları için PHP error_reporting ve hata seviyeleri yazısına bakın.

Bu uyarıların "kardeşi" olan iki durumu karıştırmayın: tanımsız bir sabit kullanmak uyarı değil Error fırlatır ve betiği durdurur; tanımsız bir fonksiyonu çağırmak da öyle. Bunların çözümü PHP fatal error: uncaught error çözümü yazısında.

Uygulamalı örnek: eksik form verisiyle çalışan fonksiyon

Gerçek kodda bu uyarılar genellikle tek başına değil, zincir hâlinde gelir. Aşağıdaki fonksiyon bir form dizisinden özet metin üretiyor; form eksik geldiğinde tek satırda üç ayrı uyarı çıkıyor:

php
<?php
function ozet(array $form): string
{
    if ($form['bulten'] === 'evet') {
        $not = ' (bülten aboneliği)';
    }
    return $form['ad'] . ' - ' . $form['eposta'] . $not;
}

echo ozet(['ad' => 'Zeynep']), PHP_EOL;
/* çıktı:
Warning: Undefined array key "bulten" in /yol/ornek.php on line 4

Warning: Undefined array key "eposta" in /yol/ornek.php on line 7

Warning: Undefined variable $not in /yol/ornek.php on line 7
Zeynep -
*/

Üç uyarının üç farklı nedeni var: bulten ve eposta anahtarları dizide yok, $not değişkeni ise yalnızca if dalı çalışırsa oluşuyor. Düzeltilmiş sürümde her dış değeri bir kez, varsayılanıyla okuyup yerel değişkene alıyoruz ve $not değişkenini koşuldan önce başlatıyoruz:

php
<?php
function ozet(array $form): string
{
    $ad = $form['ad'] ?? 'İsimsiz';
    $eposta = $form['eposta'] ?? 'e-posta yok';
    $not = '';
    if (($form['bulten'] ?? 'hayır') === 'evet') {
        $not = ' (bülten aboneliği)';
    }
    return $ad . ' - ' . $eposta . $not;
}

echo ozet(['ad' => 'Zeynep']), PHP_EOL; // çıktı: Zeynep - e-posta yok
echo ozet(['ad' => 'Ali', 'eposta' => 'ali@ornek.com', 'bulten' => 'evet']), PHP_EOL;
// çıktı: Ali - ali@ornek.com (bülten aboneliği)

Bu kalıbın ek bir yararı var: fonksiyonun hangi alanları beklediği ve eksik olduklarında ne olacağı artık en üstte, tek bakışta okunuyor. Kod büyüdükçe bu tür girişleri bir sınıfın tipli özelliklerine dönüştürmek de kolaylaşır.

Sık yapılan hatalar

Sık yapılan hatalar: isset() ile kontrol edip değeri null olan geçerli bir anahtarı "yok" saymak; bu..., empty() ile kontrol edip 0 değerli bir alanı boş saymak., $_POST['alan']...
  • isset() ile kontrol edip değeri null olan geçerli bir anahtarı "yok" saymak; bu durumda array_key_exists() gerekir.
  • empty() ile kontrol edip 0 değerli bir alanı boş saymak.
  • $_POST['alan'] değerini kontrolsüz okuyup form ilk açıldığında uyarı almak.
  • Değişkeni yalnızca if içinde tanımlayıp dışarıda kullanmak; değişkeni koşuldan önce varsayılan bir değerle başlatın.
  • Uyarıları üretimde kapatıp hiç incelememek; display_errors kapalı olsa da günlük dosyasını düzenli kontrol edin.

Sık Sorulan Sorular

Undefined variable uyarısı sitemi durdurur mu?

Hayır. Uyarı E_WARNING seviyesindedir; PHP değeri null sayar ve devam eder. Ancak bu null değer sonraki satırda bir fonksiyona tipli parametre olarak giderse TypeError oluşabilir. O hatanın çözümü TypeError ve ArgumentCountError yazısında.

isset() fonksiyon mu?

Kılavuzda fonksiyon gibi listelense de isset() ve empty() dil yapılarıdır (language construct). Bu yüzden tanımsız değişkeni argüman olarak almaları uyarı üretmez; normal bir fonksiyona tanımsız değişken geçirdiğinizde ise uyarı alırsınız.

Çok boyutlu dizide derindeki anahtarı nasıl kontrol ederim?

?? ve isset() zincirin tamamını kontrol eder: $veri['kullanici']['adres']['il'] ?? '' ara anahtarlardan biri eksik olsa bile uyarı vermez. array_key_exists() ise kılavuzun notuna göre yalnızca ilk boyuta bakar; iç içe kullanmak için her seviyeyi ayrı kontrol etmeniz gerekir.

Nesnelerde isset() ile property_exists() arasındaki fark nedir?

isset($nesne->alan), dizilerdeki gibi özelliğin erişilebilir ve null dışında bir değere sahip olup olmadığını sorar. property_exists() ise yalnızca özelliğin sınıfta bildirilmiş olup olmadığına bakar; değeri null olsa bile true döner. Yukarıdaki Musteri örneğinde $adres bildirildiği için property_exists() doğru, değeri null olduğu için isset() yanlış sonuç verdi.

Uyarıdaki satır numarası neden bazen yanlış yeri gösteriyor?

Satır numarası, tanımsız değeri okuyan satırı gösterir; değerin atanması gereken satırı değil. Yukarıdaki form örneğinde $not için uyarı 7. satırda çıktı, ama asıl sorun değişkenin 4. satırdaki koşula bağlı olarak oluşmasıydı. Uyarıyı gördüğünüz satırdan geriye doğru, değerin nereden gelmesi gerektiğini takip edin.

PHP 7'de görmediğim uyarıları neden şimdi görüyorum?

PHP 8.0 bu durumları "Notice" seviyesinden "Warning" seviyesine yükseltti ve kılavuza göre PHP 8.0 ile varsayılan olarak tüm tanılama hataları raporlanır hâle geldi. Kod aynı kalsa da çıktı değişir. Sürüm yükseltme sırasında karşılaşılan diğer uyarılar için PHP 8.4 deprecated uyarıları yazısına bakın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: PHP öğrenme rehberi · Değişkenler ve kapsam · Dizi temelleri · TypeError hataları · Fatal error çözümleri · Hata seviyeleri