Stefan: Array-/Variableninitialisierung bei PHP

Hallo,

ist das normal, dass ich in PHP bei Arrays jeden Schlüssel initialisieren muss?

Ich ging davon aus, dass es sich so verhält wie in Perl, wo ich bspw nur die Arrayvariable anlege my %array;.

In PHP erhalte ich aber bspw einen Fehler bei dem Code

$category['reporting']['period']['categorySum'] += $iCode['pi']['period']['sum'];

, wenn ich vorher kein

$category['reporting']['period']['categorySum'] = 0;

Oder habe ich ein generelles Verständnisproblem? Wie ist das, wenn ich Schlüssel dynamisch generiere?

Danke gemacht habe.

  1. Hallo Stefan,

    das liegt bei Dir an der Verwendung des += Operators. Dieser Operator nimmt den alten Wert und ändert ihn. Ohne eine Initialisierung bekommst Du da natürlich eine Fehlermeldung, dass es für den alten Wert keinen Array-Eintrag gibt.

    Es gibt die plumpe Art, das mit dem @ Operator zu unterdrücken. Das ist aber bad-practice, weil der JEDEN Laufzeitfehler unterdrückt.

    @$category['reporting']['period']['categorySum'] += $iCode['pi']['period']['sum'];
    

    Besser ist es, zu testen, ob es den Array-Eintrag schon gibt. Das geht auf die mühsame Tour so:

       // Ausführlicher IF:
       if (isset($category['reporting']['period']['categorySum']))
          $wert = $category['reporting']['period']['categorySum'];
       else
          $wert = 0;
    
       // Abgekürzte Variante mit ?: Operator
       $wert = isset($category['reporting']['period']['categorySum']) ? $category['reporting']['period']['categorySum'] : 0;
    
       // Jetzt kann addiert werden.
       $category['reporting']['period']['categorySum'] = $wert + $iCode['pi']['period']['sum'];
    

    Mit PHP 7 bekommst Du eine Abkürzung: den „null coalescing operator“ ??. Der funktioniert so, im Vergleich zu älteren Alternativen:

    // Null-coalescing "classic"
    $foo = isset($bar) ? $bar : 42;
    
    // Null-coalescing Hack ab PHP 5.3 - muss Error-Unterdrückung mit @ einsetzen - bad practice!
    $foo = @$bar ?: 42;
    
    // Null-coalescing ab PHP 7 
    $foo = $bar ?? 42;
    

    Ein null coalescing += gibt's aber nicht, d.h. du musst den null-coalescing Lesezugriff vom Addieren und Zuweisen der Summe trennen. D.h. den Arrayzugriffs-Bandwurm hast Du immer noch zweimal.

       $category['reporting']['period']['categorySum'] =
         ($category['reporting']['period']['categorySum'] ?? 0) 
       + $iCode['pi']['period']['sum'];
    

    Eine andere Variante arbeitet mit Referenzbildung und Auslagern in eine Funktion. Der Trick besteht zum einen darin, dass die Übergabe als Referenz unter der Haube die Zielvariable anlegt, ohne zu meckern. Zum anderen wird null beim Addieren wie 0 behandelt, darum kannst in der Funktion dann einfach addieren.

       add_value($category['reporting']['period']['categorySum'], iCode['pi']['period']['sum']);
    
       function add_value(&$summe, $summand)
       {
          $summe += $summand;
       }
    

    Funktionsaufrufe sind ab PHP 7 deutlich billiger geworden, aber wenn viele Additionen anstehen, ist die inline-Version immer noch die schnellere. Eine Vorab-Initialisierung ist natürlich immer noch am Besten.

    Rolf

    --
    sumpsi - posui - clusi
    1. Ui, was für eine ausführliche Antwort!

      Riesendank!!!!

  2. Hallo,

    ist das normal, dass ich in PHP bei Arrays jeden Schlüssel initialisieren muss?

    Ich ging davon aus, dass es sich so verhält wie in Perl, wo ich bspw nur die Arrayvariable anlege my %array;.

    In PHP erhalte ich aber bspw einen Fehler bei dem Code

    $category['reporting']['period']['categorySum'] += $iCode['pi']['period']['sum'];

    Die Fehlermeldung würdest Du auch in Perl bekommen wenn der Operator += auf eine Variable zugreift die nicht initialisiert ist.

    MfG

    PS: In Perl ists jedoch so, daß ein if exists $cat->{reporting}{period}{categorySum}; diese Schlüssel alle anlegen würde.

  3. In PHP erhalte ich aber bspw einen Fehler bei dem Code

    $category['reporting']['period']['categorySum'] += $iCode['pi']['period']['sum'];
    

    (Umgebaut:)

    <?php
    $category['reporting']['period']['categorySum'] += 1;
    echo '$category[\'reporting\'][\'period\'][\'categorySum\'] ist jetzt: ' . $category['reporting']['period']['categorySum'] . "\n";
    

    Ausgaben in einem Terminal:

    ~$ php /tmp/test.php
    PHP Notice:  Undefined variable: category in /tmp/test.php on line 2
    PHP Notice:  Undefined index: reporting in /tmp/test.php on line 2
    PHP Notice:  Undefined index: period in /tmp/test.php on line 2
    PHP Notice:  Undefined index: categorySum in /tmp/test.php on line 2
    $category['reporting']['period']['categorySum'] ist jetzt: 1
    

    Ergänzend: Das ist formal kein "Fehler" sondern nur eine "Notiz". Fehler führen zum Abbruch. Die "Notizen" benachrichtigen in diesem Fall darüber, dass PHP zur Vermeidung eines Abbruchs spekuliert hat - was eine Programmiersprache meinem Verständnis von "Programm" nach nie tun sollte. Wie oben zu sehen ist wirft PHP sogar bis zu 4 Notizen. Allerdings gilt es unter Programmieren aus gutem Grund (Vermeidung der extrem schwer zu findenden logischen Fehler) als zwingend diese Notizen zu vermeiden. Hier durch Instanzierung des named arrays und seiner Elemente an geeigneter Stelle.

    if ( ! isset( $array['element'] ) ) {
       $array[element] = 0;
    }
    

    wäre also womöglich (abhängig von der Aufgabe) ein richtiger Weg.

    Andere, auch nicht ganz dumme, Programmierer meinen, dass es erlaubt und klug sei, Stellen, an denen solche Notizen erwartet würden, mit einem vorangestellten @ zu beginnen um die Notiz zu vermeiden:

    <?php
    @$category['reporting']['period']['categorySum'] += 1;
    echo '$category[\'reporting\'][\'period\'][\'categorySum\'] ist jetzt: ' . $category['reporting']['period']['categorySum'] . "\n";
    ?>
    

    Ausgaben in einem Terminal:

    ~$ php /tmp/test.php
    $category['reporting']['period']['categorySum'] ist jetzt: 1
    

    Das macht aber die Fehlersuche schwieriger.