Skip to content

8. Uygulama alıştırması – sürüm 3

Daha önce incelediğimiz alıştırmayı (4.3 ve 4.4. paragraflar) bir sınıf kullanarak PHP koduyla çözeceğiz.

8.1. Komut dosyalarının ağaç yapısı

Image

8.2. [ExceptionImpots] istisnası

Sürüm 03'te, bir sınıf oluşturucu veya yöntemi bir hatayla karşılaştığında, aşağıdaki [ExceptionImpots] türünde bir istisna tetiklenir:

<?php

// ad alanı
namespace Application;

class ExceptionImpots extends \RuntimeException {

  public function __construct(string $message, int $code=0) {
    parent::__construct($message, $code);
  }

}

Açıklamalar

  • 4. satır: [ExceptionImpots] sınıfı, [Application] ad alanında yer alır;
  • 6. satır: [ExceptionImpots] sınıfı, PHP [RuntimeException] içinde önceden tanımlanmış sınıfı genişletir;
  • 8. satır: Oluşturucu iki parametre bekler:
    • $message: istisna ile ilişkili hata mesajıdır;
    • $code: istisna ile ilişkili hata kodudur. Bu kod mevcut değilse, 0 kodu kullanılır;

8.3. [TaxAdminData] sınıfı

02 sürümünde, vergi idaresine ait veriler şu şekilde bir araya getirilmiştir:

  • öncelikle bir jSON dosyasına;
  • ardından bu jSON dosyasından bir ilişkisel tabloya aktarılmıştır;

03 sürümünde, vergi idaresine ait veriler hâlâ [taxadmindata.json] dosyasında yer almaktadır, ancak öznitelik adları farklıdır:


{
    "limites": [
        9964,
        27519,
        73779,
        156244,
        0
    ],
    "coeffR": [
        0,
        0.14,
        0.3,
        0.41,
        0.45
    ],
    "coeffN": [
        0,
        1394.96,
        5798,
        13913.69,
        20163.45
    ],
    "plafondQfDemiPart": 1551,
    "plafondRevenusCelibatairePourReduction": 21037,
    "plafondRevenusCouplePourReduction": 42074,
    "valeurReducDemiPart": 3797,
    "plafondDecoteCelibataire": 1196,
    "plafondDecoteCouple": 1970,
    "plafondImpotCouplePourDecote": 2627,
    "plafondImpotCelibatairePourDecote": 1595,
    "abattementDixPourcentMax": 12502,
    "abattementDixPourcentMin": 437
}

02 sürümünde bu dosya, bir ilişkisel tablonun başlatılması için kullanılıyordu. 03 sürümünde ise dosya, aşağıdaki [TaxAdminData] sınıfını başlatacaktır:


<?php

namespace Application;

class TaxAdminData {
  // vergi dilimleri
  private $limites;
  private $coeffR;
  private $coeffN;
  // vergi hesaplama sabitleri
  private $plafondQfDemiPart;
  private $plafondRevenusCelibatairePourReduction;
  private $plafondRevenusCouplePourReduction;
  private $valeurReducDemiPart;
  private $plafondDecoteCelibataire;
  private $plafondDecoteCouple;
  private $plafondImpotCouplePourDecote;
  private $plafondImpotCelibatairePourDecote;
  private $abattementDixPourcentMax;
  private $abattementDixPourcentMin;

  // başlatma
  public function setFromJsonFile(string $taxAdminDataFilename): TaxAdminData {
    // vergi verileri dosyasının içeriği alınır
    $fileContents = \file_get_contents($taxAdminDataFilename);
    $erreur = FALSE;
    // hata mı?
    if (!$fileContents) {
      // hatayı kaydediyoruz
      $erreur = TRUE;
      $message = "Le fichier des données [$taxAdminDataFilename] n'existe pas";
    }
    if (!$erreur) {
      // yapılandırma dosyasından jSON kodunu bir ilişkisel diziye alınır
      $arrayTaxAdminData = \json_decode($fileContents, true);
      // hata mı?
      if ($arrayTaxAdminData === FALSE) {
        // hata kaydediliyor
        $erreur = TRUE;
        $message = "Le fichier de données jSON [$taxAdminDataFilename] n'a pu être exploité correctement";
      }
    }
    // hata mı?
    if ($erreur) {
      // bir istisna atılır
      throw new ExceptionImpots($message);
    }
    // sınıf özniteliklerinin başlatılması
    foreach ($arrayTaxAdminData as $key => $value) {
      $this->$key = $value;
    }
    // tüm anahtarların başlatıldığını kontrol ediliyor
    $arrayOfAttributes = \get_object_vars($this);
    foreach ($arrayOfAttributes as $key => $value) {
      if (!isset($this->$key)) {
        throw new ExceptionImpots("L'attribut [$key] de [TaxAdminData] n'a pas été initialisé");
      }
    }
    // sadece gerçek sayı değerleri olup olmadığı kontrol ediliyor
    foreach ($this as $key => $value) {
      // $value, 0'dan büyük veya 0'a eşit bir gerçek sayı ya da 0'dan büyük veya 0'a eşit gerçek sayılardan oluşan bir dizi olmalıdır
      $result = $this->check($value);
      // hata mı?
      if ($result->erreur) {
        // bir istisna atılır
        throw new ExceptionImpots("La valeur de l'attribut [$key] est invalide");
      } else {
        // değer kaydediliyor
        $this->$key = $result->value;
      }
    }
    // nesne iade ediliyor
    return $this;
  }

  private function check($value): \stdClass {

    return $result;
  }

    // toString
  public function __toString() {
    // nesnenin JSON dizesi
    return \json_encode(\get_object_vars($this), JSON_UNESCAPED_UNICODE);
  }

  // getter ve setter'lar
  public function getLimites() {
    return $this->limites;
  }

  public function getCoeffR() {
    return $this->coeffR;
  }


  }

  public function setLimites($limites) {
    $this->limites = $limites;
    return $this;
  }

  public function setCoeffR($coeffR) {
    $this->coeffR = $coeffR;
    return $this;
  }



}

Açıklamalar

  • 6-20. satırlar: jSON ve [taxadmindata.json] dosyalarındaki aynı isimli öznitelikleri barındıracak öznitelikler. Bu önemli bir noktadır: [TaxAdminData] sınıfının öznitelikleri, jSON ve [taxadmindata.json] dosyalarındakilerle aynıdır. Bu özellik, kod yazımını büyük ölçüde kolaylaştırır;
  • [TaxAdminData] sınıfında yapıcı yoktur. PHP'te birden fazla yapıcıya sahip olmak mümkün değildir. Dolayısıyla bir yapıcı belirlemek, nesneyi başka bir şekilde başlatmayı engeller. Bundan sonra, sınıflarımızda yapıcı olmayacak, ancak nesneyi farklı şekillerde başlatmaya olanak tanıyan [setFromQqChose] türünde birden fazla yöntem bulunacaktır. [TaxAdminData] türünde bir nesnenin oluşturulması şu ifadeyle gerçekleştirilir:
(new TaxAdminData())→setFromQqChose(…)
  • 23. satır: [setFromJsonFile] yöntemi, sınıfın özniteliklerini [$jsonFilename] dosyasındaki aynı isimli özniteliklerle başlatır;
  • 24-42. satırlar: jSON dosyası, [$arrayTaxAdminData] ilişkisel dizisini oluşturmak için kullanılır. Bu koda, 02 sürümündeki [main.php] betiğinde daha önce rastlamıştık;
  • satır 44-47: jSON dosyasının işlenmesinde bir hata ile karşılaşılırsa, bir istisna atılır. Bu istisna, ana komut dosyası [main.php]’e kadar iletilir;
  • satır 48-51: Sınıfın öznitelikleri başlatılır. Burada, [$arrayTaxAdminData] ilişkisel dizisi ile [TaxAdminData] sınıfının özniteliklerinin, jSON dosyasından gelen değerlerle aynı isimlere sahip olmasından yararlanılır;
  • 53-57. satırlar: [TaxAdminData] sınıfının tüm özniteliklerinin başlatılmış olduğu kontrol edilir;
  • 53. satır: [get_object_vars($this)] ifadesi, öznitelikleri [$this] nesnesine ait olan, dolayısıyla [TaxAdminData] sınıfının özniteliklerini içeren bir ilişkisel dizi döndürür. Burada, 48-51. satırlardaki başlatma işleminin [$this] nesnesine öznitelikler eklemiş olabileceği anlaşılmalıdır. Dolayısıyla şunu yazarsak:
    $this->x = "1000";

diye yazarsak, [x] özniteliği, [TaxAdminData] sınıfında bildirilmemiş olsa bile [$this] nesnesine eklenir. Kesin olan şey, 6-20. satırlardaki özniteliklerin [$this] nesnesinin bir parçası olduğu, ancak bu özniteliklerin başlatılmamış olabileceğidir. Bu, kolayca yapılabilecek bir hatadır; [taxadmindata.json] dosyasında bir öznitelik adında hata yapmak yeterlidir;

  • 54-57. satırlar: [$this] nesnesinin tüm öznitelikleri gözden geçirilir ve bunlardan herhangi biri başlatılmamışsa bir istisna atılır;
  • bir öznitelik yanlış bir değerle başlatılabilir. PHP'te özniteliklere tür atamak mümkün değildir. Dolayısıyla şu işlem:
$this→plafondQfDemiPart=’abcd’

işlemi mümkünken, [$plafondQfDemiPart] özniteliğinin gerçek sayı olması gerekir;

  • 59-71. satırlar: Sınıfın her bir özniteliğinin pozitif veya sıfır olan bir gerçek sayı değerine sahip olduğu kontrol edilir. Bu işi 76. satırdaki [check] işlevi yapar. Bu işlevin [$value] parametresi ya tek bir değer ya da bir değerler dizisidir;
  • 62. satır: [check] işlevi, iki özniteliğe sahip bir [\stdClass] türü nesne döndürür:
    • [erreur]: Hata oluşmuşsa TRUE, aksi takdirde FALSE;
    • [value]: 62. satırda parametre olarak geçirilen [$value] parametresine karşılık gelen gerçek sayısal değer;
  • 64. satır: Doğrulamanın başarılı olup olmadığına bakılır;
  • 66. satır: Bir öznitelik pozitif bir gerçek sayı veya sıfır değilse, bir istisna atılır;
  • satır 69: aksi takdirde sayısal değeri kaydedilir;
  • 73. satır: [$this] nesnesi sonuç olarak döndürülür;

[check] işlevi şu şekildedir:


private function check($value): \stdClass {
    // $value, ya bir dizi ya da tek bir öğedir
    // bir dizi oluşturulur
    if (!\is_array($value)) {
      $tableau = [$value];
    } else {
      $tableau = $value;
    }
    // bilinmeyen türdeki elemanlardan oluşan dizi, gerçek sayı dizisine dönüştürülür
    $newTableau = [];
    $result = new \stdClass();
    // dizinin elemanları pozitif veya sıfır olan ondalık sayılar olmalıdır
    $modèle = '/^\s*([+]?)\s*(\d+\.\d*|\.\d+|\d+)\s*$/';
    for ($i = 0; $i < count($tableau); $i ++) {
      if (preg_match($modèle, $tableau[$i])) {
        // float değerini newTableau'e yerleştirilir
        $newTableau[] = (float) $tableau[$i];
      } else {
        // hata kaydedilir
        $result->erreur = TRUE;
        // çıkılır
        return $result;
      }
    }
    // sonuç verilir
    $result->erreur = FALSE;
    if (!\is_array($value)) {
      // tek bir değer
      $result->value = $newTableau[0];
    } else {
      // bir değer listesi
      $result->value = $newTableau;
    }
    return $result;
  }

Açıklamalar

  • satır 1: [$value] parametresi ya bir dizidir ya da tek bir öğedir. Ayrıca, türü bilinmemektedir. Değer, [taxadmindata.json] dosyasından alınır. Bu dosyada belirtilen değerlere göre, okunan değerler tamsayılar, gerçek sayılar, dizeler veya boole değerleri olabilir. Örneğin:

"plafondQfDemiPart": 1551,
"plafondQfDemiPart": 1551.78,
"plafondQfDemiPart": "1551",
"plafondQfDemiPart": "xx",
  1. durumda değer [entier] türündedir, 2. durumda [réel] türündedir, 3. durumda [string] türündedir ve sayıya dönüştürülebilir, 4. durumda ise [string] türündedir ve sayıya dönüştürülemez;
  • satır 4-8: 1. satırda parametre olarak alınan [$value] parametresinden bir tablo oluşturulur;
  • 10. satır: Bu dizi, gerçek sayılarla doldurulacaktır;
  • 11. satır: Sonuç, [\stdClass] türünde bir nesne olacaktır;
  • satır 13: pozitif veya sıfır olan bir gerçek sayının ilişkisel ifadesi;
  • satır 14-24: [$tableau] tablosundaki tüm elemanların pozitif veya sıfır reel sayılar olduğunu kontrol ederiz ve bu elemanları [float] türüne dönüştürerek (satır 17) [$newTableau] tablosunu doldururuz;
  • satır 18-23: pozitif bir reel sayı veya sıfır olmayan bir eleman tespit edilir edilmez, hatayı sonuca kaydedip bu sonucu döndürür;
  • satır 25-34: [$tableau] dizisindeki tüm elemanların doğru olduğu durum;
  • satır 32: döndürülen değer [$result→value], [float] gerçek sayı dizisi veya tek bir gerçek sayıdır;

82-85. satırlardaki [__toString] işlevi, [$this] nesnesinin özniteliklerini ve değerlerini içeren jSON dizesini döndürür.

87-110. satırlar: sınıfın get ve set yöntemleri;

Not: Bir sınıfın tüm get / set yöntemlerini yazmak, özellikle çok sayıda öznitelik olduğunda bazen biraz zahmetli olabilir. NetBeans, bunları ve yapıcıyı otomatik olarak oluşturabilir. Bunu yapmak için, [1] özniteliklerini şu şekilde girmeniz yeterlidir:

Image

  • [2]'e ekleyin, kodu eklemek istediğiniz yere sağ tıklayın ve [Insert Code] seçeneğini seçin;

Image

  • [4]'e dönüştürmek için, oluşturucuyu oluşturmak istediğinizi belirtin;
  • [5]'te tüm öznitelikleri işaretleyin: bu, oluşturucunun her bir öznitelik için bir parametreye sahip olmasını istediğiniz anlamına gelir;
  • [6]'te, Java oluşturucularının stilini kullanın;
  • [7]'te, yapıcıdan önce [public] anahtar sözcüğünün açıkça yer almasını istediğinizi belirtin;
  • [8]'te, onaylayın;

Image

  • [9]'te, NetBeans oluşturucuyu oluşturdu. Ancak parametrelerin türlerini bilmediği için bunları ekleyemedi. Bunları kendiniz ekleyin: [10];

Getter ve setter'ları oluşturmak için 2-4. adımları tekrarlayın ve 4. adımda [Getter and Setter]'i seçin:

Image

  • [5] seçeneğinde, her bir öznitelik için get ve set yöntemleri istediğinizi belirtin;
  • [6]'te, Java'da kullanılan tarzda get ve set yöntemleri istediğinizi belirtin: setAttribut, getAttribut;
  • [7]'te, bu getter ve setter'ların public olmasını belirtin;
  • [8]'te, onaylayın;

Image

  • [9] dosyasında, NetBeans tarafından oluşturulan get ve set yöntemlerini;

Bu get ve set yöntemlerini silin ve 2-7. adımları tekrarlayın.

  • [8]'te, daha önce işaretlemediğimiz [Fluent Setter] seçeneğini işaretleyin;

Elde edilen sonuç şöyledir:

Image

Her setter, bir [return $this] işlemiyle sona erer. Bu, özniteliklerin şu şekilde başlatılmasını sağlar:

$data→setLimites($limites)→setCoeffR($coeffR)→setCoeffN($coeffN) ;

Nitekim, [$data→setLimites($limites)]'in değeri (kodun 32. satırı) [$this]'tir, dolayısıyla burada [$data] olur. Dolayısıyla, bu nesnenin [setCoeffR($coeffR)] yöntemini çağırabiliriz ve bu şekilde devam edebiliriz; zira bu yöntem de sırayla [$this]'i döndürür (kodun 37. satırı). Bir sınıfın yöntemlerinin bu şekilde yazılması, hiçbir değer döndürmemesi gereken yöntemlerin [$this] nesnesini döndürmesine neden olur ve buna “akıcı yazım” denir. Bu, bu yöntemlerin kullanımını kolaylaştırır.

8.4. [InterfaceImpots] arayüzü

Şimdi aşağıdaki [InterfaceImpots] arayüzünü tanımlıyoruz: [InterfaceImpots.php]:


<?php

// ad alanı
namespace Application;

interface InterfaceImpots {

  // vergi hesaplamasına olanak tanıyan vergi dilimlerinden verileri almak
  // ExceptionImpots istisnasını tetikleyebilir
  public function getTaxAdminData(): TaxAdminData;

  // arayüz, vergi hesaplayabilir
  public function calculerImpot(string $marié, int $enfants, int $salaire): array;

  // arayüz, metin dosyalarındaki verileri işleyebilir
  // $usersFilename: medeni durum, çocuk sayısı, yıllık maaş bilgilerini içeren kullanıcı verileri dosyası
  // $resultsFilename: medeni durum, çocuk sayısı, yıllık maaş ve vergi tutarı bilgilerini içeren sonuç dosyası
  // $errorsFilename: karşılaşılan hataların bulunduğu dosya
  // ExceptionImpots istisnasını tetikleyebilir
  public function executeBatchImpots(string $usersFileName, string $resultsFileName, string $errorsFileName): void;
}

Açıklamalar

  • 4. satır: arayüz, [Application] ad alanına yerleştirilmiştir;
  • 6. satır: Vergi hesaplamasına olanak tanıyan arayüz;
  • 10. satır: [getTaxAdminData] yöntemi, az önce tanıttığımız [TaxAdminData] türündeki bir nesneye vergi idaresinden gelen verileri alacaktır. Bu veriler bir dosyada, veritabanında veya hatta ağ üzerinde bulunabileceğinden, [getTaxAdminData] yöntemi verileri almada başarısız olabilir. Bu durumda, [ExceptionImpots] türünde bir istisna atacaktır. Bu, bir yöntem veya oluşturucuda karşılaşılan bir hatayı bildirmek için nesne yönelimli programlamada kullanılan standart yöntemdir;
  • 13. satır: [calculerImpot] yöntemi, bir kullanıcının vergisini hesaplamaya yarar;
  • 20. satır: [executeBatchImpots] yöntemi, birden fazla vergi mükellefinin vergisini hesaplamaya yarar;
    • [$usersFileName], vergi mükelleflerinin verilerini içeren metin dosyasının adıdır;
    • [$resultsFileName], bu vergi mükelleflerinin vergi tutarlarını içeren metin dosyasının adıdır;
    • [$errorsFileName], bu dosyaların işlenmesi sırasında karşılaşılan hataları içeren metin dosyasının adıdır;

[$usersFileName] metin dosyasının içeriği şu şekilde olabilir:


oui,2,55555
oui,2,50000
oui,3,50000
non,2,100000
non,3x,100000
oui,3,100000
oui,5,100000x
non,0,100000
oui,2,30000
non,0,200000
oui,3,200000
  1. ve 7. satırlarda hatalı veriler bulunduğu dikkat çekmektedir.

[$resultsFileName] metin dosyasının içeriği ise şu şekilde olacaktır:

1
2
3
4
5
6
7
8
9
{"marié":"oui","enfants":2,"salaire":55555,"impôt":2814,"surcôte":0,"décôte":0,"réduction":0,"taux":0.14}
{"marié":"oui","enfants":2,"salaire":50000,"impôt":1384,"surcôte":0,"décôte":384,"réduction":347,"taux":0.14}
{"marié":"oui","enfants":3,"salaire":50000,"impôt":0,"surcôte":0,"décôte":720,"réduction":0,"taux":0.14}
{"marié":"non","enfants":2,"salaire":100000,"impôt":19884,"surcôte":4480,"décôte":0,"réduction":0,"taux":0.41}
{"marié":"oui","enfants":3,"salaire":100000,"impôt":9200,"surcôte":2180,"décôte":0,"réduction":0,"taux":0.3}
{"marié":"non","enfants":0,"salaire":100000,"impôt":22986,"surcôte":0,"décôte":0,"réduction":0,"taux":0.41}
{"marié":"oui","enfants":2,"salaire":30000,"impôt":0,"surcôte":0,"décôte":0,"réduction":0,"taux":0}
{"marié":"non","enfants":0,"salaire":200000,"impôt":64210,"surcôte":7498,"décôte":0,"réduction":0,"taux":0.45}
{"marié":"oui","enfants":3,"salaire":200000,"impôt":42842,"surcôte":17283,"décôte":0,"réduction":0,"taux":0.41}

ve [$errorsFileName] metin dosyasının içeriği ise şu şekilde olacaktır:

la ligne 5 du fichier taxpayersdata.txt est erronée
la ligne 7 du fichier taxpayersdata.txt est erronée

8.5. [Utilitaires] sınıfı

Ayrıca, [Utilitaires.php] dosyasında [Utilitaires] sınıfını tanımlıyoruz:


<?php

// ad alanı
namespace Application;

// bir yardımcı işlev sınıfı
abstract class Utilitaires {

  public static function cutNewLinechar(string $ligne): string {
    // varsa, $ligne'in satır sonu işaretini siler
    $longueur = strlen($ligne);  // satır uzunluğu
    while (substr($ligne, $longueur - 1, 1) == "\n" or substr($ligne, $longueur - 1, 1) == "\r") {
      $ligne = substr($ligne, 0, $longueur - 1);
      $longueur--;
    }
    // son - satırı döndürür
    return($ligne);
  }
}

Açıklamalar

  • 4. satır: [Utilitaires] sınıfı da [Exemples] ad alanında yer almaktadır;
  • 9. satır: [cutNewLinechar] yöntemi, kendisine parametre olarak geçirilen metinden varsa satır sonu karakterini kaldırır. Böylece yeni bir satır oluşturur. Bunun statik bir yöntem olduğu, yani [Utilitaires::cutNewLineChar] biçiminde çağrılacağı unutulmamalıdır;

8.6. [AbstractBaseImpots] soyut sınıfı

[InterfaceImpots] arayüzü, aşağıdaki [AbstractBaseImpots] soyut sınıfı tarafından uygulanacaktır: [AbstractBaseImpots.php]:


<?php

// ad alanı
namespace Application;

// soyut sınıf tanımı AbstractBaseImpots
abstract class AbstractBaseImpots implements InterfaceImpots {
  // vergi idaresi verileri
  private $taxAdminData = NULL;

  // vergi hesaplaması için gerekli veriler
  abstract function getTaxAdminData(): TaxAdminData;

// vergi hesaplaması
// --------------------------------------------------------------------------
  public function calculerImpot(string $marié, int $enfants, int $salaire): array {
    // $marié: evet, hayır
    // $enfants: çocuk sayısı
    // $salaire: yıllık maaş
    // $this->taxAdminData: vergi dairesi verileri
    //
    // Vergi dairesi verilerinin mevcut olup olmadığı kontrol edilir
    if ($this->taxAdminData === NULL) {
      $this->taxAdminData = $this->getTaxAdminData();
    }
    // çocuklu vergi hesaplaması
    $result1 = $this->calculerImpot2($marié, $enfants, $salaire);
    $impot1 = $result1["impôt"];
    // çocuklar hariç vergi hesaplaması
    if ($enfants != 0) {
      $result2 = $this->calculerImpot2($marié, 0, $salaire);
      $impot2 = $result2["impôt"];
      // aile katsayısı üst sınırının uygulanması
      $plafonDemiPart = $this->taxAdminData->getPlafondQfDemiPart();
      if ($enfants < 3) {
        // $PLAFOND_QF_DEMI_PART euro (ilk 2 çocuk için)
        $impot2 = $impot2 - $enfants * $plafonDemiPart;
      } else {
        // $PLAFOND_QF_DEMI_PART avro ilk 2 çocuk için, sonraki çocuklar için iki katı
        $impot2 = $impot2 - 2 * $plafonDemiPart - ($enfants - 2) * 2 * $plafonDemiPart;
      }
    } else {
      $impot2 = $impot1;
      $result2 = $result1;
    }
    // en yüksek vergi tutarı alınır
    if ($impot1 > $impot2) {
      $impot = $impot1;
      $taux = $result1["taux"];
      $surcôte = $result1["surcôte"];
    } else {
      $surcôte = $impot2 - $impot1 + $result2["surcôte"];
      $impot = $impot2;
      $taux = $result2["taux"];
    }
    // olası bir indirimin hesaplanması
    $décôte = $this->getDecôte($marié, $salaire, $impot);
    $impot -= $décôte;
    // olası vergi indiriminin hesaplanması
    $réduction = $this->getRéduction($marié, $salaire, $enfants, $impot);
    $impot -= $réduction;
    // sonuç
    return ["impôt" => floor($impot), "surcôte" => $surcôte, "décôte" => $décôte, "réduction" => $réduction, "taux" => $taux];
  }

// --------------------------------------------------------------------------
  private function calculerImpot2(string $marié, int $enfants, float $salaire): array {

    // sonuç
    return ["impôt" => $impôt, "surcôte" => $surcôte, "taux" => $coeffR[$i]];
  }

  // revenuImposable=yıllıkMaaş-muafiyet
  // muafiyetin bir alt ve üst sınırı vardır
  private function getRevenuImposable(float $salaire): float {

    // sonuç
    return floor($revenuImposable);
  }

// olası bir indirimi hesaplar
  private function getDecôte(string $marié, float $salaire, float $impots): float {

    // sonuç
    return ceil($décôte);
  }

// olası bir indirimi hesaplar
  private function getRéduction(string $marié, float $salaire, int $enfants, float $impots): float {

    // sonuç
    return ceil($réduction);
  }

  public function executeBatchImpots(string $usersFileName, string $resultsFileName, string $errorsFileName): void {

  }

}

Açıklamalar

  • 4. satır: [AbstractBaseImpots] sınıfı, yazılmakta olan uygulamanın diğer öğeleri gibi [Application] ad alanında yer alacaktır;
  • 7. satır: [AbstractBaseImpots] sınıfı, [InterfaceImpots] arayüzünü uygular;
  • 9. satır: Vergi idaresine ait veriler [$taxAdminData] özniteliğine yerleştirilecektir;
  • 12. satır: Arayüzün [getTaxAdminData] yönteminin uygulanması. Bu yöntemi henüz tanımlayamıyoruz: Bir önceki paragrafta, vergi idaresi verilerinin jSON dosyasından alındığı bir örnek görmüştük. Verilerin bir veritabanından alınacağı başka bir durum da göreceğiz. [getTaxAdminData] yönteminin içeriğini tanımlamak, türetilmiş sınıflara düşecektir. Yukarıdaki iki durum, iki türetilmiş sınıfın ortaya çıkmasına neden olacaktır. Dolayısıyla [getTaxAdminData] yöntemi soyut olarak tanımlanmıştır; bu da sınıfın kendisini otomatik olarak soyut hale getirir (7. satır);
  • 15-64. satırlar: daha önce bağlantı ve bağlantı paragraflarında ele alınan vergi hesaplama fonksiyonu;
  • 02 sürümünde vergi idaresine ait veriler [$taxAdminData] adlı bir ilişkisel tabloya yerleştirilmişti. 03 sürümünde ise bu veriler [$this→taxAdminData] özniteliğine yerleştirilmiştir. Bu iki çözüm arasındaki ilk fark, vergi verilerinin görünürlük farkıdır:
    • 02 sürümünde, [$taxAdminData] ilişkisel tablosu genel görünürlükte değildi. Dolayısıyla, tüm vergi hesaplama fonksiyonlarına parametre olarak aktarılıyordu;
    • 03 sürümünde ise [$this→taxAdminData] özniteliği, sınıfın tüm yöntemleri için genel görünürlük özelliğine sahiptir. Dolayısıyla, vergi hesaplama fonksiyonlarının hiçbirine parametre olarak aktarılmaz;
  • ikinci bir fark, sürüm 03’te fonksiyonların sınıf yöntemleriyle değiştirilmiş olmasından kaynaklanmaktadır. Artık her yöntem çağrısı bir [$this→getMéthode(…)] ifadesi ile yapılmaktadır (satır 27, 31, 57, 60);
  • üçüncü bir fark ise, [calculerImpot] yöntemi çalışmaya başladığında, ihtiyaç duyduğu [private $taxAdminData] özniteliğinin başlatılıp başlatılmadığını bilememesidir. Nitekim, sınıfın oluşturucusu bu özniteliği başlatmamaktadır. Bu nedenle, bunu yapmak [calculerImpot] yönteminin görevidir ve bunu 12. satırdaki [getTaxAdminData] yöntemini kullanarak gerçekleştirir. Bu işlem 23-25. satırlarda gerçekleştirilir;
  • bu farklılıklar dışında, vergi hesaplama yöntemleri önceki sürümlerdekiyle aynı kalmıştır;

[executeBatchImpots] fonksiyonu şu şekildedir:


public function executeBatchImpots(string $usersFileName, string $resultsFileName, string $errorsFileName): void {
    // dosyalarla çalışırken pek çok hata meydana gelebilir
    try {
      // dosya açma hataları
      $errors = fopen($errorsFileName, "w");
      if (!$errors) {
        throw new ExceptionImpots("Impossible de créer le fichier des erreurs [$errorsFileName]", 10);
      }
      // sonuç dosyasının açılması
      $results = fopen($resultsFileName, "w");
      if (!$results) {
        throw new ExceptionImpots("Impossible de créer le fichier des résultats [$resultsFileName]", 11);
      }
      // kullanıcı verilerinin okunması
      // her satır, medeni durum, çocuk sayısı, yıllık maaş şeklinde düzenlenmiştir
      $data = fopen($usersFileName, "r");
      if (!$data) {
        throw new ExceptionImpots("Impossible d'ouvrir en lecture les déclarations des contribuables [$usersFileName]", 12);
      }
      // kullanıcı verileri dosyasındaki geçerli satır işlenir
      // bu satır, medeni durum, çocuk sayısı, yıllık maaş biçimindedir
      $num = 1;         // geçerli satır numarası
      $nbErreurs = 0;   // karşılaşılan hata sayısı
      while ($ligne = fgets($data, 100)) {
        // hata ayıklama
        //  print "satır no. " . ($i + 1) . " : " . $ligne;
        // olası satır sonu işaretini kaldırıyoruz
        $ligne = Utilitaires::cutNewLineChar($ligne);
        // $ligne'i oluşturan "evli:çocuk sayısı:maaş" şeklindeki 3 alanı alınır
        list($marié, $enfants, $salaire) = explode(",", $ligne);
        // bu alanlar kontrol edilir
        // medeni durum "evli" veya "evli değil" olmalıdır
        $marié = trim(strtolower($marié));
        $erreur = ($marié !== "oui" and $marié !== "non");
        if (!$erreur) {
          // Çocuk sayısı bir tamsayı olmalıdır
          $enfants = trim($enfants);
          if (!preg_match("/^\s*\d+\s*$/", $enfants)) {
            $erreur = TRUE;
          } else {
            $enfants = (int) $enfants;
          }
        }
        if (!$erreur) {
          // maaş, sent hariç tam sayı olmalıdır
          $salaire = trim($salaire);
          if (!preg_match("/^\s*\d+\s*$/", $salaire)) {
            $erreur = TRUE;
          } else {
            $salaire = (int) $salaire;
          }
        }
        // hata mı?
        if ($erreur) {
          fputs($errors, "la ligne [$num] du fichier [$usersFileName] est erronée\n");
          $nbErreurs++;
        } else {
          // vergi hesaplanıyor
          $result = $this->calculerImpot($marié, (int) $enfants, (int) $salaire);
          // sonuç, sonuç dosyasına kaydedilir
          $result = ["marié" => $marié, "enfants" => $enfants, "salaire" => $salaire] + $result;
          fputs($results, \json_encode($result, JSON_UNESCAPED_UNICODE) . "\n");
        }
        // sonraki satır
        $num++;
      }
      // hatalar mı var?
      if ($nbErreurs > 0) {
        throw new ExceptionImpots("Il y a eu des erreurs", 15);
      }
    } catch (ExceptionImpots $ex) {
      // istisna yeniden tetikleniyor
      throw $ex;
    } finally {
      // tüm dosyaları kapatıyoruz
      fclose($data);
      fclose($results);
      fclose($errors);
    }
  }

Kod açıklamaları

  • 1. satır: İşlev üç parametre alır:
    • [$usersFileName]: Vergi mükelleflerinin verilerini içeren metin dosyasının adı. Metnin her satırı, bir vergi mükellefinin verilerini şu biçimde içerir: medeni durum (evet / hayır), çocuk sayısı, yıllık maaş:
oui,2,55555
oui,2,50000
  • (devamı)
    • [$resultsFileName]: Sonuçları içerecek metin dosyasının adı. Her metin satırı aşağıdaki biçimde olacaktır:
{"marié":"oui","enfants":2,"salaire":50000,"impôt":1384,"surcôte":0,"décôte":384,"réduction":347,"taux":0.14}
{"marié":"oui","enfants":3,"salaire":50000,"impôt":0,"surcôte":0,"décôte":720,"réduction":0,"taux":0.14}
  • (devamı)
    • [$errorsFileName]: hataların yer alacağı metin dosyasının adı:

la ligne [5] du fichier [taxpayersdata.txt] est erronée
la ligne [7] du fichier [taxpayersdata.txt] est erronée
  • 3. satır: Bazı işlemler istisna oluşturabileceğinden, yöntemin tüm kodu bir try / catch / finally bloğu ile çevrelenmiştir;
  • 3-19. satırlar: Üç dosya da açılır. Dosya açma işlemi başarısız olur olmaz bir istisna atılır;
  • 24. satır: [$data] dosyasının satırları, en fazla 100 karakterlik parçalar halinde tek tek okunur (tüm satırlar 100 karakterden kısadır);
  • 28. satır: Olası satır sonu işaretlerini kaldırmak için [Utilitaires::cutNewLineChar] statik yöntemi kullanılır;
  • 30. satır: Okunan satırdaki üç öğe alınır;
  • satır 33-52: Üç öğenin geçerliliği kontrol edilir. Burada, hata olması durumunda bir istisna atılmaz, ancak hata mesajı [$errors] metin dosyasına yazılır (satır 55);
  • satır 59: okunan satır geçerliyse, vergi hesaplaması yapılır. Sonuç, ["impôt" => floor($impot), "surcôte" => $surcôte, "décôte" => $décôte, "réduction" => $réduction, "taux" => $taux] adlı bir ilişkisel tablo biçiminde elde edilir;
  • 61. satır: Elde edilen sonuca [marié, enfants, salaire] anahtarları eklenir;
  • satır 61: sonuç, elde edilen sonucun jSON dizesi biçiminde [$results] metin dosyasına yazılır;
  • satır 68-70: [$data] dosyasının işlenmesi tamamlandığında, karşılaşılan hatalı satır sayısı kontrol edilir. En az bir tane varsa, bir istisna atılır;
  • 71-74. satırlar: Kodun atmış olabileceği istisna yakalanır ve hemen yeniden atılır (73. satır). Bu yöntemin amacı, 74-79. satırlarda bir [finally] cümlesine yer verebilmektir: Yöntemin kodunun yürütülmesi nasıl sona ererse ersin, bu kod tarafından açılmış olabilecek üç dosya da kapatılır. Açılmamış bir dosyayı kapatmak hata oluşturmaz;

8.7. [ImpotsWithTaxAdminDataInJsonFile] sınıfı

Soyut [AbstractBaseImpots] sınıfı, [InterfaceImpots] arayüzündeki [getTaxAdminData] yöntemini uygulamamaktadır. Bu nedenle, bu yöntemi türetilmiş bir sınıfta tanımlamamız gerekir. Bunu, aşağıdaki türetilmiş sınıf [ImpotsWithTaxAdminDataInJsonFile]'te yapıyoruz:


<?php

// ad alanı
namespace Application;

// bir sınıfın tanımı ImpotsWithDataInArrays
class ImpotsWithTaxAdminDataInJsonFile extends AbstractBaseImpots {
  // Data türünde bir öznitelik
  private $taxAdminData;

  // oluşturucu
  public function __construct(string $jsonFileName) {
    // $this->taxAdminData, jSON dosyasından başlatılır
    $this->taxAdminData = (new TaxAdminData())->setFromJsonFile($jsonFileName);
  }

  // vergi hesaplamasına olanak tanıyan verileri döndürür
  public function getTaxAdminData(): TaxAdminData {
    // [$this->taxAdminData] özniteliği döndürülür
    return $this->taxAdminData;
  }

}

Açıklamalar

  • 7. satır: [ImpotsWithTaxAdminDataInJsonFile] sınıfı, [AbstractBaseImpots] soyut sınıfını genişletir. Bu sınıf, üst sınıfında tanımlanmamış olan [getTaxAdminData] yöntemini tanımlamalıdır;
  • 9. satır: [$taxAdminData] özniteliği, vergi idaresine ait verileri içerecektir;
  • 12-15. satırlar: Oluşturucu, vergi verilerini içeren jSON dosyasının adını tek parametre olarak alır;
  • satır 14: [TaxAdminData] türünde bir nesne oluşturulur ve ardından başlatılır. Bu işlem, [ExceptionImpots] türünde bir istisna tetikleyebilir. Bu istisna, ana komut dosyası [main.php]'e kadar iletilir;
  • 18-20. satırlar: üst sınıf tarafından tanımlanmamış olan [getTaxAdminData] yöntemine bir gövde atanır. Burada, [$this->taxAdminData] özniteliğinin oluşturucu tarafından başlatılmış olmasını sağlamak yeterlidir;

8.8. [main.php] komut dosyası

Bu sınıflar ve arayüz, aşağıdaki [main.php] komut dosyası tarafından kullanılır:


<?php

// fonksiyon parametrelerinin beyan edilen türlerine sıkı sıkıya uyulur
declare(strict_types = 1);

// ad alanı
namespace Application;

// arayüz ve sınıfların dahil edilmesi
require_once __DIR__ . '/InterfaceImpots.php';
require_once __DIR__ . "/TaxAdminData.php";
require_once __DIR__ . '/ExceptionImpots.php';
require_once __DIR__ . '/Utilitaires.php';
require_once __DIR__ . '/AbstractBaseImpots.php';
require_once __DIR__ . "/ImpotsWithTaxAdminDataInJsonFile.php";

// test -----------------------------------------------------
// sabitlerin tanımlanması
const TAXPAYERSDATA_FILENAME = "taxpayersdata.txt";
const RESULTS_FILENAME = "resultats.txt";
const ERRORS_FILENAME = "errors.txt";
const TAXADMINDATA_FILENAME = "taxadmindata.json";

try {
  // bir nesne oluşturuluyor ImpotsWithTaxAdminDataInJsonFile
  $impots = new ImpotsWithTaxAdminDataInJsonFile(TAXADMINDATA_FILENAME);
  // vergi toplu iş dosyası çalıştırılıyor
  $impots->executeBatchImpots(TAXPAYERSDATA_FILENAME, RESULTS_FILENAME, ERRORS_FILENAME);
} catch (ExceptionImpots $ex) {
  // hatayı görüntüleme
  print $ex->getMessage() . "\n";
}
// son
print "Terminé\n";
exit();


Açıklamalar

  • 4. satır: İşlev parametrelerinin türlerine sıkı bir şekilde uyulması zorunlu kılınır;
  • 7. satır: [main.php] komut dosyası da [Application] ad alanına yerleştirilir;
  • 10-15. satırlar: PHP yorumlayıcısına, betik tarafından kullanılan sınıfların ve arayüzlerin nerede bulunduğu belirtilir. Burada, komut dosyası tarafından kullanılan sınıfların tam adlarını bildirmek için use komutu kullanılmadığına dikkat edilmelidir. Komut dosyası ve sınıflar aynı [Application] ad alanında bulunduğundan, bu aslında gereksizdir;
  • 18-22. satırlar: komut dosyasında kullanılan metin dosyalarının adları;
  • 24-29. satırlar: Bir [ImpotsWithTaxAdminDataInJsonFile] nesnesi oluşturulur ve olası istisnalar yönetilir;
  • 28. satır: [executeBatchImpots] yöntemi çalıştırılır; bu yöntem, [TAXPAYERSDATA_FILENAME] dosyasındaki tüm vergi mükellefleri için vergi hesaplamasını yapar. Sonuçlar [RESULTS_FILENAME] dosyasına, olası hatalar ise [ERRORS_FILENAME] dosyasına kaydedilir;
  • 29-32. satırlar: Düzeltilemez bir hata durumunda, hata mesajı görüntülenir;

Sonuçlar

Aşağıdaki [taxpayersdata.txt] vergi mükellefi dosyası ile:


oui,2,55555
oui,2,50000
oui,3,50000
non,2,100000
non,3x,100000
oui,3,100000
oui,5,100000x
non,0,100000
oui,2,30000
non,0,200000
oui,3,200000

aşağıdaki [errors.txt] hata dosyası elde edilir:


la ligne [5] du fichier [taxpayersdata.txt] est erronée
la ligne [7] du fichier [taxpayersdata.txt] est erronée

ve aşağıdaki [resultats.txt] sonuç dosyası elde edilir:

1
2
3
4
5
6
7
8
9
{"marié":"oui","enfants":2,"salaire":55555,"impôt":2814,"surcôte":0,"décôte":0,"réduction":0,"taux":0.14}
{"marié":"oui","enfants":2,"salaire":50000,"impôt":1384,"surcôte":0,"décôte":384,"réduction":347,"taux":0.14}
{"marié":"oui","enfants":3,"salaire":50000,"impôt":0,"surcôte":0,"décôte":720,"réduction":0,"taux":0.14}
{"marié":"non","enfants":2,"salaire":100000,"impôt":19884,"surcôte":4480,"décôte":0,"réduction":0,"taux":0.41}
{"marié":"oui","enfants":3,"salaire":100000,"impôt":9200,"surcôte":2180,"décôte":0,"réduction":0,"taux":0.3}
{"marié":"non","enfants":0,"salaire":100000,"impôt":22986,"surcôte":0,"décôte":0,"réduction":0,"taux":0.41}
{"marié":"oui","enfants":2,"salaire":30000,"impôt":0,"surcôte":0,"décôte":0,"réduction":0,"taux":0}
{"marié":"non","enfants":0,"salaire":200000,"impôt":64210,"surcôte":7498,"décôte":0,"réduction":0,"taux":0.45}
{"marié":"oui","enfants":3,"salaire":200000,"impôt":42842,"surcôte":17283,"décôte":0,"réduction":0,"taux":0.41}