2015-08-04 9 views
7

Bazı birim testleri çalıştırıyordum ve kullanmakta olduğum anahtar ifadesiyle beklenmedik bir davranışla karşılaştım. Aşağıdaki koşulu izole ettim.Beklenmeyen PHP Anahtarı Davranışı

test('a') => 'first' 
test('b') => 'first' 
test('c') => 'second' 
test('d') => 'third'  
test('0') => 'third' 
test('1') => 'third' 
test('true') => 'third' 
test('false') => 'third' 

Bu güzel kendini apaçık haklı: Burada

function test($val) 
{ 
    switch($val) 
    { 
     case 'a': 
     case 'b': 
      return 'first'; 
     break; 
     case 'c': 
      return 'second'; 
     break; 
     default: 
      return 'third'; 
    } 
} 

testlerin ilk yuvarlak? Tamam şimdi şunu kontrol et:

test(0)  => 'first' // expected 'third' 
test(1)  => 'third' 
test(true) => 'first' // expected 'third' 
test(false) => 'third' 
test(null) => 'third' 
test([]) => 'third' 

0 ile doğru olan garip sonuçlarda ne var? 1/true ve 0/false aynı değerleri döndürdüğünde yazıyı yazmaya çalışırım. Ama yapmıyorlar!

Değeri bir (dize) olarak değiştirirsem, anahtar istenen şekilde çalışır. alışkanlık anahtarı "iş" Ben kullanmadan amaçladığı gibi "(string)"

birisi bu neden oluyor açıklayabilir neden

test((string) 0)  => 'third' 
test((string) 1)  => 'third' 
test((string) true) => 'third' 
test((string) false) => 'third' 

anlamıyorum?

+1

Anahtar/kasa gevşek bir karşılaştırma yapıldığını unutmayın. http://php.net/manual/en/types.comparisons.php#types.comparisions-loose –

+5

Ve evet, '0', 'a', gevşek karşılaştırmada eşit iken, '1' değil. https://eval.in/412018 – Jessica

+0

0 == 'a' .... Bu yüzden PHP olur. – GolezTrol

cevap

2

Başına PHP'nin belgeleri: geçiş

Not/vaka gevşek karşılaştırma yapar. Eğer tip karşılaştırma yapmak istiyorsanız

http://php.net/manual/en/control-structures.switch.php

, kodunuzu yeniden gerekecektir. Örnek: Ben herhangi else 's yok nasıl

function test($val) 
{ 
    if($val === 'a' || $val === 'b') 
     return 'first'; 

    if($val === 'c') 
     return 'second'; 

    return 'third'; 
} 

dikkat edin. Bunun nedeni, her ifadenin bir şey döndürmesidir ... Aksi halde işlev varsayılan olarak third değerini döndürür.

3

Bu beklenen bir davranıştır. Karşılaştırmalar yaparken, PHP, bir eşleşme arayışında bir değerin türünü değiştirecektir.

test(0)  => 'first' // 'a' is altered into int 0 and therefore matches 
var_dump((int) 'a'); // results 'int(0)' 

test(true) => 'first' // both true and 'a' are truthy statements therefore matches. 
if ('a' == true) echo "its true"; 

PHP, zayıf yazılmış bir dildir ve bazen sizi popoda ısırır. Anahtarın bir if/else if/else yapısına yeniden faktoringini düşünebilirsiniz ve güçlü karşılaştırmalar için === operatörünü kullanabilirsiniz.

0

Basit cevaplar aldınız: comaprison. Ama çözüm nedir? Eğer karşılaştırmak isterseniz numaraları dizeleri kullanmayın. Eğer boole istiyorsanız boole kullanın. İşlev/yöntem çağırılmadan önce değişkenleri doğrulamaya çalışın (veya yayınlamaya çalışın).Eğer böyle bir şey yapabilirsiniz int istiyorsanız, kesinlikle yazılı bir dilde böyle kodunuzu yazın:

/** 
* @param int $val 
*/ 
function test($val) 
{ 
    //exception 
    if (!is_int($val)) { 
     throw new InvalidArgumentException('$val expected to be int'); 
    } 
    //or cast - but may beahave unexpected 
    $val = (int)$val 

    switch($val) 
    { 
     case 0: 
      return 'first'; 
     break; 
     case 1: 
      return 'second'; 
     break; 
     default: 
      return 'third'; 
    } 
} 
0

uygun şekilde $val döküm ve/veya gerekirse türünü ortaya koymak zorundalar böylece PHP, weakly (or loosely) typed geçerli:

yukarıdaki muhtemelen istenmeyen sahip olduğunu hatırlatırız, sen testlerini gerçekleştirme sözettiğimize

$opts = [ 
    'a' => 'first', 
    'b' => 'first', 
    ... 
]; 
foreach ($opts as $opt => $response) { 
    if ($opt === $val) { 
     return $response; 
    } 
} 
return 'default'; 

:

/** 
* @param string $val 
*/ 

function test($val) 
{ 
    assert('is_string($val)', 'expecting string'); 
    switch((string)$val) { 
     case 'a': 

} 

alternatif bir sözlük kullanmaktır maskeleme maskelemesi karmaşıklığı, yani gerçekten orada count($opts) +1 karar yollarına sahipsiniz, ancak temel döngüsel karmaşıklık sadece ikiyi görür.

Daha garip kurulum, o

private function testA($val) { 
    return 'first'; 
} 
private function testB($val) { 
    return $this->testA($val); // or 'first' again 
} 
... 

public function test($val) { 
    if (!is_class_method($this, $method = 'test' . $val)) { 
     $method = 'testDefault'; 
    } 
    $this->$method($val); 
} 

yukarıda tek bir birlik içinde tüm seçenekler mevcuttur izin vermeyecek gibi dezavantajı var olabilir karmaşıklığı (yani testlerden doğru kod kapsama almak) korur (yani anahtar) ve sadece bir işlev adında kullanılabilecek değerlere sahipseniz ve onay büyük/küçük harfe duyarsızsa kabul edilebilir. Ama birkaç yolu denedikten sonra, bu işe yarabilir bir uzlaşma buluyorum.

0

Kısa cevap:

tüm yanıtlar için teşekkür ederiz giriş değeri attı. Aşağıdaki bağlantı, neler olup bittiği hakkında kafamı sarmak için çok yardımcı oldu.

http://php.net/manual/en/types.comparisons.php#types.comparisions-loose

Ben test tamsayılar ile kullanılmak üzere tasarlanmıştır asla oldu yöntem. Sadece eğlenmek için birkaç farklı tür atmak ve kazayla istenmeyen davranışlara rastladım. Bütün bu cevapları okuduktan sonra farklı bir yaklaşım benimseyecek ve bir anahtar deyimi kullanamayacağım. @lserni'nin bahsettiğim sözlük yaklaşımı, bu özel uygulama için gidilecek yol olduğunu düşünüyorum. Bununla birlikte, aynı kodu saklamak isteseydim, @Styx'in önerdiği şeyi yapmak ve değeri bir (string) olarak vermek hızlı bir çözüm olacaktır.

Teşekkürler!