PHP web tabanlı yazılım geliştirmeyi oldukça kolaylaştıran, açık kaynağa çok yatkın, öğrenmesi ve uygulaması kolay ve bu nedenlerden dolayı çok popüler bir programlama dili. Ancak bu sizi aldatmasın, programlama yapmak, kullanılan dilden bağımsız olarak dikkat, özen ve çalışma isteyen bir iş. Bunun doğal sonucu olarak hata yapmak kaçınılmaz oluyor.
Adı üstünde, isset() fonksiyonu bir değişkenin var olup olmadığını anlamak için kullanılır. Yalnız, bir değişken var olmasına rağmen değeri NULL ise isset() fonksiyonu yine de false döndürecektir. Bu sebeple dikkatle kullanılmadığında isset() problem çıkartabilir.
$degerler = array( "null" => NULL, "false" => FALSE, "true" => TRUE, "sifir" => 0, "sayi" => 1 ); // isset($degerler["null"]) = FALSE // isset($degerler["false"]) = TRUE // isset($degerler["true"]) = TRUE // isset($degerler["sifir"]) = TRUE // isset($degerler["sayi"]) = TRUE // isset($degerler["baska_bir_sey"]) = FALSE
"isset.php" isminde bir php dosyası yaratalım:
<body> <?=isset($_GET['id']) ? 'var' : 'yok'?> </body>
URL satırına "localhost/isset.php" yazarsak ekranda göreceğimiz sonuç "yok" olacaktır ancak "localhost/isset.php?id" yazarsak sonuç "var" olacaktır çünkü her ne kadar bir değer atamadıysak da $_GET içinde bir id değişkeni yarattık. Yani değeri NULL değil.
Uzun lafın kısası, isset() bazen sağduyunuza aykırı sonuç döndürebilir.
Önemli: Bir değişken var ve değeri NULL'den farklı ise isset() TRUE döndürür. Bir değişken yok ya da değeri NULL ise isset() FALSE döndürür. Eğer bir değişken var ancak herhangi bir değer atanmamış ise isset() TRUE döndürür.
Öncelikle return by reference ve return by value nedir ona bakalım. Bir fonksiyondan aldığımız dönüş eğer asli değişkenin kendisi ise buna return by reference (referans ile dönüş), eğer asli değişkenin bir kopyası ise return by value (değer ile dönüş) diyoruz.
Hadi bir class oluşturalım ve bu classdaki değerler ile biraz oynayalım:
class isim { private $deger = array( "isim" => "Mehmet", "soyad" => "Yılmaz" ); public function isimAl() { return $this->deger; } } $isim = new isim(); $isim->isimAl()["isim"] = "Hakan"; $isim->isimAl()["soyad"] = "Özakar"; $isim->isimAl()["site"] = "beltslib.net"; print_r($isim->isimAl());
Bu işlemin çıktısı şöyle olur:
Array ( [isim] => Mehmet [soyad] => Yılmaz )
Neden? İsim ve soyad değerlerini değiştirdik ve "site" adında yeni bir değer ekledik. Neden ilk halini görüyoruz?
Çünkü bu, değer döndüren (by value) bir fonksiyon. Başka bir deyişle fonksiyonun bize gönderdiği veri, asli değişkenin kendisi değil bir kopyası. Biz değişiklikleri bir kopyanın üzerinde yaptık, asıl değişken gıdıklanmadı bile...
Bu durumu nasıl düzeltiriz?
$isim = new isim(); $kukla = $isim->isimAl(); $kukla["isim"] = "Hakan"; $kukla["soyad"] = "Özakar"; $kukla["site"] = "beltslib.net"; print_r($kukla);
Bu işlemin çıktısı:
Array ( [isim] => Hakan [soyad] => Özakar [site] => beltslib.net )
İstediğimiz çıktıyı aldık. Ancak fonksiyonumuz hala by value çalışıyor yani değişiklikleri yine bir kopyada yaptık, asli değişkeni yine etkilemedik. Peki yaptığımız değişiklik asli değişkene de yansısın istersek ne yapmalıyız?
class isim { private $deger = array( "isim" => "Mehmet", "soyad" => "Yılmaz" ); public function &isimAl() { // Buradaki "&" // işaretine dikkat... return $this->deger; } } $isim = new isim(); $isim->isimAl()["isim"] = "Hakan"; $isim->isimAl()["soyad"] = "Özakar"; $isim->isimAl()["site"] = "beltslib.net"; print_r($isim->isimAl());
Bu işlemin çıktısı:
Array ( [isim] => Hakan [soyad] => Özakar [site] => beltslib.net )
Bu defa hem istediğimiz çıktıyı aldık hem de asli değişkeni değiştirdik çünkü artık fonksiyonumuz by reference çalışıyor. Peki bunu nasıl yaptık? Fonksiyon isminin başına koyduğumuz "&" işareti sayesinde. Bu işaret sayesinde fonksiyonun by reference çalışmasını istediğimizi belirttik.
Bir örnek daha inceleyelim:
class isim { private $deger = arrayObject(); public function __construct() { $this->deger = new ArrayObject(); $this->deger["isim"] = "Mehmet"; $this->deger["soyad"] = "Yılmaz"; } public function isimAl() { return $this->deger; } } $isim = new isim(); $isim->isimAl()["isim"] = "Hakan"; $isim->isimAl()["soyad"] = "Özakar"; $isim->isimAl()["site"] = "beltslib.net"; print_r($isim->isimAl());
Peki bu işlemin çıktısı nasıl olacak? İlk örnekteki gibi olacağını tahmin ettiyseniz yanıldınız. Burada fonksiyonumuz by reference çalıştı... Neden mi? Çünkü $deger degiskenini ArrayObject() olarak tanımladık. ArrayObject() aynen bir dizi (array) gibi çalışır ama aslında bir nesnedir (object) ve PHP, nesneleri her zaman by reference olarak gönderir.
Kısacası, fonksiyondan aldığımız bir veri ile çalışacaksak öncelikle bu verinin bize by value mu yoksa by reference mi ulaştığından emin olmalıyız. PHP değişkenleri ve dizi-değişkenleri by value, nesneleri by reference gönderir.
Bazen bir dizinin tüm elemanları üzerinde değişiklik yapmak isteriz ancak foreach döngüsünü alıştığımız gibi kullanarak bunu başaramayız:
$dizi = array(1, 2, 3); foreach($dizi as $deger) { $deger++; } print_r($dizi);
Sonuç:
Array ( [0] => 1 [1] => 2 [2] => 3 )
Neden? Bir önceki örnekte incelemiştik, PHP dizileri by value olarak gönderiyor. Bu nedenle yaptığımız değişiklikleri aslında dizimizde değil, onun bir kopyası üzerinde yaptık. Asıl dizi bu işten etkilenmedi. Bu nedenle bu işlemin bir anlam ifade edebilmesi için dizimizi by reference olarak göndermeliyiz:
$dizi = array(1, 2, 3); foreach($dizi as &$deger) { // "&" işaretine dikkat! $deger++; } print_r($dizi);
Sonuç:
Array ( [0] => 2 [1] => 3 [2] => 4 )
Buraya kadar güzel. Sonuç tam da beklediğimiz gibi. Ama asıl dikkat etmemiz gereken işler bundan sonra gerçekleşmeye başlıyor. Şimdi de aynı dizi üzerinde by value bir foreach çalıştırarak değerleri yazdıralım:
foreach($dizi as $deger) { echo $deger."<br>"; }
Sonuç:
2 3 3
Garip işler oluyor. Değerler 2, 3, 4 değil miydi? Sondaki "3" nereden çıktı?
İlk foreach döngüsünü by reference çalıştırdığımız için böyle oldu. İlk döngüde $deger değişkenine dizimizi by reference olarak çağırmıştık. İşlem tamamlandığında $deger değişkeni dizimizin son elemanına işaret eder şekilde kaldı, yani $deger = $dizi[2]
Yeni foreach döngümüzü by value olarak çalıştıralım ve adım adım inceleyelim:
Sonuç: Foreach döngüsünde dizimizi by reference olarak kullanacaksak işlem tamamlandıktan sonra kullandığımız değeri sıfırlamayı "unset($deger);" unutmayalım. Tabii kodumuzun devamında saç baş yoldurtan hatalarla karşılaşmak istemiyorsak...
PHP kodu yazarken genelde veri tabanından çektiğimiz bilgileri düzenleyerek kullanırız. Bir çok internet yazılımcısı için "veri tabanından bilgi kümesi çekmek" cümlesi yalnızca "SELET * FROM tablo" yazmayı ifade ediyor. Belki biraz abartılı bulabilirsiniz ama bence SQL yazmayı bilmeden PHP kullanmak okuma yazma bilmeden roman yazmaya çalışmak gibi.
İşte size süratle giden bir otomobilde frene asılmaya benzeyen bir kod:
<?php $rs = $db->query("SELECT * FROM urunler"); while($row = $rs->fetch_assoc()) { $frs = $db->query("SELECT * FROM fotolar WHERE urun_id = $row[id] LIMIT 1"); $foto = $frs->fetch_assoc(); echo '<div><img src="'.$foto['foto'].'" alt="Foto" /> '.$row['isim'].'</div>'; } ?>
Bunu yapmayın!
Onun yerine veri kümesini ihtiyaç duyacağınız tüm bilgileri içerecek şekilde oluşturun ve döngünüzü öyle başlatın:
<?php $rs = $db->query(" SELECT urunler.isim, fotolar.foto FROM urunler INNER JOIN fotolar ON fotolar.urun_id = urunler.id GROUP BY urunler.id "); while($row = $rs->fetch_assoc()) { echo '<div><img src="'.$row['foto'].'" alt="Foto" /> '.$row['isim'].'</div>'; } ?>
Döngü içinde sorgu çalıştırmak hem işlemi yavaşlatacak hem de sunucunuzun kaynaklarını sömürecektir. Mecbur kalmadığınız sürece döngü içinde sorgu çalıştırmayın. Veri tabanınızı doğru tasarlayın, SQL cümlenizi doğru yazın ve döngü içinde sorgu çalıştırmaya mecbur kalmayın!