PHP'deki döküm değişkenlerini yazın, bunu yapmanın pratik nedeni nedir?


45

PHP, çoğumuzun bildiği gibi, yazarak zayıf . Yapmayanlar için, PHP.net diyor ki:

PHP değişken bildiriminde açık tür tanımı gerektirmez (veya desteklemez); değişkenin türü değişkenin kullanıldığı bağlamda belirlenir.

Sevin ya da nefret et, PHP anında değişkenleri tekrar yayınlar. Bu nedenle, aşağıdaki kod geçerlidir:

$var = "10";
$value = 10 + $var;
var_dump($value); // int(20)

PHP ayrıca açıkça şöyle bir değişken atamanıza izin verir:

$var = "10";
$value = 10 + $var;
$value = (string)$value;
var_dump($value); // string(2) "20"

Hepsi harika… ama benim hayatım boyunca, bunu yapmak için pratik bir neden düşünemiyorum.

Java gibi, onu destekleyen dillerde yazarken bir sorunum yok. Bu iyi ve ben tamamen anlıyorum. Ayrıca, fonksiyon parametrelerindeki tip ipucunun ne kadar yararlı olduğunun farkındayım - ve tamamen anlıyorum .

Tip döküm ile ilgili problemim yukarıdaki alıntı ile açıklanmıştır. PHP, istediği zaman türleri değiştirebilirse , bir türü kullanmaya zorladıktan sonra bile yapabilir ; ve bir işlemde belirli bir türe ihtiyacınız olduğunda bunu anında yapabilir . Bu, aşağıdakileri geçerli kılar:

$var = "10";
$value = (int)$var;
$value = $value . ' TaDa!';
var_dump($value); // string(8) "10 TaDa!"

Öyleyse amaç ne?


Bu, kullanıcı tanımlı tür dökümünün PHP'de anlamlı olduğu bir dünyaya ilişkin teorik örneği ele alalım :

  1. Cast değişkenini → $fooolarak zorlarsınız .int(int)$foo
  2. Değişkende bir dize değeri kaydetmeye çalışıyorsunuz $foo.
  3. PHP bir istisna atar! ← Bu mantıklı olur. Birdenbire kullanıcı tanımlı tip dökümün nedeni var!

PHP'nin işleri gerektiği gibi değiştireceği gerçeği, kullanıcı tanımlı tip döküm belirsizliğini işaret eder. Örneğin, aşağıdaki iki kod örneği eşdeğerdir:

// example 1
$foo = 0;
$foo = (string)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

// example 2
$foo = 0;
$foo = (int)$foo;
$foo = '# of Reasons for the programmer to type cast $foo as a string: ' . $foo;

Başlangıçta bu soruyu sorduktan bir yıl sonra, kendini pratik bir ortamda typecasting kullanarak kim buldu? Saygılarımla.

Bir gereklilik, bir restoran menüsünde bir web sitesinde para değerlerini göstermekti. Sitenin tasarımı, takip eden sıfırların kırpılmasını gerektirdi, böylece ekran aşağıdaki gibi görünüyordu:

Menu Item 1 .............. $ 4
Menu Item 2 .............. $ 7.5
Menu Item 3 .............. $ 3

Değişkeni bir şamandıra olarak atmak için bu atıkları yapmanın en iyi yolu:

$price = '7.50'; // a string from the database layer.
echo 'Menu Item 2 .............. $ ' . (float)$price;

PHP, float'ın sondaki sıfırlarını keser ve ardından float'ı birleştirme için bir dize olarak yeniden ifade eder.


Bu -> $ değer = $ değer. 'TaDa!'; $ Değerinin son değerine atanmadan önce $ değerini dizgeye geri döndürürdü. Bir çeşit oyuncu kadrosu zorlarsanız, bir tür oyuncu alıştırması almanız şaşırtıcı değildir. Amacının ne olduğunu sormakta emin değil misiniz?
Chris,

"# 3. PHP bir istisna atar !! <--- Bu mantıklı olur." Aslında bu hiç mantıklı gelmiyordu. Bu bile Java, JavaScript veya bildiğim diğer C sözdizimi dillerinde sorun değil. Aklı başında kim bunu istenen davranış olarak görür? Her yerde(string) yayın yapmak ister misin ?
Nicole,

@Renesis: Beni yanlış anladınız. Demek istediğim, yalnızca bir kullanıcı değişken yazdıysa, bir istisna atılacaktı. Normal davranış (PHP sizin için döküm yapar) elbette bir istisna atmaz. Kullanıcı tanımlı tip dökümün moot olduğunu söylemeye çalışıyorum , ancak bir istisna atılacak olsaydı aniden bir anlam ifade ederdi.
Stephen,

Bir $intval.'bar'istisna atarsan, hala aynı fikirde değilim. Bu herhangi bir dilde bir istisna atmaz. (Bildiğim tüm diller ya otomatik bir ya da a .toString()). Bir $intval = $stringvalistisna atarsanız, o zaman kesinlikle yazılmış bir dilden bahsediyorsunuz. Kaba görünmek istemedim, öyleyse özür dilerim. Sadece her geliştiricinin alıştığı şeye aykırı olduğunu ve çok daha az kullanışlı olduğunu düşünüyorum.
Nicole,

@Stephen - Bir soruşturmadan sonra cevap yazdım. Gerçekten ilginç sonuçlar - Davaların 2'sinin oyuncu seçimi için kesin bir amaç göstereceğini düşündüm, ancak PHP düşündüğümden daha garip.
Nicole,

Yanıtlar:


32

Zayıf yazılmış bir dilde, yazma işlemi, yazılan işlemlerde belirsizliği ortadan kaldırmak için var olur; aksi halde derleyici / yorumlayıcı hangi işlemi kullanacağını varsaymak için düzen veya diğer kuralları kullanır.

Normalde PHP'nin bu modeli takip ettiğini söyleyebilirim, ancak kontrol ettiğim durumlardan birinde, PHP her birinde sezgisel olarak davrandı.

JavaScript'i karşılaştırma dili olarak kullananlar.

String Concatentation

Açıkçası bu, PHP'de bir sorun değil çünkü dize birleştirme ( .) ve adding ( +) operatörleri var.

JavaScript
var a = 5;
var b = "10"
var incorrect = a + b; // "510"
var correct = a + Number(b); // 15

Dize Karşılaştırma

Genellikle bilgisayar sistemlerinde "5", "10" dan büyüktür, çünkü onu sayı olarak yorumlamaz. PHP'de, her ikisi de dizge olsalar bile , sayılar olduklarını fark ederler ve oyuncular için bir gereksinimi ortadan kaldırırlar):

JavaScript
console.log("5" > "10" ? "true" : "false"); // true
PHP
echo "5" > "10" ? "true" : "false";  // false!

İşlev imzası yazma

PHP işlev imzaları üzerinde çıplak tipte bir kontrol uygular, fakat ne yazık ki o kadar kusurludur ki, muhtemelen nadiren kullanılabilir.

Yanlış bir şey yapabileceğimi düşündüm, ancak belgelere yapılan bir yorum, dizi dışındaki yerleşik türlerin PHP işlev imzalarında kullanılamayacağını doğruladı - hata mesajı yanıltıcı olsa da.

PHP
function testprint(string $a) {
    echo $a;
}

$test = 5;
testprint((string)5); // "Catchable fatal error: Argument 1 passed to testprint()
                      //  must be an instance of string, string given" WTF?

Ve bildiğim diğer dillerden farklı olarak, anladığı bir tür kullanıyor olsanız bile, null artık o argümana geçemez ( must be an instance of array, null given). Ne kadar aptal.

Boolean yorumlama

[ Düzenle ]: Bu yeni. Başka bir vaka düşündüm ve yine mantık JavaScript'ten tersine çevrildi.

JavaScript
console.log("0" ? "true" : "false"); // True, as expected. Non-empty string.
PHP
echo "0" ? "true" : "false"; // False! This one probably causes a lot of bugs.

Sonuç olarak, düşünebildiğim tek yararlı vaka ... (drumroll)

Türü kısalt

Başka bir deyişle, bir tür değerine sahipseniz (dize) ve onu başka bir tür (int) olarak yorumlamak istediğinizde ve o türdeki geçerli değerler kümesinden biri olmaya zorlamak istediğinizde:

$val = "test";
$val2 = "10";
$intval = (int)$val; // 0
$intval2 = (int)$val2; // 10
$boolval = (bool)$intval // false
$boolval2 = (bool)$intval2 // true
$props = (array)$myobject // associative array of $myobject's properties

Hangi upcasting'in (daha fazla değer içeren bir türe) gerçekten kazanacağınızı göremiyorum.

Bu yüzden, önerilen yazı tipini kullanımınıza katılmıyorum (temel olarak statik yazı tipini öneriyorsunuz , ancak belirsizliğe ancak bir türe zorla basıldığında hata yapabileceğini - bunun kafa karışıklığına neden olacağı) soru, çünkü görünüşe göre döküm PHP'de çok az bir amaç var.


Tamam, peki ya E_NOTICEsonra? :)
Stephen

@Stephen iyi E_NOTICEolabilir, ama bana göre belirsiz durum ilgili - eğer değişken bu durumda olsaydı (başka bir yere atılmış olsaydı) bir bit koda bakarak nasıl bilebilirdiniz? Ayrıca başka bir koşul buldum ve cevabımı ekledim.
Nicole,

1
Boolean değerlendirmesine gelince, PHP belgeleri, booleani değerlendirirken neyin yanlış olduğunu kabul ettiğini açıkça ifade eder ve hem boş dize hem de "0" dizgisi yanlış kabul edilir. Yani bu garip hissettirse bile, normal ve beklenen bir davranış.
Jacek Prucia

kafa karışıklığına biraz eklemek için: echo "010" == 010 ve echo "0x10" == 0x10;-)
vartec

1
Not itibariyle bu PHP 7 , skaler tip ipuçlarının bu cevabım notlar yanlıştır.
John V.

15

Zayıf / güçlü ve dinamik / statik tip kavramları karıştırıyorsunuz.

PHP zayıf ve dinamik, ancak probleminiz dinamik tip kavramı ile. Bu, değişkenlerin bir türünün olmadığı, değerlerin olduğu anlamına gelir.

'Tip döküm', farklı türde bir orijinalin yeni bir değerini üreten bir ifadedir; değişkene hiçbir şey yapmaz (eğer varsa).

Düzenli olarak cast değerleri yazdığım bir durum sayısal SQL parametrelerindedir. SQL ifadelerine eklediğiniz girdi değerlerini temizlemeniz / kaçmanız veya (çok daha iyi) parametreli sorguları kullanmanız gerekir. Ancak, bir tamsayı olması gereken bir değer istiyorsanız, sadece atması çok daha kolaydır.

Düşünmek:

function get_by_id ($id) {
   $id = (int)$id;
   $q = "SELECT * FROM table WHERE id=$id LIMIT 1";
   ........
}

ilk satırı bırakmış olsam, $idSQL enjeksiyonu için kolay bir vektör olurdu. Oyuncular, bunun zararsız bir tamsayı olmasını sağlar; SQL eklemek için yapılan herhangi bir girişim, bir sorguya yol açar.id=0


Bunu kabul edeceğim. Şimdi, Tip Dökümün faydası kadar mı?
Stephen,

SQL enjeksiyonunu getirmen çok komik. Kullanıcı girişini dezenfekte etmek için bu tekniği kullanan biriyle SO hakkında tartışıyordum. Fakat bu yöntem hangi problemi mysql_real_escape_string($id);çözmedi?
Stephen,

daha kısadır :-) tabii ki, dizgiler için parametreli sorguları kullanıyorum ya da (eski mysql uzantısını kullanıyorsanız) ondan kaçıyor.
Javier,

2
mysql_real_escape_string()'0x01ABCDEF' gibi dizgilere hiçbir şey yapmama konusunda savunmasız (örneğin bir tamsayının onaltılık gösterimi) vardır. Bazı çok baytlı kodlamalarda (Unicode değil, lucklily değil) böyle bir dize sorguyu kırmak için kullanılabilir (çünkü MySQL tarafından bir alıntı içeren bir şeye göre değerlendirilir). Bu yüzden ne tamsayı değerleri ile başa çıkmak için en iyi seçenek mysql_real_escape_string()değildir is_int(). Typecasting olduğunu.
Milch

Daha fazla ayrıntı içeren bir bağlantı: ilia.ws/archives/…
Mchl

4

Bulunduğum PHP türü döküm için bir kullanım:

Ben bir veritabanından veri almak için bir sunucudaki PHP komut dosyalarına http istekleri yapan bir android uygulaması geliştiriyorum. Komut dosyası, bir PHP nesnesi (veya ilişkisel dizi) biçiminde veri depolar ve uygulamaya bir JSON nesnesi olarak döndürülür. Döküm türü olmasaydı şöyle bir şey alırdım:

{ "user" : { "id" : "1", "name" : "Bob" } }

Ancak, (int)PHP nesnesini saklarken kullanıcının kimliğine PHP türü döküm kullanarak , bunun yerine uygulamaya geri döndürdüm:

{ "user" : { "id" : 1, "name" : "Bob" } }

Sonra JSON nesnesi uygulamada ayrıştırıldığında, kimliği bir Tamsayıya ayrıştırmak zorunda kalmamdan beni kurtarıyor!

Gördün mü, çok kullanışlı.


Tüketmek için harici, güçlü tip sistemler için veri biçimlendirme düşünmemiştim. +1
Stephen

Bu özellikle JSON’u Elasticsearch gibi harici sistemlerle konuşurken geçerlidir. Bir json_encode () - ed değeri "5", 5 değerinden çok farklı sonuçlar verir.
Johan Fredrik Varen

3

Bunun bir örneği, bir __toString yöntemle nesneler: $str = $obj->__toString();v $str = (string) $obj;. İkincisinde çok daha az yazım var ve fazladan şeyler ise noktalama, yazması daha uzun sürüyor. Ayrıca daha okunaklı olduğunu düşünüyorum, ancak diğerleri aynı fikirde olmayabilir.

Başka bir tek öğeli bir dizi yapmaktadır: array($item);v (array) $item;. Bu, herhangi bir skalar tipini (tamsayı, kaynak vb.) Bir dizinin içine koyacaktır.
Alternatif olarak, eğer $itembir nesne ise, özellikleri değerlerinin anahtarı olacaktır. Ancak, object-> array dönüşümünün biraz tuhaf olduğunu düşünüyorum: private ve korumalı özellikler dizinin bir parçası ve yeniden adlandırıldı. PHP belgelerini alıntılamak için : özel değişkenler değişken adına önceden belirlenmiş sınıf ismine sahiptir; Korunan değişkenler değişken adına önceden hazırlanmış bir '*' işaretine sahiptir.

Diğer bir kullanım, GET / POST verilerini bir veritabanı için uygun tiplere dönüştürmektir. MySQL bunu kendisi halledebilir, ancak daha ANSI uyumlu sunucuların verileri reddedebileceğini düşünüyorum. Yalnızca veritabanlarından bahsetmemin nedeni, diğer birçok durumda, verilerin üzerinde belirli bir noktada türüne göre gerçekleştirilen bir işlemi gerçekleştirmesidir (yani int / floats genellikle üzerinde hesaplamalar yapar, vb.).


Bunlar, tip dökümün nasıl çalıştığının harika örnekleridir. Ancak, bir ihtiyacı doldurduklarına ikna olmadım . Evet, bir nesneyi diziye dönüştürebilirsiniz, ancak neden? Sanırım yeni dizi üzerinde sayısız PHP dizi işlevini kullanabildiğiniz için, ancak bunun nasıl yararlı olacağını anlayamıyorum. Ayrıca, PHP genellikle MySQL veritabanına göndermek için string sorguları yaratır, bu nedenle değişken tipi alakasızdır (sorgu oluşturulurken otomatik string dönüşümü gerçekleşir intveya floatgerçekleşir). (array) $itemolan temiz , ama yararlı?
Stephen,

Ben de aynı fikirdeyim. Onları yazarken bazı kullanımları düşüneceğimi düşündüm, ama yapmadım. Veritabanı işleri için, parametreler sorgu dizesinin bir parçasıysa, haklısınızdır, döküm yapmanın bir amacı yoktur. Ancak, parametreli sorgular kullanılırken (bu her zaman iyi bir fikirdir), parametrelerin türlerini belirtmek mümkündür.
Alan Pearce,

Aha! Parametreli Sorgular ile geçerli bir nedene çarpmış olabilirsiniz.
Stephen,

0

Bu komut dosyası:

$tags = _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

para cezası için çalışacak script.php?tags[]=oneancak başarısız olacaktır script.php?tags=one, çünkü _GET['tags']birinci durumda bir dizi döndürür, ikincisinde değil. Betik bir dizi beklemek için yazıldığından (ve betiğe gönderilen sorgu dizgisi üzerinde daha az kontrole sahipseniz), sonucu uygun şekilde yayınlayarak çözülebilir _GET:

$tags = (array) _GET['tags'];
foreach ($tags as $tag) {
    echo 'tag: ', $tag;
}

0

Ayrıca, güvenilmeyen verilerin bir şeyi kırmayacağından emin olmak için hızlı ve kirli bir yöntem olarak da kullanılabilir; örneğin, hızlı onaylama olan ve yalnızca numaraları kabul etmesi gereken bir uzak hizmet kullanıyorsanız.

$amount = (float) $_POST['amount'];

if( $amount > 0 ){
    $remoteService->doacalculationwithanumber( $amount );    
}

Açıkçası bu, if ifadesinde karşılaştırma operatörü tarafından örtük olarak da açıklanır ve ele alınır, ancak kodunuzun ne yaptığını tam olarak bilmenizi sağlamaya yardımcı olur.


1
Bunun dışında kırılmaz. $_POST['amount']Bir çöp teli olsa bile , php sıfırdan büyük olmadığını değerlendirirdi. Olumlu bir sayıyı temsil eden bir dize içeriyorsa, doğru olarak değerlendirirdi.
Stephen

1
Tamamen doğru değil. $ Tutarının, şartname dahilinde bir sayı alması gereken üçüncü taraf bir servise iletildiğini düşünün. Birisi $ _POST ['miktar'] = "100 bobin" 'i geçecek olsaydı, kaldırma (kaldırma) hala koşullu geçişi sağlar ancak $ tutarı bir sayı olmazdı.
Gruffput

-2

Kullanım sırasında sık sık gördüğüm PHP değişkenlerinin yeniden kullanım değişkenlerinin "kullanımı", harici kaynaklardan (kullanıcı girişi veya veritabanı) veri alırken ortaya çıkar. Kodlayıcıların (geliştiricilerin söylemediğimi not edin ) farklı kaynaklardan edinilen farklı veri türlerini görmezden gelmesini (hatta öğrenmemesini) sağlar.

Mirasını aldığım ve hala sürdürdüğüm kodlarından biri olan bir kodlayıcı (geliştirici demediğimi belirttiğime dikkat edin) , süper değişkende döndürülen dize arasında, tamsayı işlemi arasında"20"$_GET20 + 20 ekleyeceği zaman arasında bir fark olduğunu bilmiyor gibi görünüyor . Veritabanındaki değer. PHP'nin .dize bitiştirme için kullandığı ve +diğer tüm dillerdeki gibi olmadığı için şanslı , çünkü iki dizgiyi ("MySQL'den varcahrbir değerden " a ekledi $_GET) ve bir int aldığını gördüm .

Bu pratik bir örnek mi? Yalnızca kodlayıcıların, hangi veri türleriyle çalıştıklarını bilmemekle kurtulmalarına izin vermesi anlamındadır. Şahsen nefret ediyorum.


2
Bu cevabın tartışmaya nasıl değer kattığını anlamıyorum. PHP'nin bir mühendisin (ya da programcının ya da kodlayıcının, size ait olanların) dizelerde matematik işlemlerini gerçekleştirmesine izin vermesi, soruda zaten çok açıktır.
Stephen

Teşekkürler Stephen. Belki de "PHP, bir veri tipinin ne olduğunu bilmeyen insanlara, ideal koşullar altında beklediklerini yapan uygulamalar oluşturmasını sağlar" demek için çok fazla kelime kullandım.
dotancohen
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.