Jak uniknąć isset () i empty()

Mam kilka starszych aplikacji, które rzucają wiele komunikatów "xyz is undefined" i "undefined offset" podczas uruchamiania na poziomie błędu E_NOTICE, ponieważ istnienie zmiennych nie jest jawnie sprawdzane za pomocą isset() i consorts.

Rozważam pracę nad nimi, aby były kompatybilne z E_NOTICE, ponieważ powiadomienia o brakujących zmiennych lub przesunięciach mogą uratować życie, mogą być pewne drobne ulepszenia wydajności, które można uzyskać, i ogólnie jest to czystszy sposób.

Jednak nie podoba mi się to, co zadaje setki isset() empty() i array_key_exists() S robi z moim kodem. Staje się nadęty, staje się mniej czytelny, nie zyskując niczego pod względem wartości czy znaczenia.

Jak mogę uporządkować kod bez nadmiaru sprawdzania zmiennych, jednocześnie będąc kompatybilnym z E_NOTICE?

Author: Tiny Giant, 2009-12-25

11 answers

Dla zainteresowanych rozszerzyłem ten temat na mały artykuł, który zawiera poniższe informacje w nieco lepiej zorganizowanej formie: The Definitive Guide to PHP ' s isset And empty{[35]]}


IMHO powinieneś pomyśleć nie tylko o tym, aby aplikacja była "kompatybilna z E_NOTICE", ale też o restrukturyzacji całej sprawy. Posiadanie setek punktów w kodzie, które regularnie próbują używać nieistniejących zmiennych, brzmi jak dość źle zorganizowany program. Trying to dostęp nieistniejące zmienne nigdy nie powinny się zdarzyć, inne języki balk w tym czasie kompilacji. Fakt, że PHP pozwala ci to zrobić, nie oznacza, że powinieneś.

Te ostrzeżenia są po to, by Ci pomóc, a nie by cię denerwować. Jeśli otrzymasz ostrzeżenie " próbujesz pracować z czymś, co nie istnieje!", twoja reakcja powinna być " UPS, mój błąd, pozwól mi to naprawić jak najszybciej." jak inaczej odróżnisz zmienne, które działają tylko fine undefined " i szczerze zły kod, który może prowadzić do poważnych błędów ? Jest to również powód, dla którego zawsze, zawsze , rozwijasz się z raportowaniem błędów zamienionym na 11 i trwaj przy swoim kodzie, dopóki nie zostanie wydany ani jeden NOTICE. Wyłączenie raportowania błędów dotyczy tylko środowisk produkcyjnych, aby uniknąć wycieku informacji i zapewnić lepsze wrażenia użytkownika nawet w obliczu błędnego kodu.

Do rozwinięcia:

Będziesz zawsze potrzebujesz isset lub empty gdzieś w kodzie, jedynym sposobem na zmniejszenie ich występowania jest prawidłowe zainicjowanie zmiennych. W zależności od sytuacji istnieją różne sposoby, aby to zrobić:

Argumenty funkcji:

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

Nie ma potrzeby sprawdzania, czy $bar lub $baz są ustawione wewnątrz funkcji, ponieważ po prostu je ustawisz, jedyne, o co musisz się martwić, to czy ich wartość zostanie obliczona na true lub false (lub cokolwiek innego).

Zmienne regularne anywhere:

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

Zainicjalizuj swoje zmienne u góry bloku kodu, w którym będziesz ich używać. Rozwiązuje to problem !isset, zapewnia, że zmienne zawsze mają znaną wartość domyślną, daje czytelnikowi wyobrażenie, na czym będzie działał poniższy kod, a tym samym służy również jako rodzaj samodzielnej dokumentacji.

Tablice:

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

To samo co powyżej, inicjalizujesz tablicę wartościami domyślnymi i zastępujesz je rzeczywistymi wartości.

W pozostałych przypadkach, powiedzmy w szablonie, w którym wypisujesz wartości, które mogą lub nie mogą być ustawione przez kontroler, musisz tylko sprawdzić:

<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>

Jeśli regularnie używasz array_key_exists, powinieneś ocenić, do czego go używasz. Tylko tutaj robi różnicę:

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

Jak wspomniano powyżej, jeśli prawidłowo inicjujesz zmienne, nie musisz sprawdzać, czy klucz istnieje, ponieważ wiesz, że tak. Jeśli otrzymujesz tablicę z zewnętrznego źródła, wartość najprawdopodobniej nie będzie null, ale '', 0, '0', false lub coś w tym stylu, tj. wartość, którą możesz ocenić za pomocą isset lub empty, w zależności od intencji. Jeśli regularnie ustawiasz klucz tablicy na null i chcesz, aby oznaczał cokolwiek innego niż false, tzn. jeśli w powyższym przykładzie różne wyniki isset i array_key_exists mają wpływ na logikę Twojego programu, powinieneś zadać sobie pytanie dlaczego. Samo istnienie zmiennej nie powinno być ważne, tylko jego wartość powinna mieć znaczenie. Jeśli kluczem jest true/false flag, następnie użyj true lub false, a nie null. Jedynym wyjątkiem od tego są biblioteki stron trzecich, które chcą, aby null coś znaczyło, ale ponieważ null jest tak trudny do wykrycia w PHP, nie znalazłem jeszcze żadnej biblioteki, która to robi.

 122
Author: deceze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-27 14:13:43

Po prostu napisz do tego funkcję. Coś w stylu:

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;
    }
}

Które możesz użyć jako

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

Zrób to samo dla błahych rzeczy, takich jak get_number(), get_boolean(), get_array() i tak dalej.

 36
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-25 13:43:18

Uważam, że jednym z najlepszych sposobów radzenia sobie z tym problemem jest dostęp do wartości tablic GET I POST (COOKIE, SESSION, itp.) za pośrednictwem klasy.

Tworzy klasę dla każdej z tych tablic i deklaruje metody __get i __set (overloading ). __get przyjmuje jeden argument, który będzie nazwą wartości. Ta metoda powinna sprawdzić tę wartość w odpowiedniej globalnej tablicy używając isset() lub empty() i zwrócić wartość, jeśli istnieje lub null (lub innego domyślnego wartość) inaczej.

Następnie możesz bez obaw uzyskać dostęp do wartości tablicy w następujący sposób: $POST->username i w razie potrzeby dokonać walidacji bez użycia isset() s lub empty() s. Jeśli username nie istnieje w odpowiedniej globalnej tablicy, to zostanie zwrócona null, więc nie będą generowane żadne ostrzeżenia ani ostrzeżenia.

 13
Author: Jamol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-27 23:46:40

Nie mam nic przeciwko używaniu array_key_exists(), w rzeczywistości wolę używać tej konkretnej funkcji zamiast polegać na hack funkcje, które mogą zmienić swoje zachowanie w przyszłości Jak empty i isset (striked through to avoid susceptibilities ).


Używam jednak prostej funkcji, która przydaje się w tym i w niektórych innych sytuacjach w radzeniu sobie z indeksami tablicy : {]}

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;
}

Powiedzmy, że masz następujące tablice:

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

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

Jak uzyskać "wartość" z tablic? Proste:

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');

Mamy już pokryte tablice uni i wielowymiarowe, co jeszcze możemy zrobić?


Weźmy na przykład następujący fragment kodu:

$url = 'https://stackoverflow.com/questions/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';
}

Nudne, prawda? Oto inne podejście wykorzystujące funkcję Value():

$url = 'https://stackoverflow.com/questions/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');

Jako dodatkowy przykład, weź RealIP() funkcję do testu:

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

Neat, co? ;)

 6
Author: Alix Axel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:20

Jestem tu z Tobą. Ale projektanci PHP popełnili znacznie więcej gorszych błędów niż to. Poza zdefiniowaniem funkcji niestandardowej dla dowolnego odczytu wartości, nie ma możliwości obejścia jej.

 3
Author: vava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-25 06:04:30

Używam tych funkcji

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

Przykłady

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

// this attitude is both readable and comfortable
if($login=POST("login")) // really =, not ==
if($pass=POST("pass"))
if($login=="Admin" && $pass==...) {
  // login and pass are not empty, login is "Admin" and pass is ...
  $authorized = true;
  ...
}
 3
Author: Jan Turoň,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-17 12:59:20

Tworzy funkcję, która zwraca false jeśli nie jest ustawiona, oraz, jeśli jest określona, false jeśli jest pusta. Jeśli jest poprawna, zwraca zmienną. Możesz dodać więcej opcji, jak widać w poniższym kodzie:

<?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"];
}
?>
 1
Author: dragonfire,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-12 17:18:45

Witamy w operatorze Null:

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

PHP says:

Operator zerowy (??) został dodany jako cukier składniowy w powszechnym przypadku konieczności użycia trójnika w połączeniu z isset (). Zwraca swój pierwszy operand, jeśli istnieje i nie jest NULL; w przeciwnym razie zwraca swój drugi operand.

 1
Author: Alexandre Thebaldi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-05 16:55:07

Nie jestem pewien, jaka jest twoja definicja czytelności, ale właściwe użycie bloków empty (), isset () I try / throw/catch jest dość ważne dla całego procesu. Jeśli twój E_NOTICE pochodzi z $_GET lub $_POST, to powinny one być sprawdzane przez empty() wraz ze wszystkimi innymi kontrolami bezpieczeństwa, które te dane powinny przejść. Jeśli pochodzi z zewnętrznych kanałów lub bibliotek, powinien być zawinięty w try/catch. Jeśli pochodzi z bazy danych, $db_num_rows() lub jest równoważne należy sprawdzić. Jeśli pochodzi od zmiennych wewnętrznych, powinny być prawidłowo zainicjowane. Często tego typu powiadomienia pochodzą z przypisania nowej zmiennej do zwracanej funkcji, która zwraca FALSE po niepowodzeniu, powinny być owinięte w test, który w przypadku niepowodzenia może albo przypisać zmiennej akceptowalną wartość domyślną, którą może obsłużyć kod, albo rzucić wyjątek, który może obsłużyć kod. Te rzeczy wydłużają kod, dodają dodatkowe bloki i dodają dodatkowe testy, ale nie zgadzam się z Tobą w tym, że myślę, że zdecydowanie dodać dodatkową wartość.

 0
Author: Mlutz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-25 06:35:35

Oprogramowanie nie działa magicznie dzięki łasce Boga, jeśli oczekujesz czegoś, czego brakuje, musisz to odpowiednio obsłużyć. jeśli go zignorujesz, prawdopodobnie tworzysz luki w zabezpieczeniach w swoich aplikacjach. w językach statycznych dostęp do nieokreślonej zmiennej nie jest po prostu możliwy, nie kompiluje ani nie zawiesza aplikacji, jeśli jest null. ponadto sprawia, że aplikacja jest niemożliwa do utrzymania, a Ty oszalejesz, gdy wydarzy się coś nieoczekiwanego. surowość językowa jest koniecznością a php z założenia jest błędne w wielu aspektach. to uczyni cię złym programistą, jeśli nie jesteś tego świadomy.

 -1
Author: knoopx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-25 11:52:00

A co z użyciem operatora@? np.:

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

Możesz powiedzieć, że jest źle, ponieważ nie masz kontroli nad tym, co dzieje się" wewnątrz " $foo (jeśli było to wywołanie funkcji, które zawiera na przykład błąd PHP), ale jeśli używasz tylko tej techniki dla zmiennych, jest to równoważne:

if(isset($foo) && $foo) { /* ... */ }
 -3
Author: Mat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-13 20:13:26