PHP WTF #5

…oder: Bei magischen Funktionen gut aufpassen.

class TestingEmpty
{
	public function __get($var)
	{
		if ($var == "test_empty")
			return "Hi there!";
	}
}

$t = new TestingEmpty();
echo $t->test_empty; //Hi there! 
var_dump(strlen($t->test_empty)); //int(9)
var_dump(empty($t->test_empty)); //bool(true)
$externalvar = $t->test_empty;
var_dump(empty($externalvar)); //bool(false)

Aha, also empty, aber mit String-Length von 9 Zeichen. Nach dem Zuweisen zu einer „wirklichen“ Variable dann auch nicht mehr empty.

Zur Abwechslung wollen wir aber nicht nur meckern, sondern noch konstruktiv zeigen, wie man diesen WTF (der vielleicht garkeiner ist?) behebt:

class TestingEmpty
{
	public function __get($var)
	{
		if ($var == "test_empty")
			return "Hi there!";
	}
	
	public function __isset($var)
	{
		echo "__isset triggered";
		
		if ($var == "test_empty")
			return true;
		
		return false;
	}
}

$t = new TestingEmpty();
echo $t->test_empty; //Hi there! 
var_dump(strlen($t->test_empty)); //int(9)
//__isset triggered
var_dump(empty($t->test_empty)); //bool(false)... endlich

Durchs Überschreiben der magischen Funktion __isset, die implizit von empty bei __get aufgerufen wird, können wir das Verhalten so korrigieren, wie es uns gefällt.

Auch wenn man das nicht als „Bug“ bezeichnen kann und das Verhalten genau betrachtet auch logisch ist, wäre es trotzdem schön wenn darauf im Manual zu empty besser hingewiesen werden würde.

Weitere Posts:

Dieser Beitrag wurde unter php, PHP-WTF, webdev veröffentlicht. Setze ein Lesezeichen auf den Permalink.

9 Antworten auf PHP WTF #5

  1. Dag sagt:

    Danke für dieses WTF. Ist mir so zum Glück noch nicht untergekommen aber definitiv gut zu wissen. Ich finde aber generell das der Einsatz der magischen Getter und Setter tendenziell zu schlechter wartbarem Code führt und deshalb möglichst vermieden werden sollte.

    • david sagt:

      Hi! Mir ist das Problem bei der Arbeit mit ORM untergekommen. Und so auf die Schnelle fällt mir kein Weg ein, wie man bei einem ORM auf magische Funktionen verzichten soll.

      • Dag sagt:

        Deshalb habe ich ich ja auch geschrieben der Einsatz sollte, soweit möglich, vermieden werden. Das die magischen Funktionen in PHP durchaus sinnvoll sein können will ich nicht bestreiten.

  2. Daniel sagt:

    @David: Es gibt ja auch noch die Möglichkeit, dass du die Getter und Setter generieren lässt. Stichwort Zend_CodeGenerator. Ich habe damit schon ein paar gute Erfahrungen gemacht. Ich glaube aber auch, dass Propel die nötigen Klassen inkl. aller Methoden generiert.

    • Gernot sagt:

      Getter und Setter kann mir meine IDE auch generieren. Oder ich setze gleich alles public, kommt ja am Ende aufs gleiche raus. Genau wie diese ach-so-tollen Ruby accessors (Punkt „Ruby’s syntax is impossible to understand!“). Total unnütz meiner Meinung nach. Einfach public davor und es kommt aufs Gleiche raus.

      • Daniel sagt:

        Klar kannst du auch alles public machen, ist aber nicht sauber und irgendwann macht es dir dann mal viel Kopfzerbrechen…

        • Gernot sagt:

          Und was genau bringt es mir für einen Vorteil, für die privaten Attribute stupide getter und setter einzuführen? Dann kann ich sie auch gleich Public machen. Sehe ich keinen Unterschied.

          • Daniel sagt:

            Spätestens wenn du aus irgendwelchen Gründen mehr Kontrolle über deine Properties brauchst, dann wirst du wissen was es dir bringt. Z.B. wenn du Typsicherheit sicherstellen willst oder auf eine Veränderung reagieren musst.

  3. Die Funktion empty() akzeptiert nur PHP interne Typen. Normalerweise ist der Funktion empty() der Typ „TestingEmpty“ nicht bekannt. General, sollte empty() nicht dafür verwenden um öffentliche Klassen-Attribute zu überprüfen. Versuche mal das hier:


    function my_empty($val)
    {
    return empty($val);
    }

    class TestingEmpty
    {
    public function __get($var)
    {
    if ($var == "test_empty")
    return "Hi there!";
    }
    }

    $t = new TestingEmpty();
    var_dump(my_empty($t->test_empty));
    var_dump(my_empty($t->test_is_empty));

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.