PHP 5.2+ soyut statik sınıf yöntemlerine neden izin vermiyor?


121

PHP 5.2'de katı uyarıları etkinleştirdikten sonra, başlangıçta katı uyarılar olmadan yazılmış bir projeden çok sayıda katı standart uyarısı gördüm:

Katı Standartlar : Statik işlev Program :: getSelectSQL () Program.class.inc içinde soyut olmamalıdır

Söz konusu işlev, programın soyut bir üst sınıfına aittir ve TVProgram gibi alt sınıflarında uygulanması gerektiğinden soyut statik olarak bildirilir.

Bu değişiklikle ilgili referansları burada buldum :

Bırakılan soyut statik sınıf işlevleri. Bir hata nedeniyle, PHP 5.0.x ve 5.1.x sınıflarda soyut statik işlevlere izin verdi. PHP 5.2.x'ten itibaren, yalnızca arabirimler bunlara sahip olabilir.

Sorum şu: birisi PHP'de neden soyut bir statik işlev olmaması gerektiğini açık bir şekilde açıklayabilir mi?


12
Yeni okuyucular, bu mantıksız kısıtlamanın PHP 7'de kaldırıldığına dikkat etmelidir.
Mark Amery

Yanıtlar:


76

statik yöntemler, onları bildiren sınıfa aittir. Sınıfı genişletirken, aynı adda statik bir yöntem oluşturabilirsiniz, ancak aslında statik bir soyut yöntem uygulamıyorsunuz.

Aynı şey, herhangi bir sınıfı statik yöntemlerle genişletmek için de geçerlidir. Bu sınıfı genişletir ve aynı imzanın statik bir yöntemini oluşturursanız, aslında üst sınıfın statik yöntemini geçersiz kılmıyorsunuz

EDIT (16 Eylül 2009)
Bununla ilgili güncelleme. PHP 5.3'ü çalıştırırken, iyi ya da kötü için soyut durağan geri döndüğünü görüyorum. ( Daha fazla bilgi için http://php.net/lsb adresine bakın )

DÜZELTME (philfreo tarafından)
abstract staticPHP 5.3'te hala izin verilmez, LSB ilişkilidir ancak farklıdır.


3
Tamam, peki ya getSelectSQL () işlevini soyut sınıfımı genişleten tüm çocuklarda zorlamak istersem? Ana sınıftaki getSelectSQL () var olmak için geçerli bir nedene sahip değil. En iyi eylem planı nedir? Soyut statik seçmemin nedeni, tüm alt öğelerde getSelectSQL () uygulayana kadar kodun derlenmemesidir.
Artem Russakovskii

1
Büyük olasılıkla, her şeyi yeniden tasarlamalısınız, böylece getSelectSQL () bir soyut / örnek / yöntemdir. Bu şekilde, her çocuğun / örneklerinin / örneklerinin böyle bir yöntemi olacaktır.
Matthew Flaschen

7
Soyut durağan PHP 5.3'te hala izin verilmemektedir. Geç statik bağlamaların bununla hiçbir ilgisi yoktur. Ayrıca bkz stackoverflow.com/questions/2859633
artefacto

40
Bana göre bu katı uyarı aptalca çünkü PHP "geç statik bağlama" içeriyor, bu da doğal olarak statik yöntemlerin sanki sınıfın kendisi bir nesneymiş gibi (örneğin Ruby'de) kullanılması fikrini sağlıyor. Statik yöntem aşırı yüklemesine yol açar ve abstract staticbu durumda faydalı olabilir.
dmitry

4
Bu cevap belli belirsiz yanlış. "yine de izin verilmiyor" sadece E_STRICT düzeyinde bir uyarı alacağınız anlamına gelir, en azından 5.3+ sürümünde soyut statik işlevler oluşturabilir, bunları genişletilmiş sınıflarda uygulayabilir ve sonra bunlara static :: anahtar sözcüğüyle başvurabilirsiniz. Açıkçası, ana sınıfın statik sürümü hala oradadır ve soyut olduğu için doğrudan çağrılamaz (bu sınıfın içinde self :: veya static :: aracılığıyla) ve normal bir statik olmayan soyut işlevi çağırmışsınız gibi ölümcül bir hata verecektir. İşlevsel olarak bu yararlıdır, bu etkiyle ilgili @ dmitry duygularına katılıyorum.
ahoffner

79

Uzun, üzücü bir hikaye.

PHP 5.2 bu uyarıyı ilk kez tanıttığında, geç statik bağlamalar henüz dilde değildi. Geç statik bağlamalara aşina değilseniz, bunun gibi bir kodun beklediğiniz gibi çalışmadığını unutmayın:

<?php

abstract class ParentClass {
    static function foo() {
        echo "I'm gonna do bar()";
        self::bar();
    }

    abstract static function bar();
}

class ChildClass extends ParentClass {
    static function bar() {
        echo "Hello, World!";
    }
}

ChildClass::foo();

Katı mod uyarısını bir kenara bırakırsak, yukarıdaki kod çalışmıyor. self::bar()Çağrı foo()açıkça atıfta bar()yöntemi ParentClassbile, foo()bir yöntem olarak adlandırılır ChildClass. Bu kodu katı mod kapalıyken çalıştırmayı denerseniz, " PHP Ölümcül hatası: Soyut yöntem ParentClass :: bar () çağrılamıyor " mesajını görürsünüz .

Bu göz önüne alındığında, PHP 5.2'deki soyut statik yöntemler işe yaramazdı. Tüm nokta ve daha sonra farklı çocuk sınıfları farklı uygulamaları sağlamak - soyut bir yöntemi kullanarak bunu çağırarak olacak neler uygulama bilmeden yöntemini çağıran bir kod yazabilirsiniz olmasıdır. Ancak PHP 5.2, çağrıldığı alt sınıfın statik bir yöntemini çağıran bir üst sınıfın bir yöntemini yazmak için temiz bir yol sunmadığından, soyut statik yöntemlerin bu şekilde kullanılması mümkün değildir. Dolayısıyla abstract static, PHP 5.2'deki herhangi bir kullanım kötü koddur ve muhtemelen selfanahtar kelimenin nasıl çalıştığının yanlış anlaşılmasından esinlenmiştir . Bunun üzerine bir uyarıda bulunmak tamamen mantıklıydı.

Ancak daha sonra, bir yöntemin staticanahtar kelime aracılığıyla çağrıldığı sınıfa atıfta bulunma yeteneğine PHP 5.3 eklendi ( selfher zaman yöntemin tanımlandığı sınıfa atıfta bulunan anahtar sözcüğün aksine ). Eğer değiştirirseniz self::bar()için static::bar()yukarıdaki benim örnekte, PHP 5.3 ve üzeri cezası çalışır. New self vs. new static bölümünde selfvs hakkında daha fazla bilgi edinebilirsiniz .static

Statik anahtar kelime eklendiğinde, abstract staticbir uyarı vermenin açık argümanı ortadan kalktı. Geç statik bağlamaların ana amacı, bir üst sınıfta tanımlanan yöntemlerin alt sınıflarda tanımlanacak statik yöntemleri çağırmasına izin vermekti; soyut statik yöntemlere izin verilmesi, geç statik bağların varlığı göz önüne alındığında makul ve tutarlı görünmektedir.

Yine de, sanırım uyarıyı saklamak için bir dava açabilirsiniz. Örneğin, PHP soyut sınıfların statik yöntemlerini çağırmanıza izin verdiğinden, yukarıdaki örneğimde ( selfile değiştirerek düzelttikten sonra bile static) bozuk olan ve gerçekten yapmak istemediğiniz genel bir yöntemi ParentClass::foo()ortaya koyduğunuzu iddia edebilirsiniz. maruz bırakmak. Statik olmayan bir sınıf kullanmak - yani, tüm yöntem örnek yöntemlerini yapmak ve hepsinin alt öğelerini tekil yapmak ya da başka bir şey yapmak - bu sorunu çözecektir, çünkü soyut olduğundan , örneklenemez ve bu nedenle örnek yöntemleri olamaz olarak adlandırılabilir. Bu argümanın zayıf olduğunu düşünüyorum (çünküParentClassParentClassParentClass::foo() büyük bir sorun değildir ve statik sınıflar yerine tekil kullanmak çoğu zaman gereksiz bir şekilde ayrıntılı ve çirkindir), ancak makul bir şekilde katılmayabilirsiniz - bu biraz öznel bir çağrıdır.

Yani bu argümana dayanarak, PHP geliştiricileri uyarıyı dilde tuttu, değil mi?

Ah, tam olarak değil .

Yukarıda bağlantısı verilen 53081 numaralı PHP hata raporu, yapının eklenmesi static::foo()soyut statik yöntemleri mantıklı ve kullanışlı hale getirdiğinden , uyarının kaldırılması çağrısında bulundu . Rasmus Lerdorf (PHP'nin yaratıcısı), isteği sahte olarak etiketleyerek işe başlar ve uyarıyı haklı çıkarmaya çalışmak için uzun bir kötü mantık zincirinden geçer. Sonunda, bu değişim gerçekleşir:

Giorgio

biliyorum ama:

abstract class cA
{
      //static function A(){self::B();} error, undefined method
      static function A(){static::B();} // good
      abstract static function B();
}

class cB extends cA
{
    static function B(){echo "ok";}
}

cB::A();

Rasmus

Doğru, tam olarak nasıl çalışması gerektiği bu.

Giorgio

ancak buna izin verilmiyor :(

Rasmus

Neye izin verilmiyor?

abstract class cA {
      static function A(){static::B();}
      abstract static function B();
}

class cB extends cA {
    static function B(){echo "ok";}
}

cB::A();

Bu iyi çalışıyor. Belli ki self :: B () 'yi çağıramazsınız, ancak static :: B () iyidir.

Rasmus'un kendi örneğindeki kodun "iyi çalıştığı" iddiası yanlıştır; bildiğiniz gibi katı mod uyarısı veriyor. Sanırım katı mod açılmadan test ediyordu. Ne olursa olsun, kafası karışmış bir Rasmus, talebi yanlışlıkla "sahte" olarak kapattı.

Ve bu yüzden uyarı hala dilde. Bu tamamen tatmin edici bir açıklama olmayabilir - muhtemelen buraya uyarının rasyonel bir gerekçesi olduğunu umarak geldiniz. Ne yazık ki, gerçek dünyada bazen seçimler rasyonel karar vermekten ziyade sıradan hatalardan ve kötü muhakemeden doğar. Bu sadece o zamanlardan biridir.

Neyse ki, tahmin edilebilir Nikita Popov, PHP RFC'nin bir parçası olarak uyarıyı PHP 7'deki dilden kaldırdı : E_STRICT bildirimlerini yeniden sınıflandır . Sonuçta, akıl sağlığı üstün geldi ve PHP 7 piyasaya sürüldüğünde, abstract staticbu aptal uyarıyı almadan hepimiz mutlu bir şekilde kullanabiliriz .


70

Bu konu için çok basit bir çalışma var ve bu aslında tasarım açısından mantıklı. Jonathan'ın yazdığı gibi:

Aynı şey, herhangi bir sınıfı statik yöntemlerle genişletmek için de geçerlidir. Bu sınıfı genişletir ve aynı imzanın statik bir yöntemini oluşturursanız, aslında üst sınıfın statik yöntemini geçersiz kılmıyorsunuz

Yani, etrafınızda bir çalışma olarak bunu yapabilirsiniz:

<?php
abstract class MyFoo implements iMyFoo {

    public static final function factory($type, $someData) {
        // don't forget checking and do whatever else you would
        // like to do inside a factory method
        $class = get_called_class()."_".$type;
        $inst = $class::getInstance($someData);
        return $inst;
    }
}


interface iMyFoo {
    static function factory($type, $someData);
    static function getInstance();
    function getSomeData();
}
?>

Ve şimdi MyFoo'nun alt sınıflarını oluşturan herhangi bir sınıfın bir getInstance statik yöntemi ve genel bir getSomeData yöntemi uygulamasını zorunlu kılarsınız. MyFoo'yu alt sınıflara ayırmazsanız, benzer işlevselliğe sahip bir sınıf oluşturmak için iMyFoo'yu yine de uygulayabilirsiniz.


2
Bu desenle fonksiyonu korumalı yapmak mümkün mü ? Bunu yaptığımda, MyFoo'yu genişleten sınıfların getInstance'ın genel olması gereken uyarıları attığı anlamına geliyor. Ve bir arabirim tanımına korumalı koyamazsınız.
artfulrobot

3
bazı zamanlar static::faydalı olabilir.
seyed

3
Traits ile çalışmıyor. Keşke Özellikler abstract staticPHP sorgulamadan metodlara sahip olsaydı ....
Rudie

1
Arayüz kullanımı muhtemelen burada en iyi çözümdür. +1.
Juan Carlos Coto

Bu, basitliği nedeniyle aslında çok zariftir. +1
G. Stewart

12

Bunun eski olduğunu biliyorum ama ...

Neden sadece o ana sınıfın statik yöntemine bir istisna atmıyorsunuz, bu şekilde eğer onu geçersiz kılmıyorsanız, istisna neden olur.


1
Bu yardımcı olmuyor, istisna, statik yöntemin çağrılmasıyla gerçekleşecekti - aynı zamanda, onu geçersiz kılmadığınızda bir 'yöntem mevcut değil' hatası ortaya çıkacaktır.
BT

3
@BT Demek istediğim, yöntemi soyut olarak ilan etmeyin, uygulayın, ancak çağrıldığında bir istisna atın, yani geçersiz kılınmışsa fırlatmayacak demektir.
Petah

Bu en zarif çözüm gibi görünüyor.
Alex S

Çalışma zamanında değil, derleme zamanında böyle bir şey görmek daha iyidir. Üretime geçmeden önce problemi bulmak daha kolay çünkü sadece dosyayı yüklemeniz gerekiyor, kötü olup olmadığını anlamak için kodu çalıştırmanız
gerekmiyor

4

Soyut bir sınıfın / arayüzün programcılar arasında bir sözleşme olarak görülebileceğini iddia ediyorum. Daha çok şeylerin nasıl görünmesi / davranması gerektiği ve gerçek işlevleri yerine getirmemesi ile ilgilenir. Php5.0 ve 5.1.x'te görüldüğü gibi, php geliştiricilerinin bunu yapmasını engelleyen doğal bir yasa değil, diğer dillerdeki diğer OO tasarım kalıplarıyla birlikte hareket etme dürtüsü. Temelde bu fikirler, biri zaten diğer dillere aşina ise, beklenmedik davranışları önlemeye çalışır.


Burada php ile ilgili olmasa da başka bir iyi açıklama daha var: stackoverflow.com/questions/3284/…
merkuro

3
Tanrım! Size olumsuz oy veren iki kişi tamamen akıllarından çıktı! Bu, bu konudaki en anlayışlı cevap.
Theodore R. Smith

5
@ TheodoreR.Smith Insightful? Hatalar içerir ve neredeyse tutarlıdır. Soyut sınıfların "gerçek işlevselliği uygulamadığı" iddiası mutlaka doğru değildir ve arabirimlere ek olarak var olmalarının tüm noktası budur . Bu talepte "Bunu yapmaktan önler php geliştiricileri bu doğal bir kanun olmadığını" , ben hiçbir fikrim yok "o" olduğunu. Ve "Temelde bu fikirler beklenmedik davranışları önlemeye çalışıyor" iddiasında, ne "bu fikirlerin" ne de potansiyel "beklenmedik davranışların" ne olduğu hakkında hiçbir fikrim yok. Bundan ne kadar içgörü çıkardıysan, bende kayboldu.
Mark Amery

2

Statik soyut fonksiyonları yasaklamak için herhangi bir sebep görmüyorum. Onları yasaklamak için bir neden olmadığına dair en iyi argüman, Java'da izin verilmesidir. Sorular şunlardır: - Teknik olarak uygulanabilir mi? - Evet, çünkü PHP 5.2'de var ve Java'da var. Yani yapabilirsin. Yapmalı mıyız? - Mantıklılar mı? Evet. Bir sınıfın bir bölümünü uygulamak ve bir sınıfın başka bir bölümünü kullanıcıya bırakmak mantıklıdır. Statik olmayan fonksiyonlarda anlamlıdır, statik fonksiyonlar için neden anlamlı olmasın? Statik işlevlerin bir kullanımı, birden fazla örneğin (tekil) olmaması gereken sınıflardır. Örneğin bir şifreleme motoru. Birkaç durumda olması gerekmez ve bunu önlemek için nedenler vardır - örneğin, hafızanın yalnızca bir bölümünü davetsiz misafirlere karşı korumanız gerekir. Bu nedenle, motorun bir bölümünü uygulamak ve şifreleme algoritmasını kullanıcıya bırakmak son derece mantıklıdır. Bu yalnızca bir örnektir. Statik işlevleri kullanmaya alışkınsanız, çok daha fazlasını bulacaksınız.


3
5.2'de bulunan soyut statik yöntemler hakkındaki düşünceniz büyük ölçüde yanıltıcıdır. İlk olarak, onları yasaklayan katı mod uyarısı 5.2'de tanıtıldı; 5.2'de izin verildiği gibi ses çıkarırsınız. İkinci olarak, 5.2'de "bir sınıfın bir bölümünü uygulamak ve bir sınıfın başka bir bölümünü kullanıcıya bırakmak için" kolayca kullanılamazlar çünkü Geç Statik Bağlamalar henüz mevcut değildi.
Mark Amery

0

Php 5.4+ kullanım özelliği:

trait StaticExample {
    public static function instance () {
    return new self;
    }
}

ve sınıfınızda başlangıca koyun:

use StaticExample;

1
Ben koymak başardı abstract public static function get_table_name();bir özellikte ve artık E_STRICT uyarılarıyla benim soyut sınıfın içinde bu özellik kullanın! Bu yine de çocuklarda statik yöntemin tanımlanmasını umduğum gibi zorladı. Fantastik!
Programster

-1

PHP'nin 'Geç Statik Bağlama' sorunlarına bakın. Soyut sınıflara statik yöntemler koyuyorsanız, muhtemelen daha sonra değil, daha önce karşılaşacaksınız. Kesin uyarıların size bozuk dil özelliklerini kullanmaktan kaçınmanızı söylediği mantıklıdır.


4
Sanırım "Katı Standartlar" uyarılarını kastetti.
Jacob Hume

2
Geç statik bağlamaların hangi "sorunları" olduğunu iddia ediyorsunuz? Burada kanıt veya açıklama olmadan yapılan cesur bir iddia olan "kırılmış" olduklarını iddia ediyorsunuz. Bu özellik benim için her zaman iyi çalıştı ve bu gönderinin saçma olduğunu düşünüyorum.
Mark Amery
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.