PHP Klassen übergreifend in Funktionen (mysqli)

selberbauer

Captain
Registriert
Juni 2009
Beiträge
3.604
Hallo,
ich übe gerade an einem Anmelde Script.
Ich wollte nun etwas Code auslaggern in Funktionen, diese greifen allerdings auf die mysqli Klasse zu, weswegen es eingie Probleme gibt.
Weiß gar nicht wie ich das lösen kann.

index.php
PHP:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

$db = @new mysqli('localhost', 'test_user', 'wambo', 'test');
if(mysqli_connect_errno()) {
	die('Konnte keine Verbindung zur Datenbank aufbauen: ' . mysqli_connect_error());
}

include('lib.php');

create_table();
create_user('user1', 'bingo', 'email@domain.com');


$db->close();


?>

lib.php
PHP:
<?php
function create_table() {
	$create_table = "CREATE TABLE IF NOT EXISTS 'user' (
		'ID' INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
		'username' VARCHAR(30) NOT NULL,
		'password' CHAR(32) NOT NULL,
		'mail' VARCHAR(100) NOT NULL
		)";
	if($db->query($create_table)) {
		die('Es liegt ein Fehler mit der Tabelle vor!');
	}
}

function create_user($user, $password, $mail) {
	$password_hash = md5($password);
	$sql = "INSERT IF NOT EXISTS INTO 'user' ('username', 'password', 'mail') VALUES (?, ?, ?)";
	$entry = $db->prepare($sql);
	$entry->bind_param('sss', $user, $password_hash, $mail);
	$entry->execute();
	if(!($entry->affected_rows)) {
		die('Fehler beim Schreiben des Datensatzes liegt vor!!!');
	}
}
?>
 
noch besser wäre es, wenn du den beiden funktionen einfach den datenbankstream ($sql) als variable übergibst.
 
konkret müsstest du wie mein Vorredner schon gesagt hat einfach deine funktionen um den parameter $db erweitern.

Sprich

Code:
<?php
function create_table($db) {
	$create_table = "CREATE TABLE IF NOT EXISTS 'user' (
		'ID' INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
		'username' VARCHAR(30) NOT NULL,
		'password' CHAR(32) NOT NULL,
		'mail' VARCHAR(100) NOT NULL
		)";
	if($db->query($create_table)) {
		die('Es liegt ein Fehler mit der Tabelle vor!');
	}
}
 
function create_user($user, $password, $mail, $db) {
	$password_hash = md5($password);
	$sql = "INSERT IF NOT EXISTS INTO 'user' ('username', 'password', 'mail') VALUES (?, ?, ?)";
	$entry = $db->prepare($sql);
	$entry->bind_param('sss', $user, $password_hash, $mail);
	$entry->execute();
	if(!($entry->affected_rows)) {
		die('Fehler beim Schreiben des Datensatzes liegt vor!!!');
	}
}
?>

und aufrufen tust das dann mit
Code:
create_table($db);

evtl. macht es hhier sogar sinn nur eine Referezn zu übergeben.

Am besten wäre es natürlich eine MySQL Klasse zu schreiben die das Connecten übernimmt und die methoden bereit stellt.

Global sollte man tunlichst vermeiden denn damit können funktionen direkt den Inhalt von Variablen ändern was schnell zu ungewolltem Verhalten führen kann.

Zusätzlich wäre es besser wenn bei einem DB error eine exception geworfen wird oder aber false als return wert zurückkommt statt das Script abbrupt zu beenden.
Kannst du besser auf den Fehler reagieren und vorallem die DB verbdinung schließen.
 
Danke für die Antworten.

Momentan gibt es nur noch ein problem mit bind_param().
Fatal error: Call to a member function bind_param() on a non-object in lang/PHP/MySQL/lib.php on line 18

Zusätzlich wäre es besser wenn bei einem DB error eine exception geworfen wird oder aber false als return wert zurückkommt statt das Script abbrupt zu beenden.
Kannst du besser auf den Fehler reagieren und vorallem die DB verbdinung schließen.

Dann müsste ich allerdings jedes mal den return wert prüfen und das Programm funktioniert trotzdem nicht oder nicht?


Am besten wäre es natürlich eine MySQL Klasse zu schreiben die das Connecten übernimmt und die methoden bereit stellt.
Das war meine erste Idee, eine Klasse die alles übernimmt. Allerdings ist MySQLi bereits als Klasse aufgebaut und Klasse in der Klasse?
 
Stichwort ist vererbung.

PHP:
class MySqlClass extends mysqli {

public function connec().......

}

Dann hat MySqlClass alle eigenschaften von mysqli + alle die du noch dazu definierst. Und eine Klasse in einer Klasse ist auch nicht so abwegig. Dann könnte die Klasse z.b. versuchen Fehler selbstädig zu beheben und du müsstest im Script fast nichts mehr machen, halt je nach anforderung.

deine Fehlermeldung bei bind_param sagt in dem fall das $entry kein Objekt einer Klasse ist.Die Funktion bind_param also nicht gefunden wird.

In der API steht das bei einem Fehler von mysqli::prepare false zurück gegeben wird,
also vor dem weiter arbeiten mit $entry folgendes machen

PHP:
if(!$entry) {
    echo 'Hier ist was schief gelaufen';
} else {
 // Normal weiter arbeiten
}

Und dann müsste man halt rausfinden warumprepare fehlschlägt.

Vlt. vor dem nutzen von db eine abfrage machen wie

if($db instanceof mysqli)

dann hast die sicherheit das auch wirklich ein mysqli objekt hast.

P.S.

soetwas wie "insert into if not eists" ist mir unbekannt, nimm mal den if teil raus.
 
Stichwort ist vererbung.
Wie funktioniert das dann syntaktisch?
$db = @new mysqli(.... kann ich das so einfach in die Klasse reinschreiben?

PHP:
<?php

class mysqlu extends mysqli {

	var $db = @new mysqli('localhost', 'test_user', 'wambo', 'test');
		if(mysqli_connect_errno()) {
			die('Konnte keine Verbindung zur Datenbank aufbauen: ' . mysqli_connect_error());
		}


	var $table          = 'table_name';
	var $id             = "'id' TINYINT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY"
	var $column         = array();
	var $column[1]      = 'first_column_name';
	var $column[2]      = 'second_column_name';
	var $column[3]      = 'third_column_name';
	var $column_type    = array();
	var $column_type[1] = 'VARCHAR(100) NOT NULL';
	
	function create_table($this->$db) {
		$create_table = "CREATE TABLE IF NOT EXISTS $table (
			$id,
			$column[1] $column_type[1],
			$column[2] $column_type[1],
			$column[3] $column_type[1])";
		if($this->$db->query($create_table)) {
			die('Es liegt ein Fehler mit der Tabelle vor!');
		}

}

?>

Und dann müsste man halt rausfinden warumprepare fehlschlägt.
Vlt. vor dem nutzen von db eine abfrage machen wie
if($db instanceof mysqli)
dann hast die sicherheit das auch wirklich ein mysqli objekt hast.

Ok, dass mache ich dann nachdem die Klasse am Laufen ist
 
in der klasse mysqlu stehen jetzt über den bezeichner $this alle methoden von mysqli zur verfügung.

Also alles was du vorher mit $db-> gemacht hast, kannst du nun mit $this-> machen.$this ist quasi eine referenz auf sich selber.

d.h. im erstenfall schreibst du einfach

PHP:
parent::__construct('localhost', 'test_user', 'wambo', 'test');

damit greifst du direkt auf den Konstruktor von mysqli zu, was einem new mysqli entspricht.

allerdings ist das vollkommen unnötog denn deine Klasse hat genau diesen Konstruktor schon geerbt!

also sind folgende aufrufe fast aquivalent:

PHP:
$db = new mysqli('local....);
$db2 = new mysqlu('local....);

dumusst dir vor augenführen das mysqlu jetzt eine Klasse vom Typ mysqli ist

also schreibst du einen konstructor wie diesen

PHP:
public function __construct($host, $user, $pw, $db) {
   parent::__construct($host, $user, $pw, $db);
   if(mysqli_connect_errno()) {
			die('Konnte keine Verbindung zur Datenbank aufbauen: ' . mysqli_connect_error());
		}
}

deine variante macht das erben unnötig da ein neue mysqli objekt genutzt wird.
 
Zuletzt bearbeitet:
Danke für die schnelle Antwort :)
Habe mich nochmal an den Aufbau rangesetzt:

PHP:
<?php

class mysqlu extends mysqli {

	// initiating connection
	var $host     = 'localhost';
	var $user     = 'test_user';
	var $password = 'wambo';
	var $database = 'test';
	public function __construct($host, $user, $password, $database) {
		parent::__construct($host, $user, $password, $database);
		if(mysqli_connect_errno()) {
			die('Could not connect to local database'.mysqli_connect_error());
		}

	// creating table
	var $table  = 'table';
	var $id     = "'id' TINYINT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY";
	var $column = array(); // should save column name, type other options (SQL)
	function create_table($this) {
		$sql = "CREATE TABLE $table (";
		for($i = 0; isset($column[$i]); $i++) {
			$sql_add = ','.$column[$i];
			$sql     = $sql.$sql_add;
		}
		$sql = $sql.');';
		if($this->$this->query($sql)) {
			die('Es liegt ein Fehler mit der Tabelle vor!');
		}
	}
}

?>

Mit der funktion create_table dachte ich mir einen Weg, wie ich "besser" lesbare SQL Funktionen mit der Klasse nutzen kann.
Die Anwendung sollte ungefähr so aussehen:

PHP:
$db = @new mysqlu('localhost', 'test_user', 'wambo', 'test'); // muss ich um die Variablen so zu übertragen vielleicht in der Klassen definition "class = mysqlu ($host, $user, $password, $database) extends mysqli"?

// jetzt der create_table aufruf
$db->table = 'Name_der_Tabelle';
$db->column[] = "'name' VARCHAR(100) NOT NULL";
$db->column[] = "'mail' VARCHAR(100) NOT NULL";
create_table();

Dir werden bestimmt paar Fehler aufgefallen sein, leider bin ich - wie man sieht - noch ziemlich am Anfang mit OOP und PHP.
 
Zuletzt bearbeitet:
ja ich sehe in der tat ein paar fehler, aber jeder hat mal klein angefangen, auch wenn du vlt. mal ein wenig grundlagen OOP lernen solltest, das ist eigentlich sprachen übergreifend gleich. Wichtog zu wissen wären:
- Was ist ein Constructor
(- evtl destructor)
- Polymorphie
- Verwendung von this / $this (je nach sprache, manchesmal sogar self....)
- super / eltern klasse
- Sichtbarkeit von Attributen / Methoden (public, protected, private)
( - dazu passend: getter / setter methoden)
- Klassenvariablen

passend dazu:

-Scope (Sicht/Gültigkeitsbereich von variablen)

da lernst auch gleich etwas über den Hintergrund und der Kapselung der OOP, die bei dir noch gänzlich ignoriert wird (ist alles net böse gemeint)


aber nun zu deinem Programm.

als erstes würde ich das @ vor dem new wegmachen.
Es bringt bei dir so oder so nichts, da das script mit einem die() endet wenn ein fehler auftritt.

lass bei eiem Fehler lieber eine Exception werfen und fange diese ggf. ab.

oder mit einem IF ob die erstelung erfolgreich war.

PHP:
<?php
 
class mysqlu extends mysqli {
 
	// initiating connection
	var $host     = 'localhost';   // kann weg, denn die werden nie benutzt
	var $user     = 'test_user'; // das auch
	var $password = 'wambo'; // ebenso
	var $database = 'test'; // auch
	public function __construct($host, $user, $password, $database) {
		parent::__construct($host, $user, $password, $database);
		if(mysqli_connect_errno()) {
			die('Could not connect to local database'.mysqli_connect_error());
		}
 
	// creating table
	var $table // diese 3 variablen sind auch unnötog, da sie nicht genutzt werden
        // diese lieber als argument an die methode übergeben
	var $id     = "'id' TINYINT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY";
	var $column   = array(); // should save column name, type other options (SQL)
	function create_table($this) {
		for($i = 0; isset($column[$i]); $i++) { // geht besser, entweder foreach oder über die elemente
			$sql_add = ','.$column[$i];
			$sql     = $sql.$sql_add; // lässt sich beides in einem Befehl machen
		}
	}
}
 
?>

so kommen wir zu folgendem code

PHP:
<?php
 
class mysqlu extends mysqli {
 
	// initiating connection
	public function __construct($host, $user, $password, $database) {
		parent::__construct($host, $user, $password, $database);
		if(mysqli_connect_errno()) {
			die('Could not connect to local database'.mysqli_connect_error());
		}
 
	function create_table($able, $id, $column) {
                $sql = "";
                 $anzahlColumn = count($column);
		 for($i = 0; $i < $anzahlColumn; $i++); 
			$sql  .=  ',' . $column[$i];
		}
                // $sql ausführen o.ä.
	}
}
 
?>

aufrufen wie folget:

PHP:
$db = new mysqlu('localhost'.....);
$db->createTable('Name_der_Tabelle', 10, array("'name' VARCHAR(100) NOT NULL", "'mail' VARCHAR(100) NOT NULL");

allerdings hast du hier auch noch das ein oder andere problem, in $sql wird nämlich immer etwas in dem muster stehen

Code:
,A,B,C
// oder
,test, test 2,test3

jedenfalls hast immer ein komma vorne weg, aber ich will nicht zu viel verraten ;)

an dieser stelle sei dir die foreach schleife ans herz gelegt die defakto immer schneller iteriert und zum einsatz kommt wenn kein index benötigt wird.
 
Zuletzt bearbeitet:
- Was ist ein Constructor
(- evtl destructor)
- Polymorphie
- Verwendung von this / $this (je nach sprache, manchesmal sogar self....)
- super / eltern klasse
- Sichtbarkeit von Attributen / Methoden (public, protected, private)
( - dazu passend: getter / setter methoden)
- Klassenvariablen
passend dazu:
-Scope (Sicht/Gültigkeitsbereich von variablen)
Werde ich machen, gibt es einen URL-Tipp (mit Beispielen) oder sollte ich einfach mit google die Punkte abarbeiten?
da lernst auch gleich etwas über den Hintergrund und der Kapselung der OOP, die bei dir noch gänzlich ignoriert wird (ist alles net böse gemeint)
Kein Problem, stimmt ja ;)
lass bei einem Fehler lieber eine Exception werfen und fange diese ggf. ab
Reicht dazu ein einfaches return in der Funktion am Ende und beim Funktionsaufruf ein if(!function()) { echo 'Fehler unterlaufen' }


Kaum zu glauben wieviel wegfällt :D
Ich bekomme zwar jetzt keine Fehler, allerdings legt er keine Daten an.
Das Problem ist, dass das $column[]-array nicht zur Funktion übertragen wird.

index.php
PHP:
<?php

error_reporting(E_ALL);
ini_set('display_errors', 1);


include('lib.inc.php');

$db = new mysqlu('localhost', 'user', 'wambo', 'test');

$column   = array();
$column[] = "'name' VARCHAR(100) NOT NULL";
$column[] = "'mail' VARCHAR(100) NOT NULL";
 
$db->create_table('test_table', $column[0], $column[1]);

echo 'Alle Aktionen abgeschlossen';

$db->close(); // ??? Würde sich das nicht auch am Ende der Klasse anbieten?

?>

lib.inc.php
PHP:
<?php

class mysqlu extends mysqli {

	public function __construct($host, $user, $password, $database) {
		parent::__construct($host, $user, $password, $database);
		if(mysqli_connect_errno()) {
			die('Could not connect to local database'.mysqli_connect_error());
		}
	}

	function create_table($table, $column) {
		$sql = "CREATE TABLE IF NOT EXISTS $table ('id' INT(4) NOT NULL AUTO_INCREMENT PRIMARY KEY,";
		$count_column = count($column);
		for($i = 0; $i < $count_column; $i++) {
			$sql .= ',' . $column[$i];
		}
		$sql .= ');';
		echo $sql;
		if($this->query($sql)) {
			die('Es liegt ein Fehler mit der Tabelle vor!');
		}
	}
}

?>

jedenfalls hast immer ein komma vorne weg, aber ich will nicht zu viel verraten
Dachte, ich hätte das schon gelöst :D
an dieser stelle sei dir die foreach schleife ans herz gelegt die defakto immer schneller iteriert und zum einsatz kommt wenn kein index benötigt wird.
Kucke ich mir auch nochmal an.

Gruß
 
selberbauer schrieb:
Das Problem ist, dass das $column[]-array nicht zur Funktion übertragen wird.

Dann übergib der Funktion doch einfach das Array anstatt der einzelnen Werte. ;)
 
Da hat darlis recht du musst das so aufrufe:

PHP:
$db->create_table('test_table', $column);

und es gibt noch ein problem, und zwar hast du in der zeile

Code:
		$sql = "CREATE TABLE IF NOT EXISTS $table ('id' INT(4) NOT NULL AUTO_INCREMENT PRIMARY KEY,";

am ende ein komma zu viel, zusammen mit dem restlichen sql string den du dort annüpfst bekommst du etwas wie

Code:
		$sql = "CREATE TABLE IF NOT EXISTS $table ('id' INT(4) NOT NULL AUTO_INCREMENT PRIMARY KEY, ,column1, colum2";

mach das komma bei der ID bestimmung raus und alles sollte funktionieren.
 
Danke, jetzt stimmt die Variablen Verteilung!

Habe nur noch ein Problem mit folgendem:
PHP:
if($this->query($sql)) {
			die('Es liegt ein Fehler mit der Tabelle vor!');
		}
Muss ich das auch mit parent machen?
 
Ja, das wars :D
Allerdings kriege ich jetzt mein die zurück:
Es liegt ein Fehler in der Tabelle vor.
Gibt es in php einen Modus, wo alle ausgaben angezeigt werden?

Gruß
Ergänzung ()

Der Fehler war in den " ' " an den Spaltennamen im SQL-Syntax...
 
Zurück
Oben