Konrad L. M. Rudolph: Array in $_SESSION ablegen

Moin,

ich habe Probleme, ein Array in der $_SESSION-Variable anzusprechen.

$_SESSION ist hierbei in eine Klasse gekapselt und ich benutze Interzeptoren, um __get, __set, __unset und __isset an $_SESSION weiterzuleiten. Leider macht mir PHP einen Strich durch die Rechnung, weil auf diese Weise lediglich Kopien manipuliert werden, wenn ich einen Schreibzugriff auf das Array ausführen möchte, wie beispielsweise in folgendem Code:

// $session kapset die $_SESSION-Variable  
  
$session->flash = array();  
  
// … gaanz woanders:  
  
$session->flash['xyz'] = 'abc';

Im Moment behelfe ich mir mit folgendem offensichtlichen Workaround:

$flash = $session->flash;  
$flash['xyz'] = 'abc';  
$session->flash = $flash;

Diesen Weg möchte ich aber vermeiden, da das 'flash'-Array sehr groß werden kann (um genau zu sein können dort große Mengen an Binärdaten abgelegt sein) und das andauernde Umkopieren dieser Daten möchte ich um jeden Preis vermeiden. Es ist schon schlimm genug, dass ich die überhaupt in der Session ablegen muss (das ist aber leider so).

Gibt es da eine gute Lösung für? Kann man Referenzen auf $_SESSION-Variablen anlegen? Die Doku schweigt sich hierzu aus. Ich würde vermuten, dass es geht, möchte aber nicht in subtile Schwierigkeiten rennen.

Folgender Code *scheint* zu funktionieren. Any comments?

<?php  
// Session.php  
  
class Session {  
    private static $__instance;  
  
    protected function __construct() {  
        session_start();  
    }  
  
    private function __clone() { }  
  
    public static function getInstance() {  
        if (self::$__instance == null) {  
            self::$__instance = new Session();  
        }  
        return self::$__instance;  
    }  
  
    public function __isset($key) {  
        return isset($_SESSION[$key]);  
    }  
  
    public function __get($key) {  
        return &$_SESSION[$key];  
    }  
  
    public function __set($key, &$value) {  
        $_SESSION[$key] = $value;  
    }  
  
    public function __unset($key) {  
        unset($_SESSION[$key]);  
    }  
}  
  
?>

lg, Konrad -

--
Der Genitiv ist des Dativs Tod
  1. Moin,

    Grmpf, falsches Projekt getestet. Also, der Code funktioniert so natürlich nicht. Leider auch dann nicht, wenn ich den offensichtlichen Syntaxfehler in __get korrigiere.

    Jetzt sagt mir PHP: “Couldn't execute method Session::__set”

    lg, Konrad -

    --
    Der Genitiv ist des Dativs Tod
    1. Moin,

      noch ein Nachtrag:

      Jetzt sagt mir PHP: “Couldn't execute method Session::__set”

      Klar, ich habe auch versucht, einen Ausdruck zuzuweisen. Also, nach Modifikation sehen die beiden Methoden jetzt so aus:

          public function &__get($key) {  
              return $_SESSION[$key];  
          }  
        
          public function __set($key, $value) {  
              $_SESSION[$key] = $value;  
          }
      

      lg, Konrad -

      --
      Der Genitiv ist des Dativs Tod
      1. echo $begrüßung;

        Dein Beispiel lautete

        $session->flash = array();
          $session->flash['xyz'] = 'abc';

        Du versuchst mit Referenzen bei __set() und __get() zu hantieren. Damit referenzierst du nur $session->flash. Deine Binärdaten liegen aber in einem Element dieses Arrays. Du müsstest also dieses Element referenzieren und nicht das Array selbst.

        $session->flash['xyz'] &= 'abc';

        wäre passend. Jedoch sollst du Referenzen nur dann verwenden, wenn es technisch notwendig ist, nicht wenn du dir einen Vorteil davon versprichst, den du in diesem Fall gar nicht erlangst, weil der von dir vermutete Nachteil nicht mit der internen PHP-Arbeitsweise übereinstimmt (siehe https://forum.selfhtml.org/?t=160889&m=1046650).

        Auch hier wieder gilt das Motto: Optimiere geht über studieren, nicht über probieren. Will heißen: Erst das unoptimierte System messen, dann die Optimierung vornehmen, anschließend wieder messen, ob es eine Verbesserung gegeben hat.

        echo "$verabschiedung $name";

  2. echo $begrüßung;

    $flash = $session->flash;

    $flash['xyz'] = 'abc';
    $session->flash = $flash;

    
    >   
    > Diesen Weg möchte ich aber vermeiden, da das 'flash'-Array sehr groß werden kann (um genau zu sein können dort große Mengen an Binärdaten abgelegt sein) und das andauernde Umkopieren dieser Daten möchte ich um jeden Preis vermeiden.  
      
    Deine Befürchtung wird gegenstandslos sein. PHP nimmt intern keine Umkopieraktion vor, solange die beiden Werte (der "kopierte" und der orignale) sich nicht ändern. Erst wenn du einen von beiden änderst wird eine echte Kopie angelegt. Ein Link zu einem Artikel, der das interne Variablenhandling PHPs beschreibt versteckt sich im Handbuch-Kapitel zur Funktion [debug_zval_dump()](http://de.php.net/manual/en/function.debug-zval-dump.php):  
    [References Explained (by Derick Rethans)](http://derickrethans.nl/php_references_article.php)  
      
      
    echo "$verabschiedung $name";
    
    1. Moin,

      Deine Befürchtung wird gegenstandslos sein.

      Na, meine „Befürchtungen“ sind Beobachtungen. Also das, was tatsächlich bei mir passiert ist, denn:

      PHP nimmt intern keine Umkopieraktion vor, solange die beiden Werte (der "kopierte" und der orignale) sich nicht ändern.

      Jup, ich ändere ja aber etwas an dieser „Kopie“. Das heißt, in diesem Moment wird mir PHP das gesamte Array als echte Kopie anlegen.

      Danke auf jeden Fall für die beiden Links.

      lg, Konrad -

      --
      Der Genitiv ist des Dativs Tod
      1. echo $begrüßung;

        Jup, ich ändere ja aber etwas an dieser „Kopie“. Das heißt, in diesem Moment wird mir PHP das gesamte Array als echte Kopie anlegen.

        Nein, du änderst nicht das gesamte Array sondern nur eins der Elemente darin.

        echo "$verabschiedung $name";

        1. Moin,

          Jup, ich ändere ja aber etwas an dieser „Kopie“. Das heißt, in diesem Moment wird mir PHP das gesamte Array als echte Kopie anlegen.

          Nein, du änderst nicht das gesamte Array sondern nur eins der Elemente darin.

          Ja. Alles andere würde ja auch dazu führen, dass die Referenz (also die unechte Kopie) eh verworfen wird. Nochmal im Klartext; wenn ich folgendes schreibe:

          Session->flash = array();  
          Session->flash['xyz'] = 'Hallo';
          

          Dann wird in der $_SESSION-Variable nur ein leeres Array gespeichert.

          lg, Konrad -

          --
          Der Genitiv ist des Dativs Tod
          1. echo $begrüßung;

            Session->flash = array();
            Session->flash['xyz'] = 'Hallo';
            Dann wird in der $_SESSION-Variable nur ein leeres Array gespeichert.

            Nicht nur das, es wird auch noch eine Notice-Meldung ausgegeben, die dir dieses Verhalten bestätigt. Bitte schalte zum Entwickeln immer das error_reporting auf E_ALL. Wenn du dann nach der angezeigten Notice googlest, bekommst du ein paar Informationen zu diesem Thema.

            Dieses Verhalten wurde mit Version 5.2 gändert, außerdem gab es da auch noch einen Bug.
            Du musst __get() eine Referenz zurückgeben lassen, damit du den Wert direkt bekommst und nicht nur eine Kopie.

            public function &__get($key) {
                return $_SESSION[$key];
              }

            public function __set($key, $value) {
                $_SESSION[$key] = $value;
              }

            Somit funktionierte es bei mir.

            Mit $session->flash = array(); ruft PHP __set() auf.
            Mit $session->flash['xyz'] = 'Hallo'; ruft PHP zunächst __get() auf, um $session->flash zu erhalten. Ohne Referenz schreibst du anschließend in die zurückgegebene Kopie, die dir gleich wieder verloren geht, weil nichts mehr auf sie verweist.

            echo "$verabschiedung $name";