# Variblen Global verfügbar machen



## spex (23. Dezember 2007)

Hi,

Ich suche eine Möglichkeit wie ich Variablen Global verfügbar machen kann.
Ich möchte die Varibalen aus meiner config.php auch in Funktionen verwenden können, ohne das ich jedesmal die datei neu inludieren oder per $GLOBALS darauf zugreifen muss.

Ich würd es gerne so machen wie in vBulletin, das arbeitet ja scheinbar irgendwie mit Classen oder Objekten, aber da blick ich nicht ganz durch. Weil die Classen müssten ja dann in den Funktionen wieder neu gesetzt werden ($config = new classname und genau das soll ja nicht passieren.

In vBulletin kann man (wenn die global.php includiert wurde) auf die Configeinträge bequem über $vbulletin->userinfo['username'], zugreifen ohne jedesmal eine classe neu laden zu müssen oder eine config.php Datei neu zu includieren.

Gruß


----------



## Gumbo (23. Dezember 2007)

Du könntest mit dem Singleton-Entwurfsmuster arbeiten.


----------



## spex (23. Dezember 2007)

Mal reinarbeiten in das PHP Beispiel. 
Immoment kann ich mir darunter noch nicht viel vorstellen.


----------



## maeTimmae (23. Dezember 2007)

vBulletin ist nun wirklich keine Glanzleistung in Sachen effizienter und eleganter Code...
Wenn ich es richtig in Erinnerung habe, wird in Version 3.5+ überwiegend im Global Scope (main) und mit Referenzen gearbeitet, jedoch ohne PHP5 Features. So sieht das dann zB so aus:


```
class vBulletin {
    var $user = null;

    function vBulletin( &$user ) {
        $this->user = &$user;
    }

    function doSth() {
        return $this->user->userinfo['username'];
    }
}
// ...
$user = new User();
// ...
$vbulletin = new vBulletin( $user );
// ...
var_dump( $vbulletin->doSth() );
```

Eleganter geht das via PHP5 und dem von Gumbo bereits genannten Entwurfsmuster, welches nur eine einmalige Instanzierung der Klasse zulässt. Dazu gibt es noch ein paar nette Abwandlungen, aber das liest du dir am besten selbst an.
Alternativ kannst du $GLOBALS verwenden, was jedoch eher unschön, unsauber und nicht empfehlenswert ist, so lange man nicht wirklich weiß, was man tut.


----------



## spex (23. Dezember 2007)

Das wird wol daran liegen das vBulletin auf Kompatibilität setzen muss. Nicht jeder besitzt einen eigenen Server und kann von PHP4 auf PHP5 aufrüsten. Außerdem ist vBulletin 3 dann doch schon etwas älter und die jetzt kommende Version 3.7.0 ist soweit ich weiß die Letzte und danach wird nurnoch an Version 4 gearbeitet (abgesehen von Bugfixes ...).

Und ja $GLOBALS wollte ich nicht verwenden das ist mir zu heikel.

Vondaher: Danke für eure Antworten ich werd mir mal beide Lösungen durch den Kopf gehen lassen.


----------



## spex (24. Dezember 2007)

Wäre nett wenn mir mal jemand ein Beispiel aus dem Code hier (von Wikipedia) schreiben könnte, vielleicht versteh ich dann mehr.

Was ich brauche ist wie gesagt, eine Klasse die es mir ermöglicht auf eine Variable zuzugreifen, die in der Klasse einmal aus einer Configdatei eingelesen wird, sodass ich dann immerwieder darauf zugreifen kann und das GLOBAL, also auch in Funktionen (Ohne die Configdatei erneut einlesen zu müssen.).


```
<?php
class Singleton {
  /**
   * die Instanz wird hier abgelegt
   */
  private static $instance = NULL;
  
  /**
   * Konstruktor private, damit die Klasse nur aus sich selbst heraus instanziiert werden kann.
   */
  private function __construct() {}

  /**
   * mit dieser statischen Methode bekommt man das Singleton
   */
  public static function getInstance()
  {
      if (self::$instance === NULL)
      {
          self::$instance = new Singleton;
      }
      return self::$instance;
  }

  /**
   * Klonen per 'clone' von außen verbieten
   */
  private function __clone() {}
}

$single = Singleton::getInstance();
?>
```

Gruß


----------



## Flex (25. Dezember 2007)

```
<?php
class Singleton {
  /**
   * die Instanz wird hier abgelegt
   */
  private static $instance = NULL;
  private static $config_file = './test2.txt';
  private static $array;

  /**
   * Konstruktor private, damit die Klasse nur aus sich selbst heraus instanziiert werden kann.
   */
  private function __construct() {}

  /**
   * mit dieser statischen Methode bekommt man das Singleton
   */
  public static function getInstance()
  {
	  if (self::$instance === NULL)
	  {
		  include(self::$config_file);
		  self::$array = $config;
		  self::$instance = new Singleton;
	  }
	  return self::$instance;
  }
  /**
   * mit dieser Methode bekommt man die Inhalte des Config Arrays
   */
  public static function __get($var)
  {
  	return self::$array[$var];
  }
  /**
   * Klonen per 'clone' von außen verbieten
   */
  private function __clone() {}
}

$single = Singleton::getInstance();
print $single->test;
?>
```

Dieses Beispiel geht von einer Configdatei aus, die ungefähr so aussieht:

```
<?php

$config['test'] = "abc";
$config['new_config'] = "123";

?>
```


----------



## maeTimmae (25. Dezember 2007)

Kleine Korrektur: "Keine Überladungsmethode darf statisch definiert sein.", meint die PHP-Doku, demzufolge wäre das hier "besser":


```
<?php
class Singleton {
  /**
   * die Instanz wird hier abgelegt
   */
  private static $instance = null;

  private $array;

  /**
   * Konstruktor private, damit die Klasse nur aus sich selbst heraus instanziiert werden kann.
   */
  private function __construct( array $config ) {
     $this->array = $config;
  }

  /**
   * mit dieser statischen Methode bekommt man das Singleton
   */
  public static function getInstance( array $config )
  {
      if (self::$instance === NULL)
      {
          self::$instance = new Singleton( $config );
      }
      return self::$instance;
  }
  /**
   * mit dieser Methode bekommt man die Inhalte des Config Arrays
   */
  public function __get($var)
  {
      return $this->array[$var];
  }
  /**
   * Klonen per 'clone' von außen verbieten
   */
  private function __clone() {}
}

$single = Singleton::getInstance( array( 'test' => "foobar" ) );
print $single->test;
?>
```


----------



## Flex (25. Dezember 2007)

Ooops, da hast du natürlich recht... Wobei es trotzdem funktioniert... Weißt du auch den Grund dafür? Der würde mich interessieren... 
Ich meine, gut, es steht da... Aber ohne Gründe finde ich es komisch.


----------



## maeTimmae (25. Dezember 2007)

Ui, habe mich einfach mal auf die Dokumentation gestützt, wie so oft ist die Antwort bei PHP-Eigenheiten, dass es PHP ist. Effektive Überladungen gibt es ja leider nicht, die ganze Chose der "Objektorientierung" ist in PHP generell sehr lose implementiert. Könnte mir vorstellen, dass selbst die statischen Eigenschaften und Methoden doch auch nur in eine Instanz eines Objekts in C++ gepackt werden und die Zugriffe an sich dann im entsprechenden Scope ausgewertet werden. Wird also statisch aufgerufen, wird die einmalig initialisierte, statische Instanz benutzt, wird dynamisch aufgerufen, wird die entsprechend referenzierte Instanz verwendet.

Durch die Unfunktionalität folgenden Konstrukts aber, macht es dann doch wenig Sinn es auf das Statische anzuwenden, da die dynamische Pseudoüberladung nicht mehr stattfinden kann:

```
<?php
class Test {
    private static $test1 = array();
    private $test2 = array();
    
    public function __construct() {}
    
    public function __set($name, $value) {
        $this->test2[$name] = $value;
    }
    
    public static function __set($name, $value) {
        self::$test1[$name] = $value;
    }
}
```

Wirft: _Cannot redeclare Test::__set() in <file> on line 12_

Wahrscheinlich ist die Aussage in der PHP-Doku deshalb auch eher eine Empfehlung  Ist halt ein wenig öde, wenn man nur statisch arbeitet, da man genau ein Objekt verwenden kann, wohingegen mit mehreren Instanzen der Klasse entsprechend mehrere Objekte verwaltet werden können ohne sich in die Query zu kommen. Schwer zu beschreiben, aber ich denke, du weißt, worauf es hinausläuft.


----------



## Flex (25. Dezember 2007)

Stimmt, die Logik leuchtet mir natürlich ein.
Ich hoffe, dass in PHP6 sich noch einiges zur OOP Funktionalität tut... 
Danke für die Erklärung.


----------



## R00Ki3 (26. Dezember 2007)

singleton pattern ist interressant für dinge die sich auf eine klasse alleine beziehen...
Ich nutze es zumbeispiel für die datenbank...
ansonsten gibt es auch die lösung registry...
Die klasse registry nimmt die objecte/variablen auf und gibt sie zurück und kann sie auch löschen...
Hier mal die Klasse:

```
class registry{
/**
 * @desc    Speichert ein Object der Class registry
 *
 * @var     object
 * @access     protected static
 */
    protected static $instance = null;
/**
 * @desc    Speichert Objecte so wie Alle weiteren Werte die Uebergeben werden
 *
 * @var     array
 * @access     protected static
 */
    protected $values = array();
/**
 * @desc    Erstellt ein Object der Class registry und speichert es in der Variable $this->instance
 *
 * @access     public static
 * @return    object
 */
    public static function getInstance(){
        if(self::$instance === null){
            self::$instance = new registry();
        }
        return self::$instance;
    }
/**
 * @desc    Konstruktor, ist eine geschuetzte Funktion.
 * Dies verhindert das mehr als 1 Object erstellt wird.
 * Auf die Klasse registry wird ueber die Funktion getInstance zugegriffen.
 * 
 *
 * @access     protected
 * @return    void
 */
    protected function __construct(){}
/**
 * @desc    Destruktor, ist eine geschuetzte Funktion.
 * Dies verhindert das Objecte von der Klasse registry von Hand geschlossen werden.
 *
 * @access     protected
 * @return    void
 */
    protected function __destruct(){
        unset($this);
    }
/**
 * @desc    Clone, die Funktion ist private, welches verhindert das mehr als 1 Object von registry existieren kann.
 *
 * @access     private
 * @return    void
 */
    private function __clone(){}
/**
 * @desc    Nimmt Werte entgegen und speichert sie in einem Array: $this->values
 *
 * @param     string        $key
 * @param    mixed        $value
 * @access     public
 * @return    void
 */
    public function set($key, $value){
        $this->values[$key] = $value;
    }
/**
 * @desc    Kontrolliert ob die gewuenschte Variable/Object vorhanden ist.
 * Falls ja wird die Variable/Object ausgeliefert.
 *
 * @param     string        $name
 * @access     public
 * @return    mixed
 * @see        registry::isregistry();
 */
    public function get($name){
        if($this->isregistry($name)){
            return $this->values[$name];
        }
        return false;
    }
/**
 * @desc    Kontrolliert ob die gewuenschte Variable/Object vorhanden ist.
 * Falls ja wird die Variable/Object geloescht.
 *
 * @param     string        $name
 * @access     public
 * @return    boolean
 * @see        registry::isregistry();
 */
    public function remove($name){
        if($this->isregistry($name)){
            unset($this->values[$name]);
            return true;
        }
        return false;
    }
/**
 * @desc    Kontrolliert ob die Variable/Object in der Klasse registry gespeichert ist.
 *
 * @param     string        $name
 * @access     public final
 * @return    boolean
 */
    public final function isregistry($name){
        return array_key_exists($name, $this->values);
    }
}
```
Hier ein verwendungszweck

```
$user = new User(...,...);
$config = parse_ini_file(...);
$registry = registry::getInstance();
$registry->set("user", $user);
$registry->set("config", $config);
/** Abfragen der werte **/
$getvalues = registry::getInstance();
$user = $getvalues -> get("user");
$config = $getvalues -> get("config");
```


----------



## maeTimmae (26. Dezember 2007)

Hört mir auf mit Datenbankklassen im Singleton-Pattern - Ein Mal, und nie wieder. Sobald mal mehr als eine Verbindung, oder eine erweiterte Verbindungskontrolle notwendig wird, muss das komplette Gerüst umgebaut werden. Alternative: Eine statische "Hauptinstanz", die man singleton-ähnlich verwenden kann, die jedoch nicht die einzige Instanz sein muss, oder eine andere Lösung, wie zB über ein Registry-Pattern.

Des Weiteren ist deine Registry-Lösung auch nicht das Optimale: Was ist, wenn ich eine Registry registrieren möchte oder muss? Ist mit Singleton ja leider nicht möglich, für etwaige Strukturierungen aber durchaus sinnvoll.

Ein kleiner Tipp am Rande: Die PHP SPL ist ne wunderbare Sache, so würde zB ein

```
class Registry extends ArrayObject { /** ... */ }
```
sämtliche gets und sets ersparen und implementiert auch noch gleich Array-ähnliche Zugriffe.
Weiterhin noch ein Tipp: Die Empfehlungen für die Schreibweise in PHP orientiert sich stark an jenem für Java, so sollten zB Klassennamen (auch wenn intern auf lowercase ausgewertet) groß geschrieben werden.
Außerdem noch eine kleine Anmerkung zum phpDoc:
@desc ist, wenn nicht schon abgeschafft, zumindest deprecated und für die meisten Dokumentationstools unnötig.
@access beinhaltet nur die Sichtbarkeit
@static wird zur Kennzeichnung statischer Methoden und Eigenschaften verwendet.
@return sollte so speziell wie möglich oder so abstrakt wie nötig angegeben werden. In dem Fall deiner Registry-Klasse würde zB "@return registry" klarer ausfallen.


----------



## spex (10. Februar 2009)

Hi,

nach Monaten nun hab ich mich für folgende Möglichkeit entschieden.
http://pastebin.com/mfaf5807

Die Datei einmal per require_once geladen und fertg.
So kann ich simplest per CONFIG::$db['first']['username'] etwa den Benutzername der Datenbank abfragen.
Naja und für Configfiles brauch ich ja kein Singleton-Pattern.

Gruß - sp3x


----------

