PHP 8.0: Nitelikler (3/4)
PHP 8.0 sürümü yayınlandı. Daha önceki hiçbir sürümde olmadığı kadar yeniliklerle dolu. Tanıtımları tam dört ayrı makale gerektirdi. Bu üçüncüsünde niteliklere bakacağız.

Nitelikler, sınıflara ve tüm üyelerine, ayrıca fonksiyonlara, closure'lara ve parametrelerine yapılandırılmış meta verileri yazmak için tamamen yeni bir yol getiriyor. Bu amaçla şimdiye kadar phpDoc yorumları kullanılıyordu, ancak sözdizimleri her zaman o kadar serbest ve tutarsızdı ki, makine tarafından işlenmeye başlanması mümkün değildi. Bu nedenle, sabit sözdizimine ve yansıma (reflection) sınıflarında desteğe sahip niteliklerle değiştiriliyorlar.
Dolayısıyla, şimdiye kadar phpDoc yorumlarını ayrıştırarak meta veri
elde eden kütüphaneler, bunları niteliklerle değiştirebilecekler. Örneğin
Nette, en son Application ve DI sürümlerinde @persistent
,
@crossOrigin
ve @inject
ek açıklamaları yerine
Persistent
, CrossOrigin
ve Inject
niteliklerini zaten kullanabilirsiniz.
Ek açıklamaları kullanan kod:
/**
* @persistent(comp1, comp2)
*/
class SomePresenter
{
/** @persistent */
public $page = 1;
/** @inject */
public Facade $facade;
/**
* @crossOrigin
*/
public function handleSomething()
{
}
}
Nitelikleri kullanarak aynısı:
use Nette\Application\Attributes\CrossOrigin;
use Nette\Application\Attributes\Persistent;
use Nette\DI\Attributes\Inject;
#[Persistent('comp1', 'comp2')]
class SomePresenter
{
#[Persistent]
public int $page = 1;
#[Inject]
public Facade $facade;
#[CrossOrigin]
public function handleSomething()
{
}
}
PHP, nitelik adlarını sanki sınıflarmış gibi, yani isim alanı
(namespace) ve use
yan tümceleri bağlamında değerlendirir.
Dolayısıyla, örneğin şu şekilde de yazılabilirlerdi:
use Nette\Application\Attributes;
class SomePresenter
{
#[Attributes\Persistent]
public int $page = 1;
#[\Nette\DI\Attributes\Inject]
public Facade $facade;
Niteliği temsil eden sınıf var olabilir veya olmayabilir. Ancak var olması kesinlikle daha iyidir, çünkü o zaman editör yazarken ipucu verebilir, statik analizör yazım hatalarını tanıyabilir vb.
Sözdizimi
Şık olan şey, PHP 8'den önceki sürümlerin nitelikleri yalnızca yorum olarak görmesidir, bu nedenle eski sürümlerde çalışması gereken kodlarda bile kullanılabilirler.
Tek bir niteliğin yazımı, new
operatörünü atlarsak bir
nesne örneği oluşturmaya benzer. Yani, parantez içinde argümanların takip
edebileceği sınıf adı:
#[Column('string', 32, true, false)]#
protected $username;
Ve işte PHP 8.0'ın yeni sıcak özelliğinin kullanılabileceği yer – adlandırılmış argümanlar:
#[Column(
type: 'string',
length: 32,
unique: true,
nullable: false,
)]#
protected $username;
Her öğe, ayrı ayrı veya virgülle ayrılarak yazılabilen birden fazla niteliğe sahip olabilir:
#[Inject]
#[Lazy]
public Foo $foo;
#[Inject, Lazy]
public Bar $bar;
Aşağıdaki nitelik üç özelliğin tümü için geçerlidir:
#[Common]
private $a, $b, $c;
Özelliklerin varsayılan değerlerinde, derleme sırasında değerlendirilebilen basit ifadeler ve sabitler kullanılabilir ve aynısı niteliklerin argümanları için de geçerlidir:
#[
ScalarExpression(1 + 1),
ClassNameAndConstants(PDO::class, PHP_VERSION_ID),
BitShift(4 >> 1, 4 << 1),
BitLogic(1 | 2, JSON_HEX_TAG | JSON_HEX_APOS),
]
Maalesef, bir argümanın değeri başka bir nitelik olamaz, yani nitelikler iç içe geçirilemez. Örneğin, Doctrine'de kullanılan aşağıdaki ek açıklama doğrudan niteliklere dönüştürülemez:
/**
* @Table(name="products",uniqueConstraints={@UniqueConstraint(columns={"name", "email"})})
*/
Aynı şekilde, dosyanın başında bulunan ve örneğin Nette Tester tarafından kullanılan dosya phpDoc'u için bir nitelik eşdeğeri yoktur.
Nitelikleri Bulma
Hangi öğelerin hangi niteliklere sahip olduğunu yansıma (reflection)
kullanarak öğreniriz. Yansıma sınıfları, ReflectionAttribute
nesnelerinden oluşan bir dizi döndüren yeni bir getAttributes()
metoduna sahiptir.
use MyAttributes\Example;
#[Example('Hello', 123)]
class Foo
{}
$reflection = new ReflectionClass(Foo::class);
foreach ($reflection->getAttributes() as $attribute) {
$attribute->getName(); // tam nitelik adı, örn. MyAttributes\Example
$attribute->getArguments(); // ['Hello', 123]
$attribute->newInstance(); // new MyAttributes\Example('Hello', 123) örneğini döndürür
}
Döndürülen nitelikler bir parametre ile filtrelenebilir, örn.
$reflection->getAttributes(Example::class)
yalnızca
Example
niteliklerini döndürür.
Nitelik Sınıfları
MyAttributes\Example
nitelik sınıfının var olması gerekmez.
Yalnızca newInstance()
metodunun çağrılması varlığını
gerektirir çünkü onun örneğini oluşturur. Öyleyse onu yazalım. Tamamen
sıradan bir sınıf olacak, sadece ona Attribute
niteliğini (yani
global sistem isim alanından) belirtmemiz gerekiyor:
namespace MyAttributes;
use Attribute;
#[Attribute]
class Example
{
public function __construct(string $message, int $number)
{
...
}
}
Niteliğin hangi dil öğelerinde kullanılmasının yasal olacağını sınırlamak mümkündür. Örneğin, bu şekilde yalnızca sınıflarda ve özelliklerde:
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
class Example
{
...
}
TARGET_CLASS
, TARGET_FUNCTION
,
TARGET_METHOD
, TARGET_PROPERTY
,
TARGET_CLASS_CONSTANT
, TARGET_PARAMETER
bayrakları ve
varsayılan TARGET_ALL
mevcuttur.
Ancak dikkatli olun, doğru veya yanlış kullanımın doğrulanması
şaşırtıcı bir şekilde yalnızca newInstance()
metodu
çağrıldığında gerçekleşir. Derleyicinin kendisi bu kontrolü yapmaz.
Niteliklerle Gelecek
Nitelikler ve yeni
türler sayesinde, PHP dokümantasyon yorumları tarihlerinde ilk kez
gerçekten sadece belgesel yorumlar haline gelecektir. PhpStorm, örneğin
@deprecated
ek açıklamasının yerini alabilecek özel
nitelikler ile zaten geliyor. Ve bu niteliğin bir gün PHP'de varsayılan
olarak yer alacağı varsayılabilir. Benzer şekilde, @throws
vb.
gibi diğer ek açıklamalar da değiştirilecektir.
Nette, kalıcı parametreleri ve bileşenleri işaretlemek için ilk sürümünden beri ek açıklamalar kullanmasına rağmen, bunların daha yaygın kullanımı gerçekleşmedi çünkü yerel bir dil yapısı değillerdi, bu yüzden editörler onlara ipucu vermiyordu ve içlerinde hata yapmak kolaydı. Bu sorun bugün editör eklentileriyle çözülmüş olsa da, niteliklerin getirdiği gerçekten yerel yol tamamen yeni olanaklar açıyor.
Bu arada, nitelikler, sınıf adının özgüllüğe (örn.
Product
, InvalidValue
) ek olarak genelliği de (yani
ProductPresenter
, InvalidValueException
) içermesini
gerektiren Nette Kodlama Standardı'nda bir istisna kazandı. Aksi takdirde,
kodda kullanıldığında sınıfın tam olarak neyi temsil ettiği açık
olmazdı. Nitelikler için ise bu tam tersi istenmez, yani sınıf
InjectAttribute
yerine Inject
olarak
adlandırılır.
Son bölümde, PHP'de hangi yeni fonksiyonların ve sınıfların ortaya çıktığına bakacağız ve Just in Time Compiler'ı tanıtacağız.
Yorum göndermek için lütfen giriş yapın