Nette Documentation Preview

syntax
Nesne Yönelimli Programlamaya Giriş
***********************************

.[perex]
"OOP" terimi, kodu organize etmenin ve yapılandırmanın bir yolu olan Nesne Yönelimli Programlama anlamına gelir. OOP, bir programı bir dizi komut ve işlev yerine birbirleriyle iletişim kuran nesneler topluluğu olarak görmemizi sağlar.

OOP'de "nesne", veri ve bu veri üzerinde çalışan fonksiyonları içeren bir birimdir. Nesneler, nesneler için planlar veya şablonlar olarak anlaşılabilecek "sınıflar" temelinde oluşturulur. Bir sınıfa sahip olduğumuzda, o sınıftan yapılmış belirli bir nesne olan "örneğini" oluşturabiliriz.

PHP'de basit bir sınıfı nasıl oluşturabileceğimize bakalım. Bir sınıfı tanımlarken, "class" anahtar sözcüğünü, ardından sınıf adını ve ardından sınıfın işlevlerini ("yöntemler" olarak adlandırılır) ve sınıf değişkenlerini ("özellikler" veya "nitelikler" olarak adlandırılır) çevreleyen küme parantezlerini kullanırız:

```php
class Car
{
	function honk()
	{
		echo 'Beep beep!';
	}
}
```

Bu örnekte, `honk` adında bir işlevi (veya "yöntemi") olan `Car` adında bir sınıf oluşturduk.

Her sınıf sadece bir ana görevi çözmelidir. Bir sınıf çok fazla şey yapıyorsa, onu daha küçük, uzmanlaşmış sınıflara bölmek uygun olabilir.

Sınıflar, kodu düzenli tutmak ve gezinmeyi kolaylaştırmak için genellikle ayrı dosyalarda saklanır. Dosya adı sınıf adıyla eşleşmelidir, bu nedenle `Car` sınıfı için dosya adı `Car.php` olacaktır.

Sınıfları adlandırırken, "PascalCase" kuralını takip etmek iyidir, yani isimdeki her kelime büyük harfle başlar ve alt çizgi veya başka ayırıcılar yoktur. Yöntemler ve özellikler "camelCase" kuralını takip eder, yani küçük harfle başlarlar.

PHP'de bazı yöntemlerin özel rolleri vardır ve ön ekleri `__` (iki alt çizgi) ile gösterilir. En önemli özel yöntemlerden biri `__construct` olarak etiketlenen "kurucu "dur. Yapıcı, bir sınıfın yeni bir örneğini oluştururken otomatik olarak çağrılan bir yöntemdir.

Yapıcıyı genellikle bir nesnenin başlangıç durumunu ayarlamak için kullanırız. Örneğin, bir kişiyi temsil eden bir nesne oluştururken, yaşı, adı veya diğer nitelikleri ayarlamak için kurucuyu kullanabilirsiniz.

PHP'de bir yapıcının nasıl kullanılacağını görelim:

```php
class Person
{
	private $age;

	function __construct($age)
	{
		$this->age = $age;
	}

	function howOldAreYou()
	{
		return $this->age;
	}
}

$person = new Person(25);
echo $person->howOldAreYou(); // Outputs: 25
```

Bu örnekte, `Person` sınıfının bir `$age` özelliği ve bu özelliği ayarlayan bir kurucusu vardır. Daha sonra `howOldAreYou()` yöntemi kişinin yaşına erişim sağlar.

 `new` anahtar sözcüğü bir sınıfın yeni bir örneğini oluşturmak için kullanılır. Yukarıdaki örnekte, 25 yaşında yeni bir kişi oluşturduk.

Bir nesne oluştururken belirtilmemişlerse, kurucu parametreleri için varsayılan değerler de ayarlayabilirsiniz. Örneğin:

```php
class Person
{
	private $age;

	function __construct($age = 20)
	{
		$this->age = $age;
	}

	function howOldAreYou()
	{
		return $this->age;
	}
}

$person = new Person;  // if no argument is passed, parentheses can be omitted
echo $person->howOldAreYou(); // Outputs: 20
```

Bu örnekte, bir `Person` nesnesi oluştururken bir yaş belirtmezseniz, varsayılan değer olan 20 kullanılacaktır.

Son olarak, yapıcı aracılığıyla başlatılmasıyla birlikte özellik tanımı şu şekilde kısaltılabilir ve basitleştirilebilir:

```php
class Person
{
	function __construct(
		private $age = 20,
	) {
	}
}
```


İsim Alanları .[#toc-namespaces]
--------------------------------

Ad alanları, adlandırma çakışmalarını önlerken ilgili sınıfları, işlevleri ve sabitleri düzenlememize ve gruplandırmamıza olanak tanır. Bunları, her klasörün belirli bir proje veya konuyla ilgili dosyaları içerdiği bir bilgisayardaki klasörler gibi düşünebilirsiniz.

Ad alanları özellikle büyük projelerde veya sınıf adlandırma çakışmalarının ortaya çıkabileceği üçüncü taraf kütüphaneleri kullanırken kullanışlıdır.

Projenizde `Car` adında bir sınıfınız olduğunu ve bunu `Transport` adında bir ad alanına yerleştirmek istediğinizi düşünün. Bunu şu şekilde yaparsınız:

```php
namespace Transport;

class Car
{
	function honk()
	{
		echo 'Beep beep!';
	}
}
```

 `Car` sınıfını başka bir dosyada kullanmak istiyorsanız, sınıfın hangi ad alanından geldiğini belirtmeniz gerekir:

```php
$car = new Transport\Car;
```

Basitleştirmek için, dosyanın başında belirli bir ad alanından hangi sınıfı kullanmak istediğinizi belirtebilir, böylece tam yolu belirtmeden örnekler oluşturabilirsiniz:

```php
use Transport\Car;

$car = new Car;
```


Kalıtım .[#toc-inheritance]
---------------------------

Kalıtım, mevcut sınıflara dayalı olarak yeni sınıfların oluşturulmasına, özelliklerinin ve yöntemlerinin miras alınmasına ve gerektiğinde genişletilmesine veya yeniden tanımlanmasına olanak tanıyan bir nesne yönelimli programlama aracıdır. Kalıtım, kodun yeniden kullanılabilirliğini ve sınıf hiyerarşisini sağlar.

Basitçe söylemek gerekirse, bir sınıfımız varsa ve ondan türetilmiş ancak bazı değişikliklerle başka bir sınıf oluşturmak istiyorsak, yeni sınıfı orijinal sınıftan "miras" alabiliriz.

PHP'de kalıtım `extends` anahtar sözcüğü kullanılarak gerçekleştirilir.

 `Person` sınıfımız yaş bilgisini saklar. `Person` 'i genişleten ve çalışma alanı hakkında bilgi ekleyen `Student` adlı başka bir sınıfımız olabilir.

Bir örneğe bakalım:

```php
class Person
{
	private $age;

	function __construct($age)
	{
		$this->age = $age;
	}

	function howOldAreYou()
	{
		return $this->age;
	}
}

class Student extends Person
{
	private $fieldOfStudy;

	function __construct($age, $fieldOfStudy)
	{
		parent::__construct($age);
		$this->fieldOfStudy = $fieldOfStudy;
	}

	function printInformation()
	{
		echo 'Age of student: ', $this->howOldAreYou();
		echo 'Field of study: ', $this->fieldOfStudy;
	}
}

$student = new Student(20, 'Computer Science');
$student->printInformation();
```

Bu kod nasıl çalışıyor?

- `Person` sınıfını genişletmek için `extends` anahtar sözcüğünü kullandık, yani `Student` sınıfı tüm yöntemleri ve özellikleri `Person` sınıfından devralır.

- `parent::` anahtar sözcüğü, üst sınıftaki yöntemleri çağırmamızı sağlar. Bu durumda, `Student` sınıfına kendi işlevselliğimizi eklemeden önce `Person` sınıfından kurucuyu çağırdık.

Kalıtım, sınıflar arasında "is a" ilişkisinin olduğu durumlar içindir. Örneğin, bir `Student` bir `Person`. Bir kedi bir hayvandır. Kodda bir nesne (örn. "Kişi") beklediğimiz durumlarda bunun yerine türetilmiş bir nesne (örn. "Öğrenci") kullanmamızı sağlar.

Kalıtımın birincil amacının **kod tekrarını önlemek** olmadığının farkına varmak çok önemlidir. Aksine, kalıtımın yanlış kullanımı karmaşık ve bakımı zor kodlara yol açabilir. Sınıflar arasında "is a" ilişkisi yoksa, kalıtım yerine bileşimi düşünmeliyiz.


Kompozisyon .[#toc-composition]
-------------------------------

Kompozisyon, başka bir sınıftan özellikleri ve yöntemleri miras almak yerine, sınıfımızda onun örneğini kullandığımız bir tekniktir. Bu, karmaşık kalıtım yapıları oluşturmadan birden fazla sınıfın işlevlerini ve özelliklerini birleştirmemizi sağlar.

Örneğin, bir `Engine` sınıfımız ve bir `Car` sınıfımız var. "Bir araba bir motordur" demek yerine, tipik bir bileşim ilişkisi olan "Bir arabanın motoru vardır" deriz.

```php
class Engine
{
	function start()
	{
		echo "Engine is running.";
	}
}

class Car
{
	private $engine;

	function __construct()
	{
		$this->engine = new Engine;
	}

	function start()
	{
		$this->engine->start();
		echo "The car is ready to drive!";
	}
}

$car = new Car;
$car->start();
```

Burada, `Car`, `Engine`'un tüm özelliklerine ve yöntemlerine sahip değildir, ancak `$engine` özelliği aracılığıyla bunlara erişebilir.

Kompozisyonun avantajı, daha fazla tasarım esnekliği ve gelecekteki değişiklikler için daha iyi uyarlanabilirliktir.


Görünürlük .[#toc-visibility]
-----------------------------

PHP'de sınıf özellikleri, yöntemleri ve sabitleri için "görünürlük" tanımlayabilirsiniz. Görünürlük, bu öğelere nereden erişebileceğinizi belirler.

1. **Public:** Eğer bir eleman `public` olarak işaretlenmişse, bu ona her yerden, hatta sınıf dışından bile erişebileceğiniz anlamına gelir.

2. **Korumalı:** `protected` olarak işaretlenmiş bir öğeye yalnızca sınıf ve tüm torunları (ondan miras alan sınıflar) içinde erişilebilir.

3. **Özel:** Bir eleman `private` ise, ona yalnızca tanımlandığı sınıf içinden erişebilirsiniz.

Görünürlük belirtmezseniz, PHP bunu otomatik olarak `public` olarak ayarlayacaktır.

Örnek bir koda bakalım:

```php
class VisibilityExample
{
	public $publicProperty = 'Public';
	protected $protectedProperty = 'Protected';
	private $privateProperty = 'Private';

	public function printProperties()
	{
		echo $this->publicProperty;     // Works
		echo $this->protectedProperty;  // Works
		echo $this->privateProperty;    // Works
	}
}

$object = new VisibilityExample;
$object->printProperties();
echo $object->publicProperty;        // Works
// echo $object->protectedProperty;   // Throws an error
// echo $object->privateProperty;     // Throws an error
```

Sınıf kalıtımına devam ediyoruz:

```php
class ChildClass extends VisibilityExample
{
	public function printProperties()
	{
		echo $this->publicProperty;     // Works
		echo $this->protectedProperty;  // Works
		// echo $this->privateProperty;   // Throws an error
	}
}
```

Bu durumda, `ChildClass` adresindeki `printProperties()` yöntemi public ve protected özelliklere erişebilir ancak ana sınıfın private özelliklerine erişemez.

Veri ve yöntemler mümkün olduğunca gizli olmalı ve sadece tanımlanmış bir arayüz üzerinden erişilebilir olmalıdır. Bu, kodun geri kalanını etkilemeden sınıfın dahili uygulamasını değiştirmenize olanak tanır.


Son Anahtar Kelime .[#toc-final-keyword]
----------------------------------------

PHP'de, bir sınıfın, yöntemin veya sabitin miras alınmasını veya geçersiz kılınmasını önlemek istiyorsak `final` anahtar sözcüğünü kullanabiliriz. Bir sınıf `final` olarak işaretlendiğinde genişletilemez. Bir yöntem `final` olarak işaretlendiğinde, bir alt sınıfta geçersiz kılınamaz.

Belirli bir sınıf veya yöntemin artık değiştirilmeyeceğinin farkında olmak, olası çakışmalar konusunda endişelenmeden daha kolay değişiklik yapmamızı sağlar. Örneğin, bir alt sınıfın zaten aynı isimde bir metoda sahip olmasından ve bunun bir çakışmaya yol açmasından korkmadan yeni bir metot ekleyebiliriz. Ya da bir yöntemin parametrelerini, yine alttaki bir yöntemde geçersiz kılınmış bir yöntemle tutarsızlığa neden olma riski olmadan değiştirebiliriz.

```php
final class FinalClass
{
}

// The following code will throw an error because we cannot inherit from a final class.
class ChildOfFinalClass extends FinalClass
{
}
```

Bu örnekte, `FinalClass` nihai sınıfından miras almaya çalışmak bir hatayla sonuçlanacaktır.


Statik Özellikler ve Yöntemler .[#toc-static-properties-and-methods]
--------------------------------------------------------------------

PHP'de bir sınıfın "statik" öğelerinden bahsettiğimizde, sınıfın belirli bir örneğine değil, sınıfın kendisine ait olan yöntemleri ve özellikleri kastediyoruz. Bu, onlara erişmek için sınıfın bir örneğini oluşturmanız gerekmediği anlamına gelir. Bunun yerine, bunları doğrudan sınıf adı üzerinden çağırır veya bunlara erişirsiniz.

Statik öğeler örneklere değil sınıfa ait olduğundan, statik yöntemlerin içinde `$this` sözde değişkenini kullanamayacağınızı unutmayın.

Statik özelliklerin kullanılması [tuzaklarla dolu karmaşık kodlara |dependency-injection:global-state] yol açar, bu nedenle bunları asla kullanmamalısınız ve burada bir örnek göstermeyeceğiz. Öte yandan, statik yöntemler kullanışlıdır. İşte bir örnek:

```php
class Calculator
{
	public static function add($a, $b)
	{
		return $a + $b;
	}

	public static function subtract($a, $b)
	{
		return $a - $b;
	}
}

// Using the static method without creating an instance of the class
echo Calculator::add(5, 3); // Output: 8
echo Calculator::subtract(5, 3); // Output: 2
```

Bu örnekte, iki statik yöntemi olan bir `Calculator` sınıfı oluşturduk. Bu yöntemleri, `::` operatörünü kullanarak sınıfın bir örneğini oluşturmadan doğrudan çağırabiliriz. Statik yöntemler özellikle belirli bir sınıf örneğinin durumuna bağlı olmayan işlemler için kullanışlıdır.


Sınıf Sabitleri .[#toc-class-constants]
---------------------------------------

Sınıflar içinde sabitler tanımlama seçeneğimiz vardır. Sabitler, programın yürütülmesi sırasında asla değişmeyen değerlerdir. Değişkenlerin aksine, bir sabitin değeri aynı kalır.

```php
class Car
{
	public const NumberOfWheels = 4;

	public function displayNumberOfWheels(): int
	{
		echo self::NumberOfWheels;
	}
}

echo Car::NumberOfWheels;  // Output: 4
```

Bu örnekte, `NumberOfWheels` sabitine sahip bir `Car` sınıfımız var. Sınıf içindeki sabite erişirken, sınıf adı yerine `self` anahtar sözcüğünü kullanabiliriz.


Nesne Arayüzleri .[#toc-object-interfaces]
------------------------------------------

Nesne arayüzleri sınıflar için "sözleşme" görevi görür. Bir sınıf bir nesne arayüzünü uygulayacaksa, arayüzün tanımladığı tüm yöntemleri içermelidir. Bu, belirli sınıfların aynı "sözleşmeye" veya yapıya bağlı kalmasını sağlamanın harika bir yoludur.

PHP'de arayüzler `interface` anahtar sözcüğü kullanılarak tanımlanır. Bir arayüzde tanımlanan tüm yöntemler public'tir (`public`). Bir sınıf bir arayüzü uyguladığında, `implements` anahtar sözcüğünü kullanır.

```php
interface Animal
{
	function makeSound();
}

class Cat implements Animal
{
	public function makeSound()
	{
		echo 'Meow';
	}
}

$cat = new Cat;
$cat->makeSound();
```

Bir sınıf bir arayüzü uygular, ancak beklenen tüm yöntemler tanımlanmamışsa, PHP bir hata verir. Bir sınıf aynı anda birden fazla arayüzü uygulayabilir; bu, bir sınıfın yalnızca bir sınıftan miras alabildiği kalıtımdan farklıdır.


Soyut Sınıflar .[#toc-abstract-classes]
---------------------------------------

Soyut sınıflar diğer sınıflar için temel şablon görevi görür, ancak örneklerini doğrudan oluşturamazsınız. Tam yöntemler ve tanımlanmış bir içeriği olmayan soyut yöntemlerin bir karışımını içerirler. Soyut sınıflardan miras alan sınıflar, üst sınıftaki tüm soyut yöntemler için tanımlar sağlamalıdır.

Soyut bir sınıf tanımlamak için `abstract` anahtar sözcüğünü kullanırız.

```php
abstract class AbstractClass
{
	public function regularMethod()
	{
		echo 'This is a regular method';
	}

	abstract public function abstractMethod();
}

class Child extends AbstractClass
{
	public function abstractMethod()
	{
		echo 'This is the implementation of the abstract method';
	}
}

$instance = new Child;
$instance->regularMethod();
$instance->abstractMethod();
```

Bu örnekte, bir normal ve bir soyut yöntemi olan soyut bir sınıfımız var. Ardından, `AbstractClass` adresinden miras alan ve soyut yöntem için bir uygulama sağlayan bir `Child` sınıfımız var.


Tip Kontrolü .[#toc-type-checking]
----------------------------------

Programlamada, birlikte çalıştığımız verilerin doğru türde olduğundan emin olmak çok önemlidir. PHP'de bu güvenceyi sağlayan araçlarımız vardır. Verilerin doğru türde olduğunu doğrulamaya "tür denetimi" denir.

PHP'de karşılaşabileceğimiz türler:

1. **Temel tipler**: Bunlar `int` (tam sayılar), `float` (kayan noktalı sayılar), `bool` (boolean değerler), `string` (dizeler), `array` (diziler) ve `null` içerir.
2. **Sınıflar**: Bir değerin belirli bir sınıfın örneği olmasını istediğimizde.
3. **Arayüzler**: Bir sınıfın uygulaması gereken bir dizi yöntemi tanımlar. Bir arayüzü karşılayan bir değer bu yöntemlere sahip olmalıdır.
4. **Karışık tipler**: Bir değişkenin birden fazla izin verilen türe sahip olabileceğini belirtebiliriz.
5. **Void**: Bu özel tip, bir fonksiyon veya metodun herhangi bir değer döndürmediğini belirtir.

Türleri dahil etmek için kodu nasıl değiştireceğimizi görelim:

```php
class Person
{
	private int $age;

	public function __construct(int $age)
	{
		$this->age = $age;
	}

	public function printAge(): void
	{
		echo "This person is " . $this->age . " years old.";
	}
}

/**
 * A function that accepts a Person object and prints the person's age.
 */
function printPersonAge(Person $person): void
{
	$person->printAge();
}
```

Bu şekilde, kodumuzun doğru türdeki verileri beklediğinden ve bunlarla çalıştığından emin olarak olası hataları önlememize yardımcı oluruz.


Karşılaştırma ve Kimlik .[#toc-comparison-and-identity]
-------------------------------------------------------

PHP'de nesneleri iki şekilde karşılaştırabilirsiniz:

1. Değer karşılaştırması `==`: Nesnelerin aynı sınıftan olup olmadığını ve özelliklerinde aynı değerlere sahip olup olmadığını kontrol eder.
2. Kimlik `===`: Nesnenin aynı örneği olup olmadığını kontrol eder.

```php
class Car
{
	public string $brand;

	public function __construct(string $brand)
	{
		$this->brand = $brand;
	}
}

$car1 = new Car('Skoda');
$car2 = new Car('Skoda');
$car3 = $car1;

var_dump($car1 == $car2);   // true, because they have the same value
var_dump($car1 === $car2);  // false, because they are not the same instance
var_dump($car1 === $car3);  // true, because $car3 is the same instance as $car1
```



 `instanceof` Operatör .[#toc-the-instanceof-operator]
-------------------------

 `instanceof` operatörü, belirli bir nesnenin belirli bir sınıfın örneği olup olmadığını, bu sınıfın soyundan gelip gelmediğini veya belirli bir arayüzü uygulayıp uygulamadığını belirlemenizi sağlar.

Bir `Person` sınıfımız ve `Person` sınıfının soyundan gelen başka bir `Student` sınıfımız olduğunu düşünün:

```php
class Person
{
	private int $age;

	public function __construct(int $age)
	{
		$this->age = $age;
	}
}

class Student extends Person
{
	private string $major;

	public function __construct(int $age, string $major)
	{
		parent::__construct($age);
		$this->major = $major;
	}
}

$student = new Student(20, 'Computer Science');

// Check if $student is an instance of the Student class
var_dump($student instanceof Student);  // Output: bool(true)

// Check if $student is an instance of the Person class (because Student is a descendant of Person)
var_dump($student instanceof Person);   // Output: bool(true)
```

Çıktılardan, `$student` nesnesinin hem `Student` hem de `Person` sınıflarının bir örneği olarak kabul edildiği açıktır.


Akıcı Arayüzler .[#toc-fluent-interfaces]
-----------------------------------------

"Akıcı Arayüz", OOP'de yöntemlerin tek bir çağrıda bir araya getirilmesini sağlayan bir tekniktir. Bu genellikle kodu basitleştirir ve netleştirir.

Akıcı bir arayüzün temel unsuru, zincirdeki her yöntemin geçerli nesneye bir referans döndürmesidir. Bu, yöntemin sonunda `return $this;` kullanılarak gerçekleştirilir. Bu programlama tarzı genellikle bir nesnenin özelliklerinin değerlerini ayarlayan "setter" adı verilen yöntemlerle ilişkilendirilir.

E-posta göndermek için akıcı bir arayüzün nasıl görünebileceğini görelim:

```php
public function sendMessage()
{
	$email = new Email;
	$email->setFrom('sender@example.com')
		  ->setRecipient('admin@example.com')
		  ->setMessage('Hello, this is a message.')
		  ->send();
}
```

Bu örnekte, `setFrom()`, `setRecipient()`, ve `setMessage()` yöntemleri ilgili değerleri (gönderen, alıcı, mesaj içeriği) ayarlamak için kullanılır. Bu değerlerin her birini ayarladıktan sonra, yöntemler geçerli nesneyi (`$email`) döndürür, böylece ondan sonra başka bir yöntemi zincirlememize izin verir. Son olarak, e-postayı gerçekten gönderen `send()` yöntemini çağırıyoruz.

Akıcı arayüzler sayesinde sezgisel ve kolay okunabilir kodlar yazabiliyoruz.



 `clone` ile kopyalama .[#toc-copying-with-clone]
--------------------

PHP'de `clone` operatörünü kullanarak bir nesnenin kopyasını oluşturabiliriz. Bu şekilde, aynı içeriğe sahip yeni bir örnek elde ederiz.

Bir nesneyi kopyalarken bazı özelliklerini değiştirmemiz gerekiyorsa, sınıfta özel bir `__clone()` yöntemi tanımlayabiliriz. Nesne klonlandığında bu yöntem otomatik olarak çağrılır.

```php
class Sheep
{
	public string $name;

	public function __construct(string $name)
	{
		$this->name = $name;
	}

	public function __clone()
	{
		$this->name = 'Clone of ' . $this->name;
	}
}

$original = new Sheep('Dolly');
echo $original->name . "\n";  // Outputs: Dolly

$clone = clone $original;
echo $clone->name . "\n";     // Outputs: Clone of Dolly
```

Bu örnekte, bir özelliği `$name` olan bir `Sheep` sınıfımız var. Bu sınıfın bir örneğini klonladığımızda, `__clone()` yöntemi klonlanan koyunun adının "Clone of" ön ekini almasını sağlar.


Özellikler .[#toc-traits]
PHP'deki özellikler, sınıflar arasında yöntemlerin, özelliklerin ve sabitlerin paylaşılmasını sağlayan ve kod tekrarını önleyen bir araçtır. Bunları, bir özelliğin içeriğinin sınıflara "yapıştırıldığı" bir "kopyala ve yapıştır" mekanizması (Ctrl-C ve Ctrl-V) olarak düşünebilirsiniz. Bu, karmaşık sınıf hiyerarşileri oluşturmak zorunda kalmadan kodu yeniden kullanmanıza olanak tanır.

PHP'deki özellikler, sınıflar arasında yöntemlerin paylaşılmasını sağlayan ve kod tekrarını önleyen bir araçtır. Bunları, bir özelliğin içeriğinin sınıflara "yapıştırıldığı" bir "kopyala ve yapıştır" mekanizması (Ctrl-C ve Ctrl-V) olarak düşünebilirsiniz. Bu, karmaşık sınıf hiyerarşileri oluşturmak zorunda kalmadan kodu yeniden kullanmanıza olanak tanır.

PHP'de özelliklerin nasıl kullanılacağına dair basit bir örneğe göz atalım:

```php
trait Honking
{
	public function honk()
	{
		echo 'Beep beep!';
	}
}

class Car
{
	use Honking;
}

class Truck
{
	use Honking;
}

$car = new Car;
$car->honk(); // Outputs 'Beep beep!'

$truck = new Truck;
$truck->honk(); // Also outputs 'Beep beep!'
```

Bu örnekte, bir yöntem içeren `Honking` adlı bir özelliğimiz var `honk()`. Sonra iki sınıfımız var: `Car` ve `Truck`, her ikisi de `Honking` özelliğini kullanıyor. Sonuç olarak, her iki sınıf da `honk()` yöntemine "sahiptir" ve bu yöntemi her iki sınıfın nesneleri üzerinde çağırabiliriz.

Özellikler, sınıflar arasında kolay ve verimli bir şekilde kod paylaşımı yapmanızı sağlar. Kalıtım hiyerarşisine girmezler, yani `$car instanceof Honking`, `false` döndürür.


İstisnalar
----------


OOP'de İstisnalar .[#toc-exceptions-in-oop]
-------------------------------------------

OOP'deki istisnalar, kodumuzun yürütülmesi sırasında ortaya çıkabilecek hataları ele almamızı ve yönetmemizi sağlar. Bunlar esasen programınızdaki hataları veya beklenmedik durumları kaydetmek için tasarlanmış nesnelerdir.

PHP'de, bu nesneler için yerleşik `Exception` sınıfına sahibiz. Hata mesajı, dosya ve hatanın oluştuğu satır gibi istisna hakkında daha fazla bilgi almamızı sağlayan birkaç yöntemi vardır.

Bir sorun ortaya çıktığında, bir istisna "atabiliriz" ( `throw` kullanarak). Bu istisnayı "yakalamak" ve işlemek istiyorsak, `try` ve `catch` bloklarını kullanırız.

Nasıl çalıştığını görelim:

```php
try {
	throw new Exception('Message explaining the reason for the exception');

	// This code won't execute
	echo 'I am a message that nobody will read';

} catch (Exception $e) {
	echo 'Exception caught: '. $e->getMessage();
}
```

Bir istisnanın daha derinde, diğer yöntemlere çağrı sırasında atılabileceğine dikkat etmek önemlidir.

Bir `try` bloğu için, farklı türde istisnalar bekliyorsanız birden fazla `catch` bloğu belirtilebilir.

Her istisna sınıfının bir öncekinden miras aldığı bir istisna hiyerarşisi de oluşturabiliriz. Örnek olarak, para yatırma ve çekme işlemlerine izin veren basit bir bankacılık uygulaması düşünün:

```php
class BankingException extends Exception {}
class InsufficientFundsException extends BankingException {}
class ExceededLimitException extends BankingException {}

class BankAccount
{
	private int $balance = 0;
	private int $dailyLimit = 1000;

	public function deposit(int $amount): int
	{
		$this->balance += $amount;
		return $this->balance;
	}

	public function withdraw(int $amount): int
	{
		if ($amount > $this->balance) {
			throw new InsufficientFundsException('Not enough funds in the account.');
		}

		if ($amount > $this->dailyLimit) {
			throw new ExceededLimitException('Daily withdrawal limit exceeded.');
		}

		$this->balance -= $amount;
		return $this->balance;
	}
}

$account = new BankAccount;
$account->deposit(500);

try {
	$account->withdraw(1500);
} catch (ExceededLimitException $e) {
	echo $e->getMessage();
} catch (InsufficientFundsException $e) {
	echo $e->getMessage();
} catch (BankingException $e) {
	echo 'An error occurred during the operation.';
}
```


En İyi Uygulamalar .[#toc-best-practices]
-----------------------------------------

Nesne yönelimli programlamanın temel ilkelerini öğrendikten sonra, OOP'deki en iyi uygulamalara odaklanmak çok önemlidir. Bunlar yalnızca işlevsel değil aynı zamanda okunabilir, anlaşılabilir ve bakımı kolay kodlar yazmanıza yardımcı olacaktır.

1) **İlgilerin Ayrılması**: Her sınıfın açıkça tanımlanmış bir sorumluluğu olmalı ve yalnızca bir birincil görevi ele almalıdır. Bir sınıf çok fazla şey yapıyorsa, onu daha küçük, uzmanlaşmış sınıflara bölmek uygun olabilir.
2) **Kapsülleme**: Veri ve yöntemler mümkün olduğunca gizli olmalı ve yalnızca tanımlanmış bir arayüz aracılığıyla erişilebilir olmalıdır. Bu, kodun geri kalanını etkilemeden bir sınıfın dahili uygulamasını değiştirmenize olanak tanır.
3) **Dependency Injection**: Bağımlılıkları doğrudan bir sınıfın içinde oluşturmak yerine, onları dışarıdan "enjekte etmelisiniz". Bu prensibi daha iyi anlamak için [Bağımlılık Enjeksiyonu bölümünü |dependency-injection:introduction] okumanızı tavsiye ederiz.

Bu örnekte, `catch` bloklarının sırasına dikkat etmek önemlidir. Tüm istisnalar `BankingException` adresinden miras alındığından, bu bloğu ilk olarak kullansaydık, kod sonraki `catch` bloklarına ulaşmadan tüm istisnalar bu blokta yakalanırdı. Bu nedenle, daha spesifik istisnaların (yani diğerlerinden miras kalanların) `catch` blok sıralamasında ana istisnalardan daha yukarıda olması önemlidir.

Nesne Yönelimli Programlamaya Giriş

„OOP“ terimi, kodu organize etmenin ve yapılandırmanın bir yolu olan Nesne Yönelimli Programlama anlamına gelir. OOP, bir programı bir dizi komut ve işlev yerine birbirleriyle iletişim kuran nesneler topluluğu olarak görmemizi sağlar.

OOP'de „nesne“, veri ve bu veri üzerinde çalışan fonksiyonları içeren bir birimdir. Nesneler, nesneler için planlar veya şablonlar olarak anlaşılabilecek „sınıflar“ temelinde oluşturulur. Bir sınıfa sahip olduğumuzda, o sınıftan yapılmış belirli bir nesne olan „örneğini“ oluşturabiliriz.

PHP'de basit bir sınıfı nasıl oluşturabileceğimize bakalım. Bir sınıfı tanımlarken, „class“ anahtar sözcüğünü, ardından sınıf adını ve ardından sınıfın işlevlerini („yöntemler“ olarak adlandırılır) ve sınıf değişkenlerini („özellikler“ veya „nitelikler“ olarak adlandırılır) çevreleyen küme parantezlerini kullanırız:

class Car
{
	function honk()
	{
		echo 'Beep beep!';
	}
}

Bu örnekte, honk adında bir işlevi (veya „yöntemi“) olan Car adında bir sınıf oluşturduk.

Her sınıf sadece bir ana görevi çözmelidir. Bir sınıf çok fazla şey yapıyorsa, onu daha küçük, uzmanlaşmış sınıflara bölmek uygun olabilir.

Sınıflar, kodu düzenli tutmak ve gezinmeyi kolaylaştırmak için genellikle ayrı dosyalarda saklanır. Dosya adı sınıf adıyla eşleşmelidir, bu nedenle Car sınıfı için dosya adı Car.php olacaktır.

Sınıfları adlandırırken, „PascalCase“ kuralını takip etmek iyidir, yani isimdeki her kelime büyük harfle başlar ve alt çizgi veya başka ayırıcılar yoktur. Yöntemler ve özellikler „camelCase“ kuralını takip eder, yani küçük harfle başlarlar.

PHP'de bazı yöntemlerin özel rolleri vardır ve ön ekleri __ (iki alt çizgi) ile gösterilir. En önemli özel yöntemlerden biri __construct olarak etiketlenen "kurucu "dur. Yapıcı, bir sınıfın yeni bir örneğini oluştururken otomatik olarak çağrılan bir yöntemdir.

Yapıcıyı genellikle bir nesnenin başlangıç durumunu ayarlamak için kullanırız. Örneğin, bir kişiyi temsil eden bir nesne oluştururken, yaşı, adı veya diğer nitelikleri ayarlamak için kurucuyu kullanabilirsiniz.

PHP'de bir yapıcının nasıl kullanılacağını görelim:

class Person
{
	private $age;

	function __construct($age)
	{
		$this->age = $age;
	}

	function howOldAreYou()
	{
		return $this->age;
	}
}

$person = new Person(25);
echo $person->howOldAreYou(); // Outputs: 25

Bu örnekte, Person sınıfının bir $age özelliği ve bu özelliği ayarlayan bir kurucusu vardır. Daha sonra howOldAreYou() yöntemi kişinin yaşına erişim sağlar.

new anahtar sözcüğü bir sınıfın yeni bir örneğini oluşturmak için kullanılır. Yukarıdaki örnekte, 25 yaşında yeni bir kişi oluşturduk.

Bir nesne oluştururken belirtilmemişlerse, kurucu parametreleri için varsayılan değerler de ayarlayabilirsiniz. Örneğin:

class Person
{
	private $age;

	function __construct($age = 20)
	{
		$this->age = $age;
	}

	function howOldAreYou()
	{
		return $this->age;
	}
}

$person = new Person;  // if no argument is passed, parentheses can be omitted
echo $person->howOldAreYou(); // Outputs: 20

Bu örnekte, bir Person nesnesi oluştururken bir yaş belirtmezseniz, varsayılan değer olan 20 kullanılacaktır.

Son olarak, yapıcı aracılığıyla başlatılmasıyla birlikte özellik tanımı şu şekilde kısaltılabilir ve basitleştirilebilir:

class Person
{
	function __construct(
		private $age = 20,
	) {
	}
}

İsim Alanları

Ad alanları, adlandırma çakışmalarını önlerken ilgili sınıfları, işlevleri ve sabitleri düzenlememize ve gruplandırmamıza olanak tanır. Bunları, her klasörün belirli bir proje veya konuyla ilgili dosyaları içerdiği bir bilgisayardaki klasörler gibi düşünebilirsiniz.

Ad alanları özellikle büyük projelerde veya sınıf adlandırma çakışmalarının ortaya çıkabileceği üçüncü taraf kütüphaneleri kullanırken kullanışlıdır.

Projenizde Car adında bir sınıfınız olduğunu ve bunu Transport adında bir ad alanına yerleştirmek istediğinizi düşünün. Bunu şu şekilde yaparsınız:

namespace Transport;

class Car
{
	function honk()
	{
		echo 'Beep beep!';
	}
}

Car sınıfını başka bir dosyada kullanmak istiyorsanız, sınıfın hangi ad alanından geldiğini belirtmeniz gerekir:

$car = new Transport\Car;

Basitleştirmek için, dosyanın başında belirli bir ad alanından hangi sınıfı kullanmak istediğinizi belirtebilir, böylece tam yolu belirtmeden örnekler oluşturabilirsiniz:

use Transport\Car;

$car = new Car;

Kalıtım

Kalıtım, mevcut sınıflara dayalı olarak yeni sınıfların oluşturulmasına, özelliklerinin ve yöntemlerinin miras alınmasına ve gerektiğinde genişletilmesine veya yeniden tanımlanmasına olanak tanıyan bir nesne yönelimli programlama aracıdır. Kalıtım, kodun yeniden kullanılabilirliğini ve sınıf hiyerarşisini sağlar.

Basitçe söylemek gerekirse, bir sınıfımız varsa ve ondan türetilmiş ancak bazı değişikliklerle başka bir sınıf oluşturmak istiyorsak, yeni sınıfı orijinal sınıftan „miras“ alabiliriz.

PHP'de kalıtım extends anahtar sözcüğü kullanılarak gerçekleştirilir.

Person sınıfımız yaş bilgisini saklar. Person 'i genişleten ve çalışma alanı hakkında bilgi ekleyen Student adlı başka bir sınıfımız olabilir.

Bir örneğe bakalım:

class Person
{
	private $age;

	function __construct($age)
	{
		$this->age = $age;
	}

	function howOldAreYou()
	{
		return $this->age;
	}
}

class Student extends Person
{
	private $fieldOfStudy;

	function __construct($age, $fieldOfStudy)
	{
		parent::__construct($age);
		$this->fieldOfStudy = $fieldOfStudy;
	}

	function printInformation()
	{
		echo 'Age of student: ', $this->howOldAreYou();
		echo 'Field of study: ', $this->fieldOfStudy;
	}
}

$student = new Student(20, 'Computer Science');
$student->printInformation();

Bu kod nasıl çalışıyor?

  • Person sınıfını genişletmek için extends anahtar sözcüğünü kullandık, yani Student sınıfı tüm yöntemleri ve özellikleri Person sınıfından devralır.
  • parent:: anahtar sözcüğü, üst sınıftaki yöntemleri çağırmamızı sağlar. Bu durumda, Student sınıfına kendi işlevselliğimizi eklemeden önce Person sınıfından kurucuyu çağırdık.

Kalıtım, sınıflar arasında „is a“ ilişkisinin olduğu durumlar içindir. Örneğin, bir Student bir Person. Bir kedi bir hayvandır. Kodda bir nesne (örn. „Kişi“) beklediğimiz durumlarda bunun yerine türetilmiş bir nesne (örn. „Öğrenci“) kullanmamızı sağlar.

Kalıtımın birincil amacının kod tekrarını önlemek olmadığının farkına varmak çok önemlidir. Aksine, kalıtımın yanlış kullanımı karmaşık ve bakımı zor kodlara yol açabilir. Sınıflar arasında „is a“ ilişkisi yoksa, kalıtım yerine bileşimi düşünmeliyiz.

Kompozisyon

Kompozisyon, başka bir sınıftan özellikleri ve yöntemleri miras almak yerine, sınıfımızda onun örneğini kullandığımız bir tekniktir. Bu, karmaşık kalıtım yapıları oluşturmadan birden fazla sınıfın işlevlerini ve özelliklerini birleştirmemizi sağlar.

Örneğin, bir Engine sınıfımız ve bir Car sınıfımız var. „Bir araba bir motordur“ demek yerine, tipik bir bileşim ilişkisi olan „Bir arabanın motoru vardır“ deriz.

class Engine
{
	function start()
	{
		echo "Engine is running.";
	}
}

class Car
{
	private $engine;

	function __construct()
	{
		$this->engine = new Engine;
	}

	function start()
	{
		$this->engine->start();
		echo "The car is ready to drive!";
	}
}

$car = new Car;
$car->start();

Burada, Car, Engine'un tüm özelliklerine ve yöntemlerine sahip değildir, ancak $engine özelliği aracılığıyla bunlara erişebilir.

Kompozisyonun avantajı, daha fazla tasarım esnekliği ve gelecekteki değişiklikler için daha iyi uyarlanabilirliktir.

Görünürlük

PHP'de sınıf özellikleri, yöntemleri ve sabitleri için „görünürlük“ tanımlayabilirsiniz. Görünürlük, bu öğelere nereden erişebileceğinizi belirler.

  1. Public: Eğer bir eleman public olarak işaretlenmişse, bu ona her yerden, hatta sınıf dışından bile erişebileceğiniz anlamına gelir.
  2. Korumalı: protected olarak işaretlenmiş bir öğeye yalnızca sınıf ve tüm torunları (ondan miras alan sınıflar) içinde erişilebilir.
  3. Özel: Bir eleman private ise, ona yalnızca tanımlandığı sınıf içinden erişebilirsiniz.

Görünürlük belirtmezseniz, PHP bunu otomatik olarak public olarak ayarlayacaktır.

Örnek bir koda bakalım:

class VisibilityExample
{
	public $publicProperty = 'Public';
	protected $protectedProperty = 'Protected';
	private $privateProperty = 'Private';

	public function printProperties()
	{
		echo $this->publicProperty;     // Works
		echo $this->protectedProperty;  // Works
		echo $this->privateProperty;    // Works
	}
}

$object = new VisibilityExample;
$object->printProperties();
echo $object->publicProperty;        // Works
// echo $object->protectedProperty;   // Throws an error
// echo $object->privateProperty;     // Throws an error

Sınıf kalıtımına devam ediyoruz:

class ChildClass extends VisibilityExample
{
	public function printProperties()
	{
		echo $this->publicProperty;     // Works
		echo $this->protectedProperty;  // Works
		// echo $this->privateProperty;   // Throws an error
	}
}

Bu durumda, ChildClass adresindeki printProperties() yöntemi public ve protected özelliklere erişebilir ancak ana sınıfın private özelliklerine erişemez.

Veri ve yöntemler mümkün olduğunca gizli olmalı ve sadece tanımlanmış bir arayüz üzerinden erişilebilir olmalıdır. Bu, kodun geri kalanını etkilemeden sınıfın dahili uygulamasını değiştirmenize olanak tanır.

Son Anahtar Kelime

PHP'de, bir sınıfın, yöntemin veya sabitin miras alınmasını veya geçersiz kılınmasını önlemek istiyorsak final anahtar sözcüğünü kullanabiliriz. Bir sınıf final olarak işaretlendiğinde genişletilemez. Bir yöntem final olarak işaretlendiğinde, bir alt sınıfta geçersiz kılınamaz.

Belirli bir sınıf veya yöntemin artık değiştirilmeyeceğinin farkında olmak, olası çakışmalar konusunda endişelenmeden daha kolay değişiklik yapmamızı sağlar. Örneğin, bir alt sınıfın zaten aynı isimde bir metoda sahip olmasından ve bunun bir çakışmaya yol açmasından korkmadan yeni bir metot ekleyebiliriz. Ya da bir yöntemin parametrelerini, yine alttaki bir yöntemde geçersiz kılınmış bir yöntemle tutarsızlığa neden olma riski olmadan değiştirebiliriz.

final class FinalClass
{
}

// The following code will throw an error because we cannot inherit from a final class.
class ChildOfFinalClass extends FinalClass
{
}

Bu örnekte, FinalClass nihai sınıfından miras almaya çalışmak bir hatayla sonuçlanacaktır.

Statik Özellikler ve Yöntemler

PHP'de bir sınıfın „statik“ öğelerinden bahsettiğimizde, sınıfın belirli bir örneğine değil, sınıfın kendisine ait olan yöntemleri ve özellikleri kastediyoruz. Bu, onlara erişmek için sınıfın bir örneğini oluşturmanız gerekmediği anlamına gelir. Bunun yerine, bunları doğrudan sınıf adı üzerinden çağırır veya bunlara erişirsiniz.

Statik öğeler örneklere değil sınıfa ait olduğundan, statik yöntemlerin içinde $this sözde değişkenini kullanamayacağınızı unutmayın.

Statik özelliklerin kullanılması tuzaklarla dolu karmaşık kodlara yol açar, bu nedenle bunları asla kullanmamalısınız ve burada bir örnek göstermeyeceğiz. Öte yandan, statik yöntemler kullanışlıdır. İşte bir örnek:

class Calculator
{
	public static function add($a, $b)
	{
		return $a + $b;
	}

	public static function subtract($a, $b)
	{
		return $a - $b;
	}
}

// Using the static method without creating an instance of the class
echo Calculator::add(5, 3); // Output: 8
echo Calculator::subtract(5, 3); // Output: 2

Bu örnekte, iki statik yöntemi olan bir Calculator sınıfı oluşturduk. Bu yöntemleri, :: operatörünü kullanarak sınıfın bir örneğini oluşturmadan doğrudan çağırabiliriz. Statik yöntemler özellikle belirli bir sınıf örneğinin durumuna bağlı olmayan işlemler için kullanışlıdır.

Sınıf Sabitleri

Sınıflar içinde sabitler tanımlama seçeneğimiz vardır. Sabitler, programın yürütülmesi sırasında asla değişmeyen değerlerdir. Değişkenlerin aksine, bir sabitin değeri aynı kalır.

class Car
{
	public const NumberOfWheels = 4;

	public function displayNumberOfWheels(): int
	{
		echo self::NumberOfWheels;
	}
}

echo Car::NumberOfWheels;  // Output: 4

Bu örnekte, NumberOfWheels sabitine sahip bir Car sınıfımız var. Sınıf içindeki sabite erişirken, sınıf adı yerine self anahtar sözcüğünü kullanabiliriz.

Nesne Arayüzleri

Nesne arayüzleri sınıflar için „sözleşme“ görevi görür. Bir sınıf bir nesne arayüzünü uygulayacaksa, arayüzün tanımladığı tüm yöntemleri içermelidir. Bu, belirli sınıfların aynı „sözleşmeye“ veya yapıya bağlı kalmasını sağlamanın harika bir yoludur.

PHP'de arayüzler interface anahtar sözcüğü kullanılarak tanımlanır. Bir arayüzde tanımlanan tüm yöntemler public'tir (public). Bir sınıf bir arayüzü uyguladığında, implements anahtar sözcüğünü kullanır.

interface Animal
{
	function makeSound();
}

class Cat implements Animal
{
	public function makeSound()
	{
		echo 'Meow';
	}
}

$cat = new Cat;
$cat->makeSound();

Bir sınıf bir arayüzü uygular, ancak beklenen tüm yöntemler tanımlanmamışsa, PHP bir hata verir. Bir sınıf aynı anda birden fazla arayüzü uygulayabilir; bu, bir sınıfın yalnızca bir sınıftan miras alabildiği kalıtımdan farklıdır.

Soyut Sınıflar

Soyut sınıflar diğer sınıflar için temel şablon görevi görür, ancak örneklerini doğrudan oluşturamazsınız. Tam yöntemler ve tanımlanmış bir içeriği olmayan soyut yöntemlerin bir karışımını içerirler. Soyut sınıflardan miras alan sınıflar, üst sınıftaki tüm soyut yöntemler için tanımlar sağlamalıdır.

Soyut bir sınıf tanımlamak için abstract anahtar sözcüğünü kullanırız.

abstract class AbstractClass
{
	public function regularMethod()
	{
		echo 'This is a regular method';
	}

	abstract public function abstractMethod();
}

class Child extends AbstractClass
{
	public function abstractMethod()
	{
		echo 'This is the implementation of the abstract method';
	}
}

$instance = new Child;
$instance->regularMethod();
$instance->abstractMethod();

Bu örnekte, bir normal ve bir soyut yöntemi olan soyut bir sınıfımız var. Ardından, AbstractClass adresinden miras alan ve soyut yöntem için bir uygulama sağlayan bir Child sınıfımız var.

Tip Kontrolü

Programlamada, birlikte çalıştığımız verilerin doğru türde olduğundan emin olmak çok önemlidir. PHP'de bu güvenceyi sağlayan araçlarımız vardır. Verilerin doğru türde olduğunu doğrulamaya „tür denetimi“ denir.

PHP'de karşılaşabileceğimiz türler:

  1. Temel tipler: Bunlar int (tam sayılar), float (kayan noktalı sayılar), bool (boolean değerler), string (dizeler), array (diziler) ve null içerir.
  2. Sınıflar: Bir değerin belirli bir sınıfın örneği olmasını istediğimizde.
  3. Arayüzler: Bir sınıfın uygulaması gereken bir dizi yöntemi tanımlar. Bir arayüzü karşılayan bir değer bu yöntemlere sahip olmalıdır.
  4. Karışık tipler: Bir değişkenin birden fazla izin verilen türe sahip olabileceğini belirtebiliriz.
  5. Void: Bu özel tip, bir fonksiyon veya metodun herhangi bir değer döndürmediğini belirtir.

Türleri dahil etmek için kodu nasıl değiştireceğimizi görelim:

class Person
{
	private int $age;

	public function __construct(int $age)
	{
		$this->age = $age;
	}

	public function printAge(): void
	{
		echo "This person is " . $this->age . " years old.";
	}
}

/**
 * A function that accepts a Person object and prints the person's age.
 */
function printPersonAge(Person $person): void
{
	$person->printAge();
}

Bu şekilde, kodumuzun doğru türdeki verileri beklediğinden ve bunlarla çalıştığından emin olarak olası hataları önlememize yardımcı oluruz.

Karşılaştırma ve Kimlik

PHP'de nesneleri iki şekilde karşılaştırabilirsiniz:

  1. Değer karşılaştırması ==: Nesnelerin aynı sınıftan olup olmadığını ve özelliklerinde aynı değerlere sahip olup olmadığını kontrol eder.
  2. Kimlik ===: Nesnenin aynı örneği olup olmadığını kontrol eder.
class Car
{
	public string $brand;

	public function __construct(string $brand)
	{
		$this->brand = $brand;
	}
}

$car1 = new Car('Skoda');
$car2 = new Car('Skoda');
$car3 = $car1;

var_dump($car1 == $car2);   // true, because they have the same value
var_dump($car1 === $car2);  // false, because they are not the same instance
var_dump($car1 === $car3);  // true, because $car3 is the same instance as $car1

instanceof Operatör


instanceof operatörü, belirli bir nesnenin belirli bir sınıfın örneği olup olmadığını, bu sınıfın soyundan gelip gelmediğini veya belirli bir arayüzü uygulayıp uygulamadığını belirlemenizi sağlar.

Bir Person sınıfımız ve Person sınıfının soyundan gelen başka bir Student sınıfımız olduğunu düşünün:

class Person
{
	private int $age;

	public function __construct(int $age)
	{
		$this->age = $age;
	}
}

class Student extends Person
{
	private string $major;

	public function __construct(int $age, string $major)
	{
		parent::__construct($age);
		$this->major = $major;
	}
}

$student = new Student(20, 'Computer Science');

// Check if $student is an instance of the Student class
var_dump($student instanceof Student);  // Output: bool(true)

// Check if $student is an instance of the Person class (because Student is a descendant of Person)
var_dump($student instanceof Person);   // Output: bool(true)

Çıktılardan, $student nesnesinin hem Student hem de Person sınıflarının bir örneği olarak kabul edildiği açıktır.

Akıcı Arayüzler

„Akıcı Arayüz“, OOP'de yöntemlerin tek bir çağrıda bir araya getirilmesini sağlayan bir tekniktir. Bu genellikle kodu basitleştirir ve netleştirir.

Akıcı bir arayüzün temel unsuru, zincirdeki her yöntemin geçerli nesneye bir referans döndürmesidir. Bu, yöntemin sonunda return $this; kullanılarak gerçekleştirilir. Bu programlama tarzı genellikle bir nesnenin özelliklerinin değerlerini ayarlayan „setter“ adı verilen yöntemlerle ilişkilendirilir.

E-posta göndermek için akıcı bir arayüzün nasıl görünebileceğini görelim:

public function sendMessage()
{
	$email = new Email;
	$email->setFrom('sender@example.com')
		  ->setRecipient('admin@example.com')
		  ->setMessage('Hello, this is a message.')
		  ->send();
}

Bu örnekte, setFrom(), setRecipient(), ve setMessage() yöntemleri ilgili değerleri (gönderen, alıcı, mesaj içeriği) ayarlamak için kullanılır. Bu değerlerin her birini ayarladıktan sonra, yöntemler geçerli nesneyi ($email) döndürür, böylece ondan sonra başka bir yöntemi zincirlememize izin verir. Son olarak, e-postayı gerçekten gönderen send() yöntemini çağırıyoruz.

Akıcı arayüzler sayesinde sezgisel ve kolay okunabilir kodlar yazabiliyoruz.

clone ile kopyalama


PHP'de clone operatörünü kullanarak bir nesnenin kopyasını oluşturabiliriz. Bu şekilde, aynı içeriğe sahip yeni bir örnek elde ederiz.

Bir nesneyi kopyalarken bazı özelliklerini değiştirmemiz gerekiyorsa, sınıfta özel bir __clone() yöntemi tanımlayabiliriz. Nesne klonlandığında bu yöntem otomatik olarak çağrılır.

class Sheep
{
	public string $name;

	public function __construct(string $name)
	{
		$this->name = $name;
	}

	public function __clone()
	{
		$this->name = 'Clone of ' . $this->name;
	}
}

$original = new Sheep('Dolly');
echo $original->name . "\n";  // Outputs: Dolly

$clone = clone $original;
echo $clone->name . "\n";     // Outputs: Clone of Dolly

Bu örnekte, bir özelliği $name olan bir Sheep sınıfımız var. Bu sınıfın bir örneğini klonladığımızda, __clone() yöntemi klonlanan koyunun adının „Clone of“ ön ekini almasını sağlar.

Özellikler PHP'deki özellikler, sınıflar arasında yöntemlerin, özelliklerin ve sabitlerin paylaşılmasını sağlayan ve kod tekrarını önleyen bir araçtır. Bunları, bir özelliğin içeriğinin sınıflara „yapıştırıldığı“ bir „kopyala ve yapıştır“ mekanizması (Ctrl-C ve Ctrl-V) olarak düşünebilirsiniz. Bu, karmaşık sınıf hiyerarşileri oluşturmak zorunda kalmadan kodu yeniden kullanmanıza olanak tanır.

PHP'deki özellikler, sınıflar arasında yöntemlerin paylaşılmasını sağlayan ve kod tekrarını önleyen bir araçtır. Bunları, bir özelliğin içeriğinin sınıflara „yapıştırıldığı“ bir „kopyala ve yapıştır“ mekanizması (Ctrl-C ve Ctrl-V) olarak düşünebilirsiniz. Bu, karmaşık sınıf hiyerarşileri oluşturmak zorunda kalmadan kodu yeniden kullanmanıza olanak tanır.

PHP'de özelliklerin nasıl kullanılacağına dair basit bir örneğe göz atalım:

trait Honking
{
	public function honk()
	{
		echo 'Beep beep!';
	}
}

class Car
{
	use Honking;
}

class Truck
{
	use Honking;
}

$car = new Car;
$car->honk(); // Outputs 'Beep beep!'

$truck = new Truck;
$truck->honk(); // Also outputs 'Beep beep!'

Bu örnekte, bir yöntem içeren Honking adlı bir özelliğimiz var honk(). Sonra iki sınıfımız var: Car ve Truck, her ikisi de Honking özelliğini kullanıyor. Sonuç olarak, her iki sınıf da honk() yöntemine „sahiptir“ ve bu yöntemi her iki sınıfın nesneleri üzerinde çağırabiliriz.

Özellikler, sınıflar arasında kolay ve verimli bir şekilde kod paylaşımı yapmanızı sağlar. Kalıtım hiyerarşisine girmezler, yani $car instanceof Honking, false döndürür.

İstisnalar

OOP'de İstisnalar

OOP'deki istisnalar, kodumuzun yürütülmesi sırasında ortaya çıkabilecek hataları ele almamızı ve yönetmemizi sağlar. Bunlar esasen programınızdaki hataları veya beklenmedik durumları kaydetmek için tasarlanmış nesnelerdir.

PHP'de, bu nesneler için yerleşik Exception sınıfına sahibiz. Hata mesajı, dosya ve hatanın oluştuğu satır gibi istisna hakkında daha fazla bilgi almamızı sağlayan birkaç yöntemi vardır.

Bir sorun ortaya çıktığında, bir istisna „atabiliriz“ ( throw kullanarak). Bu istisnayı „yakalamak“ ve işlemek istiyorsak, try ve catch bloklarını kullanırız.

Nasıl çalıştığını görelim:

try {
	throw new Exception('Message explaining the reason for the exception');

	// This code won't execute
	echo 'I am a message that nobody will read';

} catch (Exception $e) {
	echo 'Exception caught: '. $e->getMessage();
}

Bir istisnanın daha derinde, diğer yöntemlere çağrı sırasında atılabileceğine dikkat etmek önemlidir.

Bir try bloğu için, farklı türde istisnalar bekliyorsanız birden fazla catch bloğu belirtilebilir.

Her istisna sınıfının bir öncekinden miras aldığı bir istisna hiyerarşisi de oluşturabiliriz. Örnek olarak, para yatırma ve çekme işlemlerine izin veren basit bir bankacılık uygulaması düşünün:

class BankingException extends Exception {}
class InsufficientFundsException extends BankingException {}
class ExceededLimitException extends BankingException {}

class BankAccount
{
	private int $balance = 0;
	private int $dailyLimit = 1000;

	public function deposit(int $amount): int
	{
		$this->balance += $amount;
		return $this->balance;
	}

	public function withdraw(int $amount): int
	{
		if ($amount > $this->balance) {
			throw new InsufficientFundsException('Not enough funds in the account.');
		}

		if ($amount > $this->dailyLimit) {
			throw new ExceededLimitException('Daily withdrawal limit exceeded.');
		}

		$this->balance -= $amount;
		return $this->balance;
	}
}

$account = new BankAccount;
$account->deposit(500);

try {
	$account->withdraw(1500);
} catch (ExceededLimitException $e) {
	echo $e->getMessage();
} catch (InsufficientFundsException $e) {
	echo $e->getMessage();
} catch (BankingException $e) {
	echo 'An error occurred during the operation.';
}

En İyi Uygulamalar

Nesne yönelimli programlamanın temel ilkelerini öğrendikten sonra, OOP'deki en iyi uygulamalara odaklanmak çok önemlidir. Bunlar yalnızca işlevsel değil aynı zamanda okunabilir, anlaşılabilir ve bakımı kolay kodlar yazmanıza yardımcı olacaktır.

  1. İlgilerin Ayrılması: Her sınıfın açıkça tanımlanmış bir sorumluluğu olmalı ve yalnızca bir birincil görevi ele almalıdır. Bir sınıf çok fazla şey yapıyorsa, onu daha küçük, uzmanlaşmış sınıflara bölmek uygun olabilir.
  2. Kapsülleme: Veri ve yöntemler mümkün olduğunca gizli olmalı ve yalnızca tanımlanmış bir arayüz aracılığıyla erişilebilir olmalıdır. Bu, kodun geri kalanını etkilemeden bir sınıfın dahili uygulamasını değiştirmenize olanak tanır.
  3. Dependency Injection: Bağımlılıkları doğrudan bir sınıfın içinde oluşturmak yerine, onları dışarıdan „enjekte etmelisiniz“. Bu prensibi daha iyi anlamak için Bağımlılık Enjeksiyonu bölümünü okumanızı tavsiye ederiz.

Bu örnekte, catch bloklarının sırasına dikkat etmek önemlidir. Tüm istisnalar BankingException adresinden miras alındığından, bu bloğu ilk olarak kullansaydık, kod sonraki catch bloklarına ulaşmadan tüm istisnalar bu blokta yakalanırdı. Bu nedenle, daha spesifik istisnaların (yani diğerlerinden miras kalanların) catch blok sıralamasında ana istisnalardan daha yukarıda olması önemlidir.