SmartObject

SmartObject nesnelerin davranışını birçok yönden düzeltirdi, ancak günümüz PHP'si bu iyileştirmelerin çoğunu zaten yerel olarak içermektedir. Bununla birlikte, hala property için destek ekler.

Kurulum:

composer require nette/utils

Özellikler, Getiriciler ve Ayarlayıcılar

Modern nesne yönelimli dillerde (örneğin C#, Python, Ruby, JavaScript) property terimi, sınıfların değişken gibi görünen ancak aslında yöntemlerle temsil edilen özel üyelerini ifade eder. Bu “değişkenin” değeri atandığında veya okunduğunda, ilgili yöntem (getter veya setter olarak adlandırılır) çağrılır. Bu çok kullanışlı bir şeydir, bize değişkenlere erişim üzerinde tam kontrol sağlar. Girdiyi doğrulayabilir veya yalnızca özellik okunduğunda sonuç üretebiliriz.

PHP özellikleri desteklenmez, ancak trait Nette\SmartObject bunları taklit edebilir. Nasıl kullanılır?

  • Sınıfa şu biçimde bir ek açıklama ekleyin @property <type> $xyz
  • getXyz() veya isXyz() adında bir getter, adında bir setter oluşturun. setXyz()
  • Getter ve setter public veya protected olmalıdır ve isteğe bağlıdır, bu nedenle read-only veya write-only özelliği olabilir

$radius değişkenine yalnızca negatif olmayan sayıların girilmesini sağlamak için Circle sınıfının özelliğini kullanacağız. public $radius öğesini özellik ile değiştirin:

/**
 * @property float $radius
 * @property-read bool $visible
 */
class Circle
{
	use Nette\SmartObject;

	private float $radius = 0.0; // herkese açık değil

	//radius özelliği için getter
	protected function getRadius(): float
	{
		return $this->radius;
	}

	//radius özelliği için setter
	protected function setRadius(float $radius): void
	{
		// kaydetmeden önce değeri sanitize etme
		$this->radius = max(0.0, $radius);
	}

	// özellik için getter $visible
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10; // aslında setRadius(10) çağırır
echo $circle->radius; // getRadius() işlevini çağırır
echo $circle->visible; // isVisible() işlevini çağırır

Özellikler öncelikle sözdizimsel şeker olup, kodu basitleştirerek programcının hayatını daha tatlı hale getirmeyi amaçlar. Eğer onları istemiyorsanız, kullanmak zorunda değilsiniz.

Tarihe Bir Bakış

SmartObject, nesnelerin davranışlarını çeşitli şekillerde iyileştirmek için kullanılırdı, ancak bugünün PHP'si bu geliştirmelerin çoğunu zaten yerel olarak içeriyor. Aşağıdaki metin geçmişe nostaljik bir bakıştır ve bize işlerin nasıl geliştiğini hatırlatır.

PHP'nin nesne modeli, başlangıcından itibaren sayısız ciddi eksiklik ve yetersizlikten muzdaripti. Bu durum, bu sorunları gidermeyi ve PHP'nin kullanım rahatlığını artırmayı amaçlayan Nette\Object sınıfının (2007'de) oluşturulmasına yol açtı. İhtiyaç duyulan tek şey diğer sınıfların bu sınıftan miras almasıydı ve onlar da bu sınıfın sunduğu avantajlardan yararlanabileceklerdi. PHP 5.4 özellikler için destek sunduğunda, Nette\Object sınıfının yerini Nette\SmartObject özelliği aldı. Bu, ortak bir atadan miras alma ihtiyacını ortadan kaldırdı. Dahası, özellik zaten başka bir sınıftan miras alan sınıflarda kullanılabiliyordu. Nette\Object 'un kesin sonu, sınıfların Object olarak adlandırılmasını yasaklayan PHP 7.2'nin yayınlanmasıyla geldi.

PHP'nin gelişimi devam ettikçe, nesne modeli ve dil yetenekleri de gelişti. SmartObject sınıfının çeşitli işlevleri gereksiz hale geldi. PHP 8.2'nin yayınlanmasından bu yana, PHP'de doğrudan desteklenmeyen tek bir özellik kaldı: sözde özellikleri kullanma yeteneği.

Nette\Object ve buna bağlı olarak Nette\SmartObject hangi özellikleri sunuyordu? İşte genel bir bakış. (Örneklerde Nette\Object sınıfı kullanılmıştır, ancak çoğu özellik Nette\SmartObject özelliği için de geçerlidir).

Tutarsız Hatalar

PHP, bildirilmemiş üyelere erişirken tutarsız davranışlar sergiliyordu. Nette\Object adresinin durumu aşağıdaki gibiydi:

echo $obj->undeclared; // E_NOTICE, daha sonra E_WARNING
$obj->undeclared = 1; // raporlama yapmadan sessizce geçer
$obj->unknownMethod(); // Ölümcül hata (try/catch ile yakalanamaz)

Ölümcül hata, herhangi bir tepki verme imkanı olmadan uygulamayı sonlandırdı. Var olmayan üyelere uyarı vermeden sessizce yazmak, tespit edilmesi zor ciddi hatalara yol açabilirdi. Nette\Object Tüm bu durumlar yakalandı ve bir istisna MemberAccessException atıldı.

echo $obj->undeclared;   // throw Nette\MemberAccessException
$obj->undeclared = 1;    // throw Nette\MemberAccessException
$obj->unknownMethod();   // throw Nette\MemberAccessException

PHP 7.0'dan beri PHP artık yakalanamayan ölümcül hatalara neden olmamaktadır ve bildirilmemiş üyelere erişim PHP 8.2'den beri bir hatadır.

Ne demek istiyorsun?

Bir nesne değişkenine erişirken veya bir yöntemi çağırırken yapılan yazım hatası nedeniyle Nette\MemberAccessException hatası atıldığında, Nette\Object hata mesajında hatanın nasıl düzeltileceğine dair ikonik “demek istediniz mi?” eki şeklinde bir ipucu vermeye çalışmıştır.

class Foo extends Nette\Object
{
	public static function from($var)
	{
	}
}

$foo = Foo::form($var);
// throw Nette\MemberAccessException
// "Call to undefined static method Foo::form(), did you mean from()?"

Günümüz PHP'sinde “demek istediniz mi?” özelliği olmasa da, bu ifade Tracy tarafından hatalara eklenebilir. Hatta bu tür hataları otomatik olarak düzeltebilir.

Uzatma yöntemleri

C#'ın uzantı yöntemlerinden esinlenilmiştir. Mevcut sınıflara yeni yöntemler ekleme imkanı verdiler. Örneğin, kendi DateTimePicker'ınızı eklemek için bir forma addDateTime() yöntemini ekleyebilirsiniz.

Form::extensionMethod(
	'addDateTime',
	fn(Form $form, string $name) => $form[$name] = new DateTimePicker,
);

$form = new Form;
$form->addDateTime('date');

Uzatma yöntemlerinin pratik olmadığı kanıtlandı çünkü isimleri editörler tarafından otomatik olarak tamamlanmadı, bunun yerine yöntemin mevcut olmadığını bildirdiler. Bu nedenle destekleri kesilmiştir.

Sınıf Adını Alma

$class = $obj->getClass(); // using Nette\Object
$class = $obj::class; // PHP 8.0'dan beri

Yansıma ve Ek Açıklamalara Erişim

Nette\Object getReflection() ve getAnnotation() yöntemlerini kullanarak yansıtma ve ek açıklamaya erişim sundu:

/**
 * @author John Doe
 */
class Foo extends Nette\Object
{
}

$obj = new Foo;
$reflection = $obj->getReflection();
$reflection->getAnnotation('author'); // 'John Doe' döndürür

PHP 8.0'dan itibaren meta-bilgilere öznitelikler şeklinde erişmek mümkündür:

#[Author('John Doe')]
class Foo
{
}

$obj = new Foo;
$reflection = new ReflectionObject($obj);
$reflection->getAttributes(Author::class)[0];

Yöntem Getiriciler

Nette\Object metotları değişkenlermiş gibi ele almak için zarif bir yol sundu:

class Foo extends Nette\Object
{
	public function adder($a, $b)
	{
		return $a + $b;
	}
}

$obj = new Foo;
$method = $obj->adder;
echo $method(2, 3); // 5

PHP 8.1'den itibaren, birinci sınıf çağrılabilir sözdizimi olarak adlandırılan sözdizimini:https://www.php.net/…lable_syntax kullanabilirsiniz:

$obj = new Foo;
$method = $obj->adder(...);
echo $method(2, 3); // 5

Etkinlikler

Nette\Object olayı tetiklemek için sözdizimsel şeker sundu:

class Circle extends Nette\Object
{
	public array $onChange = [];

	public function setRadius(float $radius): void
	{
		$this->onChange($this, $radius);
		$this->radius = $radius;
	}
}

$this->onChange($this, $radius) kodu aşağıdakine eşdeğerdir:

foreach ($this->onChange as $callback) {
	$callback($this, $radius);
}

Netlik açısından sihirli yöntemden kaçınmanızı öneririz $this->onChange(). Pratik bir ikame Nette\Utils\Arrays::invoke fonksiyonudur:

Nette\Utils\Arrays::invoke($this->onChange, $this, $radius);
versiyon: 4.0