ArrayObject funktioniert nicht mit end () in PHP 7.4

9

Auf der Migration auf PHP 7.4 Ich habe mit einem anderen Verhalten einiger Array - Funktionen wie zu behandeln reset(), current()oder end()über Arrayobject. Das folgende Beispiel erzeugt verschiedene Ausgaben:

<?php

$array = new \ArrayObject(["a", "b"]);
$item = end($array);
var_dump($item);


$array = ["a", "b"];
$item = end($array);
var_dump($item);

Mit PHP 7.4 ist die Ausgabe:

bool(false)
string(1) "b"

In PHP-Versionen vor 7.4 lautet die Ausgabe wie folgt:

string(1) "b"
string(1) "b"

A end($array->getArrayCopy())erzeugt einen Hinweis, kann jedoch eine Problemumgehung sein, wenn es mit einer Variablen verwendet wird.

Gibt es eine Möglichkeit, das Verhalten von end()mit einem ArrayObjectoder zu emulieren ArrayIterator? Das ArrayObject kann sehr groß sein, eine Iteration bis zum Ende ist möglicherweise nicht die beste Lösung.

Trendfischer
quelle
Eine Alternative könnte sein $item = $array[count($array)-1];. Ich bin mir nicht sicher, ob dies die effizienteste Lösung ist.
Patrick Q
2
Ich würde sagen, dass dies als PHP-Fehler qualifiziert ist. Es gibt definitiv nichts im Änderungsprotokoll, was darauf hindeuten würde, dass dies eine beabsichtigte Änderung in 7.4 war
Uhr
Testen Sie es online: 3v4l.org/4MADI
0stone0
1
@PatrickQ Was ist, wenn es assoziativ ist?
Andreas
4
@iainn das ist definitiv kein Bug - php.net/manual/en/…
u_mulder

Antworten:

2

Ab PHP 7.4 Array-Methoden nicht mit internen Arrays, sondern mit sich ArrayObjectselbst. Ich habe zwei Lösungen dafür zusammengefasst.

1. Internes Array von Objekten abrufen.

$array = new \ArrayObject(["a", "b"]);
$item = end($array->getArrayCopy());

2. Erstellen einer Fassade ArrayObjectund Hinzufügen einer benutzerdefinierten Methode end () zur aktualisierten Klasse.

Tajniak
quelle
0

Sie können das Array-Objekt zu einem Array machen, um die Schlüssel abzurufen, und dann das Ende der Schlüssel verwenden, um den letzten Schlüssel abzurufen.

$array = new \ArrayObject(["a", "b"]);
$keys = array_keys((array)$array);
$end_key = end($keys);

var_dump($array[$end_key]);

Es ist keine schöne Lösung, aber es funktioniert.
Ich schlage vor, Sie machen es zu einer Funktion, damit Sie es bei Bedarf aufrufen können.

https://3v4l.org/HTGYn

Als eine Funktion:

function end_object($array){
    $keys = array_keys((array)$array);
    $end_key = end($keys);
    return $array[$end_key];
}


$array = new \ArrayObject(["a", "b"]);
$item = end_object($array);
var_dump($item);
Andreas
quelle
Ich sehe keinen Unterschied zwischen beiden Antworten, wenn ich mir das Ergebnis und die fraglichen Usaqe ansehe. wenn Unterschied bitte erklären
Dlk
1
Ich habe die array_keys()Lösung mit 3v4l.org/IaEMM/perf#output getestet, aber sie benötigte 20-30% mehr Speicher als end()auf einem einfachen getArrayCopy() 3v4l.org/uYv59/perf#output
Trendfischer
1
@Trendfischer Wenn Speicher das Problem ist und Sie nur verwenden möchten, endkönnen Sie eine Wrapper-Klasse erstellen, ArrayAccessdie eine zusätzliche Funktion implementiert und hat, die ein endinternes privates Array zurückgibt , das betrieben werden würde.
vivek_23
1
@ vivek_23 klingt nach einer guten Antwort
Trendfischer
3
Frage: Was ist der Zweck von array_keys? Warum wirfst du es nicht einfach direkt $arr = (array) $arrayund dann$end = end($arr)
Regen
0

Ein etwas schnellerer Ansatz ohne Casting oder Verwendung eines Iterators wäre, den Konstruktor nicht an erster Stelle zu verwenden, sondern eine appendMethode zu verwenden, mit der ein Array selbst erstellt wird und die Sie endspäter für dieses Array verwenden können

$array = new \ArrayObject();
$array->append(["a", "b"]);
$item =  end($array[count($array) - 1]);
var_dump($item);

count($array) - 1Falls Sie später ein anderes Array anhängen, stellen wir sicher, dass dies $itemimmer das letzte Element im zuletzt angehängten Array ist.

Regen
quelle
1
Vielen Dank, die Lösung mit count()könnte in einigen Fällen hilfreich sein, aber Ihr Beispiel würde für so etwas nicht funktionierennew \ArrayObject([123 => "a", 456 => "c"]);
Trendfischer
@Trendfischer Ich weiß, dass ich deshalb appendanstelle des Konstruktors verwendet habe. Die Verwendung von append mit Ihrem Beispiel wird definitiv funktionieren. $array->append([123 => "a", 456 => "c"]
Regen
@Trendfischer Bitte beachten Sie, dass dies countnicht für die Elemente Ihres Arrays gilt, sondern für das mehrdimensionale Array, appenddas erstellt wird. Für Ihr Array verwenden wir endwie gewohnt.
Regen
1
Ich schätze die Absicht, aber normalerweise verwende ich ein ArrayObject nicht als einfachen Ersatz für ein Array. Das Beispiel in der Frage ist beispielhaft, um das Problem zu zeigen. Wenn ich nur eine verwenden könnte append(), könnte ich eine count()gültige Lösung verwenden. Das könnte mit append('a')und funktionieren append('b'). Der Schlüssel wäre, assoziative Arrays nicht zuzulassen, was durch die Erweiterung von ArrayObject möglich ist.
Trendfischer