Başlarken
DemirApp uygulama çatısı, kendi ihtiyaçlarımdan yola çıkarak yazdığım, basit ve minimal tutmaya çalıştığım bir uygulama. Küçük ve orta çaplı uygulamaları rahatça geliştirebileceğiniz bu PHP çatısında, PHP ile kod yazarken sıkça ihtiyaç duyduğum metod ve fonksiyonları bir araya getirdim. Umarım faydalı olur.
https://github.com/yidemir/App
Gereksinimler
- PHP 7.1 ve üzeri
- JSON yardımcısı
- Apache, NGINX gibi bir sunucu
Kurulum
Kurulum için önerim, composer paket yöneticisini kullanmanızdır. Eğer kullanmıyorsanız, projenin Github kaynağından indirerek de kullanabilirsiniz.
Composer ile kurulum
$ composer require yidemir/app
ve kodlamaya başlayın:
<?php
use DemirApp;
require __DIR__.'/vendor/autoload.php';
App::get('/', function(){
echo 'Merhaba dünya!';
});
App::run();
Git ile kurulum
$ git clone https://github.com/yidemir/App.git
App
dizininin içine index.php
dosyası oluşturun ve çalıştırın:
<?php
use DemirApp;
require __DIR__.'/src/App.php';
App::get('/', function(){
echo 'Merhaba dünya!';
});
App::run();
İndirip, dosya sisteminden kullanım
Github’da yayımlanan sürümler sayfasından projeyi indirip, Git ile kurulum bölümünde bahsedildiği gibi kodlamaya başlayabilirsiniz.
Test
Uygulamayı test etmek için composer ve phpunit gerekmektedir.
$ composer test
Rota İşlemleri
DemirApp uygulamasında GET, POST, PATCH, PUT, DELETE ve ANY olmak üzere 6 istek metodu kullanılabilir (veya kendiniz de oluşturabilirsiniz). Bunlardan ANY metodu, herhangi bir istek metodunu ifade ediyor.
Örnek rota tanımlama:
App::map('GET', '/', function(){
echo 'Selam!';
});
Bu şekilde rotayı haritalarsak /
yolunda ve GET metodunda geri çağırım işlevimiz çalışacak ve ekrana ‘Selam!’ çıktısını verecektir. Aynı zamanda şöyle de kullanılabilir ve kullanılması önerilir:
App::get('/', function(){
echo 'Selam!';
});
Yukarıdaki yazılan kod ile bu, aynı işlevi görür. Birden fazla istek metodunu cevaplamak içinse, yine map
metodunu kullanabiliriz.
App::map(['GET', 'POST'], '/', function(){
echo 'Selam!';
});
İlk parametreye dizi halinde gönderip, içerisine istediğimiz istek metodlarını yazdığımızda, cevabımızı ona göre alırız. Bütün isteklere cevap vermek için any
metodunu kullanabiliriz:
App::any('/', function(){
echo 'Selam!';
});
Geri Çağırım Parametresi
Rota tanımlarken, geri çağırım parametresi, anonim bir fonksiyon, normal bir fonksiyon, Sınıf:metod şeklinde bir tanımlama veya doğrudan bir sınıf olabilir.
Anonim Fonksiyon:
App::get('/', function(){
echo 'Selam!';
});
Normal Fonksiyon:
function home(){
echo 'Selam!';
}
App::get('/', 'home');
Sınıf:metod işareti
class HomeController
{
public function index()
{
echo 'Selam!';
}
}
App::get('/', 'HomeController:index');
Sınıf:
class Home
{
public function __invoke()
{
echo 'Selam!';
}
}
App::get('/', new Home);
Geri Çağırım Dizesi:
class HomeController
{
public function index()
{
echo 'Selam!';
}
}
App::get('/', [new HomeController, 'index']);
Bu yöntemlerin hepsi kullanılabilir.
Rota İsimlendirme
Rota ve rota grupları tanımlarken, geri çağırım parametresinden sonra gelen parametre, rotanın ismini işaret eder.
App::get('/homepage', 'AppControllersHomeController:index', 'home');
App::url('home'); // '/homepage' değeri dönecektir
Ara Katman İşlevi (Middleware) Ekleme
Tam manasıyla bir ara katman mimarisi olmasa da, basit bir biçimde rotalara ve rota gruplarına ara katman işlevleri, rota isminden sonraki parametrede dize halinde eklenebilir. Örnek:
class CheckAdmin
{
public function __invoke()
{
if ('admin' !== 'user') {
exit('Please login');
}
}
}
function check_admin()
{
if ('admin' !== 'user') {
exit('Please login');
}
}
App::get('/', new Home, 'home', ['check_admin', new CheckAdmin]);
Yukarıdaki örnekte iki tane middleware tanımladık. Birisi fonksiyon diğer sınıf.
Rota Yolu Parametreleri
DemirApp’te rota yolları düzenle ifadelerle (Regular Expressions) oluşturulup, istek zamanı eşleştirilir. Yalnız, sık kullanılan rota yolu parametreleri hali hazırda belirli öbeklerle kullanılabilir.
Karşılaştırma:
:id ve :number öbeği rakamları ifade eder
:string öbeği dizgeyi ifade eder
:slug öbeği kalıcı bağlantıyı ifade eder
:any herhangi bir değeri ifade eder
:all bütün her şeyi ifade eder
Örnekler:
App::get('/post/:id', function($id){
echo $id;
});
// /post/5 ile eşleşir
// $id değeri 5 olur
App::get('/post/:slug', function($slug){
echo $slug;
});
// /post/merhaba-dunya-bu-ilk-yazim ile eşleşir
// $slug değer merhaba-dunya-bu-ilk-yazim olur
App::get('/post/tag/:string', function($tag){
echo strip_tags($tag);
});
// /post/tag/Yeni_Yazılar ile eşleşir
// $tag değeri Yeni_Yazılar olur
App::post('/hello/:any', function($name){
echo strip_tags($name);
});
// /hello/Demir<script> ile eşleşir
// $name değeri Demir<script> olur,
// güvenlik için süzmek gerekir
App::get('/:all', function($all){
echo $all;
});
// /merhaba/dunya/ve/url ile eşleşir
// $all değer merhaba/dunya/ve/url olur
App::post('/name/([demir]+)', function($param){
echo $param;
});
// /name/demir ile eşleşir
// /name/imedr ile eşleşir
// /name/none ile eşleşmez
App::delete('/(post|category)', function($whichOne){
$module = $whichOne;
});
// /post ile eşleşir
// /category ile eşleşir
// /setting ile eşleşmez
Rota Gruplandırma
DemirApp’te, rotaları gruplandırmak için group
metodu kullanılır.
App::group('/admin', function(){
App::get('/', 'AdminDashboardController:index');
App::get('/posts', 'AdminPostController:index');
});
// '/admin' isteğinde DashboardController:index
// '/admin/posts' isteğinde PostController:index çalışır
Aynı şekilde, geri çağırım işlevinden sonra gelen parametre grup adını, sonraki parametre ise ara katmanları (middleware) alır. Örnek:
function check_admin()
{
if ('admin' !== 'user') {
exit('Please login');
}
}
App::group('/admin', function(){
App::get('/', 'AdminDashboardController:index', 'home');
App::get('/posts', 'AdminPostController:index', 'posts.index');
}, 'admin.', ['check_admin']);
App::url('admin.posts.index'); // '/admin/posts' değer döner
Kaynak Kontrolcü Tanımlama
DemirApp’te rota tanımlarken, tek tek tüm istek metodlarını, ekle-oku-düzenle-sil (crud) işlevlerini tanımlamaya alternatif bir yöntem olan resource
metodu bulunur.
Bu metod üç parametre alır. Birisi rota yolu, diğer geri çağırım işlevi veya sınıf, diğeri ise varsa ara katmanlar. Örneğimizi görelim:
App::resource('/admin/posts', AppControllersPostController::class);
// veya
App::resource('/admin/posts' 'AppControllersPostController');
// veya
App::resource('/admin/posts', new AppControllersPostController);
// birçok şekilde tanımlama yapılabilir
// üçüncü parametre ara katmanları alır
App::resource('/posts', 'PostController', ['check_post_owner']);
Kaynak kontrolcü tanımlarken, rota isimlendirmeleri otomatik yapılır. /admin/posts
için admin.posts
isimlendirmesi yapılır ve sonrasında işlevin adı gelir.
App::resource('/posts', 'PostController');
için oluşturulan rotalar şunlardır.
Metod |
URI |
Kontrolcü Aksiyonu |
Rota ismi |
GET |
/posts |
index |
posts.index |
GET |
/posts/create |
create |
posts.create |
POST |
/posts |
store |
posts.store |
GET |
/posts/:id |
show |
posts.show |
GET |
/posts/:id/edit |
edit |
posts.edit |
PATCH |
/posts/:id |
update |
posts.update |
DELETE |
/posts/:id |
destroy |
posts.destroy |
Konteyner İşlemleri
DemirApp, içerisinde basit bir konteyner (bağımlılık yöneticisi, dependency injection) bulundurur. Örnekler:
App::container()->set('name', 'Demir');
App::container()->get('name'); // Demir değeri dönecektir
App::container()->has('name'); // true
// Bir sınıf veya işlev tanımlamak isteyebiliriz:
App::container()->set('obj', function(){
$obj = new stdClass();
$obj->name = 'Demir';
$obj->type = 'App';
return $obj;
});
$object = App::container()->get('obj');
echo $object->name; // Demir çıktısı alınır
Konteyner içinde statik halde bir öge de saklanabilir.
App::container()->singleton('pdo', function() : PDO {
return new PDO('sqlite:db.sqlite');
});
var_dump(
App::container()->get('pdo') === App::container()->get('pdo')
); // true
Konteyner içine tanımlama yaparken, parametre de gönderilebilir. Tanımalama sırasında verilen ilk parametre konteynerin kendisidir, sonrakiler kullanıcı parametreleridir.
App::container()->singleton('pdo', function($container, string $dsn, string $user, string $password) : PDO {
return new PDO($dsn, $user, $password);
});
// çağırırken
$pdo = App::container()->call('pdo', ['mysql:host=localhost;dbname=test', 'root', 'root']);
Kısa Kullanım
İstendiği taktirde getirme ve tanımlama işlevleri daha kısa kullanılabilir:
// Tanımlama:
App::container(['name' => 'Demir']);
// Getirme:
App::container('name'); // Demir
// İkinci parametre yoksa, varsayılan değeri alır:
App::container('nameless', 'DemirApp'); // DemirApp
Bu kullanım, singleton
ve call
metodları ile uyumlu değildir.
Talep/İstek (Request) İşlemleri
DemirApp’te istek işlemleri basit işlemler yapmak için oldukça kullanışlıdır. Örnekler:
App::request()->params(); // Bütün parametreleri döndürür
App::request()->all(); // params metodu ile aynı işlev
App::request()->param('page'); // page parametresi döner
App::request()->param('page', 5); // page parametresi yoksa 5'tir
App::request('page'); // param metodu ile aynı işlev
App::request('page', 5); // param metodu ile aynı işlev
App::request()->only('title', 'body', 'created_at'); // sadece belirtilen parametreler döner
App::request(['title', 'body', 'created_at']); // only metodu ile aynı işlev
App::request()->has('title'); // boolean döner, title var mı yok mu
App::request()->get('page', 5); // $_GET verisinden değer alır, yoksa varsayılan değer olan 5 döner
App::request()->post('title', 'Başlık'); // $_POST verisinden değer alır, yoksa varsayılan değer olan 'Başlık' döner
App::request()->file('image'); // $_FILES verisinden değer alır
App::request()->method(); // string Geçerli istek metodunu döndürür
App::request()->method('get'); // boolean İstek metodu belirtilen değer mi kontrol eder
App::request()->isAjax(); // boolean Ajax isteği mi kontrol eder
Session İşlemleri
Session metodu basit oturum işlemlerini yönetmemize yarar.
App::session('session-key'); // oturum değeri döner
App::session(['session-key' => 'value']); // oturum değeri belirler
Session işlemlerini gerçekleştirebilmek için evvela oturumun başlatılmış olması gerekir.
App::session()->start(); // başlatır
App::session()->set('key', 'value'); // belirler
App::session()->get('key', 'default'); // döndürür
App::session()->has('key'); // kontrol eder
App::session()->destroy('key'); // kaldırır
App::session()->destroy(); // bütün verileri kaldırır
Flaş Mesajları
Session metodunun içerisinde bir de flash
metodu bulunmaktadır. Flaş mesaj sistemi, yönlendirme yapmadan evvel belirlenip, daha sonra görünüm/view sayfasında görüntülenip kaybolurlar. Örnek:
App::session()->flash('Örnek bir flaş mesajı');
// bu şekilde belirlenen bir flaş mesajın varsayılan türü 'message'dır.
// istediğiniz türde flash mesajları belirleyebilirsiniz
// belirlenen flaş mesajlar, dize içerisinde depolanır
// diğer örnekler
// doğrudan flash metoduyla da işleminizi yapabilirisiniz
App::flash('Başlık alanı gereklidir', 'formError');
App::flash('Gövde alanı gereklidir', 'formError');
// Flash mesajlarını view'da elde etmek için:
$formErrors = App::flash()->get('formError');
// ['Başlık alanı gereklidir', 'Gövde alanı gereklidir']
Flaş sistemi içerisinde iki tane türü mevcut halde bulundurur. Bunlar message
ve error
türleridir. Şu şekilde kullanılır:
App::flash()->error('Birinci hata');
App::flash()->error('İkinci hata');
$errors = App::flash()->getErrors();
// ['Birinci hata', 'İkinci hata']
App::flash()->message('İşleminiz başarıyla tamamlandı');
$messages = App::flash()->getMessages();
// ['İşleminiz başarıyla tamamlandı']
Hata Yönetimi
DemirApp’te hata yönetimi için hali hazırda 404 sayfa bulunamadı ve istisna işleyicisi bulunmaktadır. Şu şekilde kullanılır:
App::notFound(function(){
http_response_code(404);
App::render('404'); // 404 görünümünü yorumlar
// veya
// echo '404 Sayfa Bulunamadı';
});
İstisna işleyicisi ise şu şekilde kullanılabilir:
App::error(function($error){
http_response_code(500);
if (App::container('debug') === true) {
echo $error->getMessage();
echo '<br>';
echo $error->getFile();
echo '<br>';
echo $error->getLine();
} else {
echo 'Bir uygulama hatası meydana geldi';
}
});
JSON Yanıtı
Bir rotada tek bir metod ile JSON yanıtı verilebilir.
App::get('/api/posts', function(){
$posts = Post::all();
return App::json($posts);
});
// veya 404 sayfası için:
App::notFound(function(){
http_response_code(404);
if (App::request()->isAjax()) {
return App::json(['message' => '404 page not found', 'status' => 404]);
} else {
echo '404 page not found';
}
});
Yönlendirme
App::get('/', function(){
App::redirect('homepage');
});
App::get('/home', function(){
echo 'welcome to homepage';
}, 'homepage');
Parametreli yönlendirme:
App::get('/post/:id', 'PostController:show', 'show.post');
App::redirect('show.post', 5);
URL Oluşturma
DemirApp ile isimlendirilmiş rotalar için URL oluşturulabilir.
App::url('homepage'); // '/'
App::get('/:slug/:slug', 'show_function', 'show.post');
App::url('show.post', 'category-slug', 'post-slug');
// '/category-slug/post-slug'
// diğer bir örnek:
App::post('/comment/save/:id', 'save_comment', 'save.comment');
App::url('save.comment', 5); // '/comment/save/5'
Görünüm (View) İşlemleri
DemirApp’te hali hazırda bir Görünüm sınıfı bulunur. Bu sınıfla görünüm dosyaları yorumlanıp, bloklar oluşturulup görüntülenebilir. Basit bir kullanıma sahiptir. Öncelikle bir view dosyasını nasıl yorumlarız ona bakalım.
App::get('/post/show/:id', function($id){
$post = Post::find($id);
App::render('post/show', ['post' => $post]);
});
Burada bir gönderi görüntüleme senaryosu hazırladık. $post
değişkeni içerisinde bulunan gönderi değerlerini post/show.php
dosyasına gönderdik. render
yani yorumlamanın birden fazla yöntemi vardır:
echo App::view('post/show')->with('post', $post);
echo App::view('home')->render();
App::view('home')->show();
App::view('home')->with('name', 'Yılmaz')->show();
Temel Görünüm Ekleme ve Kullanma
Görünüm dosyalarını kullanmadan evvel, bu dosyaların hangi dizinde bulunduğunu belirlemeniz gerekiyor. Bunun için sınıf içerisinde bulunan container metodundan faydalanacağız.
App::container(['views.path' => __DIR__ . '/views/dosyalarinin/dizini']);
Artık görünüm dosyalarının bulunacağı dizini işaretledik. App::render('deneme')
ifadesi, views/dosyalarinin/dizini/deneme.php
dosyasını yorumlayacaktır.
Blok/Bileşen Tanımalama ve Kullanma
DemirApp ile temel görünüm dosyası oluşturup diğer görünüm dosyalarınızda bunu rahatlıkla kullanabilirsiniz.
views/page-layout.php:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title><?= $this->has('title') ? $this->get('title') : 'DemirApp' ?></title>
</head>
<body>
<header>Header</header>
<main><?= $this->get('content') ?></main>
<footer>Footer</footer>
<?= $this->get('scripts') ?>
</body>
</html>
has
metodu, blok değerinin tanımlanıp tanımlanmadığını kontrol eder. get
metodu tanımlanan bloğu döndürür. Tanımlama yapmak için örnekleri görelim:
views/home.php:
<?php $this->layout('page-layout') ?>
<?php $this->set('title', 'Ana Sayfa') ?>
<p>Sayfa içeriği buraya gelecek</p>
<?php $this->start('scripts') ?>
<script>
console.log('javascript içeriği')
</script>
<?php $this->end() ?>
Burada evvela home.php
‘nin hangi temel görünümü kullandığını layout
metodu ile belirledik. set
metodu ile, title
değerini tanımladık. Sonrasında start
ve end
metodlarıyla scripts
bloğunu tanımladık.. set
ve start
metodları arasında ince bir fark vardır. set
metodu, ikinci parametresine ne verilirse onu alır. Ama start
metodu ile başlatıp end
metodu ile bitirdiğimizin arasına ne koyarsak blok içeriği o olur. Bunu çıktı tamponlaması ile yapar (ob_start
).
Çıktı tamponlaması yaparak belirlenen blok varsayılan olarak varsa eski blok içeriğine iliştirilir (append). Eğer iliştirmek istemiyorsanız yani üzerine yazmak istiyorsanız end
metodunun ilk parametresine false
değeri verebilirsiniz. Örn: $this->end(false);
set
metodunu varsayılan olarak değeri üzerine yazar. Yazmasını istemiyorsanız üçüncü parametresine true
değeri verebilirsiniz. $this->set('title', 'Deneme', true);
Yardımcı Fonksiyonlar
Eğer DemirApp’i composer aracılığıyla kullanıyorsanız, otomatik olarak yüklenen yardımcı fonksiyonları kullanabilirsiniz. Eğer manuel olarak kullanıyorsanız kaynak dosyaları içindeki helpers.php
dosyasını sayfaya dahil etmelisiniz. Aşağıda genel kullanım örnekleri mevcuttur.
session()->start();
session(['is_logged_in' => false]);
session('is_logged_in');
container()->set('views.path', __DIR__ . '/views');
app()->get('/', function(){
render('home');
});
// app() fonksiyonu DemirApp örneğini döndürür
app()->notFound(function(){
if (request()->isAjax()) {
return json(['status' => 404]);
} else {
return view('errors/notFound')->show();
}
});
app()->post('/save', function(){
if (empty(request()->post('title'))) {
flash()->error('Başlık alanı gereklidir');
redirect('post.create');
}
});
// URL oluşturma
// url('post.show', 5);
run();
Soruları aşağıdaki yorum bölümünden sorarsanız severek cevaplarım. Umarım işinize yarar. Sevgiler.