4. Örnekler
4.1. Vergi Uygulaması: Giriş
Burada, bundan sonra birçok kez kullanılacak olan IMPOTS uygulamasını tanıtıyoruz. Bu uygulama, bir vergi mükellefinin vergisini hesaplamaya yarar. Yalnızca maaşını beyan edecek bir vergi mükellefinin basitleştirilmiş durumunu ele alıyoruz:
- evli değilse, çalışanın pay sayısı nbParts=nbEnfants/2 +1 olarak hesaplanır, evliyse nbEnfants/2+2 olarak hesaplanır; burada nbEnfants, çocuk sayısıdır.
- En az üç çocuğu varsa, yarım pay daha ekler
- vergilendirilebilir geliri R=0,72*S olarak hesaplanır; burada S, yıllık maaşıdır
- aile katsayısı QF = R/nbParts olarak hesaplanır
- Vergisi I hesaplanır. Aşağıdaki tabloyu ele alalım:
12620,0 | 0 | 0 |
13190 | 0,05 | 631 |
15640 | 0,1 | 1290,5 |
24.740 | 0,15 | 2072,5 |
31810 | 0,2 | 3309,5 |
39.970 | 0,25 | 4900 |
48360 | 0,3 | 6898,5 |
55790 | 0,35 | 9316,5 |
92970 | 0,4 | 12106 |
127.860 | 0,45 | 16754,5 |
151250 | 0,50 | 23147,5 |
172.040 | 0,55 | 30710 |
195.000 | 0,60 | 39312 |
0 | 0,65 | 49062 |
Her satırda 3 alan vardır. I vergisini hesaplamak için, QF<=alan1 olan ilk satır aranır. Örneğin, QF=23000 ise şu satır bulunur
bulunur. I vergisi bu durumda 0,15*R - 2072,5*nbParts'e eşittir. Eğer QF, QF<=alan1 ilişkisini hiçbir zaman karşılamıyorsa, o zaman son satırdaki katsayılar kullanılır. Burada:
bu da I=0,65*R - 49062*nbParts vergi tutarını verir.
Farklı vergi dilimlerini tanımlayan veriler bir ODBC-MySQL veritabanında bulunmaktadır. MySQL, Windows ve Linux dahil olmak üzere çeşitli platformlarda kullanılabilen, kamu malı olan bir SGBD dosyasıdır. Bu SGBD dosyası ile, içinde “impots” adlı tek bir tablo bulunan “dbimpots” adlı bir veritabanı oluşturulmuştur. Veritabanına erişim, burada “admimpots/mdpimpots” olan bir kullanıcı adı ve şifre ile kontrol edilmektedir. Aşağıdaki ekran görüntüsü, MySQL dosyası ile “dbimpots” veritabanının nasıl kullanılacağını göstermektedir:
C:\Program Files\EasyPHP\mysql\bin>mysql -u admimpots -p
Enter password: *********
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18 to server version: 3.23.49-max-nt
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> use dbimpots;
Database changed
mysql> show tables;
+--------------------+
| Tables_in_dbimpots |
+--------------------+
| impots |
+--------------------+
1 row in set (0.00 sec)
mysql> describe impots;
+---------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+---------+-------+
| limites | double | YES | | NULL | |
| coeffR | double | YES | | NULL | |
| coeffN | double | YES | | NULL | |
+---------+--------+------+-----+---------+-------+
3 rows in set (0.02 sec)
mysql> select * from impots;
+---------+--------+---------+
| limites | coeffR | coeffN |
+---------+--------+---------+
| 12620 | 0 | 0 |
| 13190 | 0.05 | 631 |
| 15640 | 0.1 | 1290.5 |
| 24740 | 0.15 | 2072.5 |
| 31810 | 0.2 | 3309.5 |
| 39970 | 0.25 | 4900 |
| 48360 | 0.3 | 6898 |
| 55790 | 0.35 | 9316.5 |
| 92970 | 0.4 | 12106 |
| 127860 | 0.45 | 16754 |
| 151250 | 0.5 | 23147.5 |
| 172040 | 0.55 | 30710 |
| 195000 | 0.6 | 39312 |
| 0 | 0.65 | 49062 |
+---------+--------+---------+
14 rows in set (0.00 sec)
mysql>quit
dbimpots veritabanı, aşağıdaki şekilde ODBC veri kaynağına dönüştürülür:
- 32 bitlik ODBC veri kaynağı yöneticisi başlatılır

- [Add] düğmesi kullanılarak yeni bir veri kaynağı olan ODBC eklenir

- MySQL sürücüsünü seçip [Terminer]'i yapıyoruz
![]() |
- MySQL sürücüsü bazı bilgiler ister:
1 | ODBC veri kaynağına verilecek DSN adı - herhangi bir isim olabilir |
2 | SGBD MySQL'in çalıştığı makine - burada localhost. Veritabanının uzak bir veritabanı olabileceğini belirtmek ilginçtir. ODBC veri kaynağını kullanan yerel uygulamalar bunun farkına varmaz. Özellikle bizim PHP uygulamamızda durum böyle olur. |
3 | kullanılacak MySQL veritabanı. MySQL, ilişkilerle birbirine bağlanmış tablo kümeleri olan ilişkisel veritabanlarını yöneten bir SGBD'tir. Burada, yönetilen veritabanının adı verilir. |
4 | Bu veritabanına erişim hakkı olan bir kullanıcının adı |
5 | kullanıcının şifresi |
4.2. Vergi Uygulaması: ImpotsDSN sınıfı
Uygulamamız, aşağıdaki özniteliklere, oluşturucuya ve yönteme sahip PHP ve ImpotsDSN sınıflarını kullanacaktır:
<?php
...
// özellikler
var $limites; // sınır tablosu
var $coeffR; // coeffR tablosu
var $coeffN; // coeffN tablosu
var $erreurs; // hata tablosu
- Sorunun açıklamasında gördüğümüz gibi, üç veri dizisine ihtiyacımız vardı
12620,0 | 0 | 0 |
13190 | 0,05 | 631 |
15640 | 0,1 | 1290,5 |
24.740 | 0,15 | 2072,5 |
31810 | 0,2 | 3309,5 |
39.970 | 0,25 | 4900 |
48360 | 0,3 | 6898,5 |
55790 | 0,35 | 9316,5 |
92970 | 0,4 | 12106 |
127.860 | 0,45 | 16754,5 |
151250 | 0,50 | 23147,5 |
172.040 | 0,55 | 30710 |
195.000 | 0,60 | 39312 |
0 | 0,65 | 49062 |
İlk sütun, ImotsDSN sınıfının $limites özniteliğine, ikinci sütun $coeffR özniteliğine, üçüncü sütun ise $coeffN özniteliğine yerleştirilecektir. Bu üç öznitelik, sınıfın oluşturucusu tarafından başlatılır. Oluşturucu, verileri ODBC veri kaynağından alır. Nesnenin oluşturulması sırasında çeşitli hatalar meydana gelebilir. Bu hatalar, $erreurs özniteliğinde bir hata mesajları tablosu biçiminde raporlanır.
- Oluşturucu, aşağıdaki alanları içeren bir $impots sözlüğünü parametre olarak alır:
// dsn: sınır tablolarının değerlerini içeren veri kaynağının adı (DSN), ODBC, coeffR, coeffN
// kullanıcı: ODBC kaynağında okuma hakkına sahip bir kullanıcının adı
// pwd: şifresi
// table: değerleri (sınırlar, coeffR, coeffN) içeren tablonun adı
// sınırlar: sınır değerlerini içeren sütunun adı
// coeffR: coeffR değerlerini içeren sütunun adı
// coeffN: coeffN değerlerini içeren sütunun adı
Oluşturucu parametresi, ODBC veri kaynağından verileri okumak için gerekli tüm bilgileri sağlar
- ImpotsDSN nesnesi oluşturulduktan sonra, sınıf kullanıcıları nesnenin calculerImpots yöntemini çağırabilir. Bu yöntem, parametre olarak bir $personne sözlüğü alır:
// $kişi["marié"] : evet, hayır
// $kişi["enfants"] : çocuk sayısı
// $kişi["salaire"] : yıllık maaş
Sınıfın tamamı şu şekildedir:
<?php
// bir sınıfın tanımı objImpots
class ImpotsDSN{
// özellikler: 3 veri tablosu
var $limites; // sınır tablosu
var $coeffR; // coeffR tablosu
var $coeffN; // coeffN tablosu
var $erreurs; // hata tablosu
// üretici
function ImpotsDSN($impots){
// $impots: Aşağıdaki alanları içeren sözlük
// dsn: sınır tablolarının değerlerini içeren veri kaynağının adı DSN, ODBC, coeffR, coeffN
// kullanıcı: ODBC kaynağında okuma hakkına sahip bir kullanıcının adı
// pwd: şifresi
// table: değerleri (sınırlar, coeffR, coeffN) içeren tablonun adı
// sınırlar: sınır değerlerini içeren sütunun adı
// coeffR: coeffR değerlerini içeren sütunun adı
// coeffN: coeffN değerlerini içeren sütunun adı
// başlangıçta hata yok
$this->erreurs=array();
// çağrı doğrulaması
if (! isset($impots[dsn]) || ! isset($impots[user]) ||! isset($impots[pwd]) ||! isset($impots[table]) ||
! isset($impots[limites]) ||! isset($impots[coeffR]) ||! isset($impots[coeffN])){
// hata
$this->erreurs[]="Appel incorrect";
// son
return;
}//if
// veritabanı açılıyor DSN
$connexion=odbc_connect($impots[dsn],$impots[user],$impots[pwd]);
// hata mı?
if(! $connexion){
// hata
$this->erreurs[]="Impossible d'ouvrir la base DSN [$impots[dsn]] (".odbc_error().")";
// son
return;
}//if
// veritabanına sorgu gönderme
$requête=odbc_prepare($connexion,"select $impots[limites],$impots[coeffR],$impots[coeffN] from $impots[table]");
if(! odbc_execute($requête)){
// hata
$this->erreurs[]="Impossible d'expoiter la base DSN [$impots[dsn]] (".odbc_error().")";
// son
odbc_close($connexion);
return;
}//if
// sorgu sonuçlarının işlenmesi
$this->limites=array();
$this->coeffR=array();
$this->coeffN=array();
while(odbc_fetch_row($requête)){
// bir satır daha
$this->limites[]=odbc_result($requête,$impots[limites]);
$this->coeffR[]=odbc_result($requête,$impots[coeffR]);
$this->coeffN[]=odbc_result($requête,$impots[coeffN]);
}//while
// veritabanını kapat
odbc_close($connexion);
}//oluşturucu
// --------------------------------------------------------------------------
function calculer($personne){
// $kişi["marié"] : evet, hayır
// $kişi["enfants"] : çocuk sayısı
// $kişi["salaire"] : yıllık maaş
// nesne düzgün durumda mı?
if (! is_array($this->erreurs) || count($this->erreurs)!=0) return -1;
// çağrı doğrulaması
if (! isset($personne[marié]) || ! isset($personne[enfants]) ||! isset($personne[salaire])){
// hata
$this->erreurs[]="Appel incorrect";
// son
return -1;
}//if
// parametreler doğru mu?
$personne[marié]=strtolower($personne[marié]);
if($personne[marié]!=oui && $personne[marié]!=non){
// hata
$this->erreurs[]="Statut marital [$personne[marié]] incorrect";
}//if
if(! preg_match("/^\s*\d{1,3}\s*$/",$personne[enfants])){
// hata
$this->erreurs[]="Nombre d'enfants [$personne[enfants]] incorrect";
}//if
if(! preg_match("/^\s*\d+\s*$/",$personne[salaire])){
// hata
$this->erreurs[]="salaire [$personne[salaire]] incorrect";
}//if
// hatalar mı var?
if(count($this->erreurs)!=0) return -1;
// pay sayısı
if($personne[marié]==oui) $nbParts=$personne[enfants]/2+2;
else $nbParts=$personne[enfants]/2+1;
// en az 3 çocuk varsa 1/2 pay daha
if($personne[enfants]>=3) $nbParts+=0.5;
// vergilendirilebilir gelir
$revenuImposable=0.72*$personne[salaire];
// aile katsayısı
$quotient=$revenuImposable/$nbParts;
// tablo sınırlarının sonuna eklenir ve ardından gelen döngüyü durdurur
$this->limites[$this->nbLimites]=$quotient;
// vergi hesaplaması
$i=0;
while($quotient>$this->limites[$i]) $i++;
// $quotient'in $limites tablosunun sonuna yerleştirilmesi nedeniyle, önceki döngü
// $limites tablosunun sınırlarını aşamaz
// şimdi vergiyi hesaplayabiliriz
return floor($revenuImposable*$this->coeffR[$i]-$nbParts*$this->coeffN[$i]);
}//calculImpots
}//vergi sınıfı
?>
Bir test programı şu şekilde olabilir:
<?php
// kütüphaneler
include "ImpotsDSN.php";
// vergi nesnesi oluşturma
$conf=array(dsn=>"mysql-dbimpots",user=>admimpots,pwd=>mdpimpots,
table=>impots,limites=>limites,coeffR=>coeffR,coeffN=>coeffN);
$objImpots=new ImpotsDSN($conf);
// hatalar mı var?
if(count($objImpots->erreurs)!=0){
// mesaj
echo "Les erreurs suivantes se sont produites :\n";
$erreurs=$objImpots->erreurs;
for($i=0;$i<count($erreurs);$i++){
echo "$erreurs[$i]\n";
}//for
// son
exit(1);
}//if
// denemeler
calculerImpots($objImpots,oui,2,200000);
calculerImpots($objImpots,non,2,200000);
calculerImpots($objImpots,oui,3,200000);
calculerImpots($objImpots,non,3,200000);
calculerImpots($objImpots,array(),array(),array());
// son
exit(0);
// -----------------------------------------------------------
function calculerImpots($objImpots,$marié,$enfants,$salaire){
// yankı
echo "impots($marié,$enfants,$salaire)\n";
// vergi hesaplaması
$personne=array(marié=>$marié,enfants=>$enfants,salaire=>$salaire);
$montant=$objImpots->calculer($personne);
// hatalar mı?
if(count($objImpots->erreurs)!=0){
// mesaj
echo "Les erreurs suivantes se sont produites :\n";
$erreurs=$objImpots->erreurs;
for($i=0;$i<count($erreurs);$i++){
echo "$erreurs[$i]\n";
}//for
}else echo "montant=$montant\n";
}//calculerImp
- test programı, ImpotsDSN sınıfını içeren dosyayı "dahil eder"
- daha sonra ImpotsDSN sınıfından bir objImpots nesnesi oluşturur ve nesnenin oluşturucusuna ihtiyaç duyduğu bilgileri aktarır
- Bu nesne oluşturulduktan sonra, nesnenin calculer yöntemi beş kez çağrılacaktır
Elde edilen sonuçlar şunlardır:
dos>e:\php43\php.exe test.php
impots(oui,2,200000)
montant=22504
impots(non,2,200000)
montant=33388
impots(oui,3,200000)
montant=16400
impots(non,3,200000)
montant=22504
impots(Array,Array,Array)
Les erreurs suivantes se sont produites :
Statut marital [array] incorrect
Nombre d'enfants [Array] incorrect
salaire [Array] incorrect
Bundan sonra ImpotsDSN sınıfını, tanımını tekrar vermeden kullanacağız.
4.3. Vergi Uygulaması: sürüm 1
Şimdi IMPOTS uygulamasının 1. sürümünü sunuyoruz. Vergi hesaplaması için gerekli olan üç parametreyi elde etmek amacıyla kullanıcıya HTML arayüzünü sunan bir web uygulaması bağlamını ele alıyoruz:
- medeni durum (evli mi, değil mi)
- çocuk sayısı
- yıllık maaş

Formun görüntülenmesi, aşağıdaki PHP sayfası tarafından gerçekleştirilir:
<?php //vergi formu ?>
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
// formun sıfırlanması
with(document.frmImpots){
optMarie[0].checked=false;
optMarie[1].checked=true;
txtEnfants.value="";
txtSalaire.value="";
txtImpots.value="";
}//ile
}//sil
</script>
</head>
<body background="/poly/impots/images/standard.jpg">
<center>
Calcul d'impôts
<hr>
<form name="frmImpots" method="POST">
<table>
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" name="optMarie" value="oui" <?php echo $requête->chkoui ?>>oui
<input type="radio" name="optMarie" value="non" <?php echo $requête->chknon ?>>non
</td>
</tr>
<tr>
<td>Nombre d'enfants</td>
<td><input type="text" size="5" name="txtEnfants" value="<?php echo $requête->enfants ?>"></td>
</tr>
<tr>
<td>Salaire annuel</td>
<td><input type="text" size="10" name="txtSalaire" value="<?php echo $requête->salaire ?>"></td>
</tr>
<tr>
<td><font color="green">Impôt</font></td>
<td><input type="text" size="10" name="txtImpots" value="<?php echo $requête->impots ?>" readonly></td>
</tr>
<tr></tr>
<tr>
<td><input type="submit" value="Calculer"></td>
<td><input type="button" value="Effacer" onclick="effacer()"></td>
</tr>
</table>
</form>
</center>
<?php
// hata var mı?
if(count($requête->erreurs)!=0){
// hataların görüntülenmesi
echo "<hr>\n<font color=\"red\">\n";
echo "Les erreurs suivantes se sont produites<br>";
echo "<ul>";
for($i=0;$i<count($requête->erreurs);$i++){
echo "<li>".$requête->erreurs[$i]."</li>\n";
}
echo "</ul>\n</font>\n";
}//eğer
?>
</body>
</html>
PHP sayfası, uygulamanın ana programı tarafından $requête değişkeninde kendisine aktarılan bilgileri görüntülemekle yetinir; bu değişken, aşağıdaki alanlara sahip bir nesnedir:
"Evet" ve "Hayır" radyo düğmelerinin özellikleri - ilgili radyo düğmesini etkinleştirmek veya devre dışı bırakmak için olası değerleri "checked" veya ""'tir | |
Vergi mükellefinin çocuk sayısı | |
yıllık maaşı | |
ödenecek vergi tutarı | |
olası hata listesi - boş olabilir. |
Müşteriye gönderilen sayfa, "Effacer" düğmesiyle ilişkili effacer işlevini içeren bir JavaScript betiği barındırır; bu işlevin amacı, formu başlangıç durumuna geri döndürmektir: düğme işaretlenmemiş, giriş alanları boş. Bu sonucun, "reset" türündeki bir HTML düğmesiyle elde edilemeyeceğine dikkat edilmelidir. Zira bu tür bir düğme kullanıldığında, tarayıcı formu aldığı durumuna geri döndürür. Oysa uygulamamızda tarayıcı, boş olmayabilecek formlar alabilmektedir.
Önceki formu işleyen uygulama şöyledir:
<?php
// vergi formunu işler
// kütüphaneler
include "ImpotsDSN.php";
// uygulama yapılandırması
ini_set("register_globals","off");
ini_set("display_errors","off");
$formulaireImpots="impots_form.php";
$erreursImpots="impots_erreurs.php";
$bdImpots=array(dsn=>"mysql-dbimpots",user=>admimpots,pwd=>mdpimpots,
table=>impots,limites=>limites,coeffR=>coeffR,coeffN=>coeffN);
// parametreleri alıyoruz
$requête->marié=$_POST["optMarie"];
$requête->enfants=$_POST["txtEnfants"];
$requête->salaire=$_POST["txtSalaire"];
// tüm parametreler mevcut mu?
if(! isset($requête->marié) || ! isset($requête->enfants) || ! isset($requête->salaire)){
// boş formun hazırlanması
$requête->chkoui="";
$requête->chknon="checked";
$requête->enfants="";
$requête->salaire="";
$requête->impots="";
$requête->erreurs=array();
// formun görüntülenmesi
include $formulaireImpots;
// son
exit(0);
}//if
// parametreleri kontrol et
$requête=vérifier($requête);
// hata var mı?
if(count($requête->erreurs)!=0){
// form görüntüleme
include "$formulaireImpots";
// son
exit(0);
}//if
// yanıt hesaplama
$requête=calculerImpots($bdImpots,$requête);
// hatalar mı var?
if(count($requête->erreurs)!=0){
// hata formunun görüntülenmesi
include "$erreursImpots";
// son
exit(0);
}//if
// form görüntüleme
include "$formulaireImpots";
// son
exit(0);
// --------------------------------------------------------
function vérifier($requête){
// istek parametrelerinin geçerliliğini kontrol eder
// başlangıçta hata yok
$requête->erreurs=array();
// durum geçerli mi?
$requête->marié=strtolower($requête->marié);
if($requête->marié!="oui" && $requête->marié!="non"){
// bir hata
$requête->erreurs[]="Statut marital [$requête->marié] incorrect";
}
// çocuk sayısı geçerli mi?
if(! preg_match("/^\s*\d+\s*$/",$requête->enfants)){
// bir hata
$requête->erreurs[]="Nombre d'enfants [$requête->enfants] incorrect";
}
// maaş geçerli mi?
if(! preg_match("/^\s*\d+\s*$/",$requête->salaire)){
// bir hata
$requête->erreurs[]="Salaire [$requête->salaire] incorrect";
}
// radyo düğmelerinin durumu
if($requête->marié=="oui"){
$requête->chkoui="checked";
$requête->chknon="";
}else{
$requête->chknon="checked";
$requête->chkoui="";
}
// istek gönderiliyor
return $requête;
}//kontrol ediliyor
// --------------------------------------------------------
function calculerImpots($bdImpots,$requête){
// vergi tutarını hesaplıyor
// $bdImpots: ODBC veri kaynağını okumak için gerekli bilgileri içeren sözlük
// $requête : vergi mükellefi bilgilerini içeren sorgu
// bir ImpotsDSN nesnesi oluşturulur
$objImpots=new ImpotsDSN($bdImpots);
// hatalar mı var?
if(count($objImpots->erreurs)!=0){
// hatalar sorguya eklenir
$requête->erreurs=$objImpots->erreurs;
// bitti
return $requête;
}//if
// vergi hesaplaması
$personne=array(marié=>"$requête->marié",enfants=>"$requête->enfants",salaire=>"$requête->salaire");
$requête->impots=$objImpots->calculer($personne);
// sonuç veriliyor
return $requête;
}//calculerImpots
Açıklamalar:
- öncelikle, ImpotsDSN sınıfı dahil edilmiştir. Vergi hesaplamasının temelini oluşturan ve veritabanına erişimi gizleyecek olan bu sınıftır
- Uygulamanın bakımını kolaylaştırmak amacıyla bir dizi başlangıç ayarı yapılır. Parametrelerde değişiklik olursa, bu bölümde değerleri değiştirilir. Genellikle bu yapılandırma değerleri bir dosyada veya veritabanında saklanır.
- HTML, optMarie, txtEnfants ve txtSalaire alanlarına karşılık gelen formdaki üç parametre alınır.
- Bu parametreler tamamen veya kısmen eksik olabilir. Bu durum özellikle formun ilk talep edildiği sırada geçerlidir. Bu durumda, sadece boş bir form gönderilir.
- Ardından, alınan üç parametrenin geçerliliği kontrol edilir. Parametreler hatalı çıkarsa, form girildiği şekliyle geri gönderilir, ancak buna ek olarak bir hata listesi de eklenir.
- Üç parametrenin geçerliliği doğrulandıktan sonra vergi hesaplaması yapılabilir. Vergi, calculerImpots işlevi tarafından hesaplanır. Bu işlev, ImpotsDSN türünde bir nesne oluşturur ve bu nesnenin calculer yöntemini kullanır.
- Veritabanı kullanılamıyorsa, ImpotsDSN nesnesinin oluşturulması başarısız olabilir. Bu durumda, uygulama meydana gelen hataları belirten özel bir hata sayfası görüntüler.
- Her şey yolunda giderse, form girildiği haliyle, ancak buna ek olarak ödenecek vergi tutarı da eklenmiş olarak geri gönderilir.
Hata sayfası şöyledir:
<?php // hata sayfası ?>
<html>
<head>
<title>Application impôts indisponible</title>
</head>
<body background="/poly/impots/images/standard.jpg">
<h3>Calcul d'impôts</h3>
<hr>
Application indisponible. Recommencez ultérieurement.<br><br>
<font color="red">
Les erreurs suivantes se sont produites<br>
<ul>
<?php
// hataların görüntülenmesi
for($i=0;$i<count($requête->erreurs);$i++){
echo "<li>".$requête->erreurs[$i]."</li>\n";
}
?>
</ul>
</font>
</body>
</html>
İşte birkaç hata örneği. Öncelikle, veritabanına yanlış bir şifre giriliyor:

Formda yanlış veriler girildiğinde:

Son olarak, doğru değerler giriliyor:

4.4. Vergi Uygulaması: sürüm 2
Önceki örnekte, formdaki txtEnfants ve txtSalaire parametrelerinin geçerliliği sunucu tarafından kontrol edilmektedir. Burada, form sayfasına eklenmiş bir JavaScript komut dosyası aracılığıyla bu doğrulamayı yapmayı öneriyoruz. Böylece parametrelerin doğrulanması tarayıcı tarafından gerçekleştirilir. Sunucuya ise yalnızca parametreler geçerliyse başvurulur. Bu sayede "bant genişliği" tasarrufu sağlanır. impots-form.php görüntüleme sayfası şu şekilde olur:
<?php //vergi formu ?>
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
........
}//sil
//------------------------------
function calculer(){
// parametreleri sunucuya göndermeden önce kontrol etme
with(document.frmImpots){
//çocuk sayısı
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// şablon doğrulanmadı
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
nbEnfants.focus();
return;
}//if
//maaş
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// şablon doğrulanmadı
alert("Le salaire n'a pas été donné ou est incorrect");
salaire.focus();
return;
}//eğer
// sorun yok - form sunucuya gönderiliyor
submit();
}//with
}//hesapla
</script>
</head>
<body background="/poly/impots/images/standard.jpg">
............
<td><input type="button" value="Calculer" onclick="calculer()"></td>
<td><input type="button" value="Effacer" onclick="effacer()"></td>
........
</body>
</html>
Aşağıdaki değişikliklere dikkat edilmelidir:
- Calculer düğmesi artık submit türü değil, button türündedir ve calculer adlı bir işleve bağlıdır. txtEnfants ve txTsalaire alanlarının analizini bu işlev gerçekleştirecektir. Eğer değerler doğruysa, formdaki değerler sunucuya gönderilir (submit); aksi takdirde bir hata mesajı görüntülenir.
Hata durumunda görüntülenecek bir örnek şöyledir:

4.5. Vergi Uygulaması: sürüm 3
Uygulamaya oturum kavramını eklemek için uygulamada küçük bir değişiklik yapıyoruz. Artık bu uygulamayı bir vergi hesaplama simülasyonu uygulaması olarak ele alıyoruz. Böylece bir kullanıcı, farklı vergi mükellefi "konfigürasyonlarını" simüle edebilir ve her biri için ödenmesi gereken verginin ne olacağını görebilir. Aşağıdaki web sayfası, elde edilebilecek sonuçlara ilişkin bir örnek sunmaktadır:

Yukarıdaki sayfayı görüntüleyen program şöyledir:
<?php //vergi beyannamesi ?>
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
...
}//sil
//------------------------------
function calculer(){
...
}//hesapla
</script>
</head>
<body background="/poly/impots/images/standard.jpg">
<center>
Calcul d'impôts
<hr>
<form name="frmImpots" method="POST">
<table>
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" name="optMarie" value="oui" <?php echo $requête[chkoui] ?>>oui
<input type="radio" name="optMarie" value="non" <?php echo $requête[chknon] ?>>non
</td>
</tr>
<tr>
<td>Nombre d'enfants</td>
<td><input type="text" size="5" name="txtEnfants" value="<?php echo $requête[enfants] ?>"></td>
</tr>
<tr>
<td>Salaire annuel</td>
<td><input type="text" size="10" name="txtSalaire" value="<?php echo $requête[salaire] ?>"></td>
</tr>
<tr>
<td><font color="green">Impôt</font></td>
<td><input type="text" size="10" name="txtImpots" value="<?php echo $requête[impots] ?>" readonly></td>
</tr>
<tr></tr>
<tr>
<td><input type="button" value="Calculer" onclick="calculer()"></td>
<td><input type="button" value="Effacer" onclick="effacer()"></td>
</tr>
</table>
</form>
</center>
<?php
// hata var mı?
if(count($requête[erreurs])!=0){
// hataları göster
echo "<hr>\n<font color=\"red\">\n";
echo "Les erreurs suivantes se sont produites<br>";
echo "<ul>";
for($i=0;$i<count($requête[erreurs]);$i++){
echo "<li>".$requête[erreurs][$i]."</li>\n";
}
echo "</ul>\n</font>\n";
}//if
// simülasyonlar var mı?
else if(count($requête[simulations])!=0){
// simülasyonların görüntülenmesi
echo "<hr>\n<h2>Résultats des simulations</h2>\n";
echo "<table border=\"1\">\n";
echo "<tr><td>Marié</td><td>Enfants</td><td>Salaire annuel (F)</td><td>Impôts à payer (F)</td></tr>\n";
for($i=0;$i<count($requête[simulations]);$i++){
echo "<tr>".
"<td>".$requête[simulations][$i][0]."</td>".
"<td>".$requête[simulations][$i][1]."</td>".
"<td>".$requête[simulations][$i][2]."</td>".
"<td>".$requête[simulations][$i][3]."</td>".
"</tr>\n";
}//for
echo "</table>\n";
}//if
?>
</body>
</html>
Görüntüleme programı, önceki sürümünden bazı küçük farklılıklar göstermektedir:
- sözlük, $requête nesnesi yerine $requête sözlüğünü alır. Bu nedenle, $requête->çocuklar yerine $requête[enfants] yazılır.
- Bu sözlükte, iki boyutlu bir dizi olan simulations adlı bir alan bulunmaktadır. $requête[simulations][i], i numaralı simülasyondur. Bu da, medeni durum, çocuk sayısı, yıllık maaş ve ödenecek vergi olmak üzere dört karakter dizisinden oluşan bir dizidir.
Ana program ise daha kapsamlı bir şekilde geliştirilmiştir:
<?php
// vergi formunu işler
// kütüphaneler
include "ImpotsDSN.php";
// oturum başlatma
session_start();
// uygulama yapılandırması
ini_set("register_globals","off");
ini_set("display_errors","off");
$formulaireImpots="impots_form.php";
$erreursImpots="impots_erreurs.php";
$bdImpots=array(dsn=>"mysql-dbimpots",user=>admimpots,pwd=>mdpimpots,
table=>impots,limites=>limites,coeffR=>coeffR,coeffN=>coeffN);
// oturum parametrelerini alıyoruz
$session=$_SESSION["session"];
// oturum geçerli mi?
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// yeni oturum başlatılıyor
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// Hata var mı?
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// hata sayfası görüntüleniyor
include $erreursImpots;
// son
$session=array();
terminerSession($session);
}//if
}//if
// devam eden işlemin parametrelerini alıyoruz
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
// Tüm parametreler mevcut mu?
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
// boş form görüntülenir
$requête=array(chkoui=>"",chknon=>"checked",enfants=>"",salaire=>"",impots=>"",
erreurs=>array(),simulations=>array());
include $formulaireImpots;
// son
terminerSession($session);
}//if
// parametreleri kontrol et
$requête=vérifier($requête);
// Hata var mı?
if(count($requête[erreurs])!=0){
// form görüntüleme
include "$formulaireImpots";
// son
terminerSession($session);
}//if
// ödenecek verginin hesaplanması
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
// bir simülasyon daha
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// form görüntüleme
include "$formulaireImpots";
// son
terminerSession($session);
// --------------------------------------------------------
function vérifier($requête){
// istek parametrelerinin geçerliliğini kontrol eder
// başlangıçta hata yok
$requête[erreurs]=array();
// durum geçerli mi?
$requête[marié]=strtolower($requête[marié]);
if($requête[marié]!="oui" && $requête[marié]!="non"){
// bir hata
$requête[erreurs][]="Statut marital [$requête[marié]] incorrect";
}
// çocuk sayısı geçerli mi?
if(! preg_match("/^\s*\d+\s*$/",$requête[enfants])){
// bir hata
$requête[erreurs][]="Nombre d'enfants [$requête[enfants]] incorrect";
}
// maaş geçerli mi?
if(! preg_match("/^\s*\d+\s*$/",$requête[salaire])){
// bir hata
$requête[erreurs][]="Salaire [$requête[salaire]] incorrect";
}
// radyo düğmelerinin durumu
if($requête[marié]=="oui"){
$requête[chkoui]="checked";
$requête[chknon]="";
}else{
$requête[chknon]="checked";
$requête[chkoui]="";
}
// istek gönderiliyor
return $requête;
}//kontrol ediliyor
// --------------------------------------------------------
function terminerSession($session){
// oturum kaydediliyor
$_SESSION[session]=$session;
// komut dosyası sonu
exit(0);
}//terminerSession
- Öncelikle, bu uygulama bir oturum yönetir. Dolayısıyla, komut dosyasının başında bir oturum başlatılır:
- Oturum, tek bir değişkeni saklar: $session sözlüğü. Bu sözlüğün iki alanı vardır:
- objImpots: bir ImpotsDSN nesnesi. ODBC veritabanına gereksiz ardışık erişimleri önlemek amacıyla bu nesne, istemcinin oturumunda saklanır.
- simülasyonlar: Bir işlemden diğerine önceki işlemlerin simülasyonlarını hatırlamak için kullanılan simülasyon tablosu.
- oturum başlatıldığında, $session değişkeni alınır. Bu değişken henüz mevcut değilse, oturum başlatılır. Ardından bir ImpotsDSN nesnesi ve boş bir simülasyon tablosu oluşturulur. Gerekirse, hatalar bildirilir.
<?php
...
// oturum parametrelerini al
$session=$_SESSION["session"];
// oturum geçerli mi?
if(! isset($session) || ! isset($session[objImpots]) || ! isset($session[simulations])){
// yeni bir oturum başlatılıyor
$session=array(objImpots=>new ImpotsDSN($bdImpots),simulations=>array());
// Hata mı var?
if(count($session[objImpots]->erreurs)!=0){
$requête=array(erreurs=>$session[objImpots]->erreurs);
// hata sayfası görüntüleniyor
include $erreursImpots;
// son
$session=array();
terminerSession($session);
}//if
}//if
- oturum başarıyla başlatıldıktan sonra, veri alışverişi parametreleri alınır. Beklenen tüm parametreler mevcut değilse, boş bir form gönderilir.
<?php
...
// devam eden işlemin parametrelerini alıyoruz
$requête[marié]=$_POST["optMarie"];
$requête[enfants]=$_POST["txtEnfants"];
$requête[salaire]=$_POST["txtSalaire"];
// tüm parametreler mevcut mu?
if(! isset($requête[marié]) || ! isset($requête[enfants]) || ! isset($requête[salaire])){
// boş form görüntüleniyor
$requête=array(chkoui=>"",chknon=>"checked",enfants=>"",salaire=>"",impots=>"",
erreurs=>array(),simulations=>array());
include $formulaireImpots;
// son
terminerSession($session);
}//if
- Tüm parametreler doğru girilmişse, bunlar doğrulanır. Hata varsa, bunlar bildirilir:
<?php
...
// parametreleri kontrol et
$requête=vérifier($requête);
// Hata var mı?
if(count($requête[erreurs])!=0){
// form görüntüleme
include "$formulaireImpots";
// son
terminerSession($session);
}//if
- parametreler doğruysa, vergi hesaplanır:
<?php
...
// ödenecek verginin hesaplanması
$requête[impots]=$session[objImpots]->calculer(array(marié=>$requête[marié],
enfants=>$requête[enfants],salaire=>$requête[salaire]));
- Devam eden simülasyon simülasyon tablosuna eklenir ve bu tablo, aşağıdaki formla birlikte müşteriye gönderilir:
<?php
...
// bir simülasyon daha
$session[simulations][]=array($requête[marié],$requête[enfants],$requête[salaire],$requête[impots]);
$requête[simulations]=$session[simulations];
// form görüntüleme
include "$formulaireImpots";
// son
terminerSession($session);
- Her durumda, komut dosyası $session değişkenini oturuma kaydederek sona erer. Bu işlem, terminerSession yordamında gerçekleştirilir.
<?php
...
function terminerSession($session){
// oturumun kaydedilmesi
$_SESSION[session]=$session;
// komut dosyası sonu
exit(0);
}//terminerSession
Son olarak, veritabanına erişim hatalarını görüntüleyen programı (impots-erreurs.php) tanıtarak bitirelim:
<?php // hata sayfası ?>
<html>
<head>
<title>Application impôts indisponible</title>
</head>
<body background="/poly/impots/images/standard.jpg">
<h3>Calcul d'impôts</h3>
<hr>
Application indisponible. Recommencez ultérieurement.<br><br>
<font color="red">
Les erreurs suivantes se sont produites<br>
<ul>
<?php
// hataların görüntülenmesi
for($i=0;$i<count($requête[erreurs]);$i++){
echo "<li>".$requête[erreurs][$i]."</li>\n";
}
?>
</ul>
</font>
</body>
</html>
Burada da $requête nesnesi, $requête sözlüğüyle değiştirilmiştir.
4.6. Vergi Uygulaması: sürüm 4
Şimdi, önceki impots web uygulamasının bir web istemcisi olacak bağımsız bir uygulama oluşturacağız. Uygulama, bir DOS penceresinden başlatılan bir konsol uygulaması olacaktır:
dos>e:\php43\php.exe cltImpots.php
Syntaxe cltImpots.php urlImpots marié enfants salaire [jeton]
cltImpots web istemcisinin parametreleri şunlardır:
// $0 sözdizimi urlImpots evli çocuklar maaş jeton
// urlImpots üzerinde çalışan bir vergi hizmetinin müşterisi
// bu servise üç bilgiyi gönderir: evli, çocuk sayısı, maaş
// eğer oturum jetonu aktarılmışsa, bunu da ekleyebilir
// oturumun simülasyon tablosunu görüntüler
İşte birkaç kullanım örneği. Öncelikle, oturum belirteci içermeyen ilk örnek.
dos>e:\php43\php.exe cltImpots.php http://localhost/poly/impots/6/impots.php evet 2 200000
Jeton de session=[a6297317667bc981c462120987b8dd18]
Simulations :
[Marié,Enfants,Salaire annuel (F),Impôts à payer (F)]
[oui,2,200000,22504]
Ödenecek vergi tutarını (22504 f) başarıyla aldık. Ayrıca oturum jetonunu da aldık. Artık bunu ikinci bir sorgu için kullanabiliriz:
dos>e:\php43\php.exe cltImpots.php http://localhost/poly/impots/6/impots.php evet 3 200000 a6297317667bc981c462120987b8dd18
Jeton de session=[a6297317667bc981c462120987b8dd18]
Simulations :
[Marié,Enfants,Salaire annuel (F),Impôts à payer (F)]
[oui,2,200000,22504]
[oui,3,200000,16400]
Web sunucusu tarafından gönderilen simülasyon tablosunu başarıyla alıyoruz. Elbette alınan jeton aynı kalıyor. Veritabanı başlatılmamışken web hizmetine sorgu gönderirsek, şu sonucu elde ederiz:
dos>e:\php43\php.exe cltImpots2.php http://localhost/poly/impots/6/impots.php evet 3 200000
Jeton de session=[8369014d5053212bc42f64bbdfb152ee]
Les erreurs suivantes se sont produites :
Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)
Programlanmış bir web istemcisi yazarken, sunucunun bir istemciden gelebilecek çeşitli isteklerin yanıtı olarak tam olarak ne gönderdiğini bilmek gerekir. Sunucu, yararlı bilgiler ile yalnızca sayfa düzeni için bulunan diğer bilgileri içeren bir dizi satır HTML gönderir. PHP düzenli ifadeleri, sunucu tarafından gönderilen satır akışında yararlı bilgileri bulmamıza yardımcı olabilir. Bunun için, sunucunun farklı yanıtlarının tam biçimini bilmemiz gerekir. Bu amaçla, bir tarayıcı kullanarak web hizmetine sorgu gönderebilir ve gönderilen kaynak kodunu inceleyebiliriz. Bu yöntemin, web sunucusunun yanıtındaki başlıkları belirlemeye izin vermediğine dikkat edilmelidir. Bazen bu başlıkları bilmek yararlı olabilir. Bu durumda, önceki bölümde ele alınan iki genel web istemcisinden birini kullanabiliriz.
Önceki örnekleri tekrarlarsak, simülasyon tablosu için tarayıcı tarafından alınan HTML kodu şöyledir:
<h2>Résultats des simulations</h2>
<table border="1">
<tr><td>Marié</td><td>Enfants</td><td>Salaire annuel (F)</td><td>Impôts à payer (F)</td></tr>
<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>
<tr><td>oui</td><td>3</td><td>200000</td><td>16400</td></tr>
</table>
Bu, gönderilen belgedeki tek tablo olan HTML'tir. Dolayısıyla, "|<tr>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*</tr>|" düzenli ifadesi, belgede alınan farklı simülasyonları bulmaya olanak sağlamalıdır. Veritabanı kullanılamadığında, aşağıdaki belge alınır:
Application indisponible. Recommencez ultérieurement.<br><br>
<font color="red">
Les erreurs suivantes se sont produites<br>
<ul>
<li>Impossible d'ouvrir la base DSN [mysql-dbimpots] (S1000)</li>
</ul>
</font>
Hatayı almak için, web istemcisi web sunucusunun yanıtında "|<li>(.+?)</li>|" şablonuna uyan satırları aramalıdır.
Artık, kullanıcı önceki konsol istemcisinden vergi hesaplamasını talep ettiğinde yapılması gerekenlere ilişkin kılavuzumuz hazır:
- tüm parametrelerin geçerli olup olmadığını kontrol etmek ve gerekirse hataları bildirmek.
- İlk parametre olarak verilen URL'e bağlanmak. Bunun için, daha önce sunulmuş ve incelenmiş olan genel web istemcisi modelini takip edeceğiz
- sunucu yanıt akışında, düzenli ifadeler kullanarak ya:
- hata mesajlarını bulmak
- simülasyon sonuçlarını bulmak
cltImpots.php müşteri kodu şöyledir:
<?php
// sözdizimi $0 urlImpots evli çocuklar maaş jeton
// urlImpots üzerinde çalışan bir vergi hizmetinin müşterisi
// bu servise üç bilgiyi gönderir: evli olup olmadığı, çocuk sayısı, maaş
// eğer oturum jetonu aktarılmışsa, bunu da ekleyebilir
// oturumun simülasyon tablosunu görüntüler
// argüman sayısını kontrol eder
if(count($argv)!=5 && count($argv)!=6){
// hata
fwrite(STDERR,"Syntaxe $argv[0] urlImpots marié enfants salaire [jeton]\n");
// son
exit(1);
}//if
// URL not edilir
$urlImpots=analyseURL($argv[1]);
if(isset($urlImpots[erreur])){
// hata
fwrite(STDERR,"$urlImpots[erreur]\n");
// son
exit(1);
}//eğer
// kullanıcı isteğinin analizi
$demande=analyseDemande("$argv[2],$argv[3],$argv[4]");
// hatalar mı?
if($demande[erreur]){
// mesaj
echo "$demande[erreur]\n";
// bitti
exit(1);
}//if
// istek yapılabilir - oturum jetonu kullanılır
$impots=getImpots($urlImpots,$demande,$argv[5]);
// oturum jetonunu görüntülüyor
echo "Jeton de session=[$impots[jeton]]\n";
// hatalar mı var?
if(count($impots[erreurs])!=0){
//hata mesajları
echo "Les erreurs suivantes se sont produites :\n";
for($i=0;$i<count($impots[erreurs]);$i++){
echo $impots[erreurs][$i]."\n";
}//for
}else{
// hata yok - simülasyonların görüntülenmesi
echo "Simulations :\n";
for($i=0;$i<count($impots[simulations]);$i++){
echo "[".implode(",",$impots[simulations][$i])."]\n";
}//for
}//if
// programın sonu
exit(0);
// --------------------------------------------------------------
function analyseURL($URL){
// URL'in geçerliliğini analiz ediyor $URL
$url=parse_url($URL);
// protokolü
if(strtolower($url[scheme])!="http"){
$url[erreur]="l'URL [$URL] n'est pas au format http://machine[:port][/chemin]";
return $url;
}//if
// makine
if(! isset($url[host])){
$url[erreur]="l'URL [$URL] n'est pas au format http://machine[:port][/chemin]";
return $url;
}//if
// bağlantı noktası
if(! isset($url[port])) $url[port]=80;
// istek
if(isset($url["query"])){
$url[erreur]="l'URL [$URL] n'est pas au format http://machine[:port][/chemin]";
return $url;
}//if
// dönüş
return $url;
}//analyseURL
// -----------------------------------------------------------
function analyseDemande($demande){
// $demande : analiz edilecek dize
// evli, çocuk sayısı, maaş şeklinde olmalıdır
// geçerli biçim
if(! preg_match("/^\s*(oui|non)\s*,\s*(\d{1,3})\s*,\s*(\d+)\s*$/i",$demande,$champs)){
// geçersiz biçim
return(array(erreur=>"Format (marié, enfants, salaire) invalide."));
}
// doğru
$marié=strtolower($champs[1]);
return array(marié=>$marié,enfants=>$champs[2],salaire=>$champs[3]);
}//analyseDemande
// --------------------------------------------------------------
function getImpots($urlImpots,$demande,$jeton){
// $urlImpots : URL sorgulanmalı
// $demande : marie, çocuklar, maaş alanlarını içeren sözlük
// $jeton: olası bir oturum jetonu
// $urlImpots[port] adresindeki bağlantı noktası üzerinden $urlImpots[host] adresine bağlantı kuruluyor
$connexion=fsockopen($urlImpots[host],$urlImpots[port],&$errno,&$erreur);
// hata durumunda geri dönüş
if(! $connexion){
return array(erreurs=>array("Echec de la connexion au site ($urlImpots[host],$urlImpots[port]) : $erreur"));
}//if
// HTTP başlıklarını sunucuya gönderme
POST($connexion,$urlImpots,$jeton,
array(optMarie=>$demande[marié],txtEnfants=>$demande[enfants],txtSalaire=>$demande[salaire]));
// sunucudan gelen yanıtı okur - önce başlıkları HTTP
// ilk satır
$ligne=fgets($connexion,10000);
// URL bulundu mu?
if(! preg_match("/^(.+?) 200 OK\s*$/",$ligne)){
// URL erişilemez - hata ile geri dönülür
return array(erreurs=>array("L'URL $urlImpots[path] n'a pu être trouvée"));
}//eğer
// diğer başlıklar okunur HTTP
while(($ligne=fgets($connexion,10000)) && (($ligne=rtrim($ligne))!="")){
// henüz bulunamadıysa jeton aranıyor
if(! $jeton){
// set-cookie satırını arama
if(preg_match("/^Set-Cookie: PHPSESSID=(.*?);/i",$ligne,$champs)){
// jeton bulundu - saklanır
$jeton=$champs[1];
}//if
}//if
}//sonraki satır
// sonraki belge okunuyor
$document="";
while($ligne=fread($connexion,10000)){
$document.=$ligne;
}//while
// bağlantı kapatılır
fclose($connexion);
// belgeyi analiz ediyoruz
$impots=getInfos($document);
// sonuç verilir
return array(jeton=>$jeton,erreurs=>$impots[erreurs],simulations=>$impots[simulations]);
}//getImpots
// --------------------------------------------------------------
function POST($connexion,$url,$jeton,$paramètres){
// $connexion: web sunucusuna bağlantı
// $url: sorgulanacak URL
// $paramètres: gönderilecek parametreler sözlüğü
// POST hazırlanıyor
$post="";
while(list($paramètre,$valeur)=each($paramètres)){
$post.=$paramètre."=".urlencode($valeur)."&";
}//while
// son karakteri kaldırıyoruz
$post=substr($post,0,-1);
// HTTP isteğini HTTP/1.0 biçiminde hazırlıyoruz
$HTTP="POST $url[path] HTTP/1.0\n";
$HTTP.="Content-type: application/x-www-form-urlencoded\n";
$HTTP.="Content-length: ".strlen($post)."\n";
$HTTP.="Connection: close\n";
if($jeton) $HTTP.="Cookie: PHPSESSID=$jeton\n";
$HTTP.="\n";
$HTTP.=$post;
// HTTP isteği gönderilir
fwrite($connexion,$HTTP);
}//POST
// --------------------------------------------------------------
function getInfos($document){
// $document: HTML belgesi
// ya da hata listesi aranır
// ya da simülasyon tablosu aranıyor
// sonucun hazırlanması
$impots[erreurs]=array();
$impots[simulations]=array();
// bir hata var mı?
// hata satırı şablonu
$modErreur="|<li>(.+?)</li>|";
// belgede arama
if(preg_match_all($modErreur,$document,$champs,PREG_SET_ORDER)){
// hataların toplanması
for($i=0;$i<count($champs);$i++){
$impots[erreurs][]=$champs[$i][1];
}//için
// tamamlandı
return $impots;
}//eğer
// simülasyon tablosundan bir satır örneği
// <tr><td>evet</td><td>2</td><td>200000</td><td>22504</td></tr>
$modSimulation="|<tr>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*</tr>|";
// belgede arama
if(preg_match_all($modSimulation,$document,$champs,PREG_SET_ORDER)){
// simülasyonların alınması
for($i=0;$i<count($champs);$i++){
$impots[simulations][]=array($champs[$i][1],$champs[$i][2],$champs[$i][3],$champs[$i][4]);
}//for
// sonuç döndürme
return $impots;
}//if
// buraya gelmek normal değil
return $impots;
}//getInfos
?>
Açıklamalar:
- Program, öncelikle aldığı parametrelerin geçerliliğini kontrol eder. Bunun için iki işlev kullanır: analyseURL, URL'in geçerliliğini kontrol ederken, analyseDemande ise diğer parametreleri kontrol eder.
- Vergi hesaplaması getImpots fonksiyonu tarafından yapılır:
- getImpots fonksiyonu şu şekilde tanımlanmıştır:
<?php
...
// --------------------------------------------------------------
function getImpots($urlImpots,$demande,$jeton){
// $urlImpots : URL sorgulanmalı
// $demande : marie, çocuklar, maaş alanlarını içeren sözlük
// $jeton: olası bir oturum jetonu
- (devamı)
- $urlImpots, aşağıdaki alanları içeren bir sözlüktür:
- host: web hizmetinin bulunduğu makine
- port: hizmetin kullandığı bağlantı noktası
- path: istenen kaynağın yolu
- $demande, aşağıdaki alanları içeren bir sözlüktür:
- evli: evet/hayır: medeni durum
- çocuklar: çocuk sayısı
- maaş: yıllık maaş
- $jeton, oturum jetonudur.
- $urlImpots, aşağıdaki alanları içeren bir sözlüktür:
İşlev, aşağıdaki alanları içeren bir sözlük döndürür:
- hatalar: hata mesajları dizisi
- simülasyonlar: simülasyonlar dizisi; her bir simülasyon da dört elemanlı bir dizidir (evli, çocuklar, maaş, vergi)
- jeton: oturum jetonu
- getImpots işlevinin sonucu elde edildikten sonra, program sonuçları görüntüler ve sonlanır:
<?php
...
// oturum jetonu görüntülenir
echo "Jeton de session=[$impots[jeton]]\n";
// hatalar mı var?
if(count($impots[erreurs])!=0){
//hata mesajları
echo "Les erreurs suivantes se sont produites :\n";
for($i=0;$i<count($impots[erreurs]);$i++){
echo $impots[erreurs][$i]."\n";
}//for
}else{
// hata yok - simülasyonların görüntülenmesi
echo "Simulations :\n";
for($i=0;$i<count($impots[simulations]);$i++){
echo "[".implode(",",$impots[simulations][$i])."]\n";
}//for
}//if
// programın sonu
exit(0);
- Şimdi, getImpots($urlImpots,$demande,$jeton) işlevini inceleyelim; bu işlev
- TCP-IP bağlantı noktasında $urlImpots[port] bağlantı noktası üzerinden $urlImpots[host] makinesine bir bağlantı oluşturmalıdır
- web sunucusuna beklediği HTTP başlıklarını göndermek; özellikle varsa oturum jetonunu da göndermek
- web sunucusuna, $demande sözlüğünde bulunan parametreleri içeren bir POST isteği göndermek
- web sunucusunun yanıtını analiz ederek, içinde bir hata listesi ya da bir simülasyon tablosu olup olmadığını belirlemek.
- HTTP başlıklarının gönderilmesi, POST işlevi kullanılarak gerçekleştirilir:
<?php
...
// HTTP başlıkları sunucuya gönderiliyor
POST($connexion,$urlImpots,$jeton,
array(optMarie=>$demande[marié],txtEnfants=>$demande[enfants],txtSalaire=>$demande[salaire]));
- HTTP başlıkları gönderildikten sonra, getImpots işlevi sunucunun yanıtını tamamen okur ve $document'e yerleştirir. Yanıt, web sunucusunun talep edilen URL'i bulup bulmadığını anlamamızı sağlayan ilk satır hariç, analiz edilmeden okunur. Nitekim, URL bulunmuşsa, web sunucusu HTTP/1.X 200 OK şeklinde yanıt verir; burada X, kullanılan HTTP sürümüne bağlıdır.
<?php
...
// URL bulundu mu?
if(! preg_match("/^(.+?) 200 OK\s*$/",$ligne)){
<?php
...
// URL erişilemez - hata ile geri dönüş
return array(erreurs=>array("L'URL $urlImpots[path] n'a pu être trouvée"));
}//if
- belge, getInfos işlevi kullanılarak analiz edilir:
- Sonuç, iki alanlı bir sözlük şeklindedir:
- hatalar: hata listesi - boş olabilir
- simülasyonlar: simülasyon listesi - boş olabilir
- Böylece, getImpots işlevi sonucunu bir sözlük biçiminde döndürebilir.
<?php
...
// sonuç veriliyor
return array(jeton=>$jeton,erreurs=>$impots[erreurs],simulations=>$impots[simulations]);
- Şimdi de POST işlevini inceleyelim:
<?php
...
// --------------------------------------------------------------
function POST($connexion,$url,$jeton,$paramètres){
// $connexion: web sunucusuna bağlantı
// $url: sorgulanacak URL
// $paramètres : gönderilecek parametreler sözlüğü
// POST hazırlanıyor
$post="";
while(list($paramètre,$valeur)=each($paramètres)){
$post.=$paramètre."=".urlencode($valeur)."&";
}//while
// son karakteri kaldırıyoruz
$post=substr($post,0,-1);
// HTTP isteğini HTTP/1.0 biçiminde hazırlıyoruz
$HTTP="POST $url[path] HTTP/1.0\n";
$HTTP.="Content-type: application/x-www-form-urlencoded\n";
$HTTP.="Content-length: ".strlen($post)."\n";
$HTTP.="Connection: close\n";
if($jeton) $HTTP.="Cookie: PHPSESSID=$jeton\n";
$HTTP.="\n";
$HTTP.=$post;
// HTTP isteği gönderilir
fwrite($connexion,$HTTP);
}//POST
Program kapsamında, bir GET aracılığıyla bir web kaynağını talep etme fırsatımız olmuştu, ancak henüz bir POST ile bunu yapma fırsatımız olmamıştı. POST, sunucuya parametreleri gönderme şekli açısından GET'ten farklıdır. Aşağıdaki HTTP başlıklarını göndermelidir:
ile birlikte
- chemin: istenen web kaynağının yolu, burada $url[path]
- HTTP/1.X: istenen protokol HTTP. Burada, yanıtı tek bir blok halinde almak için HTTP/1.0 seçilmiştir. HTTP/1.1, yanıtın parçalı (chunked) olarak gönderilmesine izin verir.
- N, istemcinin sunucuya göndereceği karakter sayısını belirtir
İsteğin parametrelerini oluşturan N karakter, sunucuya gönderilen HTTP başlıklarını sonlandıran boş satırın hemen arkasına gönderilir. Bu parametreler param1=val1¶m2=val2&... biçimindedir; burada parami parametrenin adı, vali ise değeridir. Geçerli değerler, boşluk, & karakteri, = karakteri vb. gibi "sorunlu" karakterler içerebilir. Bu karakterler, XX dizesiyle değiştirilmelidir; burada XX, söz konusu karakterlerin onaltılık kodudur. PHP urlencode işlevi bu işlemi gerçekleştirir. Tersine işlem ise urldecode tarafından yapılır.
Son olarak, bir jeton varsa, bunun HTTP Cookie: PHPSESSID=jeton başlığıyla gönderildiğine dikkat edilmelidir.
- Geriye, sunucunun yanıtını analiz eden işlevi incelemek kalıyor:
<?php
...
// --------------------------------------------------------------
function getInfos($document){
// $document: HTML belgesi
// ya da hata listesi aranır
// ya da simülasyon tablosu aranıyor
// sonucun hazırlanması
$impots[erreurs]=array();
$impots[simulations]=array();
- Hataların sunucu tarafından <li>hata mesajı</li> biçiminde gönderildiğini hatırlayalım. "|<li>(.+?)</li>|" düzenli ifadesi, bu bilgileri elde etmemizi sağlamalıdır. Burada, düzenli ifadenin içinde de / işareti bulunduğu için, düzenli ifadeyi sınırlamak üzere / işareti yerine | işaretini kullandık. preg_match_all işlevi ($modèle, $document, $champs, PREG_SET_ORDER) işlevi, $document içinde bulunan tüm $modèle örneklerini alır. Bunlar, $champs dizisine eklenir. Dolayısıyla $champs[i], $document içinde $modèle'in i numaralı örneğini temsil eder. $champs[$i][0] tablosuna, şablona karşılık gelen dize yerleştirilir. Şablonda parantezler varsa, ilk paranteze karşılık gelen dize $champs[$i][1] dosyasına, ikincisi ise $champs[$i][2]'e yerleştirilir ve bu şekilde devam eder. Hataları almak için kullanılacak kod şu şekilde olacaktır:
<?php
...
// bir hata var mı?
// hata satırı şablonu
$modErreur="|<li>(.+?)</li>|";
// belgede arama
if(preg_match_all($modErreur,$document,$champs,PREG_SET_ORDER)){
// hataların kurtarılması
for($i=0;$i<count($champs);$i++){
$impots[erreurs][]=$champs[$i][1];
}//for
// bitti
return $impots;
}//if
4.7. Vergi Uygulaması: Sonuç
Vergi hesaplama amaçlı istemci-sunucu uygulamamızın farklı sürümlerini gösterdik:
- sürüm 1: hizmet, PHP programları kümesi tarafından sağlanır; istemci ise bir tarayıcıdır. Tek bir simülasyon yapar ve önceki simülasyonları hafızasında tutmaz.
- sürüm 2: tarayıcı tarafından yüklenen HTML belgesine javascript komut dosyaları ekleyerek tarayıcı tarafında bazı yetenekler eklenmiştir. Form parametrelerinin geçerliliğini kontrol eder.
- sürüm 3: Bir oturum yönetilerek, hizmetin bir istemci tarafından gerçekleştirilen farklı simülasyonları hatırlamasına olanak sağlanır. HTML arayüzü, bunları görüntülemek üzere buna göre değiştirilir.
- sürüm 4: İstemci artık bağımsız bir konsol uygulamasıdır. Bu, programlanmış web istemcileri geliştirmeye geri dönmemizi sağlar.
Bu noktada birkaç hususa değinmek gerekir:
- Sürüm 1 ila 3, JavaScript komut dosyalarını çalıştırabilme özelliği dışında başka bir özelliği olmayan tarayıcılara izin verir. Bir kullanıcının her zaman bu komut dosyalarının yürütülmesini engelleme seçeneği olduğu unutulmamalıdır. Bu durumda uygulama, sürüm 1'de yalnızca kısmen çalışır (Sil seçeneği çalışmaz) ve sürüm 2 ile 3'te hiç çalışmaz (Sil ve Hesapla seçenekleri çalışmaz). JavaScript komut dosyalarını kullanmayan bir hizmet sürümü tasarlamak ilginç olabilir.
Bir web hizmeti yazarken, hangi tür müşterileri hedeflediğimizi kendimize sormalıyız. Mümkün olduğunca çok sayıda müşteriye ulaşmak istiyorsak, tarayıcılara yalnızca HTML (javascript veya applet içermeyen) gönderen bir uygulama yazmalıyız. Bir intranet ortamında çalışıyorsanız ve bu ortamdaki bilgisayarların yapılandırmasını kontrol edebiliyorsanız, istemci açısından daha seçici olabilirsiniz.
Sürüm 4, sunucu tarafından gönderilen HTML akışından ihtiyaç duyduğu bilgileri bulan bir web istemcisidir. Çoğu zaman bu akış üzerinde kontrolümüz yoktur. Bu durum, ağda mevcut olan ve başka biri tarafından yönetilen bir web hizmeti için bir istemci yazdığımızda geçerlidir. Bir örnek verelim. Vergi hesaplama simülasyon hizmetimizin X şirketi tarafından yazıldığını varsayalım. Şu anda hizmet, simülasyonları HTML tablosunda gönderiyor ve istemcimiz bu durumu kullanarak verileri alıyor. Böylece, sunucunun yanıtındaki her satırı şu düzenli ifadeyle karşılaştırıyor:
// simülasyon tablosundaki bir satırın şablonu
// <tr><td>evet</td><td>2</td><td>200000</td><td>22504</td></tr>
$modSimulation="|<tr>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*<td>(.+?)</td>\s*</tr>|";
Şimdi, uygulama geliştiricisinin yanıtın görsel görünümünü değiştirerek simülasyonları bir tablo yerine şu biçimdeki bir listeye yerleştirdiğini varsayalım:
Bu durumda, web istemcimizin yeniden yazılması gerekecektir. İşte bu, kendi kontrolümüz altında olmayan uygulamaların web istemcileri üzerinde sürekli olarak duran bir tehdittir. XML bu soruna bir çözüm sunabilir:
- HTML yerine, simülasyon hizmeti XML üretecektir. Örneğimizde bu şöyle olabilir:
<simulations>
<entetes marie="marié" enfants="enfants" salaire="salaire" impot="impôt"/>
<simulation marie="oui" enfants="2" salaire="200000" impot="22504" />
<simulation marie="non" enfants="2" salaire="200000" impot="33388" />
</simulations>
- Bu yanıtla ilişkili bir stil sayfası, tarayıcılara bu yanıtın nasıl görüntüleneceğini belirtebilir. XML
- programlanmış web istemcileri bu stil sayfasını yok sayarak, bilgileri doğrudan yanıtın akışından alır: XML
Hizmetin tasarımcısı, sağladığı sonuçların görsel sunumunu değiştirmek isterse, XML'i değil, stil sayfasını değiştirecektir. Stil sayfası sayesinde, tarayıcılar yeni görsel biçimi gösterecek ve programlanmış web istemcilerinin kendilerinin değiştirilmesine gerek kalmayacaktır.
