Skip to content

12. SGBD ve MySQL’in kullanımı

Image

Şimdi, MySQL veritabanını kullanarak PHP komut dosyalarını yazacağız:

Image

Yukarıdaki mimaride, PHP (1) komut dosyası, SGBD (Veritabanı Yönetim Sistemi) (3) ile doğrudan iletişim kurmaz. Bu komut dosyası, SGBD sürücüsü veya SGBD sürücüsü olarak adlandırılan bir aracı aracılığıyla iletişim kurar. PHP, bu sürücüler için PDO (PHP Veri Nesneleri) adlı standart bir arayüz sağlar. Bu arayüz, her bir SGBD'e uyarlanmış farklı sınıflar tarafından uygulanır: SGBD MySQL için bir sınıf, SGBD PostgreSQL için başka bir sınıf… SGBD'i değiştirmek için sürücüyü değiştiririz:

Image

PDO sürücüsü, PHP (1) komut dosyasını SGBD (3, 6) komut dosyasından ayırır. Bu sürücüler standart bir arayüzü uyguladığından, PHP (1) komut dosyasının, SGBD MySQL (3) sürümünden SGBD PostgreSQL (6) sürümüne geçildiğinde değişmeyeceği beklenebilir. Ancak gerçekte böyle bir ideal durum söz konusu değildir. Nitekim SGBD ile iletişim kurmak için, PHP betiği SQL (Standart Sorgu Dili) komutları gönderir. Bu dil, tüm SGBD'ler tarafından desteklenir ancak eksiktir. Bu nedenle SGBD'ler, bu dile özel komutlar eklemiştir. Bu, SGBD'ler arasındaki uyumsuzluğun başlıca nedenlerinden biridir. Ayrıca, veritabanlarında kullanılabilen veri türleri, bir SGBD'ten diğerine farklılık gösterebilir. Örneğin, PostgreSQL, SGBD ve MySQL'e kıyasla çok daha fazla veri türünü desteklemektedir. Bu, uyumsuzluğun ikinci nedenidir. Bir başka neden ise otomatik birincil anahtarların yönetimi (SGBD tarafından üretilenler): neredeyse her SGBD'in kendine özgü bir politikası vardır. Vb… Uyumsuzluk nedenleri çoktur.

MySQL (3) dosyasından PostgreSQL (6) dosyasına geçerken PHP (1) komut dosyasını yeniden yazmaktan kaçınmak istiyorsak, genellikle PHP (1) betiği ile PDO (2, 5) sürücüsü arasına, iki SGBD arasındaki uyumsuzlukları ortadan kaldırmak üzere yeni bir katman eklemek zorunda kalırız. Ancak, karşılaşacağımız basit durumlarda bu ek katmana gerek kalmayacaktır.

Şimdi SGBD MySQL'i kullanacağız. Bu, Laragon paketinde yer almaktadır (bkz. bağlantı paragrafı).

Okuyucu, veritabanı ve SQL dili kavramlarına yeni ise, [http://sergetahe.com/cours-tutoriels-de-programmation/cours-tutoriel-sql-avec-le-sgbd-firebird/] belgesini okuyabilir. Bu belge, MySQL yerine SGBD Firebird’ü kullanmaktadır, ancak veritabanları ve SQL dilinin temellerini açıklamaktadır. MySQL gibi, Firebird’ün de serbestçe kullanılabilen ve az bellek kaplayan bir sürümü bulunmaktadır.

12.1. Veritabanı Oluşturma

Şimdi Laragon aracıyla bir veritabanı ve bir kullanıcı MySQL'in nasıl oluşturulacağını göstereceğiz.

Image

  • Laragon [1] başlatıldıktan sonra, bir menüden yönetilebilir;
  • [3-5]'te, henüz kurulmamışsa MySQL yönetim aracı [phpMyAdmin] kurulur;

Image

  • [6]'te, Apache web sunucusu ile SGBD ve MySQL başlatılır;
  • [7]'te Apache sunucusu başlatılır;
  • [8]'te, SBD ve MySQL başlatılır;

Image

  • [8-10]'te, [dbpersonnes] ve [11] adında bir veritabanı oluşturulur. Kişiler veritabanı oluşturulacaktır;

Image

  • [11]'te, az önce oluşturduğumuz veritabanını yöneteceğiz;

Image

  • [Bases de données] işlemi, URL ve [http://localhost/phpmyadmin]'e bir web isteği gönderir. Yanıtı Laragon'un Apache web sunucusu verir. URL [http://localhost/phpmyadmin], daha önce kurduğumuz [phpMyAdmin] yardımcı programının URL'sidir [5]. Bu yardımcı program, MySQL veritabanlarını yönetmeye olanak tanır;
  • varsayılan olarak, veritabanı yöneticisinin oturum açma bilgileri şunlardır: root [13], şifre yok [14];

Image

  • [16], daha önce oluşturduğumuz veritabanı;

Image

  • Şu anda [dbpersonnes], [17] ve boş olan [18] veritabanlarımız var;

[admpersonnes] adlı bir kullanıcı oluşturuyoruz ve bu kullanıcıya [nobody] şifresini veriyoruz; bu kullanıcı, [dbpersonnes] veritabanında tüm haklara sahip olacak:

Image

  • [19]'te, [dbpersonnes] veritabanında konumlandırılıyoruz;
  • [20]'te, [Privileges] sekmesini seçiyoruz;
  • [21-22]'te, [root] kullanıcısının [dbpersonnes] veritabanında tüm haklara sahip olduğu görülür;
  • [23]'te yeni bir kullanıcı oluşturulur;

Image

  • [25-26]'te, kullanıcının kullanıcı adı [admdbpersonnes] olacaktır;
  • [27-29]'te, şifresi [nobody] olacaktır;
  • [30] durumunda, phpMyAdmin şifrenin çok zayıf (kırılması kolay) olduğunu belirtir. Üretim ortamında, [31] ile güçlü bir şifre oluşturmak tercih edilir;
  • [32]'te, [admdbpersonnes] kullanıcısının [dbpersonnes] veritabanı üzerinde tüm haklara sahip olması gerektiği belirtilir;
  • [33]'te, verilen bilgiler onaylanır;

Image

  • [35]'te, phpMyAdmin, kullanıcının oluşturulduğunu belirtir;
  • [36]'te, veritabanında oluşturulan SQL emri;
  • [37]'te, [admpersonnes] kullanıcısının [dbpersonnes] veritabanı üzerinde tüm haklara sahip olduğu belirtilir;

Artık elimizde şunlar var:

  • MySQL ve [dbpersonnes] veritabanları;
  • bu veritabanı üzerinde tüm haklara sahip bir [admpersonnes/nobody] kullanıcısı;

Veritabanını kullanmak için PHP komut dosyaları yazacağız. PHP, veritabanlarını yönetmek için çeşitli kütüphanelere sahiptir. PHP kodu ile SGBD kodu arasına yerleştirilen PDO (PHP Data Objects) kütüphanesini kullanacağız:

Image

PDO kütüphanesi, PHP komut dosyasının kullanılan SGBD'in tam doğasından bağımsız çalışmasını sağlar. Dolayısıyla yukarıda, SGBD MySQL, SGBD PostgreSQL ile değiştirilebilir ve bu, PHP betiğinin koduna minimum düzeyde etki eder. Bu kütüphane varsayılan olarak mevcut değildir. Kullanılabilirliği şu şekilde kontrol edilebilir:

Image

  • [1-4]'te, etkin olan PDO uzantılarını kontrol ederiz;
  • [5] dosyasında, SGBD ve MySQL için PDO uzantısının etkin olduğu görülür. Diğerleri etkin değildir. Etkinleştirmek için üzerlerine tıklamak yeterlidir;

Bir uzantıyı etkinleştirmenin başka bir yolu da, PHP dosyasını yapılandıran [php.ini] dosyasını (bağlantı paragrafı) doğrudan değiştirmektir:

Image

  • [1]'te, MySQL'in PDO uzantısı etkinleştirilmiştir;
  • [2] dosyasında, Firebird'ün PDO uzantısı devre dışı bırakılmıştır;

[php.ini] dosyasını değiştirdikten sonra, değişikliklerin geçerli olması için Laragon'un PHP dosyasını yeniden başlatmanız gerekir.

12.2. MySQL veritabanına bağlanma

SGBD'e bağlantı, bir PDO nesnesinin oluşturulmasıyla sağlanır. Oluşturucu, çeşitli parametreleri kabul eder:

$dbh=new PDO(string $dsn,string $user,string $passwd,array $driver_options)

Parametrelerin anlamları şöyledir:

$dsn
(Veri Kaynağı Adı), SGBD'in türünü ve internet üzerindeki konumunu belirten bir dizedir. "mysql:host=localhost" dizesi, yerel sunucuda çalışan bir SGBD MySQL ile karşı karşıya olduğumuzu gösterir. Bu dize, SGBD'in dinleme bağlantı noktası ve bağlanılmak istenen veritabanının adı gibi başka parametreleri de içerebilir: "mysql:host=localhost:port=3306:dbname=dbpersonnes";
$user
bağlanan kullanıcının kimliği;
$passwd
şifresi;
$driver_options
SGBD sürücüsü için bir seçenek tablosu;

Yalnızca ilk parametre zorunludur. Bu şekilde oluşturulan nesne, daha sonra bağlanılan veritabanında gerçekleştirilen tüm işlemlerin temelini oluşturacaktır. PDO nesnesi oluşturulamazsa, PDOException türünde bir istisna atılır.

Aşağıda [mysql-01.php] bağlantısına ilişkin bir örnek verilmiştir:


<?php

// yerel bir veritabanına bağlanma MySql
// kullanıcı kimliği (admpersonnes,nobody)
const ID = "admpersonnes";
const PWD = "nobody";
const HOTE = "localhost";

try {
  // bağlantı
  $dbh = new PDO("mysql:host=".HOTE, ID, PWD);
  print "Connexion réussie\n";
  // bağlantının kapatılması
  $dbh = NULL;
} catch (PDOException $e) {
  print "Erreur : " . $e->getMessage() . "\n";
  exit();
}

Sonuçlar:

Connexion réussie

Açıklamalar

  • 11. satır: SGBD'e bağlantı, bir PDO nesnesinin oluşturulmasıyla sağlanır. Oluşturucu burada aşağıdaki parametrelerle kullanılır:
    • SGBD nesnesinin türünü ve internet üzerindeki konumunu belirten bir dize. "mysql:host=localhost" dizesi, yerel sunucuda çalışan bir SGBD MySQL nesnesiyle karşı karşıya olduğumuzu gösterir. Bağlantı noktası belirtilmemiştir. Bu durumda varsayılan olarak 3306 numaralı bağlantı noktası kullanılır. Veritabanı adı da belirtilmemiştir. Bu durumda SGBD MySQL'e bağlantı kurulur; belirli bir veritabanının seçimi daha sonra yapılır;
    • bir kullanıcı adı;
    • kullanıcı şifresi;
  • 14. satır: Bağlantı, başlangıçta oluşturulan PDO nesnesinin silinmesiyle sonlandırılır;
  • satır 15: SGBD nesnesine bağlantı başarısız olabilir. Bu durumda, PDOException türünde bir istisna atılır. Bu istisna, PHP [RuntimeException] istisnasından türetilmiştir;
  • 16. satır: istisnaya ait hata mesajı görüntülenir;
  1. satıra yanlış bir şifre girerek komut dosyasını yeniden çalıştıralım. Sonuç şu şekilde olur:

Erreur : SQLSTATE[HY000] [1045] Access denied for user 'admpersonnes'@'localhost' (using password: YES)

12.3. Tablo Oluşturma

[mysql-02.php] komut dosyası, bir veritabanında tablo oluşturulmasını gösterir:


<?php

// veritabanı kimliği
const DSN = "mysql:host=localhost;dbname=dbpersonnes";
// kullanıcı kimlik bilgileri
const ID = "admpersonnes";
const PWD = "nobody";

try {
  // veritabanına bağlantı MySql
  $connexion = new PDO(DSN, ID, PWD);
  // varsa "kişiler" tablosunun silinmesi
  $sql = "drop table personnes";
  $connexion->exec($sql);
  // "kişiler" tablosunun oluşturulması
  $sql = "create table personnes (prenom varchar(30) NOT NULL, nom varchar(30) NOT NULL, age integer NOT NULL, primary key(nom,prenom))";
  $connexion->exec($sql);
} catch (PDOException $ex) {
  // hata mesajını göster
  print "Erreur : " . $ex->getMessage() . "\n";
} finally {
  // gerekirse bağlantı kesilir
  $connexion = NULL;
}
// son
print "Terminé\n";
exit;

Açıklamalar

  • 11. satır: veritabanına bağlantı. Bu her zaman yapılacak ilk işlemdir. Bağlantının sonucu, veritabanı işlemlerinin gerçekleştirileceği bir [PDO] nesnesidir;
  • 13. satır: SQL [drop table personnes] komutu, [personnes] tablosunu [dbpersonnes] veritabanından silecektir. [personnes] tablosu mevcut değilse, bu durum bir hataya neden olmaz;
  • 14. satır: [dbpersonnes] veritabanında önceki SQL komutunun yürütülmesi. Bu yürütme, 18. satırda durdurulacak olan bir [PDOException] komutunu başlatabilir;
  • 16. satır: Bu SQL komutu, [personnes] adlı bir tablo oluşturur. Bir tablo satırlardan ve sütunlardan oluşur. Sütunlar, tablonun yapısını oluşturur. Satırlar ise tablonun içeriğini oluşturur. Bir veritabanı bir veya daha fazla tablo içerebilir. [personnes] tablosu burada üç sütuna sahip olacaktır:
    • ad: bir kişinin adı, en fazla 30 karakterden oluşan bir dize biçiminde;
    • soyadı: aynı kişinin soyadı, en fazla 30 karakterden oluşan bir dize biçiminde;
    • yaş: kişinin yaşı, tamsayı biçiminde;
    • Bir sütuna NOT NULL özniteliği eklenmesi, o sütunun bir değer içermesini zorunlu kılar. Değer girilmemesi durumunda [PDOException] hatası oluşur;
    • [primary key(nom,prenom)], [personnes] tablosuna bir birincil anahtar atar. Bir birincil anahtar, tablonun her satırı için benzersiz bir değere sahiptir. Burada birincil anahtar, satırdaki [nom] ve [prenom] sütunlarının birleştirilmesiyle elde edilecektir. Bu kısıtlama sayesinde, tabloda aynı adı ve soyadına sahip iki kişi, yani iki eşadlı kişi bulunamaz. Tabloda bir kişinin aynı isme sahip bir eşismini oluşturmak, [PDOException] hatasına neden olur;
  • 17. satır: [dbpersonnes] veritabanında SQL komutunun yürütülmesi;
  • satır 20: [PDOException] hatası oluşursa, ilgili hata mesajı görüntülenir;
  • satır 21-24: istisna olsun ya da olmasın her durumda [finally] cümlesine geçilir ve veritabanı bağlantısı kapatılır (satır 23);

Sonuçlar:

Komut dosyasının yürütülmesi hatasız gerçekleşirse, phpMyAdmin'te tablonun varlığı görülebilir:

Image

Image

  • [3]'te veritabanı;
  • [4]'te, sunulan tablo;
  • [5]'te, tabloların yapısı [Structure] sekmesinde gösterilmiştir;
  • [6-8]'te, tablonun üç sütunu;
  • [9]'te, üç sütundan hiçbiri boş olamaz;

Image

  • [10]'te, tablonun dizin listesi yer alır. Bir dizin, tabloda belirli bir dizine sahip satırları, tablonun satırlarını sırayla taramaya kıyasla daha hızlı bir şekilde bulmayı sağlar. Birincil anahtar her zaman dizinlerin bir parçasıdır, ancak bir dizin birincil anahtar olmayabilir;
  • [11]'te, dizin burada birincil anahtardır;
  • [12]'te, dizin her satırın [nom, prenom] sütunlarından oluşur;

Şimdi, sırasıyla veritabanı adı, kullanıcı adı ve şifre üzerinde hatalar oluşturursak ne olacağına bakalım:

Var olmayan bir veritabanı adı girersek:


Erreur : SQLSTATE[HY000] [1044] Access denied for user 'admpersonnes'@'%' to database 'dbpersonnes2'

Var olmayan bir kullanıcı adı girersek:


Erreur : SQLSTATE[HY000] [1045] Access denied for user 'admpersonnes2'@'localhost' (using password: YES)

Yanlış bir şifre girersek:


Erreur : SQLSTATE[HY000] [1045] Access denied for user 'admpersonnes'@'localhost' (using password: YES)

12.4. Bir tablonun doldurulması

Aşağıdaki [creation.txt] metin dosyasında bulunan SQL komutlarını çalıştıran PHP komut dosyasını yazacağız:

drop table if exists personnes
SET NAMES 'utf8'
create table personnes (prenom varchar(30) not null, nom varchar(30) not null, age integer not null, primary key (nom,prenom))
insert into personnes (prenom, nom, age) values('Paul','Langevin',48)
insert into personnes (prenom, nom, age) values ('Sylvie','Lefur',70)
insert into personnes (prenom, nom, age) values ('Sylvie','Lefur',70)
insert into personnes (prenom, nom, age) values ('Pierre','Nicazou',35)
insert into personnes (prenom, nom, age) values ('Géraldine','Colou',26)
insert into personnes (prenom, nom, age) values ('Paulette','Girond',56)
insert into personnes (prenom, nom, age) values ('Paulette','Girond',56)

Açıklamalar

  • SQL dili (Yapısal Sorgu Dili), SQL komutlarında büyük/küçük harf ayrımı yapmaz;
  • 1. satır: [personnes] tablosu varsa silinir;
  • 2. satır: MySQL sunucusuna, UTF-8 ile kodlanmış karakterler gönderileceğini bildirir. Örneğin, 7. satırda Géraldine'in adındaki "é" harfinin veritabanına eklenebilmesi için, MySQL'e özgü bu SQL komutu gereklidir. 2. satır eklenmezse, "é" harfi iki garip karakterden oluşan bir diziye dönüştürülecektir. İstemci, Netbeans altında yazılmış PHP komut dosyasıdır. Ancak bu komut dosyası, aşağıdaki UTF-8 ve [1-4] dosyalarını kodlar:

Image

  • 3. satır: Üç sütunlu (ad, soyad, yaş) ve birincil anahtar (soyad, ad) içeren [personnes] tablosunun oluşturulması;
  • 4-10. satırlar: [personnes] tablosuna 7 satır eklenmesi;
  • 6. satır: Bu ekleme komutu, 5. satırdakiyle aynı eklemeyi denediği için başarısız olmalıdır. Birincil anahtar kısıtlaması bu eklemeyi engellemelidir: Aynı adı ve soyadına sahip iki kişi olamaz;
  • 10. satır: Bu ekleme komutu, 9. satırdakiyle aynı eklemeyi denediği için başarısız olmalıdır;

Bu metin dosyasındaki SQL komutlarını yürütmekle görevli PHP komut dosyası şöyledir: [mysql-03.php]:


<?php

// veritabanı kimliği
const DSN = "mysql:host=localhost;dbname=dbpersonnes";
// kullanıcı kimlik bilgileri
const ID = "admpersonnes";
const PWD = "nobody";
// çalıştırılacak SQL komut metin dosyasının kimliği
const SQL_COMMANDS_FILENAME = "creation.txt";

// veritabanına bağlantı açma MySql
try {
  $connexion = new PDO(DSN, ID, PWD);
} catch (PDOException $ex) {
  // hata görüntüleme
  print "Erreur : " . $ex->getMessage() . "\n";
  exit;
}
// SGBD'te her hata durumunda bir istisna atılmasını istiyoruz
$connexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// komut dosyasının çalıştırılması SQL
$erreurs = exécuterCommandes($connexion, SQL_COMMANDS_FILENAME, TRUE, FALSE);
// bağlantının kapatılması
$connexion = NULL;
// hata sayısının görüntülenmesi
printf("\n-----------------------\nIl y a eu %d erreur(s)\n", count($erreurs));
for ($i = 0; $i < count($erreurs); $i++) {
  print "$erreurs[$i]\n";
}

// işlem tamamlandı
print "Terminé\n";
exit;

// ---------------------------------------------------------------------------------
function exécuterCommandes(PDO $connexion, string $SQLFileName, bool $suivi = FALSE, bool $arrêt = TRUE): array {
// $connexion bağlantısını kullan
// SQL metin dosyasında bulunan komutları çalıştır SQLFileName
// bu dosya, her satırda bir tane olmak üzere çalıştırılacak SQL komut dosyasıdır
// eğer $suivi=1 ise, her bir SQL komutunun yürütülmesi üzerine, komutun başarılı olup olmadığına dair bir mesaj görüntülenir
// $arrêt=1 ise, işlev karşılaşılan ilk hatada durur; aksi takdirde tüm SQL komutlarını yürütür
// fonksiyon bir dizi döndürür (hata sayısı, hata1, hata2…)
// SQLFileName dosyasının varlığı kontrol edilir

  if (!file_exists($SQLFileName)) {
    return ["Le fichier [$SQLFileName] n'existe pas"];
  }

  // SQLFileName dosyasında bulunan SQL sorgularının yürütülmesi
  // bunlar bir tabloya eklenir
  $requêtes = file($SQLFileName);
  // hata mı?
  if ($requêtes === FALSE) {
    return ["Erreur lors de l'exploitation du fichier SQL [$SQLFileName]"];
  }
  // sorguları tek tek çalıştırıyoruz - başlangıçta hata yok
  $erreurs = [];
  $i = 0;
  $fini = FALSE;
  while ($i < count($requêtes) && !$fini) {
    // sorgunun metnini alıyoruz
    // trim, satır sonu işaretini kaldıracak
    $requête = trim($requêtes[$i]);
    // boş sorgu mu?
    if (strlen($requête) == 0) {
      // isteği atlayıp bir sonraki isteğe geçilir
      $i++;
      continue;
    }
    try {
      // isteğin yürütülmesi - bir istisna atılabilir
      $connexion->exec($requête);
      // ekran izleme var mı, yok mu?
      if ($suivi) {
        print "$requête : Exécution réussie\n";
      }
    } catch (PDOException $ex) {
      // bir hata oluştu
      addError($erreurs, $requête, $ex->getMessage(), $suivi);
      // durdurulacak mı?
      $fini = $arrêt;
    }
    // sonraki istek
    $i++;
  }
  // sonuç
  return $erreurs;
}

function addError(array &$erreurs, string $requête, string $msg, bool $suivi): void {
  // bir hata mesajı ekleniyor
  $msg = "$requête : Erreur (" . $msg . ")";
  $erreurs[] = $msg;
  // ekran izleme olsun mı, olmasın mı?
  if ($suivi) {
    print "$msg\n";
  }
}

Açıklamalar

  • [exécuterCommandes] işlevi (satır 36-89), [$SQLFileName] metin dosyasında (parametre 2) bulduğu SQL komutlarını yürütmekle görevlidir. Bu komutları yürütmek için, [$connexion] (parametre 1) numaralı açık bağlantıyı MySQL sunucusuyla kullanır. Üçüncü parametre olan [$suivi], ekran görüntülemelerini kontrol eden bir boole değeridir: TRUE değerinde, yürütülen SQL komutu, başarılı veya başarısız olduğu belirtilerek ekranda görüntülenir; aksi takdirde SQL komutunun yürütülmesi sessizce gerçekleşir. Dördüncü parametre olan [$arrêt], bir SQL komutunun başarısız olması durumunda ne yapılacağını kontrol eder: TRUE'te, SQL komutlarının yürütülmesinin durdurulması gerektiği belirtilir; aksi takdirde yürütme devam eder. [exécuterCommandes] işlevi bir hata mesajları dizisi döndürür; hata yoksa bu dizi boştur;
  • 11-18. satırlar: MySQL [dbpersonnes] veritabanına bağlantı açılır. Bağlantı açılmayı başaramazsa, bir hata mesajı görüntülenir ve işlem durdurulur (14-18. satırlar);
  • 22. satır: Açılmış bağlantı, [exécuterCommandes] işlevine aktarılır. İşlevin dönüşünde bağlantı kapatılacaktır (24. satır);
  • satır 20: Bağlantı, [exécuterCommandes] işlevine aktarılmadan önce yapılandırılır. Hata durumunda, SQL işlemlerinin [PDO] nesnesiyle birlikte çalışması, ya FALSE boole değerini (varsayılan değer) döndürebilir ya da bir istisna oluşturabilir. 20. satırda bu ikinci durum tercih edilmiştir. Nitekim, bir SQL komutunun yürütülmesinden elde edilen boole değerini kontrol etmeyi “unutmak” kolaydır. Bu durum, daha sonra kodun başka bir yerinde bir hataya yol açarak hatanın asıl kaynağını tespit etmeyi zorlaştırır. Yönetilmeyen bir istisna durumunda (catch komutunun bulunmaması), istisna kodda yukarı doğru ilerleyerek bir catch komutuna rastlayana kadar veya istisnayı yakalayacak olan PHP yorumlayıcısına ulaşana kadar devam eder. Bu durumda, istisnanın türü ve koddaki kaynağı görüntülenir;
  • 22. satır: [exécuterCommandes] işlevi, SQL ve [$SQLFileName] komut dosyalarını çalıştırmak üzere çağrılır;
  • 45-47. satırlar: SQL komut dosyasının gerçekten mevcut olup olmadığı kontrol edilir. Mevcut değilse, hata kaydedilir ve bu sonuç döndürülür;
  • satır 51: SQL emirlerini [$requêtes] tablosuna aktarırız. Satır 53-55: İşlem başarısız olursa, tek bir mesaj içeren bir hata tablosu döndürürüz;
  • satır 57: hataları [$erreurs] dizisinde biriktirilir;
  • satır 58: istek numarası;
  • satır 59: [$fini] boole değeri, [$requêtes] tablosundaki SQL komutlarının yürütülmesini kontrol eder. TRUE'e geldiğinde yürütme durur;
  • 60. satır: tüm istekler üzerinde döngü yapılır;
  • 63. satır: i numaralı SQL emrinin metni çıkarılır. [trim] işlevi, SQL emrinin metninin önündeki ve arkasındaki boşlukları kaldıracaktır. "Boşluklar" derken, \b karakteri, \r karakteri, \n karakteri, \f karakteri, \t karakteri kastedilmektedir… Burada önemli olan, SQL metnindeki satır sonu karakterinin ortadan kalkacağıdır;
  • 65-69. satırlar: SQL metni boşsa, istek göz ardı edilir ve bir sonrakine geçilir;
  • 72. satır: SQL komutu, MySQL sunucusuna gönderilir. Yürütme başarısız olursa, [PDO::exec] yöntemi bir istisna oluşturur. Bu davranışın, 20. satırda yapılan yapılandırmadan kaynaklandığını hatırlatırız;
  • 79. satır: Hata mesajı hata dizisine eklenir;
  • satır 81: döngüyü kontrol eden [$fini] boole değeri ayarlanır. [$arrêt] parametresi (satır 36) TRUE değerindeyse, döngü durdurulmalıdır;
  • 74-76. satırlar: SQL komutunun yürütülmesi başarılı olursa, [$suivi] parametresi (36. satır) TRUE değerindeyse bu komut ekrana yazdırılır;
  • satır 87: Tüm SQL komutları yürütüldükten sonra, [$erreurs] hata tablosu döndürülür;

90-97. satırlardaki [adError] işlevi, [$erreurs] hata tablosuna bir hata eklemeye yarar:

  • 90. satır: İşlev 4 parametre alır:
    • [$erreurs] parametresi referans olarak aktarılır. Aslında, parametre olarak aktarılan dizi üzerinde işlem yapmak istiyoruz, bunun bir kopyası üzerinde değil;
    • [$requête] parametresi, başarısız olan emrin SQL metnidir;
    • [$msg] parametresi, başarısız olan emirle ilgili hata mesajıdır;
    • [$suivi] boole değeri, hata mesajının görüntülenip görüntülenmeyeceğini belirtir ($suivi=TRUE) veya görüntülenmemesi gerektiğini ($suivi=FALSE) belirtir;

[exécuterCommandes] işlevi, 3-33. satırlardaki komut dosyası tarafından çağrılır:

  • 11-18. satırlar: MySQL [dbpersonnes] veritabanına bağlantı kurulur;
  • 20. satır: bağlantı yapılandırılır;
  • 22. satır: Ardından SQL komut dosyası çalıştırılır;
  • 24. satır: bağlantı kapatılır;
  • satır 26-29: [exécuterCommandes] işlevi tarafından döndürülen hatalar görüntülenir;

Ekran sonuçları:


drop table if exists personnes : Exécution réussie
SET NAMES 'utf8' : Exécution réussie
create table personnes (prenom varchar(30) not null, nom varchar(30) not null, age integer not null, primary key (nom,prenom)) : Exécution réussie
insert into personnes (prenom, nom, age) values('Paul','Langevin',48) : Exécution réussie
insert into personnes (prenom, nom, age) values ('Sylvie','Lefur',70) : Exécution réussie
insert into personnes (prenom, nom, age) values ('Sylvie','Lefur',70) : Erreur (SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Lefur-Sylvie' for key 'PRIMARY')
insert into personnes (prenom, nom, age) values ('Pierre','Nicazou',35) : Exécution réussie
insert into personnes (prenom, nom, age) values ('Géraldine','Colou',26) : Exécution réussie
insert into personnes (prenom, nom, age) values ('Paulette','Girond',56) : Exécution réussie
insert into personnes (prenom, nom, age) values ('Paulette','Girond',56) : Erreur (SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Girond-Paulette' for key 'PRIMARY')

-----------------------
Il y a eu 2 erreur(s)
insert into personnes (prenom, nom, age) values ('Sylvie','Lefur',70) : Erreur (SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Lefur-Sylvie' for key 'PRIMARY')
insert into personnes (prenom, nom, age) values ('Paulette','Girond',56) : Erreur (SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'Girond-Paulette' for key 'PRIMARY')
Terminé

Yapılan eklemeler phpMyAdmin ile görüntülenebilir:

Image

12.5. Herhangi bir SQL komutunun yürütülmesi

Aşağıdaki komut dosyası, aşağıdaki [sql.txt] metin dosyasındaki SQL komutlarının yürütülmesini gösterir:

1
2
3
4
5
6
7
8
9
select * from personnes
select nom,prenom from personnes order by nom asc, prenom desc
select * from personnes where age between 20 and 40 order by age desc, nom asc, prenom asc
insert into personnes values('Josette','Bruneau',46)
update personnes set age=47 where nom='Bruneau'
select * from personnes where nom='Bruneau'
delete from personnes where nom='Bruneau'
select * from personnes where nom='Bruneau'
xselect * from personnes where nom='Bruneau'

Bu SQL komutları arasında, veritabanından sonuçları döndüren select komutu, sonuç döndürmeden veritabanını değiştiren insert, update, delete komutları ve son olarak da son komut (xselect) gibi hatalı komutlar bulunmaktadır. [mysql-04.php] komut dosyası şöyledir:


<?php

// veritabanı kimliği
const DSN = "mysql:host=localhost;dbname=dbpersonnes";
// kullanıcı kimlik bilgileri
const ID = "admpersonnes";
const PWD = "nobody";
// çalıştırılacak SQL komut metin dosyasının kimliği
const SQL_COMMANDS_FILENAME = "sql.txt";

try {
  // veritabanına bağlantı MySql
  $connexion = new PDO(DSN, ID, PWD);
} catch (PDOException $ex) {
  // hata görüntüleme
  print "Erreur : " . $ex->getMessage() . "\n";
  exit;
}
// SGBD'te her hata durumunda bir istisna atılmasını istiyoruz
$connexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// komut dosyasının çalıştırılması SQL
$erreurs = exécuterCommandes($connexion, SQL_COMMANDS_FILENAME, TRUE, FALSE);
// bağlantının kapatılması
$connexion = NULL;
// hata sayısının görüntülenmesi
printf("\n-----------------------\nIl y a eu %d erreur(s)\n", count($erreurs));
for ($i = 0; $i < count($erreurs); $i++) {
  print "$erreurs[$i]\n";
}

// işlem tamamlandı
print "Terminé\n";
exit;

// ---------------------------------------------------------------------------------
function exécuterCommandes(PDO $connexion, string $SQLFileName, bool $suivi = FALSE, bool $arrêt = TRUE): array {
………………………………………………………….
  // istekler tek tek yürütülüyor - başlangıçta hata yok
  $erreurs = [];
  $i = 0;
  $fini = FALSE;
  while ($i < count($requêtes) && !$fini) {
    // isteğin metni alınıyor
    // trim, satır sonu işaretini kaldıracak
    $requête = trim($requêtes[$i]);
    // boş istek mi?
    if (strlen($requête) == 0) {
      // isteği atlayıp bir sonraki isteğe geçiliyor
      $i++;
      continue;
    }
    // isteğin yürütülmesi
    // adı alınır
    $commande = "";
    if (preg_match("/^\s*(\S+)/", $requête, $champs)) {
      $commande = strtolower($champs[0]);
    }
    try {
      // Bu bir SELECT emri mi?
      if ($commande === "select") {
        $résultat = $connexion->query($requête);
      } else {
        $résultat = $connexion->exec($requête);
      }
      // Ekran izleme var mı, yok mu?
      if ($suivi) {
        print "[$requête] : Exécution réussie\n";
      }
      // çalıştırma sonucu görüntüleniyor
      afficherInfos($commande, $résultat);
    } catch (PDOException $ex) {
      // bir hata oluştu
      addError($erreurs, $requête, $ex->getMessage(), $suivi);
      // durulacak mı?
      $fini = $arrêt;
    }
    // sonraki istek
    $i++;
  }
  // sonuç
  return $erreurs;
}

function addError(array &$erreurs, string $requête, string $msg, bool $suivi): void {
  
}

// ---------------------------------------------------------------------------------
function afficherInfos(string $commande, $résultat): void {
  // bir SQL sorgusunun sonucunu görüntüler: $résultat
  // Bu bir SELECT sorgusu muydu?
  switch ($commande) {
    case "select" :
      // alanların adları görüntülenir
      $titre = "";
      $nbColonnes = $résultat->columnCount();
      for ($i = 0; $i < $nbColonnes; $i++) {
        $infos = $résultat->getColumnMeta($i);
        $titre .= $infos['name'] . ",";
      }
      // son karakteri kaldırıyoruz,
      $titre = substr($titre, 0, strlen($titre) - 1);
      // alanların listesi görüntüleniyor
      print "$titre\n";
      // ayırıcı satır
      $séparateurs = "";
      for ($i = 0; $i < strlen($titre); $i++) {
        $séparateurs .= "-";
      }
      print "$séparateurs\n";
      // veriler
      foreach ($résultat as $ligne) {
        $data = "";
        for ($i = 0; $i < $nbColonnes; $i++) {
          $data .= $ligne[$i] . ",";
        }
        // son karakteri kaldırır,
        $data = substr($data, 0, strlen($data) - 1);
        // görüntülenir
        print "$data\n";
      }
      break;
    case "update":
    case "insert":
    case "delete";
      print " $résultat lignes(s) a (ont) été modifiée(s)\n";
      break;
  }
}

Açıklamalar

  • 36-83. satırlar: [exécuterCommandes] işlevi biraz değiştirilmiştir: SQL ve [select] komutları, diğer SQL komutlarıyla aynı şekilde çalışmaz. Bu komut, sonuç olarak bir tablo, yani veritabanındaki satır ve sütunlardan oluşan bir küme döndüren tek komuttur;
  • 55-57. satırlar: SQL komutunun ilk kelimesi, düzenli ifade kullanılarak ayrıştırılır;
  • 60-64. satırlar: SQL komutu [select] ise, [PDO::query] yöntemi kullanılır; aksi takdirde [PDO::exec] yöntemi kullanılarak SQL komutu yürütülür. Her iki durumda da, yürütme başarısız olursa bir istisna atılır ve 71-77. satırlarda yakalanır. Yürütme başarılı olursa, 70. satırda sonuç görüntülenir;
  • 90-130. satırlar: afficherInfos işlevi, bir SQL emrinin yürütülme sonucuna ilişkin bilgileri görüntüler;
  • 94. satır: [select] durumu işlenir. Sonucu, [PDOStatement] türünde bir nesnedir;
  • 96. satır: [PDOStatement::getColumnCount()] yöntemi, select komutunun sonuç tablosundaki sütun sayısını döndürür;
  • 98-99. satırlar: [PDOStatement::getMeta(i)] yöntemi, select.'in sonuç tablosundaki i numaralı sütunla ilgili bilgileri içeren bir sözlük döndürür. Bu sözlükte, 'name' anahtarına atanan değer, sütunun adıdır;
  • 97-102. satırlar: select sonuç tablosundaki sütun adları bir karakter dizisi halinde birleştirilir;
  • 105-110. satırlar: daha önce oluşturulan karakter dizisiyle aynı uzunlukta bir ayırma satırı oluşturulur;
  • satır 112-121: PDOStatement türündeki bir nesne, bir foreach döngüsüyle taranabilir. Her yinelemede elde edilen öğe, select'in sonuç tablosundan alınan bir satırdır ve bu satır, satırın farklı sütunlarındaki değerleri temsil eden bir değerler dizisi biçimindedir. Tüm bu değerler bir for döngüsüyle görüntülenir (satır 114-116);
  • 123-127. satırlar: insert, update ve delete emirlerinin yürütülmesinin sonucu, emir tarafından değiştirilen satır sayısıdır;

Ekran sonuçları:


[set names 'utf8'] : Exécution réussie
[select * from personnes] : Exécution réussie
prenom,nom,age
--------------
Géraldine,Colou,26
Paulette,Girond,56
Paul,Langevin,48
Sylvie,Lefur,70
Pierre,Nicazou,35
[select nom,prenom from personnes order by nom asc, prenom desc] : Exécution réussie
nom,prenom
----------
Colou,Géraldine
Girond,Paulette
Langevin,Paul
Lefur,Sylvie
Nicazou,Pierre
[select * from personnes where age between 20 and 40 order by age desc, nom asc, prenom asc] : Exécution réussie
prenom,nom,age
--------------
Pierre,Nicazou,35
Géraldine,Colou,26
[insert into personnes values('Josette','Bruneau',46)] : Exécution réussie
 1 lignes(s) a (ont) été modifiée(s)
[update personnes set age=47 where nom='Bruneau'] : Exécution réussie
 1 lignes(s) a (ont) été modifiée(s)
[select * from personnes where nom='Bruneau'] : Exécution réussie
prenom,nom,age
--------------
Josette,Bruneau,47
[delete from personnes where nom='Bruneau'] : Exécution réussie
 1 lignes(s) a (ont) été modifiée(s)
[select * from personnes where nom='Bruneau'] : Exécution réussie
prenom,nom,age
--------------
[insert into personnes values('Josette','Bruneau',46)] : Exécution réussie
 1 lignes(s) a (ont) été modifiée(s)
[xselect * from personnes where nom='Bruneau'] : Erreur (SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xselect * from personnes where nom='Bruneau'' at line 1)

-----------------------
Il y a eu 1 erreur(s)
[xselect * from personnes where nom='Bruneau'] : Erreur (SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xselect * from personnes where nom='Bruneau'' at line 1)
Terminé

12.6. Hazırlanmış SQL emirlerinin kullanımı

12.6.1. Örnek 1

Aşağıdaki [mysql-05.php] komut dosyasını inceleyelim:


<?php

// veritabanı kimliği
const DSN = "mysql:host=localhost;dbname=dbpersonnes";
// kullanıcı kimlikleri
const ID = "admpersonnes";
const PWD = "nobody";

try {
  // veritabanına bağlanma MySql
  $connexion = new PDO(DSN, ID, PWD);
  // SGBD'te her hata durumunda bir istisna atılmasını istiyoruz
  $connexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  // Kişiler tablosu boşaltılır
  $connexion->exec("delete from personnes");
  // kişi listesi
  $personnes = [];
  $personnes[] = ["nom" => "Langevin", "prenom" => "Paul", "age" => 47];
  $personnes[] = ["nom" => "Lefur", "prenom" => "Sylvie", "age" => 28];
  // bu kişileri veritabanına ekleyeceğiz
  $statement = $connexion->prepare("insert into personnes (nom, prenom, age) values (:nom, :prenom, :age)");
  for ($i = 0; $i < count($personnes); $i++) {
    $statement->execute($personnes[$i]);
  }
} catch (PDOException $ex) {
  // hata mesajı görüntülenir
  print "Erreur : " . $ex->getMessage() . "\n";
} finally {
// bağlantı kapatılıyor
  $connexion = NULL;
}

// işlem tamamlandı
print "Terminé\n";
exit;

Açıklamalar

Burada, [dbpersonnes] veritabanındaki kişi tablosuna iki kişi ekleyen 16-24. satırlarla ilgileniyoruz.

  • 21. satır: Parametreleri ayarlanmış bir SQL emri “hazırlanıyor”. Parametrelerin başında : :soyadı, :adı, :yaş karakterleri yer alır. Bir SQL emrini “hazırlamak” için [PDO::prepare] yöntemi kullanılır. Sonuç, [PDOStatement] türündedir. “Hazırlama” bir yürütme değildir: hiçbir şey yürütülmez;
  • 23. satır: ‘hazırlanan’ emrin [PDOStatement::execute] yöntemi ile yürütülmesi. Bunun için :soyadı, :ad ve :yaş parametrelerine değerler verilmelidir. Bunu yapmanın birkaç yolu vardır. Burada, [PDOStatement::execute] yöntemine aktarılan, hazırlanan emrin parametrelerini anahtar olarak kullanan bir sözlük kullanıyoruz. Bunu yapmanın başka bir yolu da, [PDOStatement::bindValue($paramètre,$valeur)] yöntemiyle parametrelere değer vermektir. Örneğin burada:

$statement→bindValue(“nom”,”Langevin”);
$statement→bindValue(“prenom”,”Paul”);
$statement→bindValue(“age”,47);
$statement→execute();

Bunun dezavantajı, bu komutu her parametre için tekrarlamak zorunda kalınmasıdır. Bu durumda sözlük yöntemi daha pratik olabilir. [PDOStatement::execute] yöntemi, yürütme başarısız olursa FALSE sonucunu verir;

  • burada eklemeler yapmak için kullanılan yöntem:
    • hazırlanmış bir yöntem;
    • hazırlanan komutun n kez yürütülmesi;

n adet farklı SQL emrini çalıştırmaktan daha az yürütme süresi gerektirir. Dolayısıyla bu yöntem tercih edilmelidir. Bu yöntem, SQL select, update, delete, insert emirleri için kullanılabilir. SQL select komutunda, [PDOStatement::execute] ile yürütülmesinden sonra, sonuç satırları [PDOStatement::fetchAll] yöntemi ile alınır;

12.6.2. Örnek 2

Aşağıdaki [mysql-06.php] komut dosyası, SQL select işlemi için hazırlanmış bir komutun kullanımını ve bu işlem tarafından döndürülen satırları almanın çeşitli yollarını göstermektedir:


<?php

// veritabanı kimliği
const DSN = "mysql:host=localhost;dbname=dbpersonnes";
// kullanıcı kimlik bilgileri
const ID = "admpersonnes";
const PWD = "nobody";

try {
  // veritabanına bağlantı MySql
  $connexion = new PDO(DSN, ID, PWD);
  // SGBD'te her hata durumunda bir istisna atılmasını istiyoruz
  $connexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  // Kişiler tablosu boşaltılsın
  $connexion->exec("delete from personnes");
  // bu kişileri veritabanına ekleyeceğiz
  $statement = $connexion->prepare("insert into personnes (nom, prenom, age) values (:nom, :prenom, :age)");
  for ($i = 0; $i < 10; $i++) {
    $statement->execute(["nom" => "nom" . $i, "prenom" => "prenom" . $i, "age" => $i * 10]);
  }
  // veritabanını sorgulayacağız
  $statement = $connexion->prepare("select nom, prenom, age from personnes");
  $statement->execute();
  // 1. satır
  $ligne = $statement->fetch();
  var_dump($ligne);
  // 2. satır
  $ligne = $statement->fetch(PDO::FETCH_ASSOC);
  var_dump($ligne);
  // 3. satır
  $ligne = $statement->fetch(PDO::FETCH_OBJ);
  var_dump($ligne);
  // 4. satır
  $statement->setFetchMode(PDO::FETCH_CLASS, "Person");
  $ligne = $statement->fetch();
  var_dump($ligne);
  // tüm satırların sırayla okunması
  $statement = $connexion->prepare("select nom, prenom, age from personnes");
  $statement->execute();
  $statement->setFetchMode(PDO::FETCH_CLASS, "Person");
  while ($personne = $statement->fetch()) {
    print "$personne\n";
  }
} catch (PDOException $ex) {
  // hata mesajı
  print "Erreur : " . $ex->getMessage() . "\n";
} finally {
// bağlantıyı kapat
  $connexion = NULL;
}

// işlem tamamlandı
print "Terminé\n";
exit;

class Person {
  private $nom;
  private $prenom;
  private $age;

  public function __toString() {
    return "Personne[$this->nom,$this->prenom,$this->age]";
  }

}

Açıklamalar

  • 17-20. satırlar: [admpersonnes] veritabanındaki [personnes] tablosuna 10 satır eklenir:

Image

  • 22. satır: SQL [select] emri “hazırlanır” ve 23. satırda yürütülür;
  • 25. satır: [PDOStatement::fetch] yöntemi ile, yürütülen SQL [select] işleminin sonuçlarından bir satır alınır. [PDOStatement::fetch] yöntemi, önceden hazırlanmış bir SQL [select] işleminin sonuç satırlarını çeşitli şekillerde alabilir. Komut dosyası bunlardan birkaçını göstermektedir. Parametresiz [PDOStatement::fetch] yöntemi, [select] işleminin geçerli satırını, hem sütun numaralarına hem de sütun adlarına göre indekslenmiş bir sözlük biçiminde döndürür;
  • 26. satır: aşağıdaki sonucu görüntüler:

array(6) {
  ["nom"]=>
  string(4) "nom0"
  [0]=>
  string(4) "nom0"
  ["prenom"]=>
  string(7) "prenom0"
  [1]=>
  string(7) "prenom0"
  ["age"]=>
  string(1) "0"
  [2]=>
  string(1) "0"
}
  • 28-29. satırlar: [PDO::FETCH_ASSOC] parametresi, döndürülen satırın tablonun sütun adlarına göre indekslenmiş bir sözlük olmasını sağlar:
1
2
3
4
5
6
7
8
array(3) {
  ["nom"]=>
  string(4) "nom1"
  ["prenom"]=>
  string(7) "prenom1"
  ["age"]=>
  string(2) "10"
}
  • 31-32. satırlar: [PDO::FETCH_OBJ] parametresi, döndürülen satırın, öznitelikleri tablonun sütun adları olan [stdclass] türünde bir nesne olmasını sağlar:
1
2
3
4
5
6
7
8
object(stdClass)#2 (3) {
  ["nom"]=>
  string(4) "nom2"
  ["prenom"]=>
  string(7) "prenom2"
  ["age"]=>
  string(2) "20"
}
  • satır 34: [fetch] yönteminin arama modu, [PDOStatement::setFetchMode] yöntemi ile belirlenir. Bu mod, ya başka bir [PDOStatement::setFetchMode] işlemiyle ya da daha önce yapıldığı gibi [PDOStatement::fetch] yöntemine bir mod parametresi geçirilerek değiştirilmediği sürece varsayılan mod olarak kalır. [setFetchMode(PDO::FETCH_CLASS, "Person")] işlemi, okunan satırın [Person] türünde bir nesneye yerleştirilmesi gerektiğini belirtir. Bu sınıfın öznitelikleri arasında, okunan satırın sütun adlarını taşıyan öznitelikler bulunmalıdır. 56-63. satırlarda tanımlanan [Person] sınıfı buna bir örnektir;
  • 36. satırda şu sonuç görüntülenir:
1
2
3
4
5
6
7
8
object(Person)#4 (3) {
  ["nom":"Person":private]=>
  string(4) "nom3"
  ["prenom":"Person":private]=>
  string(7) "prenom3"
  ["age":"Person":private]=>
  string(2) "30"
}
  • 38-43. satırlar: [select] sınıfının sonuçlarının sırayla nasıl kullanılacağını gösterir;
  • 42. satır: [$personne]'in görüntülenmesi, [Person] sınıfındaki [__toString] yöntemini kullanacaktır;

12.7. İşlemlerin Kullanımı

Bir işlem, bir dizi SQL komutunu tek bir yürütme birimi altında toplar: ya tüm komutlar başarılı olur ya da bunlardan biri başarısız olur ve bu durumda, bu komuttan önceki tüm SQL komutları iptal edilir. Başka bir deyişle, SQL emirlerini yürütmek için bir işlem kullanıldığında, işlemin yürütülmesinden sonra veritabanı kararlı bir duruma geçer:

  • ya işlemin tüm SQL emirlerinin başarılı bir şekilde yürütülmesiyle oluşturulan yeni bir durumda;
  • ya da işlemin yürütülmeye başlamasından önceki durumdadır;

"Bağlantı" başlıklı paragrafta incelenen bir metin dosyasında bulunan SQL komutlarının yürütülmesi örneğine geri döneceğiz. Bu yürütmeyi bir işleme dahil edeceğiz. SQL komutları, aşağıdaki [sql2.txt] dosyasında yer alacaktır:


set names 'utf8'
select * from personnes
select nom,prenom from personnes order by nom asc, prenom desc
select * from personnes where age between 20 and 40 order by age desc, nom asc, prenom asc
insert into personnes values('Josette','Bruneau',46)
update personnes set age=47 where nom='Bruneau'
select * from personnes where nom='Bruneau'
delete from personnes where nom='Bruneau'
select * from personnes where nom='Bruneau'
insert into personnes values('Josette','Bruneau',46)
select * from personnes where nom='Bruneau'
xselect * from personnes where nom='Bruneau'
  1. satırdaki hatalı komut, tüm işlemin başarısız olmasına neden olacaktır. Dolayısıyla, veritabanı işlemin öncesindeki haline geri dönmelidir. Yukarıdaki örnekte, 10. satırda eklenen satırın tabloda görünmemesi gerekir. Komut dosyasında çok az değişiklik yapılmıştır. Yine de [mysql-07.php] kodunun tamamını aşağıda veriyoruz:

<?php

// veritabanı kimliği
const DSN = "mysql:host=localhost;dbname=dbpersonnes";
// kullanıcı kimlik bilgileri
const ID = "admpersonnes";
const PWD = "nobody";
// çalıştırılacak SQL komut metin dosyasının kimliği
const SQL_COMMANDS_FILENAME = "sql2.txt";

try {
  // veritabanına bağlantı MySql
  $connexion = new PDO(DSN, ID, PWD);
} catch (PDOException $ex) {
  // hata görüntüleme
  print "Erreur : " . $ex->getMessage() . "\n";
  exit;
}
// SGBD'te her hata durumunda bir istisna atılmasını istiyoruz
$connexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// komut dosyasının çalıştırılması SQL
$erreurs = exécuterCommandes($connexion, SQL_COMMANDS_FILENAME, TRUE);
// bağlantının kapatılması
$connexion = NULL;
// hata sayısının görüntülenmesi
printf("\n-----------------------\nIl y a eu %d erreur(s)\n", count($erreurs));
for ($i = 0; $i < count($erreurs); $i++) {
  print "$erreurs[$i]\n";
}

// işlem tamamlandı
print "Terminé\n";
exit;

// ---------------------------------------------------------------------------------
function exécuterCommandes(PDO $connexion, string $SQLFileName, bool $suivi = FALSE): array {
// $connexion bağlantısını kullan
// SQL metin dosyasında bulunan komutları çalıştır SQLFileName
// bu dosya, her satırda bir tane olmak üzere çalıştırılacak SQL komut dosyasıdır
// SQL komutları bir işlem içinde yürütülür
// Emirlerden biri başarısız olursa, işlem iptal edilir ve veritabanı işlem öncesindeki durumuna geri döner
// $suivi=1 ise, her bir SQL komutunun yürütülmesi sırasında, komutun başarılı olup olmadığına dair bir mesaj görüntülenir
// fonksiyon bir dizi döndürür (hata sayısı, hata1, hata2…)
//
// SQLFileName dosyasının varlığı kontrol edilir
  if (!file_exists($SQLFileName)) {
    return ["Le fichier [$SQLFileName] n'existe pas"];
  }
  // SQLFileName dosyasında bulunan SQL sorguları yürütülür
  // bunlar bir tabloya eklenir
  $requêtes = file($SQLFileName);
  // hata mı?
  if ($requêtes === FALSE) {
    return ["Erreur lors de l'exploitation du fichier SQL [$SQLFileName]"];
  }
  // sorgular bir işleme yerleştirilecek
  $connexion->beginTransaction();
  // sorgular tek tek çalıştırılıyor - başlangıçta hata yok
  $erreurs = [];
  $i = 0;
  $fini = FALSE;
  while ($i < count($requêtes) && !$fini) {
    // sorgunun metni alınır
    // trim, satır sonu işaretini kaldıracak
    $requête = trim($requêtes[$i]);
    // Sorgu boş mu?
    if (strlen($requête) == 0) {
      // isteği atlayıp bir sonraki isteğe geçilir
      $i++;
      continue;
    }
    // isteğin yürütülmesi
    // adı alınır
    $commande = "";
    if (preg_match("/^\s*(\S+)/", $requête, $champs)) {
      $commande = strtolower($champs[0]);
    }
    try {
      // Bu bir SELECT emri mi?
      if ($commande === "select") {
        $résultat = $connexion->query($requête);
      } else {
        $résultat = $connexion->exec($requête);
      }
      // Ekran izleme var mı, yok mu?
      if ($suivi) {
        print "[$requête] : Exécution réussie\n";
      }
      // çalıştırma sonucu görüntüleniyor
      afficherInfos($commande, $résultat);
    } catch (PDOException $ex) {
      // bir hata oluştu
      addError($erreurs, $requête, $ex->getMessage(), $suivi);
      // bir sonraki döngüde duruluyor
      $fini = TRUE;
    }
    // sonraki istek
    $i++;
  }
  // işlem sonu
  if (!$fini) {
    // hata yok: işlem onaylanıyor
    $connexion->commit();
  } else {
    // hatalar oluştu: işlem iptal ediliyor
    $connexion->rollBack();
    // hata eklendi
    addError($erreurs, "", "Transaction annulée", $suivi);
  }
  // sonuç
  return $erreurs;
}

function addError(array &$erreurs, string $requête, string $msg, bool $suivi): void {
  
}

// ---------------------------------------------------------------------------------
function afficherInfos(string $commande, $résultat): void {
  
}

Açıklamalar

Orijinal [mysql-04.php] betiğindeki değişiklikleri vurguladık.

  • 22. ve 36. satırlar: [exécuterCommandes] işlevi, dördüncü parametresi olan [$arrêt=TRUE]'i kaybetmiştir. Nitekim, SQL komutları bir işlem içinde yürütüldüğünden, herhangi bir hata işlemin durdurulmasına neden olacaktır;
  • satır 40-41: bir işlem fonksiyonunun çağrılması;
  • 57. satır: bir işlem başlatılır. Bundan sonra, 62-99. satırlardaki döngü içinde yürütülen tüm SQL komutları bu işlem kapsamında yürütülür;
  • satır 101-109: Hata oluşmuşsa (satır 95), [$fini] boole değeri TRUE olur. Değeri FALSE olduğunda, hata olmamıştır ve işlem onaylanır (satır 103). Değeri TRUE olduğunda, hatalar meydana gelmiştir ve işlem iptal edilir (satır 106) ve işlem hatası hata listesine eklenir (satır 108);

Sonuçlar

Komut dosyası çalıştırılmadan önce, [admpersonnes] veritabanı aşağıdaki durumdadır:

Image

[mysql-07.php] komut dosyası çalıştırılır. Bu durumda ekran görüntüleri şu şekildedir:


[set names 'utf8'] : Exécution réussie
[select * from personnes] : Exécution réussie
prenom,nom,age
--------------
prenom0,nom0,0
prenom1,nom1,10
prenom2,nom2,20
prenom3,nom3,30
prenom4,nom4,40
prenom5,nom5,50
prenom6,nom6,60
prenom7,nom7,70
prenom8,nom8,80
prenom9,nom9,90
[select nom,prenom from personnes order by nom asc, prenom desc] : Exécution réussie
nom,prenom
----------
nom0,prenom0
nom1,prenom1
nom2,prenom2
nom3,prenom3
nom4,prenom4
nom5,prenom5
nom6,prenom6
nom7,prenom7
nom8,prenom8
nom9,prenom9
[select * from personnes where age between 20 and 40 order by age desc, nom asc, prenom asc] : Exécution réussie
prenom,nom,age
--------------
prenom4,nom4,40
prenom3,nom3,30
prenom2,nom2,20
[insert into personnes values('Josette','Bruneau',46)] : Exécution réussie
 1 lignes(s) a (ont) été modifiée(s)
[update personnes set age=47 where nom='Bruneau'] : Exécution réussie
 1 lignes(s) a (ont) été modifiée(s)
[select * from personnes where nom='Bruneau'] : Exécution réussie
prenom,nom,age
--------------
Josette,Bruneau,47
[delete from personnes where nom='Bruneau'] : Exécution réussie
 1 lignes(s) a (ont) été modifiée(s)
[select * from personnes where nom='Bruneau'] : Exécution réussie
prenom,nom,age
--------------
[insert into personnes values('Josette','Bruneau',46)] : Exécution réussie
 1 lignes(s) a (ont) été modifiée(s)
[select * from personnes where nom='Bruneau'] : Exécution réussie
prenom,nom,age
--------------
Josette,Bruneau,46
[xselect * from personnes where nom='Bruneau'] : Erreur (SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xselect * from personnes where nom='Bruneau'' at line 1)
[] : Erreur (Transaction annulée)

-----------------------
Il y a eu 2 erreur(s)
[xselect * from personnes where nom='Bruneau'] : Erreur (SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xselect * from personnes where nom='Bruneau'' at line 1)
[] : Erreur (Transaction annulée)
Terminé
  • 53. satır: [xselect] komutunda bir hata oluşur;
  • 54. satır: işlem iptal edilir;

Veritabanının durumunu kontrol ettiğimizde, komut dosyasının çalıştırılmasından önceki durumuyla aynı olduğunu görürüz. Özellikle, yukarıdaki sonuçların 52. satırındaki [Josette, Bruneau, 46] satırı burada görünmemektedir.

Image

Özet

  • bir işlem [PDO::beginTransaction] yöntemi ile başlar;
  • [PDO::commit] yöntemi ile başarıyla sonlandırılır;
  • [PDO::rollback] yöntemi ile işlem başarısızlıkla sonlandırılır;

Bir veritabanını kullanırken, veritabanındaki diğer kullanıcılardan izole olmak için tüm SQL işlemlerini bir işlem içine almak iyi bir uygulamadır (bu yöntem aynı zamanda bu işlevi de görür). Bir işlem mümkün olduğunca kısa olmalıdır. Bu nedenle, duruma göre [commit] veya [rollback] ile işlemi sonlandırmayı unutmamak gerekir.