Festlegen eines Standardwerts, wenn eine Variable leer ist

76929
Matt

Dies ist das Muster, das ich für "wenn die Variable leer ist, einen Standardwert festlegen" verwendet hat, und für Variablen wie $muffin, hat alles gut und gut geschienen. Aber im folgenden realen Beispiel gibt mir dieses Muster eine super lange Linie, die mich ein bisschen riecht. Gibt es einen saubereren Weg, dies zu tun?

$newsItems[0]['image_url']= $newsItems[0]['image_url']=='' ? '/img/cat_placeholder.jpg' : $newsItems[0]['image_url'];

Könnte sein:

$newsItems[0]['image_url']= $newsItems[0]['image_url']=='' ? 
    '/img/cat_placeholder.jpg' : 
    $newsItems[0]['image_url'];   //this doesn't look much better to me?
Antworten
31
Ab PHP 7.0 ist [die Antwort von Langen] (http://codereview.stackexchange.com/a/114326/80949) wahrscheinlich die beste: `$ newsItems [0] ['image_url'] = $ newsItems [0] [' Bild URL'] ?? '/img/cat_placeholder.jpg'; `http://php.net/manual/de/language.operators.comparison.php#language.operators.comparison.coalesce oliverpool vor 2 Jahren 0

6 Antworten auf die Frage

32
Quentin Pradet

Bevor ich mich mit Ihrer Frage beschäftige, möchte ich darauf hinweisen, dass Sie sie wahrscheinlich verwenden möchten empty() Vergleich mit einem leeren String verwenden möchten und dass Sie den ===Operator verwenden, wenn Sie wirklich mit einem leeren String vergleichen möchten .

Wie auch immer, Standardinitialisierung ist ein echtes Problem in vielen Sprachen. C # hat sogar eine ?? Betreiber, um diese Art von Problem zu lösen! Versuchen wir also, den Code schrittweise zu verbessern.

Ternärer Operator nicht mehr

Der Einzeiler ist zu lang, und die Verwendung des ternären Operators in mehreren Zeilen ist eine schlechte Idee: Dafür gibt es eine if-Anweisung:

if (empty($newsItems[0]['image_url'])) {
    $newsItems[0]['image_url'] = '/img/cat_placeholder.jpg';
}

Dies ist aus zwei Gründen besser lesbar: Es ist einfacher, die Syntax zu verstehen, aber was noch wichtiger ist, es ist einfacher zu erkennen, dass Sie einfach einen Standardwert wünschen, wenn nichts vorhanden ist. Wir brauchen nicht den anderen Teil, der mit dem ternären Operator verwirrt.

Anmerkung: Wie von Simon Scarfe erwähnt und von mseancole korrigiert, hat PHP auch eine spezielle ternäre Syntax, um dies zu tun:

$newsItems[0]['image_url'] = $newsItems[0]['image_url'] ?: 'img/cat_placeholder.jpg';

Faktorisieren Sie es!

Wenn Sie dies nur ein- oder zweimal tun, ist alles gut, aber ansonsten möchten Sie es in eine Funktion einbeziehen, da Sie sich nicht wiederholen, oder? Der einfache Weg ist:

function default_value($var, $default) {
    return empty($var) ? $default : $var;
}

$newsItems[0]['image_url'] = default_value($newsItems[0]['image_url'], '/img/cat_placeholder.jpg');

(Der ternäre Operator ist hier sinnvoll, da Variablennamen kurz sind und beide Verzweigungen der Bedingung nützlich sind.)

Wir suchen jedoch $newsItems[0]['image_url']beim Anrufen nach default_value, und dies ist möglicherweise nicht definiert und wird eine Fehlermeldung / Warnung auslösen. Wenn dies ein Anliegen ist (sollte es), bleiben Sie bei der ersten Version oder schauen Sie sich diese andere Antwort an, die eine robustere Lösung bietet, die auf Kosten der Speicherung von PHP-Code als String beruht und daher nicht syntaktisch überprüft werden kann.

Immer noch zu lang

Wenn wir uns nicht um die Warnung / den Fehler kümmern, können wir es besser machen? Ja wir können! Wir schreiben den Variablennamen zweimal, aber die Referenzübergabe kann uns hier helfen:

function default_value(&$var, $default) {
    if (empty($var)) {
        $var = $default;
    }
}

default_value($newsItems[0]['image_url'], '/img/cat_placeholder.jpg');

Es ist viel kürzer und fühlt sich eher deklarativ an: Sie können sich eine Reihe von default_valueAnrufen ansehen und sehen, was die Standardwerte sofort sind. Aber wir haben immer noch das gleiche Warn- / Fehlerproblem.

Die zweiten beiden hier vorgestellten Optionen führen zu einem Fehler und wirken sich daher (auch) auf die Leistung aus. Ich sollte das Leistungsbit jedoch nicht erwähnen, da ein Fehler (Warnung, in diesem Fall undefinierter Index) einfach ausreichen sollte. Selbst wenn die Berichterstellung / Protokollierung deaktiviert ist, führt die Tatsache, dass der Interpreter überhaupt bemerkt, zu einer größeren Beeinträchtigung der Leistung als "empty" und "isset" in Verbindung mit einem "if". Halten Sie sich an das "Wenn" oder verwenden Sie eine Lösung wie die unten abgebildete. Mike vor 5 Jahren 1
Danke, Upvoted. Natürlich spielt die Leistung hier keine Rolle, es sei denn, der Code ist Teil eines Engpasses. Quentin Pradet vor 5 Jahren 0
@MichaelJMulligan Ist meine Bearbeitung fair? Quentin Pradet vor 5 Jahren 0
@QuentinPradet, Fair. Können Sie bei der syntaktischen Prüfung meine Antwort erläutern? Ich wäre interessiert Mike vor 5 Jahren 0
18
Simon Scarfe

Von hier :

Seit PHP 5.3 ist es möglich, den mittleren Teil des ternären Operators wegzulassen. Ausdruck expr1?: Expr3 gibt expr1 zurück, wenn expr1 als TRUE ausgewertet wird, andernfalls expr3.

Könntest du schreiben ?:

$newsItems[0]['image_url'] = $newsItems[0]['image_url'] ?: '/img/cat_placeholder.jpg';
Dies ist eines meiner Lieblingssachen in PHP. Es fühlt sich gleichzeitig elegant und frech an! :) Ich verwende es normalerweise nur, wenn ich es einer anderen Variablen zuweise oder von einer Funktion zurückkomme. David Harkness vor 7 Jahren 3
Wenn Ihre Variable nicht definiert ist, gibt PHP Hinweise darüber aus. Dies ist leider kein Ersatz für $ var = isset ($ var)? $ var: 'Standardwert'; ` Brad vor 5 Jahren 7
Das ist erstaunlich, so sauber und elegant :-) Avishai vor 3 Jahren 0
13
Lando

Und siehe da, die Kraft des Null-Koaleszenzoperators von PHP 7 ! Es wird aus offensichtlichen Gründen auch als ternet-Operator bezeichnet.

Hier ist eine Passage aus der Wiki-Seite von php.net

Vorschlag

Der Operator coalesce oder ?? wird hinzugefügt, der das Ergebnis seines ersten Operanden zurückgibt, wenn er existiert und nicht NULL ist, oder seinen zweiten Operanden. Das heißt, das $_GET['mykey'] ?? ""ist absolut sicher und wird nicht erhöht E_NOTICE.

So sieht es aus

$survey_answers = $_POST['survey_questions'] ?? null;

Es kann sogar verkettet werden. Es wird der erste vorhandene Wert verwendet, der nicht null ist. Beachten Sie Folgendes:

$a = 11;
$b = null;
$c = 'test';

$d = $a ?? $b ?? $c;

print $d;

Dies würde 11jedoch dazu führen, wenn $a = null;es drucken würde test.

Dies ist jetzt integriert: http://php.net/manual/de/language.operators.comparison.php#language.operators.comparison.coalesce oliverpool vor 2 Jahren 0
5
mdo

OOP-Stil

Wenn Sie OOP ausführen, können Sie auch überdenken, ob Sie ein einfaches Array verwenden. Ich denke, das image_urlist nicht das einzige Attribut Ihrer Nachrichten, konvertieren Sie also diese Array-Struktur in ein Objekt . Darüber hinaus $newsItemskann ein Array von NewsItemInstanzen geändert werden .

/img/cat_placeholder.jpgkönnte der Standardwert des Attributs image_urlin Ihrer neuen Klasse NewsItem sein. Entweder entscheidet Ihr Client-Code, ob ein neuer Wert festgelegt werden soll (z. B. je nach dem Sein !empty()), oder Ihre NewsItemKlasse verfügt über eine Setter-Methode für das Attribut image_url', die automatisch den Standardwert setzt, wenn eine leere Zeichenfolge angezeigt wird.

5
Ariel

Die extract()Funktion kann auch helfen.

Es funktioniert nicht für einzelne Array-Schlüssel wie hier, aber es kann unter anderen Umständen nützlich sein.

extract(array('foo'=>'bar'), EXTR_SKIP);

Dies wird nur dann als Standardwert zugewiesen bar, $foowenn es noch nicht festgelegt ist. Wenn $foobereits eingestellt, wird es übersprungen. Sie können mehrere Variablen in das Array aufnehmen.

2
Mike

Warnung

Führen Sie keines dieser beiden Dinge aus:

$array['index'] = $array['index'] ?: $default;

function default_value(&$var, $default) {
    if (empty($var)) {
        $var = $default;
    }
}

default_value($array['index'], $default);

Beides führt zu Fehlern aufgrund eines undefinierten Index (beide versuchen zuerst, den Wert am Index zuerst abzurufen), wenn der Index nicht festgelegt ist. Dies ist eine kleine Sache, aber selbst wenn die Fehlerberichterstattung UND Protokollierung deaktiviert ist, wird der Fehler ausgelöst, erstellt und durch den Stack weitergeleitet, bevor geprüft wird, ob der Fehler irgendwo vorhanden ist. Wenn Sie also einfach etwas tun, das KANN einen Fehler auslösen kann, führt dies zu einem Performance-Erfolg.

Beachten Sie dies, denn dies gilt für alle Fehlertypen (Benachrichtigungen, Warnungen usw.) und kann bei großen Apps eine große Sache sein. Um dies zu vermeiden, müssen Sie eine Funktion verwenden, die keinen Fehler für einen fehlenden Index wie isset($array['index'])oder empty($array['index'])auslöst, und nicht das verkürzte ternäre Formular verwenden.

Versuchen Sie eine Funktion wie:

function apply_default(Array &$array, $path, $default) {
    // may need to be modified for your use-case
    preg_match_all("/\[['\"]*([^'\"]+)['\"]*\]/im",$path,$matches);
    if(count($matches[1]) > 0) {
        $destinaion =& $array;
        foreach ($matches[1] as $key) {
            if (empty($destinaion[$key]) ) {
                $destinaion[$key] = array();
            }
            $destinaion =& $destinaion[$key];
        }
        if(empty($destinaion)) {
            $destinaion = $default;
            return TRUE;
        }
    }
    return FALSE;
}

$was_applied = apply_default($array,
    "['with a really long name']['and multiple']['indexes']",
    $default_value);

Dies führt nicht zu Fehlern und erstellt den Pfad, falls er nicht existiert. Es ist auch vorteilhaft, ifwenn die Logik konsolidiert wird, um die Standardwerte an einem Ort und nicht an allen Stellen festzulegen.

Wie an anderer Stelle erwähnt, besteht die andere Möglichkeit darin:

if(empty($array['key'])) {
    $array['key'] = $default;
}

Welches ist genauso gut und wahrscheinlich performanter.

Trotzdem sollte dies idealerweise nur im verbrauchenden Service behandelt werden. Wenn der Wert leer ist, sollte der Verbraucher (ob eine Ansicht oder ein anderer Client [wie Javascript]) diese Auswahl selbst treffen dürfen. Da tatsächlich die semantischen Daten für das Modell leer sind. Wie dies gehandhabt wird, sollte dem Verbraucher selbst überlassen werden, dh wenn es sich um eine Vorlage / Ansicht handelt, sollte die Bedingung während der Anzeige behandelt werden. Wenn es als JSON gesendet wird, sollte der Client die Wahl treffen können.

Sie fragen nach Stilen, das ist wahrscheinlich das Wichtigste. Der beste Stil ist, wenn Sie einem Kunden Daten zur Verfügung stellen, fälschen Sie sie nicht (dokumentieren Sie jedoch die mögliche leere Antwort).

Tolle Antwort, und das hatte noch niemand gesehen. Sie haben also nach den Syntaxfehlern gefragt. Angenommen, Sie schreiben "[mit einem wirklich langen Namen"] [und mehrere "]". Wenn dies nicht in einer PHP-Zeichenfolge wäre, sondern tatsächlich von PHP als PHP-Code interpretiert würde, würden Sie einen aussagekräftigen Fehler erhalten, aber in Ihrer Funktion würde die Regex einfach scheitern, schätze ich. Quentin Pradet vor 5 Jahren 0
Es wäre, du bist richtig. Ich werde darüber nachdenken, wie man das nicht saugen kann. Mike vor 5 Jahren 0
Sie können stattdessen ein Array von Strings senden. Nicht sicher, dass es sich lohnt, darüber nachzudenken! Quentin Pradet vor 5 Jahren 0