Skip to content

20. Uygulama alıştırması – sürüm 10

Önceki sürümde, uygulamanın tüm kullanıcıları tarafından paylaşılan vergi verilerinin [Application] kapsamındaki bir bellekte depolanması gerektiği gösterilmişti. Bunu uygulamak için bir Redis sunucusu [https://redis.io] kullanacağız.

20.1. Redis

[Application] kapsamlı bellek, bir Redis sunucusu tarafından uygulanacaktır. Bu uygulama belleğine ihtiyaç duyan PHP komut dosyaları, bu sunucunun istemcileri olacaktır:

Image

20.2. Redis Kurulumu

Laragon, varsayılan olarak etkinleştirilmemiş bir Redis sunucusuyla birlikte gelir. Bu nedenle, öncelikle bunu etkinleştirmek gerekir:

Image

  • [3]'te, [Redis] sunucusunu etkinleştirin;
  • [4]'te, Redis istemcilerinin varsayılan olarak kullandığı [6379] bağlantı noktasını olduğu gibi bırakın;

Redis etkinleştirildikten sonra Laragon hizmetleri otomatik olarak yeniden başlatılır:

Image

20.3. Komut modunda Redis istemcisi

Redis sunucusu komut modunda sorgulanabilir. Bir Laragon terminali açın (bkz. bağlantı paragrafı):

Image

  • [1]'te, [redis-cli] komutu, Redis sunucusunun komut modunda istemciyi başlatır;

Temmuz 2019 itibarıyla, Redis istemcisi sunucuyla iletişim kurmak için 172 komut kullanabilir [https://redis.io/commands#list]. Bunlardan biri olan [command count] [2], bu sayıyı [3] görüntüler.

Uygulamamızda ihtiyaç duyacağımız komutları PHP sunacağız. Redis'i tek bir amaç için kullanacağız: bir diziyi [‘attribut’=>’valeur’] Redis belleğinde depolamak. Bu işlem, [set attribut valeur] [4] Redis komutuyla gerçekleştirilir. Değer daha sonra [get attribut] [5] komutuyla geri alınabilir. İhtiyacımız olan tek şey budur.

Redis belleğini boşaltmak gerekebilir. Bu, [flushdb] [6] komutuyla yapılır. Ardından, [titre] [7] özniteliğinin değerini sorguladığımızda, özniteliğin bulunamadığını belirten [nil] [8] referansı elde edilir. Bir özniteliğin varlığını kontrol etmek için [exists] [9-10] komutunu da kullanabiliriz.

Redis istemcisinden çıkmak için [quit] [11] komutunu girin.

20.4. PHP için bir Redis istemcisi kurulumu

Şimdi PHP için bir Redis istemcisi kurmamız gerekiyor:

Image

Redis istemcisini uygulayan çeşitli kütüphaneler mevcuttur. Biz [Predis] [https://github.com/nrk/predis] (Temmuz 2019) kütüphanesini kullanacağız. Bu kütüphane, öncekiler gibi [composer] ile birlikte bir Laragon terminalinde kurulur:

Image

20.5. Sunucu kodu

Image

[config-server.json] yapılandırma dosyası şu şekilde değişir:


{
    "rootDirectory": "C:/myprograms/laragon-lite/www/php7/scripts-web/impots/version-10",
    "databaseFilename": "Data/database.json",
    "relativeDependencies": [
        "/../version-08/Entities/BaseEntity.php",
        "/../version-08/Entities/ExceptionImpots.php",
        "/../version-08/Entities/TaxAdminData.php",
        "/../version-08/Entities/Database.php",
        "/../version-08/Dao/InterfaceServerDao.php",
        "/../version-08/Dao/ServerDao.php",
        "/../version-09/Dao/ServerDaoWithSession.php",
        "/../version-08/Métier/InterfaceServerMetier.php",
        "/../version-08/Métier/ServerMetier.php",
        "/../version-09/Utilities/Logger.php",
        "/../version-09/Utilities/SendAdminMail.php"
    ],
    "absoluteDependencies": [
        "C:/myprograms/laragon-lite/www/vendor/autoload.php",
        "C:/myprograms/laragon-lite/www/vendor/predis/predis/autoload.php"
    ],
    "users": [
        {
            "login": "admin",
            "passwd": "admin"
        }
    ],
    "adminMail": {
        "smtp-server": "localhost",
        "smtp-port": "25",
        "from": "guest@localhost",
        "to": "guest@localhost",
        "subject": "plantage du serveur de calcul d'impôts",
        "tls": "FALSE",
        "attachments": []
    },
    "logsFilename": "Data/logs.txt"
}

Yorumlar

  • 5-15. satırlar: 10. sürüm, [impots-server.php] betiği dışında yeni bir şey getirmiyor. 08 ve 09 sürümlerinden öğeler kullanıyor;
  • 19. satır: Az önce kurduğumuz [predis] kütüphanesine gerekli bir bağımlılık;

[impots-server.php] sunucu kodunda aşağıdaki değişiklikler yapılmıştır:


<?php

// fonksiyon parametrelerinin beyan edilen türlerine sıkı bir şekilde uyulması
declare (strict_types=1);

// ad alanı
namespace Application;

// PHP ile hata yönetimi
ini_set("display_errors", "0");
//
// yapılandırma dosyasının yolu
define("CONFIG_FILENAME", "Data/config-server.json");
// sınıf takma adı
use \Application\ServerDaoWithSession as ServerDaoWithRedis;

// oturum
$session = new Session();
$session->start();


// 1. günlük
$logger->write("\n---nouvelle requête\n");

// geçerli isteği alıyoruz
$request = Request::createFromGlobals();
// sadece ilk seferde kimlik doğrulama
if (!$session->has("user")) {

} else {
  // günlük
  $logger->write("Authentification prise en session…\n");
}

// geçerli bir kullanıcı var - alınan parametreler kontrol ediliyor
$erreurs = [];
// üç parametre olması gerekir GET
$method = strtolower($request->getMethod());


// hatalar mı var?
if ($erreurs) {
// müşteriye 400 hata kodu HTTP_BAD_REQUEST gönderiliyor
  sendResponse($response, ["erreurs" => $erreurs], Response::HTTP_BAD_REQUEST, [], $logger);
  // işlem tamamlandı
  exit;
} else {
  // günlükler
  $logger->write("paramètres ['marié'=>$marié, 'enfants'=>$enfants, 'salaire'=>$salaire] valides\n");
}

// Çalışmak için gereken her şeye sahibiz
// Redis
\Predis\Autoloader::register();
try {
  // [predis] istemcisi
  $redis = new \Predis\Client();
  // sunucuya bağlanıp orada olup olmadığını kontrol ediyoruz
  $redis->connect();
} catch (\Predis\Connection\ConnectionException $ex) {
  // iç sunucu hatası
  doInternalServerError("[redis], " . utf8_encode($ex->getMessage()), $response, $config['adminMail'], $logger);
  // tamamlandı
  exit;
}

// [dao] katmanının oluşturulması
if (!$redis->get("taxAdminData")) {
  // vergi verileri veritabanından alınır
  $logger->write("données fiscales prises en base de données\n");
  try {
    // [dao] katmanının oluşturulması
    $dao = new ServerDaoWithRedis($config["databaseFilename"], NULL);
    // vergi verileri [application] kapsam belleğine aktarılıyor
    // [TaxAdminData]->__toString yöntemi örtük olarak çağrılacaktır
    $redis->set("taxAdminData", $dao->getTaxAdminData());
  } catch (\RuntimeException $ex) {
    // hata kaydedildi
    doInternalServerError("[dao], " . utf8_encode($ex->getMessage()), $response, $config['adminMail'], $logger, $redis);
    // işlem tamamlandı
    exit;
  }
} else {
  // vergi verileri [application] kapsam belleğinden alınır
  $arrayOfAttributes = \json_decode($redis->get("taxAdminData"), true);
  $taxAdminData = (new TaxAdminData())->setFromArrayOfAttributes($arrayOfAttributes);
  // [dao] katmanının örneklenmesi
  $dao = new ServerDaoWithRedis(NULL, $taxAdminData);
  // günlükler
  $logger->write("données fiscales prises dans redis\n");
}
// [métier] katmanının oluşturulması
$métier = new ServerMetier($dao);
// vergi hesaplaması
$result = $métier->calculerImpot($marié, (int) $enfants, (int) $salaire);
// cevap veriliyor
sendResponse($response, $result, Response::HTTP_OK, [], $logger, $redis);
// son
exit;

function doInternalServerError(string $message, Response $response, array $infos,
  Logger $logger = NULL, \Predis\Client $predisClient = NULL) {
  // $message: hata mesajı
  // $response : yanıt HTTP
  // $infos: e-posta gönderimi için bilgi tablosu
  // $result: sonuç tablosu
  // $logger: uygulamanın günlük kaydı
  // $predisClient: bir istemci [predis]
  //
  // yöneticiye bir e-posta gönderilir
  // SendAdminMail tüm istisnaları yakalar ve kendisi günlüğe kaydeder
  $infos['message'] = $message;
  $sendAdminMail = new SendAdminMail($infos, $logger);
  $sendAdminMail->send();
  // istemciye 500 hata kodu gönderilir
  sendResponse($response, ["erreur" => $message], Response::HTTP_INTERNAL_SERVER_ERROR, [], $logger, $predisClient);
}

// HTTP yanıtını müşteriye gönderme işlevi
function sendResponse(Response $response, array $result, int $statusCode,
  array $headers, Logger $logger = NULL, \Predis\Client $predisClient = NULL) {
  // $response: HTTP yanıtı
  // $result: sonuç tablosu
  // $statusCode : yanıtın durumu HTTP
  // $headers : yanıtta yer alması gereken başlıklar
  // $logger: uygulamanın günlüğü
  // $predisClient: bir istemci [predis]
  //
  // HTTTP durumu
  $response->setStatusCode($statusCode);
  // gövde
  $body = \json_encode(["réponse" => $result], JSON_UNESCAPED_UNICODE);
  $response->setContent($body);
  // başlıklar
  $response->headers->add($headers);
  // gönderim
  $response->send();
  // günlük
  if ($logger != NULL) {
    $logger->write("$body\n");
    $logger->close();
  }
  // bağlantının kapatılması [redis]
  if ($predisClient != NULL) {
    $predisClient->disconnect();
  }
}

Açıklamalar

  • 15. satır: Sunucu betiğinin uygulama değişikliğini yansıtmak için [\Application\ServerDaoWithSession] sınıfına [ServerDaoWithRedis] takma adı verilir;
  • 18-19. satırlar: oturum korunur. Burada hatırlamamız gereken iki bilgi vardır:
    • kullanıcının başarılı bir şekilde kimlik doğrulaması yapmış olması. Bu bilgi [session] kapsamındadır: belirli bir kullanıcıyla ilişkilidir ve diğer kullanıcılar için geçerli değildir;
    • vergi idaresi verileri. Bu bilgi [application] kapsamındadır: belirli bir kullanıcıyla ilişkili değildir, ancak tüm kullanıcılar için geçerlidir;
  • 54-64. satırlar: [redis] sunucusuyla iletişim kuracak olan [redis] istemcisinin oluşturulması. Bu istemci, sunucunun varsayılan bağlantı noktası üzerinden iletişim kuracaktır. Sunucu varsayılan bağlantı noktası üzerinden iletişim kurmuyorsa veya [localhost] makinesinde bulunmuyorsa, bu bilgilerin [\Predis\Client] sınıf oluşturucusuna aktarılması gerekir;
  • 59. satır: Sunucunun yanıt verip vermediğini öğrenmek için istemciyi hemen sunucuya bağlarız;
  • 60-65. satırlar: Redis sunucusuna bağlantı kurulamazsa, istemciye bir hata yanıtı gönderilir ve uygulama yöneticisine bir e-posta gönderilir;
  • 67. satır: [redis] sunucusundan [taxAdminData] anahtarı istenir. Anahtar bulunamazsa, vergi verileri veritabanından alınır (72. satır);
  • 75. satır: [taxAdminData] anahtarı, [TaxAdminData] türünde bir nesne olan [$taxAdminData] değişkeninin jSON dizesiyle ilişkili [redis] belleğine yerleştirilirQX türünde bir nesne olan [$taxAdminData] değişkeninin jSON dizesiyle ilişkili [taxAdminData] belleğine yerleştirilmiştir. [$redis→set] yöntemi, anahtar değeri olarak bir karakter dizisi bekler. Dolayısıyla, [TaxAdminData] türündeki nesneyi [string] türüne dönüştürmeye çalışacaktır. Bu durumda, dolaylı olarak [TaxAdminData->__toString] yöntemi çağrılacaktır. Bu yöntem, [TaxAdminData] nesnesinden jSON karakter dizisini üretir;
  • 84. satır: [taxAdminData] anahtarı [redis] belleğinde bulunur, bu nedenle değeri alınır. Bunun, [TaxAdminData] nesnesinin jSON dizesi olduğu bilinmektedir. Bunu deşifre ederek bir öznitelik dizisi elde ediyoruz;
  • 85. satır: Bu diziden yola çıkarak yeni bir [TaxAdminData] nesnesi oluşturulur;
  • 87. satır: [dao] katmanı oluşturulur;

20.6. Müşteri kodu

Image

Müşterinin 10. sürümü, 9. sürümle aynıdır. Yalnızca [config-client.json] yapılandırma dosyasında değişiklik vardır:


{
    "rootDirectory": "C:/Data/st-2019/dev/php7/poly/scripts-console/impots/version-10",
    "taxPayersDataFileName": "Data/taxpayersdata.json",
    "resultsFileName": "Data/results.json",
    "errorsFileName": "Data/errors.json",
    "dependencies": [
        "/../version-08/Entities/BaseEntity.php",
        "/../version-08/Entities/TaxPayerData.php",
        "/../version-08/Entities/ExceptionImpots.php",
        "/../version-08/Utilities/Utilitaires.php",
        "/../version-08/Dao/InterfaceClientDao.php",
        "/../version-08/Dao/TraitDao.php",
        "/../version-09/Dao/ClientDao.php",
        "/../version-08/Métier/InterfaceClientMetier.php",
        "/../version-08/Métier/ClientMetier.php"
    ],
    "absoluteDependencies": [
        "C:/myprograms/laragon-lite/www/vendor/autoload.php"
    ],
    "user": {
        "login": "admin",
        "passwd": "admin"
    },
    "urlServer": "https://localhost:443/php7/scripts-web/impots/version-10/impots-server.php"
}

Değişen tek şey, 24. satırda sunucunun URL değeridir.

Sonuçlar, 09 sürümündekilerle aynıdır. Şimdi yeni bir hata durumunu test edelim:

Image

Konsoldaki sonuç şöyledir:


L'erreur suivante s'est produite : {"statut HTTP":500,"erreur":"[redis], Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée. [tcp:\/\/127.0.0.1:6379]"}
Terminé

20.7. [Codeception] istemci testleri

Image

Sürüm 10'daki [ClientMetierTest] test sınıfı, tek bir istisna dışında sürüm 09'dakiyle aynıdır:


<?php

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

// ad alanı
namespace Application;

// sabitlerin tanımlanması
define("ROOT", "C:/Data/st-2019/dev/php7/poly/scripts-console/impots/version-10");



}
  • 10. satır: test ortamı, sürüm 10'daki müşteri ortamıdır;

Testlere başlamadan önce, [redis-cli] istemcisini kullanarak [redis] sunucusunun belleğinden [taxAdminData] anahtarını silelim:

Image

Şimdi testi çalıştıralım:

Image

Şimdi sunucunun [logs.txt] günlüklerini inceleyelim:


05/07/19 08:52:16:396 :
---nouvelle requête
05/07/19 08:52:16:403 : Autentification en cours…
05/07/19 08:52:16:403 : Authentification réussie [admin, admin]
05/07/19 08:52:16:403 : paramètres ['marié'=>oui, 'enfants'=>2, 'salaire'=>55555] valides
05/07/19 08:52:16:407 : données fiscales prises en base de données
05/07/19 08:52:16:420 : {"réponse":{"impôt":2814,"surcôte":0,"décôte":0,"réduction":0,"taux":0.14}}
05/07/19 08:52:16:546 :
---nouvelle requête
05/07/19 08:52:16:555 : Autentification en cours…
05/07/19 08:52:16:555 : Authentification réussie [admin, admin]
05/07/19 08:52:16:556 : paramètres ['marié'=>oui, 'enfants'=>2, 'salaire'=>50000] valides
05/07/19 08:52:16:559 : données fiscales prises dans redis
05/07/19 08:52:16:559 : {"réponse":{"impôt":1384,"surcôte":0,"décôte":384,"réduction":347,"taux":0.14}}
05/07/19 08:52:16:668 :
---nouvelle requête
05/07/19 08:52:16:675 : Autentification en cours…
05/07/19 08:52:16:675 : Authentification réussie [admin, admin]
05/07/19 08:52:16:675 : paramètres ['marié'=>oui, 'enfants'=>3, 'salaire'=>50000] valides
05/07/19 08:52:16:678 : données fiscales prises dans redis
05/07/19 08:52:16:678 : {"réponse":{"impôt":0,"surcôte":0,"décôte":720,"réduction":0,"taux":0.14}}
05/07/19 08:52:16:776 :
---nouvelle requête

Daha önce de belirtildiği gibi, her testte test sınıfının oluşturucusu yeniden çalıştırılır; bu nedenle, test edilen [ClientDao] sınıfı her testte var olmayan bir oturum çerezi ile örneklenir. Dolayısıyla, sanki 11 test, 11 farklı kullanıcıyı ve 11 farklı oturumu temsil ediyormuş gibi görünür.

  • 6. satır: Vergi verileri veritabanından alınır;
  • 13. ve 20. satırlar: Vergi verileri [redis] belleğinden alınır. Dolayısıyla burada, uygulamanın tüm kullanıcıları tarafından paylaşılan [application] kapsamlı bir bellek söz konusudur;

20.8. [Redis] sunucusunun web arayüzü

[Redis] sunucusunun komut modunda yönetilebileceğini görmüştük. Bu sunucu, bir web arayüzü aracılığıyla da yönetilebilir:

Image

  • [4]'te, yönetim sunucusu URL;
  • [5], sunucu tarafından kaydedilen anahtarlar;
  • [6], sunucunun mevcut durumu;

[5]'e tıklandığında, [taxAdminData] anahtarı hakkında bilgiler elde edilir:

Image

  • [7]'te, URL anahtarının bilgilerine erişim sağlayan [taxAdminData] ve [8];
  • [9]'e, anahtarın durumu;
  • [10]'te, anahtarın değeri: jSON dizesi, [TaxAdminData] türündeki bir nesneye ait olduğu anlaşılır;
  • [11]'te anahtar silinebilir;
  • [12]'te, başka bir anahtar eklenebilir;