PHP'de Namespace Kullanımı

PHP
PHP ile ilgili yazılmış kaynakların birçoğu ne yazık ki modern standartların çok gerisinde ve herhangi bir arama motoru sorgusunda en üstte çıkarak birçok yeni kullanıcının gelişimini olumsuz etkilemekte. Daha da kötüsü, Türkçe kaynak sayısı az olduğu gibi, daha büyük ve geri dönülemeyecek hatalara sebebiyet verebilecek yanlış yönlendirmelerle dolu. Doğal olarak da bu durum ülkemizde birçok insanın PHP konusunda olumsuz düşüncelere sahip olmasında büyük bir etken oluyor.

Josh Lockhart'ın Modern PHP kitabı şu ana kadar gördüğüm en sade ve net anlatıma sahip, PHP'yi doğru kullanmanız için sizi en iyi yönlendiren kitap. Eğer İngilizceniz yeterliyse bu kitabı okumanızı öneririm. Ben ise sırasıyla, onun anlatımına benzer şekilde, bu yazıda namespace, diğer yazılarımda da sırasıyla interface, trait, generator ve closure kavramlarını açıklayıp, neden ve nasıl kullanmanız gerektiğini anlatacağım.

Namespace, sanal bir hiyerarşide, dosyalarınızın organizasyonunu sağlayabildiğiniz düzendir. Genellikle verilen en klişe örnek ise namespace kullanmadan aynı isme sahip iki farklı class oluşturup, kullanmayacağınız yönündedir. Bir örnekle bu durumu açıklayacak olursam, kullanıcılarınızı yönetmek için User ismine sahip bir class(sınıf) oluşturduğunuzu düşünün. Daha sonrasında sisteminizden ayrı, üçüncü parti, içerisinde birçok class bulunan bir alışveriş sepeti paketi entegre etmek istediniz. Eğer bu alışveriş sepeti paketinin içerisinde de User adına sahip bir başka class varsa, PHP aynı isme sahip iki class'ınız olamayacağı konusunda sizi uyaracaktır. Bunun sebebi de, namespace belirtilmediği sürece, her iki class'ın da aynı global namespace içerisinde olmasından ötürü bir aynı niteliklere sahip olduğunu düşünmesinden kaynaklanmaktadır.

PHP 5.3 öncesinde namespace kavramı olmadığı için, birçok kullanıcı kısmi olarak class isimlerini alt çizgi ile ayırarak namespace belirtmekteydi. Örneğin benim çıkartmış olduğum bir CMS paketi olsun ve bu paketin içerisindeki Models klasöründe de User class'ı olsun. Bu çakışmaları önleyebilmek adına örnek olarak bu class'ı alttaki gibi oluşturmam gerekecekti.
<?php

class Burak_CMS_Models_User{}
Artık, namespace kullanımı ile birlikte, bunu alttaki gibi belirtmem yeterli.
<?php

namespace Burak\CMS\Models;

class User{}
Üstteki namespace kullanımını sadece yazıya dökerek anlatacak olursam, aslında Burak'ın(vendor yani yayınlayıcı, genellikle kişi, marka veya organizasyon belirtmek için kullanılır) CMS paketinin içerisindeki Models klasöründe User adında bir class'ın olduğunu sanal bir hiyerarşide belirtmiş oluyorsunuz. Örneğin bu class'ı Controllers klasöründe yer alan UserController classınızda kullanmak istediğinizi düşünün.

Biraz önceki namespace'inizi kullanarak User modelinizi iki farklı şekilde kullanabilirsiniz.

Global referansıyla User modelinize ulaşıp kullanmak isterseniz alttaki gibi bir kullanımı tercih edebilirsiniz.
<?php

namespace Burak\CMS\Controllers;

class UserController
{

public function get($id)
{
$user = new \Burak\CMS\Models\User($id);
// başka işlemler
}

public function delete($id)
{
$user = new \Burak\CMS\Models\User($id);
// başka işlemler
}

}
Ancak daha pratik olan kullanım ise User modelinizi direkt olarak global scope'ta(alanda) import yani dahil etmeniz üzerine kurulu.
<?php

namespace Burak\CMS\Controllers;

use Burak\CMS\Models\User;

class UserController
{

public function get($id)
{
$user = new User($id);
// başka işlemler
}

public function delete($id)
{
$user = new User($id);
// başka işlemler
}

}
Daha az karakter kullanmak konusunda takıntılarınız varsa, User yerine U alias'ını(takma ad) kullanarak da User modelinize aynı şekilde erişebilirsiniz.
<?php

namespace Burak\CMS\Controllers;

use Burak\CMS\Models\User as U; // Bu örnektekinin aksine U aliasını belirtmezseniz, PHP bunu yandaki gibi algılar => use Burak\CMS\Models\User as User;

class UserController
{

public function get($id)
{
$user = new U($id);
// başka işlemler
}

public function delete($id)
{
$user = new U($id);
// başka işlemler
}

}
Gelelim namespace'i nerede belirtmeniz gerektiğine.

Namespace belirtirken direkt olarak <?php tag'inden(etiket) sonra belirtmelisiniz. Burası global scope'ta(alan) olduğu için en başta yer alması gereken \ işaretini kullanmadan class'ınızı kullanabilirsiniz.

İlk ve ikinci örneği tekrar inceleyelim.

İlk örnekte, get fonksiyonunun içerisi, global scope'ta yer almadığı için, bold(koyu) olan kısımda, en başta kullandığım işaretine dikkat edin.
<?php

namespace Burak\CMS\Controllers;

class UserController
{

public function get($id)
{
$user = new \Burak\CMS\Models\User($id);
// başka işlemler
}

}
İkinci örnekte ise direkt olarak use ile nasıl User class'ını kullandığımıza dikkat edin, direkt olarak global scope'ta belirttiğimiz için, PHP bunu başında \ varmış gibi algılandığından, referansı tamamen nitelenmiş olarak kabul ediyor ve derlenme zamanında bu tanımlama yapılıyor.
<?php

namespace Burak\CMS\Controllers;

use Burak\CMS\Models\User;

class UserController
{

public function get($id)
{
$user = new User($id);
// başka işlemler
}

}
Ancak bazı durumlarda global namespace içerisinde yer alan, örneğin PHP'nin Exception kütüphanesi, herhangi bir class'ı kullanırken, başta yer alması gereken \ işaretine dikkat etmeniz lazım.

Örneğin alttaki gibi bir class oluşturduğunuzu düşünün.
<?php

namespace Burak\CMS\Controllers;

use Burak\CMS\Models\User;

class UserController
{

public function login($credentials)
{
$user = new User($credentials)
if(!$user)
return new Exception();
}

}
Üstteki kod parçası eğer Controllers klasörünüz içerisinde Exception adında bir class'ınız yoksa size hata verecektir. Bunun sebebi, PHP'nin Controllers içerisindeki Exception class'ını arayacak olmasıdır. Bu yüzden PHP'nin kendi Exception class'ını kullanabilmeniz için, global scope'ta tanımlı olan Exception class'ı için gerekli olan referansı baştaki \ vermeniz gerekir.
<?php

namespace Burak\CMS\Controllers;

use Burak\CMS\Models\User;

class UserController
{

public function login($credentials)
{
$user = new User($credentials)
if(!$user)
return new \Exception();
}

}
Son bir not daha, üstteki gibi kullanımlarda autolader kullanmadığınız sürece PHP hangi class'ın nerede olduğunu aslında direkt olarak anlayamıyor. Use ile o class'ları kullandığınızı belirtmeniz o class'ları require veya include ile dahil etmediğiniz sürece fiziksel olarak bir referans sunmuyor. Ancak Composer kullanıyorsanız, PSR4 autoloading standartı sayesinde namespace'leriniz otomatik olarak, class'larınızın bulunduğu fiziksel lokasyonların referansını sunar ve bu şekilde tek bir autoloader dosyasını dahil ederek, her bir class'ı, tam referansını vererek dahil etmek zorunda kalmazsınız. Bu konuya bir başka yazıda tekrar değineceğim, o yüzden şimdilik kısa kesiyorum.

Sonuç olarak, modern standartları takip edebilmeniz, ayrıca büyük projelerdeki karmaşıklığını azaltmak ve olası çakışmalardan kurtulabilmeniz için, hem de kodların birbirleriyle olan bağlantısını, kısacası kohezyonunu, daha iyi görebilmeniz için namespace kullanımı büyük önem arz etmektedir.