ich habe da mal ne frage.
und zwar bin ich dabei eine datenbank klasse zu schreiben. Testweise und vermutlich auch mit anwendungszweck.
Die Klasse heisst db und wird um die Klasse MySQLi erweitert.
sooooo
Im Konstruktor werden nur die Varibalen uebergeben, die als Parameter zur Erstellung der Klasse verlangt werden und es wird die Funktion open() aufgerufen, um eine MySQLi Verbindung aufzubauen. Hier bekomme ich schon ein paar Schwierigkeiten.
Hier der Code:
[CODE]
<?php
class db extends MySQLi
{
/* DATABASE VARIABLES */
public $host;
public $user;
public $pass;
public $dbase;
public $mysqli;
public $mysqli_stmt;
public $error;
public $errno;
public function __construct($host, $user, $pass, $dbase)
{
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$this->dbase = $dbase;
$this->open();
}
public function open()
{
$this->mysqli = parent::__construct($this->host, $this->user, $this->pass, $this->dbase);
if ($this->mysqli_stmt = parent::stmt_init())
{
return true;
}
else
{
return false;
}
}
}
?>
[/CODE]so wie es da steht funktioniert alles.
Jetzt will ich aber, dass die Variable $mysqli das MySQLi Objekt der extended Klasse enthaelt und ich mit dieser weiter arbeiten kann.
Probiert habe ich es an diese Zeile:
$this->mysqli_stmt = $this->mysqli->stmt_init()Worauf ich folgende Fehlermeldung erhalten habe:
Fatal error: Call to a member function stmt_init() on a non-object in C:\Programs\xampp\htdocs\randomchat\rara\class_db.php on line 27
Interpretiert habe ich den Fehler so, dass die Variable $mysqli NICHT das MySQLi Objekt erhalten hat.
Wie muss ich verfahren, um der Variable $mysqli as MySQLi Objket zuzuweisen?
mfg
Mad Dog
EDIT:
Habe (glaube ich) die Loesung des Problems gefunden.
Anstatt den Konstruktor der MySQLi Klasse aufzurufen, habe ich es so gemacht:
$this->mysqli = new mysqli ($this->host,
$this->user,
$this->pass,
$this->dbase);
Ist das eine gute Loesung oder gibt es einen besseren Weg?
[php]<?php
class db extends MySQLi
{
/* DATABASE VARIABLES */
public $host;
public $user;
public $pass;
public $dbase;
public $mysqli;
public $mysqli_stmt;
public $error;
public $errno;
public function __construct($host, $user, $pass, $dbase)
{
parent::__construct($host, $user, $pass, $dbase);
oha gut zu wissen.
danke mermshaus.
okay, also wenn das $this das mysqli-objket ist, kann ich es trotzdem noch nutzten um z.b. definierte variablen der db klasse zu benutzten.
z.b. wie hier:
$this->host = $host;
oder ist das $this objekt dann nur noch das mysqli-objekt und nichts anderes?
Ja, kannst du. Die Methoden und Instanzvariablen von erweiterter (MySQLi) und erweiternder (db) Klasse werden gewissermaßen „zusammengeschmissen“. Dabei überschreiben jedoch Bestandteile der erweiternden Klasse gleichnamige Teile der Elternklasse.
[php]<?php
class A
{
protected $_field = ‚Wert, der in A gesetzt wurde‘;
public function testA()
{
echo 'test in A<br>';
}
public function sayHello()
{
echo 'Hallo aus A<br>';
}
}
class B extends A
{
protected $_test = ‚Wert, der in B gesetzt wurde‘;
public function testB()
{
echo 'test in B<br>';
}
// override
public function sayHello()
{
echo 'Hallo aus B<br>';
}
public function sayHelloFromParent()
{
parent::sayHello();
}
public function getField()
{
return $this->_field;
}
public function getTest()
{
return $this->_test;
}
}
$b = new B();
$b->testA(); // „test in A“
$b->testB(); // „test in B“
$b->sayHello(); // „Hallo aus B“
$b->sayHelloFromParent(); // „Hallo aus A“
echo $b->getField(), ‚ ‘; // „Wert, der in A gesetzt wurde“
echo $b->getTest(), ‚ ‘; // „Wert, der in B gesetzt wurde“[/php]
Ich weiß nicht genau, was du vorhast, aber wahrscheinlich ist es günstiger, nicht MySQLi zu erweitern, sondern eine MySQLi-Instanz als Instanzvariable einer neuen Klasse anzulegen und innerhalb der Klasse mit dieser MySQLi-Instanz zu arbeiten.
Was Datenbanken angeht so sehen diese öfters mal anders aus. Deswegen würde ich das nicht um die Klasse erweitern, sondern würde ein Datenbank Interface schreiben. Ein Interface ist Praktisch eine Schablone für eine Klasse. Es sagt welche Attribute und welche Methoden in der Klasse vorhanden sein müssen.
So kann es deiner Business-Logik egal sein welchen typ von Datenbank du hast, da alle die gleichen Methoden haben. Dafür fragst du dann nur ab, ob sie vom Typ Datenbank sind.
Würde dann so aussehen in seehr gekürzter Form
[php]
interface Database {
public function connect();
public function close();
public function select( $query );
}
class MysqlDatabase implements Database {
public function connect() {
//connecten
}
public function close() {
//schliessen
}
public function select( $query ) {
//führe select query aus
}
}
hmmm okay darueber muss ich noch mal nachdenken
gibt es eine moeglichkeit, dass ich eine variable in der klasse db, dass mysqli objekt zuweise?
also $this soll NICHT das mysqli objekt sein z.b. $mysqli, eine variable aus der db klasse.
abstract class Db_Adapter_Abstract
{
abstract public function __construct($host, $user, $pass, $dbase);
abstract public function select($query);
abstract public function fetchRow();
abstract public function fetchAll();
public static function createNewAdapter($type, $host, $user, $pass, $dbase)
{
if ($type == 'mysqli') {
return new Db_Adapter_Mysqli($host, $user, $pass, $dbase);
}
// Besser: Fehler werfen statt return null
return null;
}
/**
*
* @var MySQLi_Result
*/
protected $_result = null;
public function __construct($host, $user, $pass, $dbase)
{
$this->_mysqli = new MySQLi($host, $user, $pass, $dbase);
}
public function select($query)
{
$this->_result = $this->_mysqli->query($query);
}
/**
* Gibt eine Zeile des aktuellen Results zurück
*
* @return array
*/
public function fetchRow()
{
return $this->_result->fetch_assoc();
}
/**
* Gibt das komplette Result zurück
*
* @todo Ab PHP 5.3 besitzt MySQLi_Result bereits eine fetch_all-Methode
* @return array
*/
public function fetchAll()
{
$rows = array();
while ($row = $this->_result->fetch_assoc()) {
$rows[] = $row;
}
return $rows;
}
}
/**
Im Code braucht niemals ein konkreter Adapter (z. B. Db_Adapter_MySQLi)
genannt zu werden. Welches DB-Backend eingesetzt wird, wird lediglich durch
einen Konfigurationswert bestimmt und von Db_Adapter_Abstract generiert
*/
abstract heißt doch, sofern ich das richtig verstanden habe (habe leider bislang noch nicht die Chance gehabt mich voll mit OOP PHP5 zu beschäftigen), dass eine Elternklasse eine function enthält, welche in einer „Kind“-Klasse verwendet wird / werde könnte.
Welchen Vorteil hat das? Ist das nicht „Speicherlastig“ wenn ich eine leer function deklariere und später diese überschreibe? Im Normalfall mache ich ja auch nicht
[php]
<?
functon printTest(){ }
function printTest(){
echo "huhu";
}
?>
[/php]
Und was ist dann interface?! Uff…ich denke ich muss mich da endlich hinsetzen und damit beschäftigen…
Abstrakte Klassen wurden soweit ich weiß vor Interfaces benutzt als Ersatz für Interfaces. (Korrigiert mich wenn ich falsch liege) Ein Interface ist eine Art Schablone für den Programmierer. Bei Datenbanken ist das besonders gut zu erklären.
Es ist ja möglich, dass man eine Mysql Datenbank benutzt oder aber auch eine MSSQL Datenbank. Die Datenbanken arbeiten mit verschiedenen befehlen (zum beispiel mysql_connect bzw mssql_connect). Aber die Grundsätzliche Idee ist ja immer die selbe. Verbinden, auslesen, eintragen, verändern, löschen und schließen. Deswegen sollte man sehen, dass alle Datenbank-klassen die selben Methoden Namen und Attribute benutzen. Um das umzusetzen benutzt man Interface als eine Art Schablone/Vorlage. Wenn die MysqlDatabase Klasse nicht die Methode connect hätte würde PHP meckern. Das ganze hat nun den Vorteil, das man die Datenbank Ebene ohne Probleme wechseln kann ohne etwas an der Business-Logik zu ändern. (Oder vielleicht nur 2 Zeilen ;)) Da man ja speziell nach dem Typ fragen kann, wenn ein Parameter mitgegeben wurde, kann man hier das Interface als Typ angeben. nehmen wir folgendes an.
[php]
interface Lebewesen {
private $name;
public function atmen($sauerstoff);
}
class Mensch implements Lebewesen {
private $name;
public function atme($sauerstoff) {
if($sauerstoff < 1) { echo „Ich krieg keien Luft mehr!“; }
else { echo „atme“; }
}
}
class Hund implements Lebewesen {
private $name;
public function atme($sauerstoff) {
if($sauerstoff < 1) { echo „jaul“; }
else { echo „wuff“; }
}
}
class Haus {
public function hineinGehen(Lebewesen $lebewesen) {
echo „reingeh“;
}
}
[/php]
Nun kann nur etwas an die Funktion reinGehen der Klasse Haus übergeben werden was vom Typ Lebewesen ist.
Das Beispiel mag ein wenig komisch aussehen, aber mir ist nichts besseres eingefallen
Hoffe es ist einigermaßen klar geworden
Was spräche denn in deinem Beispiel gegen Vererbung?
Ein Interface ist für mich eher eine Schnittstelle zwischen zwei Klassen (oder auch zwischen zwei Vererbungshierarchien), die in keiner unmittelbaren Beziehung zueinander stehen. Dadurch, dass eine der Klassen das Interface der anderen implementiert, kann sie dann auch dort eingesetzt werden, wo normalerweise eine Instanz der anderen Klasse verlangt wäre. Ich habe leider absolut kein einleuchtendes Beispiel dafür.
Nicht falsch verstehen. Hab nichts gegen die vererbung
Wenn absolut identische Methoden existieren bietet sich das gut an. Es ist eher ein Zusatz. Muss auch zugeben, dass ich mich verlesen habe Es gibt ja schon eine vorgefertigte Mysqli-Klasse welche hier ja nur tatsächlich erweitert werden soll.
Klar ist ein interface eine Schnittstelle. Jedoch nicht für 2 völlig „verschiedene“ Klassen. Die Klassen müssen ja die vorgegebenen Methoden und Attribute besitzen. In dem Datenbank-Fall würde man mit Interface nur erreichen, dass der Code modular ist und man einfach eine andere Datenbank einsetzen könnte ohne dass deine Logik etwas davon bemerkt.
Ich glaube PEAR arbeitet auch so. Mit einer Factory wird ein Objekt der Datenbanklasse erstellt, dass man grade braucht und dieses wird dann an die Logik übergeben. Die logik interessiert es garnicht, wie das Datenbankobjekt auf die Datenbank zugreift, solange das Objekt die logik versteht. Hauptsache er kennt zum Beispiel die Methode connect
Mehr interessiert ihn da nicht. Hoffe mein Beispiel zeigt so einigermaßen was ich meine. Bin nicht so der profi im erklären und arbeite auch erst seit 2-3 Monaten mit Interfaces
Ja, ich sehe das Problem, was vor allem in diesem speziellen Fall die Erklärung angeht. (Wir sind allerdings beruhigenderweise nicht die Einzigen, die das bei Interface vs. abstrakte Klasse nicht hinbekommen.) Ich habe gestern knapp 400 Wörter für diesen Thread als Antwort geschrieben, die aber nicht abgeschickt, weil ich kein einsichtiges Beispiel für einen Fall hatte, in dem ich ein Interface verwenden würde. Ich glaube, die Notwendigkeit für Interfaces ist stark kontextabhängig. Deshalb halte ich viele der vorhandenen Erklärungen für völlig unbrauchbar. (Logger_MySQList ein logger, die Klasse implementiert nicht lediglich auch die Funktionalität eines Loggers. Für mich klare Vererbung.)
Das Beispiel in Peter Kropffs Tutorial ist grundsätzlich etwas brauchbarer gedacht, jedoch letztlich auch wenig überzeugend. Die Implementierung des Interfaces Traummann (ich lese da: „verhält sich wie Traummann“) in der Klasse Mann macht jeden Mann zu einem Traummann. Deshalb die Frage: Was unterscheidet einen Traummann dann noch von einem Mann, wenn doch ausgeschlossen ist, dass es Männer gibt, die keine Traummänner sind? Und: Welche Klasse sollte das Interface Traummann noch implementieren, die selbst kein Mann ist beziehungsweise die auch nicht von Mann abgeleitet ist? Wozu brauche ich ein Interface und eine Klasse, wenn ich doch nicht zwischen beidem unterscheiden kann? Wesentlich logischer wäre es, Traummann von Mann abzuleiten und ihn vielleicht ein Interface IstHygienisch implementieren zu lassen.
[php]class Traummann extends Mann implements IstHygienisch {}[/php]
IstHygienisch ist ein Interface, das durchaus noch von anderen Klassen implementiert werden könnte, da es sich nicht einzig auf die Männer bezieht.
[php]class Traumfrau extends Frau implements IstHygienisch {}[/php]
Eine Funktion, die nun ein „hygienisches“ Objekt verlangt, würde Instanzen von Traummann bzw. Traumfrau akzeptieren.
Dieses Beispiel krankt natürlich daran, dass es nicht ganz leicht ist, sich eine Anwendung für etwas auszudenken, das „hygienisch“ ist, über das aber sonst keine weiteren Aussagen getroffen werden. Die Funktion könnte nur mit den Methoden arbeiten, die das IstHygienisch-Interface definiert.
Zudem geht das Design davon aus, dass es sinnvoll ist, Männer und Frauen zu trennen und nicht zum Beispiel lediglich eine Klasse Mensch mit den Feldern geschlecht und istHygienisch zu implementieren. Ob das günstig ist, lässt sich kontextlos ebenfalls nicht sagen.
[CENTER]* * *[/CENTER]
Aber mal zu deinem Beispiel.
Sie müssen die vorgegebenen Methoden und Attribute des gemeinsamen Interfaces implementieren, brauchen aber sonst absolut nichts miteinander zu tun zu haben.
Ein gutes Beispiel dafür ist das Interface IstSerialisierbar. Es besagt, dass sich ein Objekt in einen String überführen und aus einem String wieder rekonstruieren lässt (zur Übertragung oder Speicherung). Sowohl Traummann als auch Db_MySQLi können dieses Interface implementieren und somit diese Funktionalität gewährleisten.
Das ist bei Vererbung exakt dasselbe, wie ich in #7 (erstes Codebeispiel) dargestellt habe.
Ein Interface könntest du dann einsetzen, wenn du etwa so etwas machen wolltest:
[php]interface Db_Interface
{
public function connect($host, $user, $password, $db);
public function query($query);
}
abstract class Application
{
protected $_db = null;
abstract function renderPage($url);
public function setDatabaseHandler(Db_Interface $db)
{
$this->_db = $db:
}
public function getDatabaseHandler()
{
return $this->_db;
}
}
class MyApplication extends Application implements Db_Interface
{
public function connect($host, $user, $password, $db)
{
mysql_connect($host, $user, $password);
mysql_select_db($db);
}
public function query($query)
{
return mysql_query($query);
}
public function renderPage($url)
{
$db = $this->getDatabaseHandler();
$result = $db->query("SELECT content FROM table WHERE url = $url");
// ...
echo $content;
}
}
$app = new MyApplication();
$app->connect(‚localhost‘, ‚user‘, ‚password‘, ‚db‘);
$app->setDatabaseHandler($app);
$app->renderPage(‚index.html‘);[/php]
MyApplication implementiert die notwendige Datenbankfunktionalität einfach selbst und verzichtet darauf, eine Instanz einer weiteren Klasse übergeben zu bekommen. Sie kann also auch als Datenbankklasse eingesetzt werden, obwohl sie eigentlich eine Application ist. Sowas geht nur mit Interfaces.
[center]* * *[/center]
Edit:
Ein externer DB-Adapter, der MyApplication übergeben werden könnte, könnte so aussehen:
[php]<?php
interface Db_Interface
{
public function connect($host, $user, $password, $db);
public function query($query);
}
abstract class Db_Abstract implements Db_Interface
{
// Gemeinsam von allen DB-Adaptern nutzbarer Code
public function quote($value)
{
if (is_string($value)) {
return „'“ . str_replace(„'“, „\'“, $value) . „'“;
}
return (int) $value;
}
}
class Db_MySQL extends Db_Abstract
{
public function connect($host, $user, $password, $db)
{
mysql_connect($host, $user, $password);
mysql_select_db($db);
}
public function query($query)
{
return mysql_query($query);
}
}
error_reporting(-1);
$db = new Db_MySQL(‚localhost‘, ‚user‘, ‚password‘, ‚database‘);
echo $db->quote(„Hier ist ein ‚Teststring‘.“);[/php]
Das Interface ist also gewissermaßen die Erweiterung des Vererbungskonzepts, um auch Klassen, die nicht Teil der Vererbungshierarchie sind, die Möglichkeit zu geben, entsprechende Fremd-Funktionalität zu implementieren.
Ob das erstrebenswert oder gar notwendig ist, hängt stark vom Kontext ab.
Ich weiß, ist was älter, aber das Thema wollte ich nur mal erläutern…
Da ich neben PHP auch Java kenne, kann ich sagen, dass der Sinn von Interfaces in PHP von sehr beschränktem Sinn ist und eigentlich nichts nützt. Die Interfaces in Java haben eine ganz andere Bedeutung und ich habe den Eindruck, dass in PHP Interfaces einfach nur da sind, um als richtige objektorientiere Programmiersprache angesehen zu sein.
Außerdem ist es mehr oder weniger eine Konvention ein Interface nach der Klasse zu bennen, der sie dienen soll (oder ähnlichen Namen), nur sollte ein kleines „i“ ganz am Anfang sein.
Da ich neben PHP auch Java kenne, kann ich sagen, dass der Sinn von Interfaces in PHP von sehr beschränktem Sinn ist und eigentlich nichts nützt. Die Interfaces in Java haben eine ganz andere Bedeutung und ich habe den Eindruck, dass in PHP Interfaces einfach nur da sind, um als richtige objektorientiere Programmiersprache angesehen zu sein.
Dann…
Ich weiß, ist was älter, aber das Thema wollte ich nur mal erläutern…
…erläutere doch mal. Was unterscheidet die Interfaces in Java und PHP so sehr voneinander und warum „nützen“ sie in PHP fast nichts?
Außerdem ist es mehr oder weniger eine Konvention ein Interface nach der Klasse zu bennen, der sie dienen soll (oder ähnlichen Namen), nur sollte ein kleines „i“ ganz am Anfang sein.
Ein großes „I“, oder? Würde mich wundern, wenn Java einen „Klassen“-Namen, der mit einem Kleinbuchstaben beginnt, durchgehen lässt. Diese „Mehr oder weniger“-Konvention für Interfaces, die im Prinzip eine Klasse symbolisieren, gibt es aber, das stimmt.[1]
Die Frage bei derlei Überlegungen bleibt für mich in erster Linie, ob es sinnvoll ist, ein Interface für eine beliebige Klasse zu erstellen. Dazu muss für mich die Notwendigkeit bestehen, auch außerhalb der Vererbungshierarchie einer unabhängigen Dritt-Klasse die Möglichkeit zu geben, sich per Interface wie ein Objekt dieses Typs verhalten zu können.
Es gibt solche Fälle, in denen Interfaces notwendig sind, weil eine Funktionalität anders nicht zu realisieren ist. Mir fällt allerdings nach wie vor kein einfaches Beispiel dazu ein.
Im Grunde geht es mir darum, herauszustellen, dass Vererbung der „Normalfall“ ist und Interfaces die „Ausnahme“.
1: Auch wenn es kaum anders geht, macht das in meinen Augen die Erklärung allerdings nicht gerade einfacher. (Und ob Typenpräfixe so schön sind, lasse ich mal dahingestellt.) Ich mag die „Interfaces sind Adjektive“-Analogie: