# Klasse 3 soll Klasse 2 UND 1 erben



## Irgendjemand_1 (7. November 2006)

Hallo.
Ich habe das Problem, dass ich nicht weiß, wie ich 2 Klassen an eine andere vererben kann.
Also zB sowas:

```
class eins { /* ... */ }
class zwei { /* ... */ }
class drei extends eins extends zwei { /* Hat alle Eigenschaften/Funktionen beider Klassen */ }
```
Hat jemand eine Ahnung, wie das geht? Auf diese Weise gibt es einen Parse Error (unexpected T_EXTENDS)


----------



## Dennis Wronka (7. November 2006)

Ich hatte das vor Kurzem auch mal probiert, und das scheint so leider nicht zu gehen.


----------



## Headymaster (7. November 2006)

ÄHm ist das nicht Stichwort für Class Nesting?!

Also kann ja auch falsch liegen aber ich würde das ganze über Class NEsting regeln ;-)

MFG Niels


----------



## Irgendjemand_1 (7. November 2006)

Headymaster hat gesagt.:


> ÄHm ist das nicht Stichwort für Class Nesting?!
> 
> Also kann ja auch falsch liegen aber ich würde das ganze über Class NEsting regeln ;-)
> 
> MFG Niels


Jetzt müsste man nur noch Class Nesting können/kennen/usw 
Ein Fall für Google? Oder hast du grad einen konkreten Link dazu?


----------



## Headymaster (7. November 2006)

Also habs auch erst vor ein paar tagen gelernt....ist total einfach ;-)


Schau dir das hier mal an:
http://www.devmag.net/webprog/php_class_nesting.htm

MFG Niels


----------



## Matthias Reitinger (7. November 2006)

Hallo,

die in diesem Artikel vorgestellte Methode ist allerdings nicht recht sinnvoll (vielleicht ist auch nur das Beispiel schlecht gewählt). Warum sollte die Gästebuch-Klasse das Datenbank-Objekt instantiieren bzw. überhaupt das Wissen besitzen, wie dieses instantiiert werden muss? Das kann dieser Klasse eigentlich ziemlich egal sein. Sie muss nur wissen, wie sie mit einem Datenbank-Objekt umgehen kann. Daher fände ich es logischer, das Datenbank-Objekt an anderer Stelle zu erzeugen und dem Gästebuch-Objekt eine Referenz darauf zu übergeben.

Außerdem ist der Begriff „Class Nesting“ irreführend. Damit wird eigentlich eine Klassendefinition innerhalb einer anderen Klassendefinition gemeint. Das ist in PHP aber nicht möglich:

```
class Foo {
	function test() {
		class Bar { # => Fatal error:  Class declarations may not be nested
		}
	}
}
```

Aber um jetzt mal zu einer möglichen Lösung des Problems zu kommen: Mehrfachvererbung wird von PHP nicht unterstützt. Als „Ausweichmöglichkeit“ bleiben dir Interfaces (ab PHP5) oder Komposition statt Vererbung. Wie die Umsetzung jeweils aussehen würde, hängt ganz von deinem Klassenmodell ab.

Grüße,
 Matthias


----------



## Headymaster (7. November 2006)

Also mit dem Beispeil was dort geschildert wird komme ich eigentlich sehr gut zurecht.

So habe ich zum Beispiel eine Art Hauptklasse:

-Mysql
-Mail
-Ftp
-Image
-BBCode
-Pagnitation
-usw.

Und diese Binde ich dann in die Klasse die diese Klassen brauchen ein, zb.:

-GB   ----> Mysql, Mail, BBCode, Pagnitation...

Ich weiß net wie man es sonst machen sollte und so wie ich es jetzt mache ist es sehr übersichtlich und außerdem sehr Ressourcensparend.....meine Scripte laufen immer noch recht schnell....

Aber wenn ich mich irre oder einen fatalen Fehler mache so kläre mich auf denn ich bin auch nur ein Mensch der grade erst anfängt mit Programmieren 

MFG Niels


----------



## Matthias Reitinger (7. November 2006)

Hallo,



Headymaster hat gesagt.:


> Und diese Binde ich dann in die Klasse die diese Klassen brauchen ein, zb.:


Wie sieht denn diese Einbindung aus? Übergibst du da auch für jede verwendete Klasse den Datei- und Klassennamen?

Grüße,
 Matthias


----------



## Marvin Schmidt (8. November 2006)

Hallo,



Headymaster hat gesagt.:


> Also mit dem Beispeil was dort geschildert wird komme ich eigentlich sehr gut zurecht.
> 
> So habe ich zum Beispiel eine Art Hauptklasse:
> 
> ...



Ich hatte heute mit einem ähnlichen Problem zu tun, da ich mehrere Klassen habe, die Datenbankzugriff brauchen, ich aber vermeiden wollte, dass jede Klasse ein eigenes Datenbank-Objekt erzeugt. Dabei habe ich die Begriffe Singleton Pattern (deutsch: Einzelstück Entwurfsmuster) und Registry Pattern kennen gelernt.
Der Zweck von Singleton Pattern ist es sicher zu stellen, dass nur eine Instanz einer Klasse erzeugt wird, welche dann global erreichbar ist. Registry Pattern werden zur Verwaltung von Instanzen eingesetzt.

Ein Anwendungsbeispiel:

```
/* Registry.class.php - verwaltet erzeugte Instanzen */
class Registry {

    private $store = array();
    static private $thisInstance = null;

    private function __construct() {
    }

    // Singleton Pattern stellt sicher, dass nur eine Instanz der Registry-Klasse erzeugt wird
    static public function getInstance() {
        if (self::$thisInstance == null) {
            self::$thisInstance = new Registry();
        }
        return self::$thisInstance;
    }

    public function register($label, $object) {
        if (!isset($this->store[$label])) {
            $this->store[$label] = $object;
        }
    }

    public function unregister($label) {
        if (isset($this->store[$label])) {
            unset($this->store[$label]);
        }
    }

    public function get($label) {
        if (isset($this->store[$label])) {
            return $this->store[$label];
        }
        return false;
    }

    public function has($label) {
        if (isset($this->store[$label])) {
            return true;
        }
        return false;
    }
}


/* index.php: */

// Einbindung der benötigten Dateien
[...]
require_once 'Registry.class.php';

// globale Objekte instanziieren...
$dsn = array(
    'phptype'        => 'mysql',
    'username'        => $dbuser,
    'password'        => $dbpass,
    'hostspec'        => $dbhost,
    'database'        => $dbname
);
$db =& DB::connect($dsn);
if (DB::isError($db)) {
    die($db->getMessage());
}
$db->setFetchMode(DB_FETCHMODE_ASSOC);

$tpl = new Smarty;
$tpl->template_dir = './templates';
$tpl->compile_dir = './templates/tmp';
$tpl->force_compile = true;

// ...und registrieren
$registry = Registry::getInstance();
$registry->register('database', $db);
$registry->register('smarty', $tpl);

$gbook = new Guestbook(...);

[...]


/* Guestbook.class.php: */
class Guestbook {

	var $db;
	var $tpl;

	function Guestbook(...) {
        // Zugriff auf DB- und Template-Objekt über globale Registry-Instanz
		$registry = Registry::getInstance();
        // ggf. mit has-Methode Verfügbarkeit überprüfen
		$this->db  = $registry->get('database');
		$this->tpl = $registry->get('smarty');
	}

        [...]

}
```

Ich hatte noch nicht viel Zeit, damit zu arbeiten, da ich heute das erste Mal damit in Kontakt gekommen bin, aber bisher finde ich diese Handhabung von Objekten, die in mehreren Klassen Verwendung finden, sehr praktisch und übersichtlich.

Gruß
Marvin


----------



## Headymaster (8. November 2006)

Gibt es denn dazu ein Tutorials oder haste nen Link wo du das her hast....weil würde mir das gerne mal genauer ansehn und druchlesen.

Dann noch ne Frage.

Was ist denn das Problem daran, wenn jede Klasse seine eigene Mysql Instanz hat?

MFG Niels


----------



## Matthias Reitinger (8. November 2006)

Hallo,

@Marvin Schmidt: sehr schön, so stell ich mir das vor 



Headymaster hat gesagt.:


> Gibt es denn dazu ein Tutorials oder haste nen Link wo du das her hast....weil würde mir das gerne mal genauer ansehn und druchlesen.


Wenn du dich mit Design Patterns allgemein beschäftigen willst, würde ich dir Head First Design Patterns wärmstens empfehlen. Die Beispiele darin sind zwar in Java implementiert, aber das sehr gut vermittelte Wissen kann meist problemlos in andere Sprachen übertragen werden. Bei PHP-spezifischer Literatur kann dir Marvin sicher mehr sagen.



> Was ist denn das Problem daran, wenn jede Klasse seine eigene Mysql Instanz hat?


Es ist umständlich, weil man für jedes Objekt eine eigene MySQL-Instanz erzeugen muss. Dabei wiederholt sich gezwungenermaßen ein Codeblock immer wieder quer über die Klassendeklarationen hinweg. Will man an der Instantiierung der MySQL-Klasse projektglobal eine Kleinigkeit ändern, so muss man das für jede Klasse extra tun. Das wirkt sich natürlich negativ auf die Wartbarkeit aus.

Dann wäre es zu Analysezwecken vielleicht auch mal ganz interessant, sämtliche SQL-Abfragen eines Seitenaufrufs mitzuloggen und eine entsprechende Statistik am Seitenende auszugeben. Mit einer Singleton-Instanz der MySQL-Klasse kein größeres Problem – hat man aber viele Instanzen über die Applikation hinweg verteilt, muss man hierzu erst mal feststellen, welche Objekte gerade im Speicher liegen, welche davon eine eigene MySQL-Instanz besitzen und schlussendlich von diesen die Informationen abfragen und sinnvoll zusammenführen. Wenn man Pech hat, sind aber zum Zeitpunkt der Auswertung einige MySQL-Instanzen schon wieder verschwunden und man verliert damit Informationen.

Das allein wären jetzt schon zwei Gründe, die massiv gegen deine Art des Vorgehens sprechen. Natürlich funktioniert deine Lösung auch, das stelle ich nicht in Frage. Aber man verschenkt damit eben ein großes Maß an Flexibilität.

Grüße,
 Matthias


----------



## Headymaster (8. November 2006)

Ah ok danke  du hast mich überzeugt....naja dann werde ich wohl nochmal bissle was umschreiben müssen.....zum Glück war ich mit dem CMS noch net so weit....und zum Glück ist OOP ja so wunderbar schön leicht veränderbar 

Also danke sag ich denn ma 

MFG Niels


----------



## Headymaster (8. November 2006)

So also habe mal bissle was schon geändert und in Singelton geschrieben.

Nun stellt sich mir ein Prob in den Weg.

Und zwar bekomme ich bei meiner counter.php-Klasse folgenden Fehler:

Fatal error: Call to a member function

Das heißt ja eigentlich, dass versucht wurde auf eine Funktion der Klasse zuzugreifen, dies aber nicht geht, da die Klasse nicht aufgerufen wurde.

So aber das Kann nicht sein.

In meiner index.php habe ich z.b. nach diese Reihenfolge:

```
<?php
require_once("module/registry.php");
include("module/mysql.php");
include("module/parser.php");
include("module/counter.php");

// ......

#########################################################
############# Klassenobjecte laden ######################
#########################################################
$Connection = new mysql($dbhost, $dbuser, $dbpw, $dbname);
$tpl = new tpl();
#########################################################
############# Objecte instanzieren ######################
#########################################################
$registry = Registry::getInstance();
$registry->register('mysql', $Connection);
$registry->register('showtpl', $tpl);

//.....
// So und hier die gesamte counter.php-Klasse

   class counter extends mysql
   {
        var $ip;
		var $zeiger;
		var $datum;
		var $db;
		
		   //##################################################################################//
		   // 1. Initalisiert Instanzen                                                        //
		   // 2. Klassen einbinden                                                             //
		   //##################################################################################//
		   function init_classes()
		   {
		   $registry = Registry::getInstance();
		   $this->db = $registry->get('mysql');
		   }
		   
		   //##################################################################################//
		   // 1. Initaliesert benötigte Infos                                                  //
		   //##################################################################################//
		   function init()
		   {
		       $this->ip = $_SERVER['REMOTE_ADDR'];
			   $this->datum = date("d.m.Y");
		   }
		   //##################################################################################//
		   // 1. Überprüft, ob IP schon in entsprechender Datenbank besteht(an jetzigem Datum) //
		   //     -> Wenn JA, zeiger=1                                                         //
		   //     -> Wenn NEIN, zeiger =0                                                      //
		   //##################################################################################//
		   function ip_pruefen($table)
		   {
			   $ipselect = $this->db->query("SELECT * FROM `$table` WHERE ip='".$this->ip."' AND date='".$this->datum."'");
			   $iprows = mysql_num_rows($ipselect);
			      if($iprows > 0)
				  {
				     $this->zeiger = TRUE;
				  }
				  if($iprows = 0)
				  {
				     $this->zeiger = FALSE;
				  }
			  return $this->zeiger;
		   }
		   
		   //###########################################################################//
		   // 1. Schreibt eine Ip in eine Datenbank $table                              //
		   //###########################################################################//
		   function ip_schreiben($table)
		   {
			   $ip_write_task = $this->db->query("INSERT INTO $table (ip, date) VALUES('".$this->ip."', '".$this->datum."')");
			   if (!$ip_write_task)
			     {
				 return FALSE;
				 }
				 else
				 {
				 return TRUE;
				 }
		   }
   }
?>
```


Mache ich mit der Reihenfolge vielleicht was falsch?

Würde mich sehr doll über Hilfe freuen 

MFG Niels


----------



## Matthias Reitinger (8. November 2006)

Hallo,

in welcher Zeile wird der Fehler gemeldet? Verwendest du PHP4 oder PHP5? Wozu das extends mysql?

Grüße,
 Matthias


----------



## Headymaster (8. November 2006)

Ah siehste das habe ich vergessen beim Umbauen wegzumachen 

Ändert aber nichts am Fehler :

Fatal error: Call to a member function query() on a non-object in K:\Webserver\xampp\htdocs\cmsoop\module\counter.php on line 34

Also der Fehler wird halt dadurch verursacht, dass ich nun in der Klasse Counter auf die Instanz mysql mit $this->db->query zugreife...

MFG Niels


----------



## Matthias Reitinger (8. November 2006)

Hallo,

wird counter::init_classes irgendwo aufgerufen? Das würde sich eigentlich im Konstruktor ganz gut machen. Hast du die Registry-Klasse aus diesem Thema 1:1 übernommen? Dann wäre es eigentlich nicht verkehrt, wenn du auch mit den OOP-Konstrukten von PHP5 arbeiten würdest. 

Grüße,
 Matthias


----------



## Headymaster (8. November 2006)

OOP Konstrukten von PHP5?! Also ich lerne jez seit 2 wochen so nach der schule ein bissle OOP deswegen bin ich da noch net so wirklicj drinne.....ich möchte na klaro alles auf php5 Basis machen 

Und Nein das wird nirgends aufgerufen 

MFG Niels

EDIT: Counter Init Classes wird jez aufgerufen^^.....sonst kann es ja gar net gehen 

Aber bekomme immer noch den Fehler.......

Fatal error: Call to a member function init_classes

MFG Niels


----------



## Marvin Schmidt (9. November 2006)

Headymaster hat gesagt.:


> Fatal error: Call to a member function query() on a non-object in K:\Webserver\xampp\htdocs\cmsoop\module\counter.php on line 34
> 
> Also der Fehler wird halt dadurch verursacht, dass ich nun in der Klasse Counter auf die Instanz mysql mit $this->db->query zugreife...



Hi,
wenn du das tust, muss gewährleistet sein, dass die Klassenvariable _db_ auch auf die Instanz der MySQL-Klasse zeigt. Deswegen solltest du das - wie schon von Matthias gesagt - im Konstruktor erledigen, da die Objekte ja auf jeden Fall benötigt werden.


```
class Counter {
    private $ip_table;
    private $data_table;
    private $db;
        
    function __construct($data_table, $ip_table) {
        $this->data_table = $data_table;
        $this->ip_table = $ip_table;

        $registry = Registry::getInstance();
        $this->db = $registry->get('mysql');
    }
    
    [...]
}
```

Die _init_-Methode könntest du dir einsparen, indem du für zeit- und datumsspezifische Spalten die dafür vorgesehenen _Typen DATE, DATETIME und TIMESTAMP_ verwendest und dann mit den MySQL-eigenen _Datums- und Zeitfunktionen_ arbeitest.

Beispiel (IP-Sperre für die Zählung von Besuchern):

```
function updateIPTable() {
    $sql = "DELETE FROM " .
                $this->ip_table . "
            WHERE
                UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(count_time) > 300";

    $res =& $this->db->query($sql);
    if (PEAR::isError($res)) {
        die($res->getMessage());
    }

    return $this->db->affectedRows($res);
}
```

Wie man sieht, kann man so alle IP-Einträge, die älter als 5 Minuten sind, ganz einfach aus der Tabelle löschen und muss nicht mit PHP das Datum zurückrechnen.
Außerdem erleichert es einem die Arbeit sehr, wenn es darum geht Besucherstatistiken eines bestimmten Zeitraumes zu ermitteln.

```
/* aktueller Tag */
... WHERE count_date = CURDATE()
/* voheriger Tag */
... WHERE count_date = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
/* gesamte Woche */
... WHERE WEEK(count_date) = WEEK(CURDATE())
```

Bezüglich PHP Patterns kann ich www.patternsforphp.com empfehlen.

Gruß
Marvin


----------



## Headymaster (9. November 2006)

Also fehler sind nun alle beseitigt^^

Aber trotzdem danke für diesen Thread und die Hilfe 

MFG Niels


----------



## Headymaster (9. November 2006)

So nochma ne Frage zum Thema OOP solange es den Thread noch gibt^^

Also ich habe jetzt die Abfrage um die Kategorien einer Gallerie auszulesen gekürzt, und zwar in der Klasse der gallerie lese ich die Kategorien so aus:


```
<?php
//#################################################################//
		 // 1. Liest die Kategorien aus und gibt diese zurück               //
		 //#################################################################//
		 function gal_kats()
		 {
		   $kats = $this->db->query("SELECT * FROM `gal_kats` ORDER BY `erstellt` DESC");
		   return $this->db->fetch_array($kats);
		 }
?>
```

So und in der Gallery.php verarbeite ich diese dann so:

```
<?php
// Galleriekategorien auslesen
while ($kat_data = $gallery->gal_kats())
{
$gal_content .= $index->showtpl("gallery/gal_kats", array("gal_name" => $kat_data['name'],
                                                          "gal_fotos" => $kat_data['anzahl'],
														  "gal_datum" => $kat_data['erstellt'],
														  "gal_link" => "?gallery.php&id=".$kat_data['cid']));
}
?>
```

So das Prob ist als ich noch alles umständlich ohne gallery-klasse ausgelsen habe,
da ging es aber nun bekomme ich eine Art Dauerschleife, denn mein Browser läd und läd und läd aber nix passiert... 

Habt ihr ne Idee....wird vielleicht bei der Funktion zum Auslesen mit return nicht alles zurückgegeben?

MFG Niels


----------



## Marvin Schmidt (9. November 2006)

Um genau zu sein, ist es eine Dauerschleife, da die Bedindung deiner while-Schleife mit _$kat_data = $gallery->gal_kats()_ immer erfüllt wird, da die gal_kats-Methode die Kategorien ermittelt und sie zurückgibt.

```
$categories = $gallery->gal_kats();
foreach ($categories as $categorie) {
    [...]
}
```
Das wäre eine Lösung des Problems, aber ich denke von der logischen Struktur her, wäre es wohl besser, wenn die Gallerie-Klasse eine Methode zum Anzeigen der Kategorien und Fotos bieten würde.

Gruß
Marvin


----------



## Headymaster (9. November 2006)

Hmm also ich hatte mir gedacht, dass ich daruch halt die Kategorien ausgeben lasse und dann noch ne Methode die halt alle Fotos anzeigt, die die cid=$_GET['cid'] haben.....

Verstehste was ich meine?!

Wie würdest du es denn machen?!

EDIT: Die foreach methode klappt auch net so.....

```
<?php
$kat_datas = $gallery->gal_kats();
foreach ($kat_datas as $kat_data)
{
$gal_content .= $index->showtpl("gallery/gal_kats", array("gal_name" => $kat_data['name'],
                                                          "gal_fotos" => $kat_data['anzahl'],
														  "gal_datum" => $kat_data['erstellt'],
														  "gal_link" => "?gallery.php&id=".$kat_data['cid']));
}
?>
```
Da kommt nur Zahlen- und Buchstabensalat raus...

MFG Niels


----------



## Headymaster (9. November 2006)

So das Problem hat sich nun erledigt 

Habe in der gallerie-klasse einfach die Template-Instanz eingebunden und gebe dann das fertig ausgefüllte Template aus der methode zurück 

Danke für die Hilfe trotzdem...^^

MFG Niels


----------

