PHP 7 arayüzleri, dönüş türü ipuçları ve self


89

GÜNCELLEME : PHP 7.4 artık bu soruda ortaya çıkan ana sorunu ele alan kovaryansı ve kontraveriansı destekliyor .


PHP 7'de dönüş türü ipucu kullanmakla ilgili bir sorunla karşılaştım. Anladığım kadarıyla ipucu : self, bir uygulama sınıfının kendisini döndürmesini istediğiniz anlamına geliyor. Bu nedenle : selfarayüzlerimde bunu belirtmek için kullandım, ancak arayüzü gerçekten uygulamaya çalıştığımda uyumluluk hataları aldım.

Aşağıda karşılaştığım sorunun basit bir gösterimi var:

interface iFoo
{
    public function bar (string $baz) : self;
}

class Foo implements iFoo
{

    public function bar (string $baz) : self
    {
        echo $baz . PHP_EOL;
        return $this;
    }
}

(new Foo ()) -> bar ("Fred") 
    -> bar ("Wilma") 
    -> bar ("Barney") 
    -> bar ("Betty");

Beklenen çıktı şuydu:

Fred Wilma Barney Betty

Aslında aldığım şey:

PHP Önemli hata: Foo :: bar bildirimi (int $ baz): Foo, iFoo :: bar (int $ baz) ile uyumlu olmalıdır: 7. satırdaki test.php içindeki iFoo

Mesele şu ki Foo, iFoo'nun bir uygulaması, bu yüzden uygulamanın verilen arayüzle mükemmel bir şekilde uyumlu olması gerektiğini söyleyebildiğim kadarıyla. Bu sorunu, kullanmak yerine arayüzü ada göre ipucu döndürmek için arabirimi veya uygulama sınıfını (veya her ikisini) değiştirerek çözebilirdim self, ancak benim anladığım kadarıyla anlamsal olarak şu selfanlama geliyor: ". Bu nedenle, arayüze değiştirmek teoride, amacım çağrılan örnek için olduğunda, arayüzü uygulayan bir şeyin herhangi bir örneğini döndürebileceğim anlamına gelir.

Bu PHP'de bir gözetim mi yoksa kasıtlı bir tasarım kararı mı? Eski ise, PHP 7.1'de düzeltildiğini görme şansı var mı? Değilse, arabiriminizin zincirleme için yöntemi çağırdığınız örneği döndürmenizi beklediğini ima eden doğru dönüş yolu nedir?


Bence PHP dönüş tipi ipucu vermede bir hata, belki bunu bir hata olarak yükseltmelisiniz ; ancak herhangi bir düzeltmenin bu geç aşamada PHP 7.1'e geçme olasılığı düşük
Mark Baker

7.1'in son beta sürümü birkaç gün önce çevrimiçi hale geldiğinden, herhangi bir düzeltmenin 7.1'e girmesi pek olası değil.
Charlotte Dunois

İlginin dışında, selfdönüş türünün nasıl çalışması gerektiğine dair yorumunuzu nerede okuyorsunuz ?
Adam Cameron

@Adam: self"Bunu çağırdığınız örneği geri verin, aynı arayüzü uygulayan başka bir örneği değil" demek mantıklı görünüyor . Java'nın da benzer bir dönüş türüne sahip olduğunu hatırlıyorum (Java programlamasını
yapmayalı epey oldu

1
Merhaba Gordon. Bir yerde işe yaradığı belgelenmediği sürece, gerçek olmanın mantıklı olabileceğine güvenmem. TBH, tarif ettiğim durumla, olabildiğince açıklayıcı olurum ve dönüş türü olarak iFoo'yu kullanırdım. Gerçekte işe yaramayacağı bir durum var mı? (Bunun "cevap" yerine "tavsiye" / fikir olduğunu anlıyorum, dedi.
Adam Cameron

Yanıtlar:


94

selförneğe atıfta bulunmaz, geçerli sınıfı ifade eder. Bir arabirimin aynı örneğin döndürülmesi gerektiğini belirtmesinin bir yolu yoktur - selfdenediğiniz şekilde kullanmak yalnızca döndürülen örneğin aynı sınıftan olmasını zorunlu kılar.

Bununla birlikte, PHP'deki dönüş türü bildirimleri, yaptığınız şey kovaryant iken değişmez olmalıdır.

Kullanımınız selfşuna eşdeğerdir:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}

buna izin verilmez.


Dönüş Türü Beyannameler RFC sahip bu demek :

Devralma sırasında bildirilen dönüş türünün uygulanması değişmez; bu, bir alt tür bir üst yöntemi geçersiz kıldığında, alt türün dönüş türünün üst öğe ile tam olarak eşleşmesi gerektiği ve atlanamayabileceği anlamına gelir. Ebeveyn bir dönüş türü bildirmezse, çocuğun bir dönüş türü bildirmesine izin verilir.

...

Bu RFC başlangıçta kovaryant dönüş türleri önermiştir, ancak birkaç sorun nedeniyle değişmez olarak değiştirilmiştir. Gelecekte bir noktada kovaryant dönüş türleri eklemek mümkündür.


Şimdilik en azından yapabileceğiniz en iyi şey:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}

19
Bununla birlikte, staticişe yarayacak bir dönüş ipucu bekliyordum , ancak bu bile tanınmadı
Mark Baker

Durumun bu olacağını düşündüm ve sorunu bu şekilde çözeceğim. Bununla birlikte, mümkünse sadece şunu kullanmayı tercih ederdim: eğer bir sınıf bir arayüz uyguluyorsa, self'in dönüş değeri de dolaylı olarak arayüzün bir örneğinin dönüş değeridir.
GordonM

19
Paul, burada sildiğiniz yorumları silmek aslında zararlıdır çünkü (A) önemli bilgileri kaybeder ve (B) diğer yorumlara göre tartışma akışını bozar. Mark ve Gordon'a dayanan yorumlarınızın neden silinmesi gerektiğine dair herhangi bir sebep göremiyorum. Aslında, bunu her yerde yapıyorsunuz ve durması gerekiyor. Bir yıllık soruya geri dönüp tüm yorumlarınızı kaldırıp tartışma akışını tamamen yok etmenin kesinlikle iyi bir nedeni yoktur. Aslında zararlı ve yıkıcıdır.
Cody Grey

Burada gördüğünüz bir alıntı için önemli bir önsöz var: " Kovaryant dönüş türleri tür sesi olarak kabul edilir ve diğer birçok dilde kullanılır (C ++ ve Java, ancak C # inanmıyorum). Bu RFC başlangıçta ortak değişken dönüş türlerini önerdi ancak birkaç sorun nedeniyle değişmez olarak değiştirildi. " PHP'nin hangi sorunları olduğunu merak ediyorum. Tasarım seçimleri, özelliklerle ilgili tuhaf sorunlara da neden olan çeşitli sınırlamalara neden olur ve dönüş türü kısıtlamalarını gevşetmediğiniz sürece (aşağıdaki bazı yanıtlarda görüldüğü gibi) çoğu durumda onları işe yaramaz hale getirir. Çok sinir bozucu.
John Pancoast

1
@MarkBaker dönüş türü staticPHP 8'e ekleniyor.
Ricardo Boss

16

Ayrıca arayüzde dönüş türünü açıkça tanımlamadığınız, yalnızca PHPDoc'da tanımlamadığınız ve daha sonra uygulamalarda belirli dönüş türünü tanımlayabileceğiniz bir çözüm olabilir:

interface iFoo
{
    public function bar (string $baz);
}

class Foo implements iFoo
{
    public function bar (string $baz) : Foo  {...}
}

2
Veya Foosadece kullanmak yerine self.
bunun yerine

2

Arabirimden zorlamak istediğinizde, bu yöntem nesneyi döndürecektir, ancak nesne türü arabirim türü olmayacak, sınıfın kendisi olacaktır, o zaman bunu şu şekilde yazabilirsiniz:

interface iFoo {
    public function bar (string $baz) : object;
}

class Foo implements iFoo {
    public function bar (string $baz) : self  {...}
}

PHP 7.4'ten beri çalışıyor.



0

Bu bana beklenen davranışa benziyor.

Sadece Foo::bargeri dönmek iFooyerine yönteminizi değiştirin selfve onunla bitirin.

Açıklama:

selfarayüzde kullanıldığı şekliyle "tipte bir nesne" anlamına gelir iFoo.
selfuygulamada kullanıldığı şekliyle "türden bir nesne" anlamına gelir Foo.

Bu nedenle, arayüzdeki ve gerçeklemedeki dönüş türleri açıkça aynı değildir.

Yorumlardan biri Java'dan ve bu sorunu yaşayıp yaşamayacağınızdan bahseder. Cevap evet, Java böyle kod yazmanıza izin verseydi aynı sorunu yaşarsınız - ki öyle değil. Java, PHP'nin selfkısayolu yerine türün adını kullanmanızı gerektirdiğinden , bunu gerçekte asla görmeyeceksiniz. ( Java'da benzer bir sorunla ilgili tartışma için buraya bakın .)


Yani beyan etmek self, ilan etmeye benzer MyClass::classmi?
peterchaula

1
@Laser Evet, öyle.
Moshe Katz

4
Ancak Foo, iFoo'yu uygularsa, o zaman Foo, iFoo türünün tanımı gereğidir
GordonM
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.