İsset () ve empty () nasıl önlenir


98

E_NOTICE hata seviyesinde çalışırken çok sayıda "xyz is undefined" ve "undefined offset" mesajlarını atan birkaç eski uygulamaya sahibim, çünkü değişkenlerin varlığı açık bir şekilde kontrol edilmiyor isset()ve uyumlu.

Bunları E_NOTICE uyumlu hale getirmek için çalışmayı düşünüyorum, çünkü eksik değişkenler veya ofsetler ile ilgili bildirimler hayat kurtarıcı olabilir, elde edilecek bazı küçük performans iyileştirmeleri olabilir ve bu genel olarak daha temiz bir yoldur.

Ancak, koduma yüzlerce isset() empty()ve array_key_exists()s verenin ne yaptığı hoşuma gitmiyor . Değer veya anlam açısından hiçbir şey kazanmadan şişirilir, daha az okunabilir hale gelir.

E_NOTICE uyumlu olurken, fazla değişken kontrolü olmadan kodumu nasıl yapılandırabilirim?


6
Tamamen katılıyorum. Bu yüzden Zend Framework'ü çok seviyorum, orada istek modülü çok iyi. Küçük bir uygulama üzerinde çalışıyorsam, genellikle ZF'nin isteğine benzer şekilde çalışan __set ve __get sihirli yöntemleriyle bazı basit istek sınıflarını kodlarım. Bu şekilde, kodumdaki tüm isset ve boş olaylardan kaçınırım. Bu şekilde kullanmanız gereken tek şey, diziler üzerinde yinelemeden önce if (count ($ arr)> 0) ve birkaç kritik yerde if (null! == $ variable). Çok daha temiz görünüyor.
Richard Knop

Yanıtlar:


128

İlgilenenler için, bu konuyu, aşağıdaki bilgileri biraz daha iyi yapılandırılmış bir biçimde sağlayan küçük bir makaleye genişlettim: The Definitive Guide to PHP's isset And empty


IMHO sadece uygulamayı "E_NOTICE uyumlu" yapmayı değil, her şeyi yeniden yapılandırmayı düşünmelisiniz. Having yüzlerce kodunuzda noktalarının düzenli oldukça kötü yapılandırılmış program gibi varolmayan değişkenler sesleri kullanmayı deneyin. Varolmayan değişkenlere erişmeye çalışmak asla olmamalıdır, diğer diller derleme zamanında buna karşı koyarlar. PHP'nin bunu yapmanıza izin vermesi, yapmanız gerektiği anlamına gelmez.

Bu uyarılar sizi kızdırmak için değil, size yardımcı olmak için var. "Var olmayan bir şeyle çalışmaya çalışıyorsunuz!" Uyarısı alırsanız , tepkiniz "Oops, benim hatam, en kısa zamanda düzeltmeme izin verin." Başka nasıl ayırt edeceğiz "iş gayet tanımlanmamış olduğu değişkenler" ve ciddi hatalara yol açabilir dürüstçe yanlış kod ? Bu aynı zamanda, her zaman, her zaman , hata raporlamasını 11'e döndürerek geliştirmenizin ve tek bir hata kalmayıncaya kadar kodunuzu kapatmaya devam etmenizin nedenidir.NOTICEverilir. Hata raporlamayı kapatmak, bilgi sızıntısını önlemek ve hatalı kod karşısında bile daha iyi bir kullanıcı deneyimi sağlamak için yalnızca üretim ortamları içindir.


Detaylandırmak için:

Her zaman ihtiyacınız olacak issetveya emptykodunuzun herhangi bir yerinde, bunların oluşumunu azaltmanın tek yolu değişkenlerinizi doğru şekilde başlatmaktır. Duruma bağlı olarak, bunu yapmanın farklı yolları vardır:

Fonksiyon argümanları:

function foo ($bar, $baz = null) { ... }

Olmadığını kontrol etmek için gerek yoktur $barveya $bazvarsa onların değer değerlendirir etmektir hakkında sadece bunları çünkü işlevi içinde ayarlanır, bütün endişe gerekir trueveya false(veya ne olursa olsun başka).

Her yerde normal değişkenler:

$foo = null;
$bar = $baz = 'default value';

Değişkenlerinizi kullanacağınız bir kod bloğunun en üstünde başlatın. Bu, !issetsorunu çözer, değişkenlerinizin her zaman bilinen bir varsayılan değere sahip olmasını sağlar, okuyucuya aşağıdaki kodun ne üzerinde çalışacağı konusunda bir fikir verir ve böylece bir tür kendi kendini belgelendirme işlevi görür.

Diziler:

$defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value');
$values = array_merge($defaults, $incoming_array);

Yukarıdaki ile aynı şekilde, diziyi varsayılan değerlerle başlatıyorsunuz ve bunların üzerine gerçek değerlerle yazıyorsunuz.

Kalan durumlarda, bir denetleyici tarafından ayarlanabilen veya ayarlanmayan değerleri çıktıladığınız bir şablon diyelim, sadece kontrol etmeniz gerekecek:

<table>
    <?php if (!empty($foo) && is_array($foo)) : ?>
        <?php foreach ($foo as $bar) : ?>
            <tr>...</tr>
        <?php endforeach; ?>
    <?php else : ?>
        <tr><td>No Foo!</td></tr>
    <?php endif; ?>
</table>

Kendinizi düzenli olarak kullanırken bulursanız array_key_exists, ne için kullandığınızı değerlendirmelisiniz. Fark yarattığı tek zaman burası:

$array = array('key' => null);
isset($array['key']); // false
array_key_exists('key', $array); // true

Yine de yukarıda belirtildiği gibi, değişkenlerinizi doğru şekilde başlatıyorsanız, anahtarın var olup olmadığını kontrol etmenize gerek yoktur, çünkü var olduğunu biliyorsunuz. Eğer harici bir kaynaktan dizi alıyorsanız, değer büyük olasılıkla olmayacak nullama '', 0, '0', falseveya bir benzeri, yani bir değer ile değerlendirebilir issetya emptyda niyet bağlı. Düzenli olarak bir dizi anahtarını ayarladıysanız nullve bunun bir şey ifade etmesini istemiyorsanız false, örneğin yukarıdaki örnekte program mantığınızın farklı sonuçları issetve array_key_existsprogram mantığınızda bir fark yaratıyorsa, kendinize nedenini sormalısınız. Bir değişkenin salt varlığı önemli olmamalı, sadece değeri önemli olmalıdır. Anahtar bir true/ falsebayrak ise, o zaman kullanıntrueya da falsedeğil null. Bunun tek istisnası, bir nullşey ifade etmek isteyen 3. parti kütüphaneler olabilir , ancak nullPHP'de tespit edilmesi çok zor olduğundan , bunu yapan herhangi bir kütüphane bulamadım.


4
Doğru, ancak başarısız erişim girişimlerinin çoğu if ($array["xyz"])yerine isset()veya array_key_exists()benim biraz meşru bulduğum, kesinlikle yapısal sorunlar değil (yanılıyorsam beni düzeltin). Eklemek array_key_exists()bana korkunç bir israf gibi görünüyor.
Pekka

9
array_key_existsBasit isset($array['key'])veya yerine kullanacağım herhangi bir durum düşünemiyorum !empty($array['key']). Elbette, her ikisi de kodunuza 7 veya 8 karakter ekler, ancak bunu bir sorun olarak görmüyorum. Ayrıca kodunuzu netleştirmenize de yardımcı olur: if (isset($array['key']))bu değişkenin gerçekten isteğe bağlı olduğu ve bulunmayabileceği if ($array['key'])anlamına gelir , ancak "doğruysa" anlamına gelir. İkincisi için bir uyarı alırsanız, mantığınızın bir yere saplandığını bilirsiniz.
deceze

6
İsset () ve array_key_exists () arasındaki farkın, değer NULL ise ikincisinin true döneceğine inanıyorum. isset () olmayacak.
Htbaa

1
Doğru, ancak var olmayan bir değişken ile değeri boş olan bir ayar anahtarı arasında ayrım yapmam gereken mantıklı bir kullanım durumu düşünemedim. Değer YANLIŞ olarak değerlendirilirse, ayrımın hiçbir farkı olmaması gerekir. :)
deceze

1
Dizi anahtarları kesinlikle tanımlanmamış değişkenlerden daha can sıkıcıdır. Eğer emin bir dizi bir anahtar içerir olsun veya olmasın Ama eğer anlama ya sen Array kendini tanımlamak vermedi ya sen kontrol etmediğini bir kaynaktan çekerek ediyoruz. Her iki senaryo da çok sık gerçekleşmemelidir; ve eğer bu olursa, dizinin içerdiğini düşündüğünüz şeyi içerip içermediğini kontrol etmek için her türlü nedene sahipsiniz. Bu bir güvenlik önlemi IMO.
kijin

37

Bunun için bir fonksiyon yazmanız yeterli. Gibi bir şey:

function get_string($array, $index, $default = null) {
    if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) {
        return get_magic_quotes_gpc() ? stripslashes($value) : $value;
    } else {
        return $default;
    }
}

hangisini kullanabilirsin

$username = get_string($_POST, 'username');

Gibi önemsiz şeyler için de yapın get_number(), get_boolean(), get_array()ve bu kadar.


5
Bu iyi görünüyor ve magic_quotes denetimi de yapıyor. Güzel!
Pekka

Harika işlev! Paylaştığınız için çok teşekkürler.
Mike Moore

3
$ _POST ['bir şey'] dizi döndürebilir, örneğin ile girişler <input name="something[]" />. Bu, yukarıdaki kodu kullanarak hataya neden olabilir (dizilere trim uygulanamadığı için), bu durumda biri kullanılmalı is_stringve muhtemelen kullanılmalıdır strval. Bu, get_arraykullanıcı girişi (kötü amaçlı), herhangi bir şey ve kullanıcı giriş ayrıştırıcısı hiçbir şekilde hata vermemesi nedeniyle , birinin kullanılması gereken bir durum değildir .
Ciantic

1
Aynı tür bir işlevi kullanıyorum ama şu şekilde tanımlıyorum: function get_value (& $ öğe, $ varsayılan = NULL) {dönüş isset ($ öğe)? $ öğe: $ varsayılan; } Bu işlevin avantajı, onu diziler, değişkenler ve nesnelerle çağırabilmenizdir. Dezavantajı, $ item'in, eğer değilse, daha sonra başlatılması (boşa) olmasıdır.
Mat

Sihirli alıntıları 1 işlevde ele almak yerine küresel olarak kapatmalısınız. İnternette sihirli alıntıları açıklayan birçok kaynak var.
Kayla

13

Bu problemle baş etmenin en iyi yollarından birinin bir sınıf üzerinden GET ve POST (COOKIE, SESSION, vb.) Dizilerinin değerlerine erişmek olduğuna inanıyorum.

Bu dizilerin her biri için bir sınıf oluşturun ve açıklama __getve __setyöntemler ( aşırı yükleme ). __getbir değerin adı olacak bir argümanı kabul eder. Bu yöntem, bu değeri karşılık gelen global dizide kontrol etmeli isset()veya empty()varsa değeri veya null(veya başka bir varsayılan değeri) döndürmeli ve döndürmelidir .

Bundan sonra, dizi değerlerine bu şekilde güvenle erişebilirsiniz: $POST->usernameve gerekirse herhangi bir isset()s veya empty()s kullanmadan herhangi bir doğrulama yapabilirsiniz . Eğer usernamekarşılık gelen küresel dizide yok o zaman nullhiçbir uyarı veya bildirimler oluşturulur, böylece iade edilecektir.


1
Bu harika bir fikir ve kodu yeniden yapılandırmaya hazır olduğum bir şey. +1
Pekka

Maalesef bu örnekleri, oldukça çirkin olan $ _GET veya $ _POST'a atamadığınız sürece bu örnekleri süper küresel yapamazsınız. Ama elbette statik sınıfları da kullanabilirsiniz ...
ThiefMaster

1
Alıcı ve ayarlayıcıları "statik sınıflar" üzerinde kullanamazsınız. ve değişken başına bir sınıf yazmak kötü bir uygulamadır, çünkü kötü bir kod kopyası anlamına gelir. Bu çözümün en uygun çözüm olduğunu düşünmüyorum.
Mat

Bir sınıfın genel statik üyesi bir süper küresel gibi davranır, yani: HTTP :: $ POST-> kullanıcı adı, burada kullanımdan önce bir noktada HTTP :: $ POST'u başlatırsınız, yani. Sınıf HTTP {public static $ POST = array (); ...}; HTTP :: $ POST = new someClass ($ _ POST); ...
velcrow

6

array_key_exists()İşlevi kullanmak benim için sorun değil . Aslında, kullanmayı tercih bu özel işlevi yerine güvenerek kesmek ileride davranışlarını değiştirebilir fonksiyonları gibi emptyveisset (önlemek için strikedthrough duyarlılıkları ).


Bununla birlikte, bunda kullanışlı olan basit bir işlevi ve dizi indeksleriyle uğraşırken diğer bazı durumlarda kullanıyorum :

function Value($array, $key, $default = false)
{
    if (is_array($array) === true)
    {
        settype($key, 'array');

        foreach ($key as $value)
        {
            if (array_key_exists($value, $array) === false)
            {
                return $default;
            }

            $array = $array[$value];
        }

        return $array;
    }

    return $default;
}

Aşağıdaki dizilere sahip olduğunuzu varsayalım:

$arr1 = array
(
    'xyz' => 'value'
);

$arr2 = array
(
    'x' => array
    (
        'y' => array
        (
            'z' => 'value',
        ),
    ),
);

Dizilerden "değeri" nasıl çıkarırsınız? Basit:

Value($arr1, 'xyz', 'returns this if the index does not exist');
Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist');

Halihazırda örtülü tek ve çok boyutlu dizilerimiz var, başka ne yapabiliriz?


Örneğin aşağıdaki kod parçasını ele alalım:

$url = '/programming/1960509';
$domain = parse_url($url);

if (is_array($domain) === true)
{
    if (array_key_exists('host', $domain) === true)
    {
        $domain = $domain['host'];
    }

    else
    {
        $domain = 'N/A';
    }
}
else
{
    $domain = 'N/A';
}

Oldukça sıkıcı değil mi? İşte Value()işlevi kullanan başka bir yaklaşım :

$url = '/programming/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');

Ek bir örnek olarak, bir test için RealIP()işlevi alın :

$ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR')));

Düzgün, ha? ;)


6
"Gelecekte davranışlarını değiştirebilecek hack işlevlerine güvenmek" ?! Üzgünüm ama bu hafta boyunca duyduğum en saçma şeydi. Her şeyden önce, issetve emptyvardır dil yapıları değil, işlevleri. İkinci olarak, herhangi bir temel kütüphane işlevi / dil yapısı davranışlarını değiştirirse, mahvolabilir veya olmayabilirsiniz. Ya array_key_existsdavranışını değiştirirse? Cevap, belgelendiği gibi kullandığınız sürece olmayacak. Ve issetaynen böyle kullanıldığı belgelenmiştir. En kötü durum işlevleri, bir veya iki ana sürüm sürümünde kullanımdan kaldırılmıştır. NIH sendromu kötü!
deceze

Özür deceze değilim, ama her şeyden önce hack olduğu italik Fark etmedi durumda. =) İkinci olarak, bir dizidearray_key_exists() bir anahtarın var olup olmadığını kontrol etmeye güvenmemesi gerektiğini mi söylüyorsunuz ? oldu bunun için tam olarak oluşturulan , ben yerine bu amaç için güveniyorlar ve özel olarak kimin resmi açıklamasıdır: eğer gerçekten varsa "bir değişkenin boş olup olmadığını belirlemek", bir şey söz etmez. Yorumunuz ve olumsuz oyunuz, tüm ay tanık olduğum en saçma şeylerden biri . array_key_exists()isset()empty()
Alix Axel

3
Demek issetve emptyvardır hiçbir az ya da çok güvenilir daha array_key_existsve aynı işi yapabilir. İkinci, uzun soluklu örneğiniz, $domain = isset($domain['host']) ? $domain['host'] : 'N/A';yalnızca temel dil özellikleriyle yazılabilir , ekstra işlev çağrıları veya bildirimler gerekmez (üç terimli operatörün kullanımını mutlaka savunmadığımı unutmayın; o)). Sıradan skaler değişkenler için hala issetveya kullanmanız gerekecek emptyve bunları diziler için de aynı şekilde kullanabilirsiniz. "Güvenilirlik" bunu yapmamak için kötü bir nedendir.
deceze

1
Söylediğin şeylerin çoğuna katılmama rağmen, demek istediğini söyledin. Bence% 90'dan fazla durumda yanlış anladınız, mesela formlarda her zaman gizli alanlarda "0" değerini kullanıyorum. Yine de sağladığım çözümün olumsuz oylamayı hak etmediğine ve Pekka'nın işine yarayabileceğine inanıyorum .
Alix Axel

2
@Deceze özel işlevlerle ilgili bir noktaya sahipken - genellikle aynı duruşu benimsiyorum - value () yaklaşımı, ona bir göz atacak kadar ilginç görünüyor. Bence cevabın ve takibinin, ona daha sonra rastlayan herkesin kendi kararını vermesini sağlayacağını düşünüyorum. +1.
Pekka

3

Burada seninleyim. Ancak PHP tasarımcıları bundan çok daha kötü hatalar yaptı. Herhangi bir değer okuması için özel bir işlev tanımlamanın dışında, etrafında herhangi bir yol yoktur.


1
isset () şeyler. Her şeyi varsayılan olarak boş yapmak birçok sorundan kurtarır.
vava

2
Ve bu 'her şey' nedir? PHP için akla gelebilecek her değişken adını hayal etmek ve her birini NULL olarak ayarlamak, sadece tembel bir geliştiricinin 5 karakter yazmaktan kaçınabilmesi için bir israf gibi görünüyor.
Lotus Notes

5
@Byron, bak, gerçekten çok basit, pek çok başka dil bunu yapıyor, Ruby ve Perl birkaç örnek olarak. VM değişkenin daha önce kullanılıp kullanılmadığını bilir, değil mi? Hata mesajıyla veya hata mesajı olmadan başarısız olmak yerine her zaman null döndürebilir. Ve bu kötü 5 karakterle ilgili params["width"] = params["width"] || 5değil, isset()aramalarla ilgili tüm o saçmalıklar yerine varsayılanları ayarlamak için yazmakla ilgili .
vava

3
Eski bir konuyu dirilttiğim için özür dilerim. PHP'nin en kötü iki hatası register_globalsve idi magic_quotes. Bu teşvik edici problemler başlatılmamış değişkenleri kıyaslandığında neredeyse zararsız hale getiriyor.
staticsan

3

Bu fonksiyonları kullanıyorum

function load(&$var) { return isset($var) ? $var : null; }
function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; }

Örnekler

$y = load($x); // null, no notice

// this attitude is both readable and comfortable
if($login=POST("login") and $pass=POST("pass")) { // really =, not ==
  // executes only if both login and pass were in POST
  // stored in $login and $pass variables
  $authorized = $login=="root" && md5($pass)=="f65b2a087755c68586568531ad8288b4";
}

2
Bunu da kullanıyorum, ancak bazı durumlarda değişkenlerinizin otomatik olarak başlatılacağını unutmayın: örneğin, load ($ dizi ['FOO']) $ dizide bir FOO anahtarı oluşturacaktır.
Mat

2

Boş birleştirme operatörüne hoş geldiniz (PHP> = 7.0.1):

$field = $_GET['field'] ?? null;

PHP diyor:

Sıfır birleştirme operatörü (??), isset () ile birlikte bir üçlü kullanmayı gerektiren yaygın durum için sözdizimsel şeker olarak eklenmiştir. Varsa ve NULL değilse ilk işlenenini döndürür; aksi takdirde ikinci işlenenini döndürür.


1

falseAyarlanmamışsa ve belirtilmişse falseboşsa dönen bir işlev yapın . Geçerliyse değişkeni döndürür. Aşağıdaki kodda görüldüğü gibi daha fazla seçenek ekleyebilirsiniz:

<?php
function isset_globals($method, $name, $option = "") {
    if (isset($method[$name])) {    // Check if such a variable
        if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty 
        if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); }    // Check length of string -- used when checking length of textareas
        return ($method[$name]);
    } else { return false; }
}

if (!isset_globals("$_post", "input_name", "empty")) {
    echo "invalid";
} else {
    /* You are safe to access the variable without worrying about errors! */
    echo "you uploaded: " . $_POST["input_name"];
}
?>

0

Yazılım sihirli bir şekilde tanrının lütfuyla çalıştırılmaz. Eksik olan bir şey bekliyorsanız, onu düzgün bir şekilde halletmeniz gerekir.

Bunu görmezden gelirseniz, muhtemelen uygulamalarınızda güvenlik açıkları oluşturuyorsunuz. Statik dillerde tanımlanmamış bir değişkene erişim mümkün değildir. Eğer boşsa uygulamanızı basitçe derlemez veya çökertmez.

Dahası, uygulamanızı sürdürülemez hale getirir ve beklenmedik şeyler olduğunda delirirsiniz. Dil katılığı bir zorunluluktur ve PHP, tasarım gereği pek çok açıdan yanlıştır. Farkında değilseniz, sizi kötü bir programcı yapar.


PHP'nin eksikliklerinin çok iyi farkındayım. Soruda da belirttiğim gibi eski projelerin elden geçirilmesinden bahsediyorum.
Pekka

Kabul. Ling-time PHP geliştiricisi olarak, her şeyi beyan etmeniz gereken Java gibi yeni dillere girişmek benim için oldukça zor.
Dzhuneyt

0

Okunabilirlik tanımınızın ne olduğundan emin değilim, ancak empty (), isset () ve try / throw / catch bloklarının doğru kullanımı tüm süreç için oldukça önemlidir.

E_NOTICE bilginiz $ _GET veya $ _POST'tan geliyorsa, bu verilerin geçmesi gereken diğer tüm güvenlik kontrolleriyle birlikte empty () ile karşılaştırılmalıdır.

Harici beslemelerden veya kitaplıklardan geliyorsa, dene / yakala içine sarılmalıdır.

Veritabanından geliyorsa, $ db_num_rows () veya eşdeğeri kontrol edilmelidir.

Dahili değişkenlerden geliyorsa, uygun şekilde başlatılmaları gerekir. Genellikle, bu tür bildirimler, başarısızlık durumunda FALSE döndüren bir işlevin dönüşüne yeni bir değişken atamaktan gelir. Bunlar, bir hata durumunda, değişkene kodun işleyebileceği kabul edilebilir bir varsayılan değer atayabilecek veya kodun işleyebileceği bir istisna atabilecek bir teste sarılmalıdır.

Bu şeyler kodu uzatır, ekstra bloklar ekler ve ekstra testler ekler, ancak kesinlikle ekstra değer kattıklarını düşündüğüm için size katılmıyorum.


-2

@Operatörü kullanmaya ne dersiniz ?

Örneğin:

if(@$foo) { /* Do something */ }

Bunun kötü olduğunu söyleyebilirsin çünkü $ foo "içinde" ne olacağı konusunda hiçbir kontrole sahip değilsin (örneğin bir PHP hatası içeren bir işlev çağrısıysa), ancak bu tekniği sadece değişkenler için kullanıyorsanız, bu şuna eşdeğerdir:

if(isset($foo) && $foo) { /* ... */ }

if(isset($foo))aslında yeterli. TRUEİfade değerlendirilirse geri dönecektir TRUE.
Dzhuneyt

2
@ ColorWP.com, ifade yanlış olarak değerlendirilirse de doğru döndürür.
Jon Hulka

@ Parametresini (bildirimi göz ardı etmek için) yalnızca daha fazla geliştirme aşamasında olmayan kodda veya başka kimseye göstermek istemediğiniz tek seferlik kodda veya mevcut projelerde hızlı düzeltmede kullanmalısınız. Ancak, hızlı bir saldırı için yaygın bir çözümdür.
rubo77
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.