PhalconPHP: Yüksek Yüklü RESTful API'ler için Bir Çözüm

Yayınlanan: 2022-03-11

Bir PHP MVC çerçevesine dayalı yüksek yüklü bir proje oluşturmanız gerektiğini varsayalım. Muhtemelen mümkün olan her yerde önbelleğe almayı kullanırsınız. Belki projeyi tek bir dosyada oluşturursunuz, hatta belki minimum işlevsellik ile kendi MVC çerçevenizi yazarsınız veya başka bir çerçevenin bazı bölümlerini yeniden yazarsınız. Evet, bu işe yarasa da, biraz zor, değil mi? Neyse ki, bu manipülasyonların çoğunu gereksiz kılan bir çözüm daha var (belki önbellek için tasarruf edin) ve bu çözüme PhalconPHP çerçevesi denir.

PhalconPHP Nedir?

PhalconPHP, C ile yazılmış ve derlenmiş bir PHP uzantısı olarak sağlanan PHP için bir MVC çerçevesidir. Bu onu mevcut en hızlı çerçevelerden biri yapan şeydir (tamamen en hızlısı Yaf'tır, ancak bu bir mikro çerçevedir ve Phalcon'dan çok, çok daha sınırlı işlevselliğe sahiptir). PhalconPHP, PHP dosyalarıyla uzun işlemlere ihtiyaç duymaz ve her istekte yorumlanması gerekmez; web sunucunuz başlatıldığında bir kez RAM'e yüklenir ve çok az kaynak tüketir.

MVC çerçeveleri, web geliştirmede uzun süredir en iyi uygulama olarak kabul ediliyor - şimdiye kadar bir tür profesyonel standart haline geldi, bu nedenle çoğu web geliştiricisi PHP için en az bir MVC çerçevesine aşinadır: Symfony, Yii, Laravel, CodeIgniter, Zend Çerçeve vb. Her birinin kendi avantajları ve dezavantajları vardır, ancak hepsinin ortak noktası nedir? Hepsi PHP ile yazılmıştır ve kodunuz her çalıştığında yorumlayıcı tarafından her istekte çalıştırılması gereken büyük miktarda mantık içeren birçok PHP dosyasından oluşur. Bu büyük bir şeffaflık sağlarken, performansla ödeme yapıyoruz. Büyük miktarda kod ve çok sayıda dahil edilen dosya, özellikle PHP'de (yorumlandığından, derlenmediğinden) çok fazla belleğe ve zamana mal olur. Evet, PHP 7'de durum çok daha iyi hale geldi, ancak hala iyileştirilecek çok şey var ve PhalconPHP bu iyileştirmeleri masaya getiriyor.

Bazı kriterlere bir göz atalım.

PhalconPHP Karşılaştırmaları

Resmi ölçütler beş yaşında - şu anda geçerli olamayacak kadar eski, ancak o zaman bile PhalconPHP'yi neyin ayırt ettiğini çarpıcı biçimde görebilirsiniz. Daha yeni bir şeye bakalım. 2016 karşılaştırmasında, Phalcon, profesyonel çerçeveler arasında bariz bir lider olan ve yalnızca ham PHP ve bazı mikro çerçeveleri kabul eden ilk beşi yerleştirir.

PhalconPHP karşılaştırmaları

Yani, Phalcon hızlıdır. Raw PHP de hızlıdır, ancak bir MVC çerçevesinin sunduğu tüm olanaklara ihtiyacımız var ve Phalcon, aşağıdakiler gibi bileşenler de dahil olmak üzere bu zorluğun üstesinden geliyor:

  • ORM
  • Volt şablonu Motor
  • Bağımlılık Enjeksiyonu (DI) Konteyneri
  • Önbelleğe almak
  • Kerestecilik
  • Yönlendirme sistemleri
  • Güvenlik bloğu
  • otomatik yükleyici
  • Formlar modülü

Bu sadece birkaç isim. Uzun lafın kısası, PhalconPHP, yüksek yüklü bir sistem için RESTful API gibi büyük bir kurumsal uygulama oluşturmak için ihtiyacınız olan her şeye sahiptir.

Phalcon ile ilgili bir başka güzel şey de küçük stilidir - sadece Phalcon ORM ve muazzam Doctrine 2'yi karşılaştırın.

Şimdi bir PhalconPHP projesi oluşturmaya bir göz atalım.

İki Tip Phalcon Projesi: Full-stack ve Micro

Genel olarak, iki tür MVC çerçevesi vardır: tam yığın çerçeveler (Symfony, Yii gibi) ve mikro çerçeveler (Lumen, Slim, Silex gibi).

Tam yığın çerçeveler, daha fazla işlevsellik sağladıkları için büyük bir proje için iyi bir seçimdir, ancak çalıştırmak için biraz daha fazla nitelik ve zamana ihtiyaçları vardır.

Mikro çerçeveler, hafif prototipleri çok hızlı bir şekilde oluşturmanıza izin verir, ancak işlevsellikten yoksundurlar ve bu nedenle, bunları büyük projeler için kullanmaktan kaçınmak genellikle daha iyidir. Bununla birlikte, mikro çerçevelerin bir avantajı performanslarıdır. Genellikle tam yığın olanlardan çok daha hızlıdırlar (örneğin, Yaf çerçevesinin performansı yalnızca ham PHP'den daha düşüktür).

PhalconPHP her ikisini de destekler: Tam yığın veya mikro uygulama oluşturabilirsiniz. Daha da iyisi, projenizi bir mikro uygulama olarak PhalconPHP'de geliştirdiğinizde, yine de Phalcon'un güçlü özelliklerinin çoğuna erişiminiz olur ve performansı hala tam yığın bir uygulamadan daha hızlı kalır.

Geçmişteki bir işte, ekibimin yüksek yüklü bir RESTful sistemi kurması gerekiyordu. Yaptığımız şeylerden biri, Phalcon'daki tam yığın uygulama ile Phalcon mikro uygulaması arasındaki prototip performansını karşılaştırmaktı. PhalconPHP'nin mikro uygulamalarının çok daha hızlı olma eğiliminde olduğunu bulduk. NDA nedenleriyle size herhangi bir kıyaslama gösteremem, ancak bence Phalcon'un performansından en iyi şekilde yararlanmak istiyorsanız mikro uygulamaları kullanın. Bir mikro uygulamayı kodlamak full-stack olandan daha az kullanışlı olsa da, PhalconPHP'nin mikro uygulamaları projeniz için ihtiyaç duyabileceğiniz her şeye ve daha iyi performansa sahiptir. Örneklemek için, Phalcon'da çok basit bir RESTful mikro uygulaması yazalım.

RESTful API Oluşturma

Bir RESTful uygulamasının hemen hemen tüm permütasyonlarının ortak bir yanı vardır: bir User varlığı. Bu nedenle, örnek projemiz için kullanıcıları oluşturmak, okumak, güncellemek ve silmek için küçük bir REST uygulaması oluşturacağız (CRUD olarak da bilinir).

Bu projeyi tamamen tamamlanmış GitLab depomda görebilirsiniz. Orada iki dal var çünkü bu projeyi iki bölüme ayırmaya karar verdim: birinci dal, master , herhangi bir özel PhalconPHP özelliği olmadan yalnızca temel işlevleri içerirken, ikincisi, logging-and-cache , Phalcon'un günlüğe kaydetme ve önbelleğe alma işlevini içerir. Bunları karşılaştırabilir ve bu tür işlevleri Phalcon'da uygulamanın ne kadar kolay olduğunu görebilirsiniz.

Kurulum

Kurulumdan geçmeyeceğim: İstediğiniz herhangi bir veritabanını, herhangi bir işletim sistemini ve herhangi bir web sunucusunu kullanabilirsiniz. Resmi kurulum belgelerinde iyi açıklanmıştır, bu nedenle işletim sisteminize bağlı olarak talimatları uygulamanız yeterlidir.

Web sunucusu kurulum notları ayrıca resmi Phalcon belgelerinde mevcuttur.

PHP sürümünüzün 5.6'dan az olmaması gerektiğini unutmayın.

Ubuntu 16.10, PostgreSQL 9.5.6, Nginx 1.10.0, PHP 7 ve Phalcon 3.0 kullanıyorum. Projeye bir Nginx yapılandırma örneği ve bir PostgreSQL döküm dosyası ekledim, bu yüzden bunları kullanmaktan çekinmeyin. Başka bir konfigürasyon tercih ederseniz, değiştirmek zor olmayacaktır.

Proje Yapısı ve Konfigürasyonu

Her şeyden önce, ilk proje yapısını oluşturun.

Phalcon, istediğiniz herhangi bir yapıyı kullanmanıza izin verirken, bu alıştırma için seçtiğim yapı kısmen bir MVC modeli uygular. RESTful bir proje olduğu için görüşümüz yok, ancak her biri kendi klasörüne ve hizmetlerine sahip denetleyicilerimiz ve modellerimiz var. Hizmetler, MVC'nin “model” bölümünü iki parçaya bölen, projenin iş mantığını uygulayan sınıflardır: veri modelleri (veritabanıyla iletişim kuran) ve iş mantığı modelleri.

public klasöründe bulunan index.php , gerekli tüm parçaları ve konfigürasyonu yükleyen bir önyükleme dosyasıdır. Tüm yapılandırma dosyalarımızın config klasörüne yerleştirildiğini unutmayın. Bunları önyükleme dosyasına koyabiliriz (ve resmi belgelerde gösterilen yol budur), ancak bence bu, büyük projelerde okunamaz hale geliyor, bu yüzden en baştan klasör ayrımını tercih ediyorum.

index.php oluşturma

index.php ilk geçişimiz, konfigürasyonu ve otomatik yükleme sınıflarını yükleyecek, ardından rotaları, bir bağımlılık enjeksiyon konteynerini ve PhalconPHP mikro uygulamasını başlatacak. Ardından kontrolü, istekleri rotalara göre ele alacak, iş mantığını çalıştıracak ve sonuçları döndürecek olan mikro uygulama çekirdeğine aktaracaktır.

Şimdi koda bir göz atalım:

 <?php try { // Loading Configs $config = require(__DIR__ . '/../app/config/config.php'); // Autoloading classes require __DIR__ . '/../app/config/loader.php'; // Initializing DI container /** @var \Phalcon\DI\FactoryDefault $di */ $di = require __DIR__ . '/../app/config/di.php'; // Initializing application $app = new \Phalcon\Mvc\Micro(); // Setting DI container $app->setDI($di); // Setting up routing require __DIR__ . '/../app/config/routes.php'; // Making the correct answer after executing $app->after( function () use ($app) { // Returning a successful response } ); // Processing request $app->handle(); } catch (\Exception $e) { // Returning an error response }

Bir \Phalcon\Config Nesnesini Yapılandırma

Yapılandırma dosyalarını Phalcon'da saklamanın birkaç yolu vardır:

  • Bir YAML dosyası
  • bir JSON dosyası
  • Bir INI dosyası
  • Bir PHP dizisi

Yapılandırmanızı bir PHP dizisinde saklamak en hızlı seçenektir ve yüksek yüklü bir uygulama yazdığımız ve performansımızı yavaşlatmamıza gerek olmadığı için yapacağımız şey budur. Spesifik olarak, yapılandırma seçeneklerimizi projeye yüklemek için bir \Phalcon\Config nesnesi kullanacağız. Çok kısa bir yapılandırma nesnemiz olacak:

 <?php return new \Phalcon\Config( [ 'database' => [ 'adapter' => 'Postgresql', 'host' => 'localhost', 'port' => 5432, 'username' => 'postgres', 'password' => '12345', 'dbname' => 'articledemo', ], 'application' => [ 'controllersDir' => "app/controllers/", 'modelsDir' => "app/models/", 'baseUri' => "/", ], ] );

Bu dosya, biri veritabanı için diğeri uygulama için olmak üzere iki temel yapılandırma içerir. Açıkçası, veritabanı yapılandırması veritabanına bağlanmak için kullanılır ve application dizisine gelince, Phalcon'un sistem araçları tarafından kullanıldığı için daha sonra buna ihtiyacımız olacak. Resmi belgelerde Phalcon konfigürasyonları hakkında daha detaylı bilgi edinebilirsiniz.

loader.php yapılandırma

Bir sonraki yapılandırma dosyamız loader.php . loader.php dosyası, ad alanlarını \Phalcon\Loader nesnesi aracılığıyla karşılık gelen dizinlerle kaydeder. Daha da basit:

 <?php $loader = new \Phalcon\Loader(); $loader->registerNamespaces( [ 'App\Services' => realpath(__DIR__ . '/../services/'), 'App\Controllers' => realpath(__DIR__ . '/../controllers/'), 'App\Models' => realpath(__DIR__ . '/../models/'), ] ); $loader->register();

Artık bu ad alanlarındaki tüm sınıflar otomatik olarak yüklenecek ve kullanılabilir olacak. Yeni bir ad alanı ve dizin eklemek istiyorsanız, bu dosyaya bir satır eklemeniz yeterlidir. Belirli dizinleri veya belirli dosyaları kaydederek ad alanlarını kullanmaktan da kaçınabilirsiniz. Tüm bu olasılıklar PhalconPHP yükleyici belgelerinde açıklanmıştır.

Bağımlılık Enjeksiyon Kapsayıcısını Yapılandırma

Diğer birçok çağdaş çerçeve gibi, Phalcon bir bağımlılık enjeksiyonu (DI) modeli uygular. Nesneler, DI kapsayıcısında başlatılacak ve ondan erişilebilir olacaktır. Benzer şekilde, DI kabı uygulama nesnesine bağlıdır ve denetleyicilerimiz ve hizmetlerimiz gibi \Phalcon\DI\Injectable sınıfından miras alan tüm sınıflardan erişilebilir olacaktır.

Phalcon'un DI modeli çok güçlüdür. Bu bileşenin bu çerçevedeki en önemli bileşenlerden biri olduğunu düşünüyorum ve nasıl çalıştığını anlamak için tüm belgelerini okumanızı şiddetle tavsiye ediyorum. Phalcon'un birçok fonksiyonunun anahtarını sağlar.

Bunlardan birkaçına bir göz atalım. di.php dosyamız şöyle görünecek:

 <?php use Phalcon\Db\Adapter\Pdo\Postgresql; // Initializing a DI Container $di = new \Phalcon\DI\FactoryDefault(); /** * Overriding Response-object to set the Content-type header globally */ $di->setShared( 'response', function () { $response = new \Phalcon\Http\Response(); $response->setContentType('application/json', 'utf-8'); return $response; } ); /** Common config */ $di->setShared('config', $config); /** Database */ $di->set( "db", function () use ($config) { return new Postgresql( [ "host" => $config->database->host, "username" => $config->database->username, "password" => $config->database->password, "dbname" => $config->database->dbname, ] ); } ); return $di;

Gördüğünüz gibi, bağımlılık ekleme (DI) dosyamız biraz daha karmaşık ve bilmeniz gereken bazı ayrıntılar var. Öncelikle, başlatma dizesini düşünün: $di = new \Phalcon\DI\FactoryDefault(); . \Phalcon\Di devralan bir FactoryDefault nesnesi oluşturuyoruz (Phalcon, istediğiniz herhangi bir DI fabrikasını oluşturmanıza olanak tanır). Belgelere göre, FactoryDefault “framework tarafından sağlanan tüm hizmetleri otomatik olarak kaydeder. Bu sayede geliştiricinin, tam bir yığın çerçevesi sağlayan her bir hizmeti ayrı ayrı kaydetmesi gerekmez.” Bu, Request ve Response gibi ortak hizmetlere çerçeve sınıfları içinde erişilebileceği anlamına gelir. Bu tür hizmetlerin tam listesini Phalcon hizmet belgelerinde görebilirsiniz.

Bir sonraki önemli şey ayar sürecidir: DI kapsayıcısına bir şey kaydetmenin birkaç yolu vardır ve bunların hepsi PhalconPHP kayıt belgelerinde tamamen açıklanmıştır. Ancak projemizde üç yol kullanıyoruz: anonim bir işlev, bir değişken ve bir dize.

Anonim işlev, sınıfı başlatırken birçok şey yapmamızı sağlar. Özellikle bu projede, projenin tüm yanıtları için content-type JSON olarak ayarlamak için önce bir Response nesnesini geçersiz kılıyoruz ve ardından yapılandırma nesnemizi kullanarak bir veritabanı bağdaştırıcısını başlatıyoruz.

Daha önce de bahsettiğim gibi bu proje PostgreSQL kullanıyor. Başka bir veritabanı motoru kullanmaya karar verirseniz, db set işlevinde veritabanı bağdaştırıcısını değiştirmeniz yeterlidir. Kullanılabilir veritabanı bağdaştırıcıları ve veritabanı katmanı hakkında daha fazla bilgiyi PhalconPHP'nin veritabanı belgelerinde okuyabilirsiniz.

Üçüncü not, \Phalcon\Config hizmetini uygulayan bir $config değişkeni kaydettiğimdir. Örnek projemizde aslında kullanılmasa da, en sık kullanılan hizmetlerden biri olduğu için buraya eklemeye karar verdim; diğer projelerin hemen hemen her yerde yapılandırmaya erişmesi gerekebilir.

Buradaki son ilginç şey, gerçek setShared yönteminin kendisidir. Bunu çağırmak, bir hizmeti "paylaşılan" yapar, bu da bir singleton gibi davranmaya başladığı anlamına gelir. Belgelere göre: "Hizmet ilk kez çözümlendiğinde, bir tüketici hizmeti konteynerden her aldığında aynı örneği döndürülür."

routes.php Yapılandırma …veya Değil

En son eklenen dosya routes.php . Şimdilik boş bırakalım - kontrolörlerimizle birlikte dolduracağız.

RESTful Core Uygulaması

Bir web projesini RESTful yapan nedir? Wikipedia'ya göre, bir RESTful uygulamasının üç ana bölümü vardır: - Temel URL - Durum geçiş veri öğelerini tanımlayan bir internet medya türü - Standart HTTP yöntemleri ( GET , POST , PUT , DELETE ) ve standart HTTP yanıt kodları (200, 403, 400, 500, vb.).

Projemizde, temel URL'ler routes.php dosyasına yerleştirilecek ve belirtilen diğer noktalar şimdi açıklanacaktır.

Talep verilerini application/x-www-form-urlencoded olarak alacağız ve yanıt verilerini application/json olarak göndereceğiz. Gerçek bir uygulamada x-www-form-urlencoded kullanmanın iyi bir fikir olduğuna inanmasam da (çünkü x-www-form-urlencoded -www-form-urlencoded ile karmaşık veri yapıları ve ilişkisel diziler göndermekte zorlanacaksınız), ben basitlik adına bu standardı uygulamaya karar verdi.

Hatırlarsanız, DI dosyamızda yanıt JSON başlığımızı zaten ayarladık:

 $di->setShared( 'response', function () { $response = new \Phalcon\Http\Response(); $response->setContentType('application/json', 'utf-8'); return $response; } );

Şimdi yanıt kodlarını ve yanıt biçimini ayarlamamız gerekiyor. Resmi öğretici, her bir yöntemde JSON yanıtları oluşturmamızı öneriyor, ancak bunun iyi bir fikir olduğunu düşünmüyorum. Denetleyici yöntemi sonuçlarını diziler olarak döndürmek ve ardından bunları standart JSON yanıtlarına dönüştürmek çok daha evrenseldir. Ayrıca proje içinde tek bir yerde HTTP yanıt kodlarını oluşturmak daha akıllıcadır; bunu index.php dosyamızda yapacağız.

Bunu yapmak için, $app->before() ve $app->after() yöntemleriyle istek işlemeden önce ve sonra Phalcon'un kod yürütme yeteneğini kullanacağız. Amacımız için $app->after() yöntemine bir geri arama yapacağız:

 // Making the correct answer after executing $app->after( function () use ($app) { // Getting the return value of method $return = $app->getReturnedValue(); if (is_array($return)) { // Transforming arrays to JSON $app->response->setContent(json_encode($return)); } elseif (!strlen($return)) { // Successful response without any content $app->response->setStatusCode('204', 'No Content'); } else { // Unexpected response throw new Exception('Bad Response'); } // Sending response to the client $app->response->send(); }

Burada dönüş değerini alıyoruz ve diziyi JSON'a dönüştürüyoruz. Her şey yolundaysa, ancak dönüş değeri boşsa (örneğin, yeni bir kullanıcıyı başarıyla eklemiş olsaydık), 204 HTTP kodu verir ve içerik göndermezdik. Diğer tüm durumlarda, bir istisna atarız.

İstisnaları İşleme

RESTful uygulamasının en önemli yönlerinden biri doğru ve bilgilendirici yanıtlardır. Yüksek yüklü uygulamalar genellikle büyüktür ve çeşitli türlerde hatalar her yerde ortaya çıkabilir: doğrulama hataları, erişim hataları, bağlantı hataları, beklenmeyen hatalar vb. Tüm bu hataları birleşik HTTP yanıt kodlarına dönüştürmek istiyoruz. İstisnalar yardımıyla kolayca yapılabilir.

Projemde, iki farklı istisna türü kullanmaya karar verdim: "yerel" istisnalar vardır - hizmetler, modeller, adaptörler vb. ile ayrılmış, \RuntimeException sınıfından miras alınan özel sınıflar (böyle bir bölüm, her seviyeyi ele almaya yardımcı olur) MVC modelinin ayrı bir modeli olarak)- sonra AbstractHttpException sınıfından miras alınan HttpExceptions vardır. Bu istisnalar HTTP yanıt kodlarıyla tutarlıdır, dolayısıyla adları Http400Exception , Http500Exception vb. şeklindedir.

AbstractHttpException sınıfının üç özelliği vardır: httpCode , httpMessage ve appError . İlk iki özellik mirasçılarında geçersiz kılınır ve httpCode: 400 ve httpMessage: Bad request gibi temel yanıt bilgilerini içerir. appError özelliği, hata açıklaması da dahil olmak üzere ayrıntılı hata bilgilerinin bir dizisidir.

index.php son versiyonumuz üç tür istisna yakalayacaktır: Yukarıda açıklandığı gibi AbstractHttpExceptions ; Bir istek ayrıştırılırken oluşabilecek Phalcon İstek İstisnaları; ve diğer tüm beklenmeyen istisnalar. Hepsi güzel bir JSON formatına dönüştürülür ve standart Phalcon Response sınıfı aracılığıyla istemciye gönderilir:

 <?php use App\Controllers\AbstractHttpException; try { // Loading Configs $config = require(__DIR__ . '/../app/config/config.php'); // Autoloading classes require __DIR__ . '/../app/config/loader.php'; // Initializing DI container /** @var \Phalcon\DI\FactoryDefault $di */ $di = require __DIR__ . '/../app/config/di.php'; // Initializing application $app = new \Phalcon\Mvc\Micro(); // Setting DI container $app->setDI($di); // Setting up routing require __DIR__ . '/../app/config/routes.php'; // Making the correct answer after executing $app->after( // After Code ); // Processing request $app->handle(); } catch (AbstractHttpException $e) { $response = $app->response; $response->setStatusCode($e->getCode(), $e->getMessage()); $response->setJsonContent($e->getAppError()); $response->send(); } catch (\Phalcon\Http\Request\Exception $e) { $app->response->setStatusCode(400, 'Bad request') ->setJsonContent([ AbstractHttpException::KEY_CODE => 400, AbstractHttpException::KEY_MESSAGE => 'Bad request' ]) ->send(); } catch (\Exception $e) { // Standard error format $result = [ AbstractHttpException::KEY_CODE => 500, AbstractHttpException::KEY_MESSAGE => 'Some error occurred on the server.' ]; // Sending error response $app->response->setStatusCode(500, 'Internal Server Error') ->setJsonContent($result) ->send(); }

Phalcon Geliştirme Araçları ile Model Oluşturma

Çağdaş bir IDE kullanıyorsanız, muhtemelen vurgulamayı ve tamamlamayı kodlamaya alışmışsınızdır. Benzer şekilde, tipik bir PHP çerçevesinde, yalnızca tek bir tıklamayla bir işlev bildirimine gitmek için çerçeveli bir klasör ekleyebilirsiniz. Phalcon bir uzantı olduğu için bu seçeneği otomatik olarak alamıyoruz. Neyse ki, bu boşluğu dolduran ve Composer aracılığıyla kurulabilen “Phalcon Dev Tools” adlı bir araç var (hala ne olduğunu bilmiyorsanız, şimdi bu harika paket yöneticisini tanımanın zamanı geldi). Phalcon Geliştirme Araçları, Phalcon'daki tüm sınıflar ve işlevler için kod taslaklarından oluşur ve PhalconPHP web sitesinde belgelenen hem konsol hem de GUI sürümleriyle bazı kod oluşturucuları sağlar. Bu araçlar, MVC modelinin tüm parçalarını oluşturmaya yardımcı olabilir, ancak biz yalnızca model oluşturmayı ele alacağız.

Tamam, Composer aracılığıyla Phalcon Dev Tools'u yükleyelim. composer.json dosyamız şöyle görünecek:

 { "require": { "php": ">=5.6.0", "ext-phalcon": ">=3", "ext-pgsql": "*" }, "require-dev": { "phalcon/devtools": "3.*.*@dev" } }

Gördüğünüz gibi, PHP 5.6, Phalcon 3 ve pgsql uzantısına ihtiyacımız var (bunları veritabanı uzantınızla değiştirebilir veya tamamen hariç tutabilirsiniz).

Doğru PHP, Phalcon ve DB uzantı sürümlerine sahip olduğunuzdan emin olun ve besteciyi çalıştırın:

 $ composer install

Bir sonraki adım, veritabanımızı oluşturmaktır. Çok basittir ve yalnızca bir tablodan oluşur, users . Projeye bir pg_dump dosyası eklemiş olmama rağmen, işte PostgreSQL lehçesindeki SQL:

 CREATE DATABASE articledemo; CREATE TABLE public.users ( id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('users_id_seq'::regclass), first_name CHARACTER VARYING(255), last_name CHARACTER VARYING(255), pass CHARACTER VARYING(255), login CHARACTER VARYING(255) NOT NULL );

Veritabanı oluşturulduğuna göre artık model oluşturma sürecine geçebiliriz. Phalcon Dev Tools, bir uygulamanın Phalcon projesi olup olmadığını algılamak için boş bir .phalcon klasörü kullanır, bu nedenle proje kökünüzde bu boş klasörü oluşturmanız gerekir. Ayrıca, oluşturduğumuz yapılandırma dosyasındaki bazı ayarları kullanır—tüm değişkenler application bölümünde ve adapter database bölümünde saklanır. Modelimizi oluşturmak için proje kök klasöründen aşağıdaki komutu çalıştırmamız gerekiyor:

 $ php vendor/phalcon/devtools/phalcon.php model users --namespace="App\Models" --get-set

Önceki tüm adımlar doğru bir şekilde yapıldıysa, models klasörünüzde, komut satırında belirtildiği gibi alıcılar ve ayarlayıcılar içeren bir ad alanına yerleştirilmiş, Users.php adlı çalışan bir model dosyası alırsınız. Sonraki kontrolör.

Kontrolörler ve Yönlendirme

Uygulamamız yalnızca CRUD kullanıcıları (oluşturur, okur, günceller ve siler) oluşturduğundan, yalnızca bir denetleyici oluşturacağız, aşağıdaki işlemlere sahip Users denetleyicisi:

  • Kullanıcı Ekle
  • Kullanıcı listesini göster
  • Kullanıcıyı güncelle
  • Kullanıcıyı sil

Denetleyiciler Phalcon Dev Tools yardımıyla oluşturulabilirken, bunu manuel olarak yapacağız ve AbstractController ile alt UsersController uygulayacağız.

AbstractController oluşturmak Phalcon için iyi bir karar çünkü bağımlılık enjeksiyonundan alacağımız tüm gerekli sınıfları PHPDoc bloğuna yerleştirebiliriz. Bu, IDE otomatik tamamlama işlevine yardımcı olacaktır. Tüm potansiyel kontrolörlerde ortak olan bazı hata sabitlerini de programlayabiliriz.

Şimdilik soyut denetleyicimiz şöyle görünecek:

 <?php namespace App\Controllers; /** * Class AbstractController * * @property \Phalcon\Http\Request $request * @property \Phalcon\Http\Response $htmlResponse * @property \Phalcon\Db\Adapter\Pdo\Postgresql $db * @property \Phalcon\Config $config * @property \App\Services\UsersService $usersService * @property \App\Models\Users $user */ abstract class AbstractController extends \Phalcon\DI\Injectable { /** * Route not found. HTTP 404 Error */ const ERROR_NOT_FOUND = 1; /** * Invalid Request. HTTP 400 Error. */ const ERROR_INVALID_REQUEST = 2; }

Genişletme sözdiziminde belirtildiği gibi yalnızca basit bir extends enjekte edilebilir sınıf, başka bir şey değil. Ardından, UsersController iskeletini oluşturalım:

 <?php namespace App\Controllers; /** * Operations with Users: CRUD */ class UsersController extends AbstractController { /** * Adding user */ public function addAction() { } /** * Returns user list * * @return array */ public function getUserListAction() { } /** * Updating existing user * * @param string $userId */ public function updateUserAction($userId) { } /** * Delete an existing user * * @param string $userId */ public function deleteUserAction($userId) { } }

Şu anda, yalnızca sonunda karşılık gelen HTTP isteklerini tutacak boş eylemlere sahip bir sınıftır.

Şimdi routes.php dosyasını doldurmanın zamanı geldi. Phalcon mikro uygulamalarında, her denetleyici için bir koleksiyon oluşturuyoruz ve işlenen tüm istekleri get , post , put , delete yöntemleri olarak ekliyoruz, bunlar bir rota deseni ve bir işlem işlevi argüman olarak alıyor. Devam eden bir işlevin ya anonim bir işlev ya da bir denetleyicinin yöntem adı olması gerektiğini unutmayın. İşte routes.php dosyamız şöyle görünür:

 <?php $usersCollection = new \Phalcon\Mvc\Micro\Collection(); $usersCollection->setHandler('\App\Controllers\UsersController', true); $usersCollection->setPrefix('/user'); $usersCollection->post('/add', 'addAction'); $usersCollection->get('/list', 'getUserListAction'); $usersCollection->put('/{userId:[1-9][0-9]*}', 'updateUserAction'); $usersCollection->delete('/{userId:[1-9][0-9]*}', 'deleteUserAction'); $app->mount($usersCollection); // not found URLs $app->notFound( function () use ($app) { $exception = new \App\Controllers\HttpExceptions\Http404Exception( _('URI not found or error in request.'), \App\Controllers\AbstractController::ERROR_NOT_FOUND, new \Exception('URI not found: ' . $app->request->getMethod() . ' ' . $app->request->getURI()) ); throw $exception; } );

Ayrıca bir işleme denetleyicisi ve bir URI öneki belirledik. Örneğimiz için, bir URI http://article.dev/user/add gibi görünecek ve bir post isteği olmalıdır. Kullanıcı verilerini değiştirmek istiyorsak, URI bir put request olmalı ve kimliği 12 olan kullanıcının verilerini değiştirmek için http://article.dev/user/12 gibi görünmelidir. Ayrıca, hata veren, bulunamayan bir URL işleyicisi tanımlarız. Daha fazla bilgi için, tam yığın uygulamasındaki rotalar ve bir mikro uygulamadaki rotalar için PhalconPHP belgelerine bakın.

Denetleyicinin gövdesine ve özellikle addAction yöntemine geçelim (diğerlerinin tümü benzer; bunları uygulama kodunda görebilirsiniz). Bir denetleyici yöntemi beş şey yapar:

  1. İstek parametrelerini alır ve doğrular
  2. Servis yöntemi için veri hazırlar
  3. Servis yöntemini çağırır
  4. İstisnaları ele alır
  5. yanıtı gönderir

Doğrulama ile başlayarak her adımı inceleyelim. Phalcon'un güçlü bir doğrulama bileşeni olmasına rağmen, bu durumda verileri eski tarzda doğrulamak çok daha uygundur, bu nedenle doğrulama bloğumuz şöyle görünecektir:

 $errors = []; $data = []; $data['login'] = $this->request->getPost('login'); if (!is_string($data['login']) || !preg_match('/^[A-z0-9_-]{3,16}$/', $data['login'])) { $errors['login'] = 'Login must consist of 3-16 latin symbols, numbers or \'-\' and \'_\' symbols'; }

Burada post parametresinin normal bir ifadeyle eşleşen bir dize olup olmadığını kontrol ediyoruz. Tüm değerler, daha sonra UsersService sınıfına geçirilen $data dizisine konur. Tüm hatalar $errors dizisine yerleştirilir, bu daha sonra Http400Exception içindeki bir hata ayrıntıları dizisine eklenir ve burada index.php içinde görülen ayrıntılı yanıta dönüştürülür:

UsersService (henüz oluşturmadığımız) createUser yöntemine yapılan bir çağrıyı içeren, tüm doğrulamalarıyla birlikte tam addAction yöntem kodu:

 public function addAction() { /** Init Block **/ $errors = []; $data = []; /** End Init Block **/ /** Validation Block **/ $data['login'] = $this->request->getPost('login'); if (!is_string($data['login']) || !preg_match('/^[A-z0-9_-]{3,16}$/', $data['login'])) { $errors['login'] = 'Login must consist of 3-16 latin symbols, numbers or \'-\' and \'_\' symbols'; } $data['password'] = $this->request->getPost('password'); if (!is_string($data['password']) || !preg_match('/^[A-z0-9_-]{6,18}$/', $data['password'])) { $errors['password'] = 'Password must consist of 6-18 latin symbols, numbers or \'-\' and \'_\' symbols'; } $data['first_name'] = $this->request->getPost('first_name'); if ((!empty($data['first_name'])) && (!is_string($data['first_name']))) { $errors['first_name'] = 'String expected'; } $data['last_name'] = $this->request->getPost('last_name'); if ((!empty($data['last_name'])) && (!is_string($data['last_name']))) { $errors['last_name'] = 'String expected'; } if ($errors) { $exception = new Http400Exception(_('Input parameters validation error'), self::ERROR_INVALID_REQUEST); throw $exception->addErrorDetails($errors); } /** End Validation Block **/ /** Passing to business logic and preparing the response **/ try { $this->usersService->createUser($data); } catch (ServiceException $e) { switch ($e->getCode()) { case AbstractService::ERROR_ALREADY_EXISTS: case UsersService::ERROR_UNABLE_CREATE_USER: throw new Http422Exception($e->getMessage(), $e->getCode(), $e); default: throw new Http500Exception(_('Internal Server Error'), $e->getCode(), $e); } } /** End Passing to business logic and preparing the response **/ }

Gördüğünüz gibi, bu son bölümde bilinen iki istisnayı ele alıyoruz: user already exists ve veritabanı bağlantı hatası gibi bazı dahili sorunlardan dolayı unable to create user . Varsayılan olarak, bilinmeyen istisnalar HTTP 500 (dahili sunucu hatası) olarak atılır. Son kullanıcıya herhangi bir ayrıntı vermesek de, tüm hata ayrıntılarını (izleme dahil) günlükte saklamanız şiddetle tavsiye edilir.

Ve lütfen, diğer ad alanlarından ödünç alınan gerekli tüm sınıfları use unutmayın:

 use App\Controllers\HttpExceptions\Http400Exception; use App\Controllers\HttpExceptions\Http422Exception; use App\Controllers\HttpExceptions\Http500Exception; use App\Services\AbstractService; use App\Services\ServiceException; use App\Services\UsersService;

İş mantığı

Oluşturulacak son kısım iş mantığıdır. Tıpkı denetleyicilerde olduğu gibi, soyut bir hizmet sınıfı oluşturacağız:

 <?php namespace App\Services; /** * Class AbstractService * * @property \Phalcon\Db\Adapter\Pdo\Postgresql $db * @property \Phalcon\Config $config */ abstract class AbstractService extends \Phalcon\DI\Injectable { /** * Invalid parameters anywhere */ const ERROR_INVALID_PARAMETERS = 10001; /** * Record already exists */ const ERROR_ALREADY_EXISTS = 10002; }

Fikir, denetleyici bloğundakiyle tamamen aynı, bu yüzden yorum yapmayacağım. İşte UsersService sınıfımızın iskeleti:

 <?php namespace App\Services; use App\Models\Users; /** * business logic for users * * Class UsersService */ class UsersService extends AbstractService { /** Unable to create user */ const ERROR_UNABLE_CREATE_USER = 11001; /** * Creating a new user * * @param array $userData */ public function createUser(array $userData) { } }

Ve createUser yönteminin kendisi:

 public function createUser(array $userData) { try { $user = new Users(); $result = $user->setLogin($userData['login']) ->setPass(password_hash($userData['password'], PASSWORD_DEFAULT)) ->setFirstName($userData['first_name']) ->setLastName($userData['last_name']) ->create(); if (!$result) { throw new ServiceException('Unable to create user', self::ERROR_UNABLE_CREATE_USER); } } catch (\PDOException $e) { if ($e->getCode() == 23505) { throw new ServiceException('User already exists', self::ERROR_ALREADY_EXISTS, $e); } else { throw new ServiceException($e->getMessage(), $e->getCode(), $e); } } }

Bu yöntem olabildiğince kolaydır. Biz sadece yeni bir model nesnesi yaratırız, onun ayarlayıcılarını çağırırız (bu, nesnenin kendisini döndürür; bu, bir çağrı zinciri yapmamızı sağlar) ve bir hata durumunda bir ServiceException oluştururuz. Bu kadar! Artık teste geçebiliriz.

Test yapmak

Şimdi Postman kullanarak sonuçlara bakalım. Önce bazı çöp verilerini test edelim:

Geçersiz veriye sahip postacı.

İstek:

 POST http://article.dev/user/add login:1 password:1 first_name:Name last_name:Sourname

Yanıt (400: Hatalı İstek):

 { "error": 2, "error_description": "Input parameters validation error", "details": { "login": "Login must consist of 3-16 latin symbols, numbers or '-' and '_' symbols", "password": "Password must consist of 6-18 latin symbols, numbers or '-' and '_' symbols" } }

Bu kontrol ediyor. Şimdi bazı doğru veriler için:

Geçerli verileri olan postacı.

İstek:

 POST http://article.dev/user/add login:user4 password:password4 first_name:Name last_name:Sourname

Yanıt (204):

No content, which is what we expected. Now let's make sure it worked and get the full user list (which we didn't describe in the article, but you can see it in the application example):

Request:

 GET http://article.dev/user/list

Response (200 OK):

 [ { "id": 1, "login": "user4", "first_name": "Name", "last_name": "Sourname" } ]

Well, it works!

Logging and Caching

It's hard to imagine a high-load application without logging and caching, and Phalcon provides very seductive classes for it. But I'm writing an article here, not a book; I've added logging and caching to the sample application, but I've placed this code into another branch called logging-and-cache so you can easily look at it and see the difference in the code. Just like the other Phalcon features, these two are well-documented: Logging and Caching.

Dezavantajları

As you can see, Phalcon is really cool, but like other frameworks, it has its disadvantages, the first of which is the same as its main advantage—it's a compiled C extension. That's why there is no way for you to change its code easily. Well, if you know C, you can try to understand its code and make some changes, run make and get your own modification of Phalcon, but it is much more complicated than making some tweaks in PHP code. So, generally, if you find a bug inside Phalcon, it won't be so easy to fix.

This is partially solved in Phalcon 2 and Phalcon 3, which let you write extensions to Phalcon in Zephir. Zephir is a programming language designed to ease the creation and maintainability of extensions for PHP with a focus on type and memory safety. Its syntax is very close to PHP and Zephir code is compiled into shared libraries, same as the PHP extension. So, if you want to enhance Phalcon, now you can.

The second disadvantage is the free framework structure. While Symfony makes developers use a firm project structure, Phalcon has very few strict rules; developers can create any structure they like, though there is a structure that is recommended by its authors. This isn't a critical disadvantage, but some people may consider it too raw when you write the paths to all the directories in a bootstrap file manually.

PhalconPHP: Not Just For High-load Apps

I hope you've enjoyed this brief overview of PhalconPHP's killing features and the accompanying simple example of a Phalcon project. Obviously, I didn't cover all the possibilities of this framework since it's impossible to describe all of them in one article, but fortunately Phalcon has brilliantly detailed documentation with seven marvelous tutorials which help you understand almost everything about Phalcon.

You've now got a brand new way to create high load applications easily, and you'll find, if you like Phalcon, it can be a good choice for other types of applications too.