Sihirli Yöntemler

Sihirli yöntemler, bir nesne üzerinde belirli eylemler gerçekleştirildiğinde PHP'nin öntanımlı eylemini geçersiz kılan özel yöntemlerdir.

Dikkat

__ ile başlayan tüm yöntem isimleri PHP tarafından ayrılmıştır. Bu nedenle, PHP'nin davranışını geçersiz kılmadıkça bu tür yöntem adlarının kullanılması önerilmez.

Aşağıdakiler sihirli yöntem olarak ele alınır: __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __serialize(), __unserialize(), __toString(), __invoke(), __set_state(), __clone(), __debugInfo(), __clone() ve __debugInfo().

Uyarı

__construct(), __destruct() ve __clone() hariç tüm sihirli yöntemler mutlaka public olarak bildirilmelidir, aksi takdirde bir E_WARNING çıktılanır. PHP 8.0.0 öcesinde, __sleep(), __wakeup(), __serialize(), __unserialize() ve __set_state() sihirli yöntemleri için böyle bir uyarı yapılmazdı.

Uyarı

Bir sihirli yöntemin tanımında yapılan tür bildirimleri bu belgede açıklandığı gibi yapılmalıdır. Aksi takdirde, ölümcül hata çıktılanır. PHP 8.0.0 öncesinde hiçbir uyarı yapılmazdı. Bundan başka, __construct() ve __destruct() yöntemleri de bir dönüş türü bildirmemelidir, yoksa ölümcül hata oluşur.

__sleep() ve __wakeup()

public __sleep(): array
public __wakeup(): void

serialize() işlevi, sınıfın __sleep() adında sihirli bir işleve sahip olup olmadığına bakar. Böyle bir işlev varsa herhangi bir dizileştirme işleminden önce bu işlev çalıştırılır. Bu işlev ile nesne üzerinde temizlik yapılabilir ve dizileştirilmesi gereken nesnenin tüm değişken isimlerinin bir dizi halinde döndürülmesi sağlanabilir. Eğer işlev hiçbir şey döndürmemişse null dizileştirilir ve bir E_NOTICE çıktılanır.

Bilginize:

Ebeveyn sınıflardaki private özelliklerin isimlerini döndürmek __sleep() için imkansızdır. Bu yapılırsa E_NOTICE seviyesinde bir hata iletisi çıktılanır. Bunun yerine Serializable arayüzünü kullanılabilir.

Bilginize:

PHP 8.0.0 ve sonrasında, __sleep() işlevinden dizi olmayan bir değerin döndürülmesi uyarı üretimine sebep olur. Evvelce, bildirim üretimine sebep oluyordu.

__sleep() işlevinin asıl kullanım amacı askıdaki veriyi göndermek gibi temizliğe benzer işlemler yapmaktır. Ayrıca, tümüyle kaydedilmesi gerekmeyen büyük bir nesne sözkonusu olduğunda da bu işlevden yararlanılabilir.

unserialize() işlevi tersine bir işlem yaparak __wakeup() adında bir sihirli işlevin varlığını araştırır. Böyle bir işlev varsa, bu işlev nesnenin sahip olduğu tüm özkaynakları yeniden oluşturabilir.

__wakeup() işlevinin asıl kullanım amacı, dizileştirme sırasında kaybedilebilen veritabanı bağlantılarını yeniden oluşturmak ve diğer ilklendirme işlemlerini yeniden yapmaktır.

Örnek 1 - Uyutma ve uyandırma

<?php
class Bağlantı
{
protected
$hat;
private
$dsn, $kullanıcı, $parola, $db;

public function
__construct($dsn, $kullanıcı, $parola, $db)
{
$this->dsn = $dsn;
$this->kullanıcı = $kullanıcı;
$this->parola = $parola;
$this->db = $db;
$this->bağlan();
}

private function
bağlan()
{
$this->hat = new PDO($this->dsn, $this->kullanıcı, $this->parola);
}

public function
__sleep()
{
return array(
'dsn', 'kullanıcı', 'parola');
}

public function
__wakeup()
{
$this->bağlan();
}
}
?>

__serialize() ve __unserialize()

public __serialize(): array
public __unserialize(array $data): void

serialize() işlevi, sınıfın __serialize() sihirli adına sahip bir işlevinin olup olmadığına bakar. Varsa, bu işlev herhangi bir dizileştirmeden önce çalıştırılır. Nesnenin dizileştirilmiş biçimini temsil eden ilişkisel bir anahtar/değer çiftleri dizisi oluşturmalı ve döndürmelidir. Hiçbir dizi döndürülmezse, bir TypeError yavrulanır.

Bilginize:

Aynı nesnede hem __serialize() hem de __sleep() tanımlanmışsa, sadece __serialize() çağrılır. __sleep() ise yok sayılır. Nesne Serializable arayüzünü gerçekliyorsa, arayüzün serialize() yöntemi yok sayılır ve yerine __serialize() kullanılır.

__serialize() yönteminin amaçlanan kullanımı, nesnenin dizileştirmeye uygun keyfi bir gösterimini tanımlamaktır. Dizinin öğeleri, nesnenin özelliklerine karşılık gelebilir, ancak bu gerekli değildir.

unserialize() işlevi ise tersine sınıfın __unserialize() sihirli adına sahip bir işlevinin olup olmadığına bakar. Varsa, bu işlev __serialize() işlevinden döndürülen geri yüklenmiş diziye aktarılır. Daha sonra nesnenin özelliklerini uygun şekilde bu diziden geri yükleyebilir.

Bilginize:

Aynı nesnede hem __unserialize() hem de __wakeup() tanımlanmışsa, yalnızca __unserialize() çağrılır. __wakeup() ise yok sayılır.

Bilginize:

Özellik PHP 7.4.0'dan beri kullanılabilmektedir.

Örnek 2 - Dizileştirme ve Nesneleştirme

<?php
class Connection
{
protected
$link;
private
$dsn, $username, $password;

public function
__construct($dsn, $username, $password)
{
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->connect();
}

private function
connect()
{
$this->link = new PDO($this->dsn, $this->username, $this->password);
}

public function
__serialize(): array
{
return [
'dsn' => $this->dsn,
'user' => $this->username,
'pass' => $this->password,
];
}

public function
__unserialize(array $data): void
{
$this->dsn = $data['dsn'];
$this->username = $data['user'];
$this->password = $data['pass'];

$this->connect();
}
}
?>

__toString()

public __toString(): string

__toString() yöntemi, sınıf bir dizgeye dönüştürüldüğünde sınıfın nasıl tepki vereceğine karar vermeyi sağlar. Örneğin, echo $obj; ile ne basılacağı gibi.

Uyarı

PHP 8.0.0'dan itibaren, dönüş değerine standart PHP tür anlamlandırması uygulanmaktadır, yani katı kodlama devre dışı bırakılırsa, mümkün olduğu takdirde değer string türe zorlanır.

strict typing etkinken Stringable nesnesi string tür bildirimi tarafından kabul edilmez. Bunun olması isteniyorsa Stringable ve string türleri birleşim türü bildirimi içinde birleştirilmelidir.

PHP 8.0.0'dan itibaren, __toString() yöntemi içeren her sınıf örtük olarak Stringable arayüzünü de gerçekler ve dolayısıyla bu arayüz için tür sınamaları yapılmaz. Bu durumda, arayüzün doğrudan gerçeklenmesi tercih edilmelidir.

PHP 7.4'te, döndürülen değer mutlaka string türünde olmalıdır, aksi takdirde bir Error yavrulanır.

PHP 7.4.0 öncesinde, döndürülen değer mutlaka string türünde olmalıydı, aksi takdirde ölümcül bir E_RECOVERABLE_ERROR çıktılanırdı.

Uyarı

__toString() yönteminin içinden bir istisna yavrulatılamaz. PHP 7.4.0 öncesinde, bunun yapılması ölümcül hata ile sonuçlanırdı.

Örnek 3 - Basit bir örnek

<?php
// Basit bir sınıf tanımlayalım
class TestClass
{
public
$foo;

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

public function
__toString()
{
return
$this->foo;
}
}

$class = new TestClass('Merhaba');
echo
$class;
?>

Yukarıdaki örneğin çıktısı:

Merhaba

__invoke()

__invoke( ...$values): mixed

__invoke() yöntemi, bir betik bir nesneyi bir işlev olarak çağırmaya çalışırsa çağrılır.

Örnek 4 - __invoke() kullanımı

<?php
class CallableClass
{
public function
__invoke($x)
{
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>

Yukarıdaki örneğin çıktısı:

int(5)
bool(true)

Örnek 5 - __invoke() kullanımı

<?php
class Sort
{
private
$key;

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

public function
__invoke(array $a, array $b): int
{
return
$a[$this->key] <=> $b[$this->key];
}
}

$müşteriler = [
[
'no' => 1, 'isim' => 'John', 'soyisim' => 'Do'],
[
'no' => 3, 'isim' => 'Alice', 'soyisim' => 'Gustav'],
[
'no' => 2, 'isim' => 'Bob', 'soyisim' => 'Filipe']
];

// $müşterileri isme göre sırala
usort($müşteriler, new Sort('isim'));
print_r($müşteriler);

// $müşterileri soyisme göre sırala
usort($müşteriler, new Sort('soyisim'));
print_r($müşteriler);
?>

Yukarıdaki örneğin çıktısı:

Array
(
    [0] => Array
        (
            [no] => 3
            [isim] => Alice
            [soyisim] => Gustav
        )

    [1] => Array
        (
            [no] => 2
            [isim] => Bob
            [soyisim] => Filipe
        )

    [2] => Array
        (
            [no] => 1
            [isim] => John
            [soyisim] => Do
        )

)
Array
(
    [0] => Array
        (
            [no] => 1
            [isim] => John
            [soyisim] => Do
        )

    [1] => Array
        (
            [no] => 2
            [isim] => Bob
            [soyisim] => Filipe
        )

    [2] => Array
        (
            [no] => 3
            [isim] => Alice
            [soyisim] => Gustav
        )

)

__set_state()

static __set_state(array $özellikler): object

Bu statik yöntem, var_export() tarafından ihraç edilen sınıflar için çağrılır.

Bu yöntemin tek bağımsız değişkeni ['özellik' => değer, ...] biçeminde ihraç edilen özellikleri içeren bir dizidir.

Örnek 6 - __set_state() kullanımı

<?php

class A
{
public
$var1;
public
$var2;

public static function
__set_state($bir_dizi)
{
$obj = new A;
$obj->var1 = $bir_dizi['var1'];
$obj->var2 = $bir_dizi['var2'];
return
$obj;
}
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';

$b = var_export($a, true);
var_dump($b);

eval(
'$c = ' . $b . ';');
var_dump($c);
?>

Yukarıdaki örneğin çıktısı:

string(60) "A::__set_state(array(
   'var1' => 5,
   'var2' => 'foo',
))"
object(A)#2 (2) {
  ["var1"]=>
  int(5)
  ["var2"]=>
  string(3) "foo"
}

Bilginize: Bir nesne ihraç edilirken, var_export() işlevi __set_state() nesnenin sınıfı tarafından gerçeklenmiş mi diye bakmaz, dolayısıyla böyle nesnelerin yeniden ithali __set_state() hiç gerçeklenmemiş gibi bir Error istisnası yavrulanmasına sebep olur. Bu kısmen bazı dahili sınıfları da etkiler. Sadece, __set_state() gerçekleyen sınıfın nesnelerinin yeniden ithal edilmesini sağlamak yazılımcının sorumluluğundadır.

__debugInfo()

__debugInfo(): array

Gösterilmesi gereken özelliklerini döndürmek için bir nesne dökümleneceği zaman bu yöntem var_dump() tarafından çağrılır.

Örnek 7 - __debugInfo() kullanımı

<?php
class C {
private
$prop;

public function
__construct($val) {
$this->prop = $val;
}

public function
__debugInfo() {
return [
'propSquared' => $this->prop ** 2,
];
}
}

var_dump(new C(42));
?>

Yukarıdaki örneğin çıktısı:

object(C)#1 (1) {
  ["propSquared"]=>
  int(1764)
}