PHP Undefined index, obwohl vorhanden



  • Hey,

    folgender Code:

    $x = ...;
    var_dump( $x );
    /* =>
    array(22) {
      ["8001"]=>
      object(stdClass)#8 (3) {
        ["time"]=>
        int(25)
      }
      ,
      ...
    }*/
    
    var_dump( $x["8001"] );
    /*
    => PHP Notice:  Undefined index: 8001 <= !!
    */
    

    Das kann ich mir definitiv nicht erklären 😕 Ich bin mir sicher, ich sehe den Wald vor lauter Bäumen nicht.

    Hat da jemand eine Erklärung für mich?



  • Ok, hab die Lösung. Hiermit küre ich PHP wieder einmal offiziell zur schlechtesten Sprache der Welt.

    Also: PHP wandelt den Zugriff $x["8001"] in $x[8001] um. Trotz loose typing erkennt er aber "8001" und 8001 hier nicht als gleich an.
    Das Problem tritt normalerweise nicht auf, weil bei $x["8001"]=... der Key 8001 anstatt "8001" geschrieben wird. Erhält man das Array aber aus einer JSON-Quelle, versagt PHP hier vollkommen. Herzinfarkt ich komme 😡



  • edit: war vll doch zu gemein ..



  • PRIEST schrieb:

    edit: war vll doch zu gemein ..

    Ruhig raus damit, dass ich keine Ahnung hab 🙂

    Ich hatte mich gestern ziemlich aufgeregt, hatte 30 Minuten mit der Ursachensuche verschwendet. Ich sehe den Fehler hier aber klar bei PHP. Oder sollte das etwa doch meine Schuld sein?



  • PHPler123 schrieb:

    Oder sollte das etwa doch meine Schuld sein?

    JA!



  • inflames2k schrieb:

    PHPler123 schrieb:

    Oder sollte das etwa doch meine Schuld sein?

    JA!

    Dann bitte ich doch um Aufklärung! 🙂

    Das Array ist in der gezeigten Form unbrauchbar, wenn ich, außer mit Durchlaufen, nicht auf die Indizes zugreifen kann. Was geht, ist

    $x = (object)$x;
    $num = 8001;
    $field = $x->$num;
    

    . Sehr schön, Eleganz pur!

    Die Sache ist doch die: PHP erlaubt "eigentlich" keine Arrays, die eine Zahl in Stringform als Index haben. Deshalb kann man ja auch nicht darauf zugreifen. Andererseits stellt es aber Standardfunktionen bereit, die fälschlicherweise solche Arrayindizes erstellen.



  • entweder die object-syntax verwenden ( $x->{8001} ) oder die Funktionsbeschreibung von json_decode nochmal durchlesen

    PHPler123 schrieb:

    Oder sollte das etwa doch meine Schuld sein?

    Muss mich meinem Vorredner anschließen: ja. Das ist kein Problem von PHP



  • zwutz schrieb:

    entweder die object-syntax verwenden ( $x->{8001} )

    Es gibt einen hässlichen Workaround, ja, keine Frage.

    oder die Funktionsbeschreibung von json_decode nochmal durchlesen

    php.net schrieb:

    Takes a JSON encoded string and converts it into a PHP variable.

    😕
    Oder willst du auf den assoc -Parameter hinaus? Das Array ist eingebettet in Objekte, die auch Objekte sein sollen.

    Muss mich meinem Vorredner anschließen: ja. Das ist kein Problem von PHP

    Ernsthaft? Dass ein Array erstellt wird, auf dessen Indizes man nicht zugreifen kann? Und dass in diesem Fall nicht "8001"==8001 gilt?



  • Mal der Ablauf im Detail: Ich habe auf zwei Seiten PHP, die miteinander kommunizieren, die Daten dabei im JSON-Format übermitteln.

    // Sender
    $x = new stdClass();
    $x->someArray = array( "8001"=>"a", "8002"=>"b" );
    $json = json_encode( $x );
    
    // Wird übertragen: {"someArray":{"8001":"a","8002":"b"}}
    
    // Empfänger
    $json = ...;
    $y = json_decode( $json );
    

    Dadurch, dass PHPs assoziative Arrays in JSON mit der Objekt-Syntax dargestellt werden, "entpackt" json_decode die Variable someArray auch als Objekt anstatt als Array. Völlig OK soweit.
    Um aber alles wieder in das Ursprungsformat zu bringen, verwende ich

    $y->someArray = (array)$y->someArray;
    

    Laut Doku sollten die Schlüssel dann Integer sein:

    http://php.net/manual/en/language.types.array.php schrieb:

    Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.

    Das Zitat stammt aus der Doku zu der Funktion array() , trifft aber auch auf jeglichen Zugriff $a[...] zu. Eine Spezifikation des array-cast-Operators konnte ich nicht finden oder existiert nicht.

    Dass dann beim casten die Schlüssel nicht konvertiert werden, halte ich definitiv für einen Bug.

    Die Funktion sollte etwa so implementiert sein:

    function makeArray( $obj ) {
    	$arr = array();
    	foreach ( get_object_vars($obj) as $k=>$v ) {
    		$arr[$k] = $v;
    	}
    	return $arr;
    }
    

    In dieser Form wird das Array auch korrekt erstellt (8001 statt "8001" als Key). Ich schätze aber, der array-cast ist nativ implementiert und dort wurde offensichtlich jener "8001"->8001 cast schlichtweg vergessen.



  • Alright, hab jetzt doch die Doku zum array-cast gefunden:

    http://www.php.net/manual/en/language.types.array.php#language.types.array.casting schrieb:

    If an object is converted to an array, the result is an array whose elements are the object's properties. The keys are the member variable names, with a few notable exceptions: integer properties are unaccessible [...]

    Dann gilt meine Gratulation demjenigen, der diesen cast entworfen hat. Immerhin ist es dokumentiert und damit kein Bug.



  • PHPler123/fdfdg schrieb:

    zwutz schrieb:

    entweder die object-syntax verwenden ( $x->{8001} )

    Es gibt einen hässlichen Workaround, ja, keine Frage.

    das ist kein Workaround, sondern die Art und Weise, wie man in PHP auf Objekteigenschaften zurückgreift, deren Name nicht den üblichen Regeln folgt.

    Warum serialisierst du dein Objekt nicht? Im Gegensatz zu json werden dir da die Typen erhalten.

    json verwende ich nur, wenn ich mit was anderem als PHP kommunizieren will



  • zwutz schrieb:

    das ist kein Workaround, sondern die Art und Weise, wie man in PHP auf Objekteigenschaften zurückgreift, deren Name nicht den üblichen Regeln folgt.

    Das mag sein, aber ich will diese Daten ja als Array und nicht als Objekt vorliegen haben. Die Rohdaten sollen auf beiden Seiten (logischerweise) in gleichen Strukturen gehalten werden. Mit $y->someArray=get_object_vars($y->someArray) klappt das jetzt zum Glück auch super.

    zwutz schrieb:

    Warum serialisierst du dein Objekt nicht? Im Gegensatz zu json werden dir da die Typen erhalten.

    json verwende ich nur, wenn ich mit was anderem als PHP kommunizieren will

    So halte ich es im Regelfall auch. Die Schnittstelle soll aber eine ganze Weile erhalten bleiben und es ist nicht unwahrscheinlich, dass eine der Seiten die Sprache bald wechselt - oder auch ein Dritter ins Spiel kommt, auf dessen Sprachwahl ich keinen Einfluss habe 🙂


Anmelden zum Antworten