Willkommen in der Webstatt Zum Webstatt Blog und Stories
Rebel4s Rebel4s am 20.03.07 18:12

Das Problem:
Ich habe eine Template Klasse und eine MySQL Klasse. Wenn ich jetzt in meiner Template Klasse einen mysql query ausführen will, kann ich ja die Mysql Klasse nicht einfach erben, da die MySQL Klasse die DB-Zugangsdaten durch den Konstruktor übermittelt bekommt.

Hatte das Problem schonmal hier kruz angesprochen und ich bekam nur das Stichwort "Klassen Manager" oder "Master Class" oder so.
Nur ich kann mir da nix Handfestes drunter vorstellen, bzw. nur zum Teil.

Also die Template Klasse müsste ja dann quasi auf ein Objekt der MySQL Klasse zugreifen. Jedoch finde ich es sehr unschön ein Objekt in der Template Klasse. Gibt es da eine Lösung durch Vererbung?

Ich hoffe ihr versteht mein Problem.

PS: Wür dmich über ein Beispiel freuen. :D

netcup.de Warum gibt es hier Werbung?
Snake am 20.03.07 18:19

nunja, am einfachsten wäre doch, der template klasse einfach das bestehende object der mysql klasse als referenz zu geben.

könntest dir noch en bein ausrenken und es über statische variabeln und den klassennamen machen, aber bei dem problem wohl nicht nötig

Rebel4s Rebel4s am 20.03.07 18:29

Quote
Original von Snake
nunja, am einfachsten wäre doch, der template klasse einfach das bestehende object der mysql klasse als referenz zu geben.

könntest dir noch en bein ausrenken und es über statische variabeln und den klassennamen machen, aber bei dem problem wohl nicht nötig


Und was ist wenn ich mehrere Klassen habe, die ich miteinander verknüpfen will, was empfehlst du mir dann?

Snake am 20.03.07 18:36

wie meinst mehrere klassen?

typischerweise nutzt man oft factory klassen. also ein object das nichts anderes tut, als alle objecte in sich zu halten. dann musst immer nur eins weiter geben. wenn du php5 hast, dann geht das sogar noch einfacher.

in php5 könntest zu z.b. machen:


<?php

class db { }
class tpl { }
class abc { }

class F {
protected static $db;
protected static $tpl;
protected static $abc;

public function __construct() {
self::$db = new db();
self::$tpl = new tpl();
self::$abc = new abc();
}

public static function GetDb() {
return self::$db;
}
public static function GetTpl() {
return self::$tpl;
}
public static function GetAbc() {
return self::$abc;
}
}

class blah {

public function __construct() {
$db = F::GetDb();
$tpl = F::GetTpl();
$abc = F::GetAbc();
//möglich auch:
//F::GetDb()->query()... etc.
}
}
new F();
new blah();


?>


//oh, ich glaub die 1. version hätte nicht funktioniert *g* repariert!

Rebel4s Rebel4s am 20.03.07 18:46

Vielen dank, so in etwa hatte ich mir das vorgestellt.

Rebel4s Rebel4s am 21.03.07 18:15

Ich hab mir nun folgende Klasse gecoded:

<?PHP
/*
* Klasse zum erstellen von Objekten
*/

class factory {

protected static $klassen;

public function __construct() {
self::$klassen = array(
'test' => DIR.'lib/test.php'
);
}

static public function factory (/* String */ $className, /* Array */ $params = null)
{
if (!is_string($className) || !strlen($className))
{
die('Die zu ladende Klasse muss in einer Zeichenkette benannt werden');
}

elseif(!isset(self::$klassen[$className]))
{
die('Die zu ladende Klasse "'.$className.'" ist unbekannt');
}

elseif(!file_exists(self::$klassen[$className]))
{
die('Die zu ladende Datei "'.self::$klassen[$className].'" existiert nicht');
}

else
{
require_once(self::$klassen[$className]);

if(!class_exists($className))
{
die('Die zu ladende Klasse "'.$className.'" existiert nicht');
}
else
{
return new $className($params);
}
}
}

}

?>


Nun Frag ich mich, wie ich beim erstellen eines Objekts mehrere Parameter angeben kann. Momentan ja durch den Array, aber der Array ist ja nur 1 Parameter.
z.B. wenn ich meine MySQL Klasse habe und beim erstellen des Objekts die zugangsdaten angeben will:
db($server,$user,&$password,$database)

Finde das über einen Array sehr unschön.

Wasili am 21.03.07 19:01

http://ch2.php.net/func_get_args
Damit kannst du auf alle ARgumente einer Funktion zugreifen, ohne deren Namen zu wissen (Also unendlich viele, theoretisch).

Funktionen wie call_user_func_array sind auch noch intressant - Liess dich da mal durch die verschiedenen Funktionen durch :D

Michael Michael am 21.03.07 19:18

Das schriet gerade danach, dies mit autoload zu verbinden

Al3x0r Al3x0r am 21.03.07 19:53

Absolut Off-Topic: Oh Yes... super das dieses Thema nun Gegenstand einer Diskussion hier wird... schreibt daraus einen meterlangen Thread... das Thema interessiert mich nämlich auch tierisch und vorallem wie man sowas richtig, vernünftig umsetzt.

Rebel4s Rebel4s am 21.03.07 20:05

Quote
Original von Wasili
http://ch2.php.net/func_get_args
Damit kannst du auf alle ARgumente einer Funktion zugreifen, ohne deren Namen zu wissen (Also unendlich viele, theoretisch).

Funktionen wie call_user_func_array sind auch noch intressant - Liess dich da mal durch die verschiedenen Funktionen durch :D


Ich weiß nicht ob func_get_args dafür die richtige Lösung ist.

func_get_arg (0) ist dann der Klassenname
Die Parameter wären dann in func_get_arg (>0)

Wie könnt ich dann geschickt die Paramter in
return new $className($params);
unterbringen?

Michael Michael am 21.03.07 20:15

Hmm, beispielsweise könntest du anstelle verschiedener Parameter IMMER einen Array übergeben.
Also anstelle von
... testFunktion($parameter1,$parameter2,...)

so etwas
$parameter = array('parameter1'=>$parameter1,'parameter2'=>$parameter2,...);
...testFunktion($parameter);

Der Vorteil: es gibt immer eine feste Anzahl an Parametern, den einen Array. Folglich könntest du den Aufruf des Konstuktors standardisieren.

Anbei noch eine Funktion, die ich in einem aktuellen Projekt für das automatische Laden der Dateien verwende:

function __autoload($classname) {
$dirs = explode('_',$classname);
unset($dirs[0]);
unset($dirs[count($dirs)]);
$dir = '';
foreach($dirs as $key => $value) {
$dir .= "$value/";
}
$filename = $dir.$classname.'.class.php';
if(file_exists("toao/$filename")) {
include_once($filename);
} else {
trigger_error(toao_messages::AUTOLOADING_NOT_FOUND." $classname",toao_constants::ERROR);
}
}


Anhand eines Bezeichners wird hier der Pfad bestimmt (an Perl angelehnt). Aus eine_klasse_irgendwo_test
wird eine/klasse/irgendwo/test.class.php
Meiner Meinung nach, kann man durch eine solche aufeinander aufbauende Verzeichnisstruktur viel an Übersichlichkeit gewinnen.
Der trigger_error Kram kann ja ignoriert werden.

@Al3x0r: Ernst gemeint oder Ironie?

// edit

Noch schöner als ein Array mit den Parametern ist natürlich ein Parameter Objekt.

Al3x0r Al3x0r am 21.03.07 20:26

Quote
Original von Michael
@Al3x0r: Ernst gemeint oder Ironie?


Absolut ernstgemeint... Ich will schon voll lange mein Projekt mit OOP aufziehen aber sowas wie Klassenmanagement ist halt absolut neuland für mich, sodass dieser Thread unwahrscheinlich gut für mich werden kann...

mfg alex

Rebel4s Rebel4s am 21.03.07 20:44

Hier noch mal genauer mein Anliegen:
static public function factory (/* String */ $className, /* Array */ $params = null)
Da will ich bei $params weiterhin einen array nutzen

Jedoch will ich hier:
return new $className($params);
Dann statt $params, die einzelnen parameter aus dem array einzeln übergeben:
z.B:
return new $className($server,$user,&$password,$database)
Das jedoch dynamisch, geht das?

Michael Michael am 21.03.07 20:50

Das geht zumindest über dynamische Variablen.
ABER ist es nicht viel einfacher dies im Konstruktor der Klasse zu machen? Da weißt du ja genau welche Parameter gebraucht werden:

protected $parameter1 = 'Defaultwert';
protected $parameter2 = 0;
...
public function __construct($parameter) {
$this->parameter1 = array_key_exists('parameter1',$parameter) ? $parameter['parameter1'] : $this->parameter1;
...
}


// edit

Das geht natürlich auch automatisch, wenn die Klassenattribute gleich den Schlüsseln im Array heißen

Rebel4s Rebel4s am 21.03.07 21:26

Mit:
$args = '"Hallo", "asdasd"';

eval ("new test($args);");


Werden 2 Parameter übergeben, daraus bau ich dann morgen mal was feines.

Rebel4s Rebel4s am 22.03.07 21:05

Also momentan sieht die Klasse so aus:

<?PHP
/* Factory
* Klasse zum erstellen von Objekten
*/

class f {

protected static $klassen; //Array, Enthält den Pfad zu den Klassen

/* Konstruktor */
public function __construct() {

/*
Klassen werden in dem Array gespeichert
Klassen, die in diesem Array nicht existieren können nicht aufgerufen werden
*/
self::$klassen = array(
'test' => DIR.'lib/test.php'
);
}

/* Factory */
static public function get (/* String */ $className, /* Array */ $params = null)
{
// $className wird überprüft
if (!is_string($className) || !strlen($className))
{
die('Die zu ladende Klasse muss in einer Zeichenkette benannt werden');
}

// Wenn Klasse nicht im Array steht
elseif(!isset(self::$klassen[$className]))
{
die('Die zu ladende Klasse "'.$className.'" ist unbekannt');
}

// Wenn Datei der Klasse nicht existiert
elseif(!file_exists(self::$klassen[$className]))
{
die('Die zu ladende Datei "'.self::$klassen[$className].'" existiert nicht');
}

// Wenn keine Fehler
else
{
// Datei wird included
require_once(self::$klassen[$className]);

// Wenn Klasse nicht existiert
if(!class_exists($className))
{
die('Die zu ladende Klasse "'.$className.'" existiert nicht');
}
else
{
// Parameter: Array zu String
if(is_array($params)) $params = implode('", "', $params);

// Klasse wird zurückgegeben
eval ("return new test(\"$params\");");
}
}
}

}

?>


kann man eigentlich __autoload auch als methode benutzen oder ist das nur eine funktion?

Michael Michael am 22.03.07 21:24

1) __autoload ist eine Funktion und müsste daher vor der Klasse definiert werden. Dann fällt aber schon einmal das include/require(_once) weg.

2) Das mit eval klappt vielleicht. Aber ein Objekt oder Array mit den Parametern wäre schöner.

3) Warum ist das überhaupt statisch. Ist würde eine normale Klasse benutzen

4) Bisher ist die Klasse "nur" eine generische Funktion zum Erstellen von Instanzen. Hier gibt es noch keinen wirklichen Vorteil zu new Objekt($parameter). Eine factory macht dann Sinn wenn auch mehr möglich ist. Ein verdeutlichendes Beispiel: Man könnte eine Methode get_user haben, die nur ein User-Objekt zurück gibt wenn der User aktiv ist.

5) Ich würde daher anstelle von get($className,$params) get_$className bentuzen und das über __call steuern. Wir wissen ja alle __call wird aufgerufen wenn es die Methode nicht gibt, also eröffnet das die Möglichkeit entweder eine spezielle Methode get_user zu definieren und wenn es die nicht gibt wird über __call die generische Version aufgerufen

6) $params = NULL würde ich in $params = array() ändern. Dann sparst du dir das is_array beim Bilden der Parameter

Al3x0r Al3x0r am 30.04.07 12:53

Gibt es zu Factory-Klassen noch irgendwelche interessanten Links im Internet, die einem vllt. nochmal ein bisschen mehr vermitteln wie das so alles funktioniert ?

mfg ALex

Dustwolf Dustwolf am 29.09.07 19:16

Guten Abend.

Bis dato hat __autoload() bei mir immer funktioniert, aber nun ist ein Problem aufgetreten. Ich skizziere das Problem mal im Allgemeinen.

Meine Dateien:

index.php
Eigentliches Seitenscript!
objects/framework.php
Anbindung meines Frameworks. Hier steht die __autoload() und einige bei diesem Problem unwichtige Konstanten.
objects/database.php
Ein Singleton-Objekt für Datenbankabfragen.
objects/user.php
Ein Singleton-Objekt für Useroperationen.

Ich schreibe also ein Framework, welches man durch include('objects/framework.php'); nutzen kann. Ab hier werden alle Objekte durch __autoload() in der framework.php eingelesen. Das funktioniert, folgendermaßen: Autoload prüft, ob die class.php vorhanden ist und bindet die (vorhandene) Datei ein. (Relative Pfade: objects/[objektname].php)

Das funktioniert auch gut, so lange die Objekte das erste Mal in der index.php aufgerufen wird. Wenn ich aber z.B. von user.php aus einer Methode eine Datenbankabfrage vornehmen muss und dies die erste Verwendung des Datenbankobjekts ist, dann findet er die entsprechende PHP-Datei nicht mehr.

Außerhalb der Klasse (daher: in der index.php) funktioniert die Zeile aber problemlos, die in der Methode nicht funktioniert.

Beispiel eines solchen Aufrufs: database::getInstance()->methode();

Wieso funktioniert die selbe __autoload()-Funktion nur, wenn das Nachladen eines Objekts außerhalb von Objekten erfolgt? Der Code der Funktion kann es nicht sein, denn der ist wirklich grundlegend. (Abfrage mit wahlweise include() oder Fehlerausgabe per echo()!)

Vielen Dank für alle Antworten schon mal im vorraus. :D

Creative Commons Lizenzvertrag
Alle Inhalte des Webstatt-Archivs stehen unter einer Creative Commons Namensnennung - Weitergabe unter gleichen Bedingungen 3.0 Unported Lizenz.

Impressum & Kontakt