Dynamischer Content mit "frühem Include" auf der Seite

Klein0r

Erfahrenes Mitglied
Hallo zusammen!

Ich habe ein Problem für das mir einfach keine schönere Lösung einfällt:
Ich binde auf meiner Homepage den Inhalt dynamisch über index.php?load=dateiname ein. Dabei nutze ich folgendes Script:

PHP:
<?php
 
$page = $_GET['load'];
 
if (empty($page))
{
   include './verz/news.php';
}
else
{
   if (file_exists('./verz/' . $page . '.php'))
   {
	  include './verz/' . $page . '.php';
   }
   else
   {
	  include './verz/fehlerseite.html';
   }
}
 
?>

Der Nachteil an dem Script ist allerdings, dass ich den Quellcode genau da hinschreiben muss wo ich auch den Inhalt einbinden will. Da ich aber z.B. Titelzeilenergänzungen einbinden möchte (ohne eine weitere Datei anzulegen) - Include ich die Datei schon über dem <html>-Tag. Damit mir der Inhalt der Seite nicht dort schon angezeigt wird habe ich das Ausgeben des Inhalts in eine Funktion ausgelagert (function show_content). Diese Funktion muss logischerweise in jeder Datei vorhanden sein.

Wenn ich nun an der Stelle bin wo ich den Inhalt anzeigen möchte, prüft ich mit function_exists ob die funktion definiert wurde und rufe sie auf wenn das der Fall ist.

Gibt es dafür nicht eine einfachere Lösung die ein wenig schöner ist? Nachteil ist zudem auchnoch, dass ich nicht an die Variablen komme die außerhalb der Funktion deklariert sind ohne global zu nutzen - das ist natürlich sehr sehr unschön!

Wer weiß eine bessere Lösung?

lg
 
Hi, nicht unbedingt eleganter, aber auch möglich, wäre folgendes:
PHP:
$page = $_GET['load'];

ob_start();

if (empty($page))
{
   include './verz/news.php';
}
else
{
   if (file_exists('./verz/' . $page . '.php'))
   {
      include './verz/' . $page . '.php';
   }
   else
   {
      include './verz/fehlerseite.html';
   }
}

$content = ob_get_contents();
ob_end_clean();
Das sorgt dafür, das alle Ausgaben per echo usw. zwischengespeichert werden, und danach in der Variablen $content als Text zur Verfügung stehen. Das ist wahrscheinlich die bessere Lösung, erst recht, wenn du mal mehr als eine Seite dynamisch einbinden willst, da du dann nicht noch weitere Funktionen definieren musst.

Ausserdem tritt bei dieser Lösung natürlich kein "global-Problem" bei Variablen auf. ;)
 
Zuletzt bearbeitet:
So ich habe mir folgende objektorientierte Lösung überlegt:

Als erstes lege ich ein Interface an:
PHP:
interface iIncludeFunctions {
	public function save_values($filename, $file_parameters, $sqlconn, $user, $bbcode);
	public function get_needed_parameters();
	public function get_title_extention();
	public function show_content_of_file();
}

Dieses Interface müssen später alle Klassen implementieren die als "Contentausgabe" arbeiten wollen. Was ich mir bei den einzelnen Funktionen gedacht habe:

save_values
speichert alle Werte in dem einzelnen Objekt welche ich für die weitere Verarbeitung brauche - Also mein Objekt für die SQL-Verbindung, BB-Code-Parser-Objekt, den Dateinamen für neue Links usw.

get_needed_parameters
Ist eine Funktion für meine Interne Parameterübergabe = uninteressant

get_title_extention
Da der Include der einzelnen Dateien noch vor dem ersten HTML-Tag geschieht, nutze ich die "Chance" um meine Titelzeile zu optimieren - also in abhängigkeit der angezeigten Seite wird dabei angegeben in welchem Bereich man sich befindet z.B.

show_content_of_file
Zeigt den Inhalt in Abhängigkeit der in der 1. Funktion übergebenen $file_parameters (array).

Nun meine index.php (Auszug):
PHP:
$include_error = false;
$page = $_GET['load'];
if (empty($page))
{
	include './links/news.php';
	$titleext = 'News';
}
else
{
	if (file_exists('./links/' . $page . '.php'))
	{
		include './links/' . $page . '.php';
		$titleext = ucfirst($page);
	}
	else
	{
		$include_error = true;
		$titleext = 'Fehler';
	}
}

Davon habe ich ja noch nichts da ich kein Objekt kenne. Deswegen muss ich erstmal prüfen ob eine Funktion existiert die mir ein objekt geben kann. Diese muss ich leider noch in jeder Datei definieren - aber das ist ja jeweils nur ein Dreizeiler:

Beispieldatei Downloads:
PHP:
function get_inc_object() {
	return new inc_downloads();
}

class inc_downloads implements iIncludeFunctions {
	
	var $filename;
	var $parameters;
	var $sqlconn;
	var $user;
	var $bbcode;
	
	public function save_values($filename, $file_parameters, $sqlconn, $user, $bbcode) {
		$this->filename = $filename;
		$this->file_parameters = $file_parameters;
		$this->sqlconn = $sqlconn;
		$this->user = $user;
		$this->bbcode = $bbcode;
	}
	
	public function get_needed_parameters() {
		return 'showkat|file';
	}
	
	public function get_title_extention() {
		$showkat = $this->file_parameters['showkat'];
		$file = $this->file_parameters['file'];
		
		if (!empty($showkat)) {
			$abf = $this->sqlconn->queryResult("SELECT name FROM page_downloads_kateg WHERE id = $showkat LIMIT 1;");
			return 'Kategorie '.$abf->getObjOfPosition(0)->name;
		}
		else if (!empty($file)) {
			// Dateidownload wird vorbereitet
			return 'Datei '.$file;
		}
		else {
			// Kategorieliste laden
			return 'Kategorieübersicht';
		}
	}
	
	public function show_content_of_file() {
		$showkat = $this->file_parameters['showkat'];
		$file = $this->file_parameters['file'];
		
		if (!empty($showkat)) {
			// Files der einzelnen Kategorie werden angezeigt
			$this->show_kategorie_details($showkat);
		}
		else if (!empty($file)) {
			// Dateidownload wird vorbereitet
			$this->show_download($file);
		}
		else {
			// Kategorieliste laden
			$this->show_overview();
		}
	}

............

}

Hierbei sind die 3 Funktionen show_kategorie_details($showkat), show_download($file) und show_overview() private Funktionen der Klasse. Die anderen Funktionen müssen ja implementiert werden da das Interface implementiert wurde.

Nun weiter in der index.php:
PHP:
$file_parameters = array();
$include_page = null;
if (function_exists("get_inc_object")) {
	$include_page = get_inc_object();
	
	// Prüft ob das Objekt auch das Interface implementiert und ein gültiges Objekt ist
	if (is_object($include_page) && $include_page instanceof iIncludeFunctions) {
		
		$parameterstring = $include_page->get_needed_parameters();
		$exploded_names = explode("|", $parameterstring);
		
		for ($paramcounter=0;$paramcounter<count($exploded_names);$paramcounter++) {
			$file_parameters[$exploded_names[$paramcounter]] = $parameters[$paramcounter];
		}
		
		$include_page->save_values($page, $file_parameters, $sqlconn, $user, $bbcode);
		
		$titleext .= ' ('.$include_page->get_title_extention().')';
	}
	else {
		// Falls eine option nicht zutrifft wird das Objekt wieder auf eine NullReferenz gesetzt
		$include_page = null;
	}
}
else {
	$include_error = true;
}

Erstmal prüfe ich ob es die funktion get_inc_object gibt die mir ein Objekt liefern soll. Danach prüft ich ob der erhaltene Wert überhaupt ein Objekt ist und ob das Objekt eine Instanz von meinem Interface ist und deswegen alle Funktionen implementieren muss die das Interface vorgibt.

Außerdem lese ich die title_extention aus um meine Titelleiste der Seite zu aktualisieren.

Falls irgendwas falsch läuft wird die Referenz auf null gesetzt.

Zum ausgeben prüfe ich nur ob meine Objektreferenz null ist.
PHP:
						if ($include_page != null)
						{
							$include_page->show_content_of_file();
						}
						else
						{
							draw_wrong_parameter();
						}

Das ist meine Lösung.

Falls jemand Verbesserungsvorschläge hat kann er sie gerne hier anbringen.
Fragen werden natürlich von mir beantwortet.

lg
 
Hi, eine objektorientierte Lösung ist natürlich auch sehr schön, ich hätte noch ein paar Vorschläge zur "Verbesserung" deiner Lösung:

1. Du benutzt ja sowieso schon PHP 5, dann benutz doch für die Eigenschaften deiner Klasse statt "var" die neuen Möglichkeiten, "public", "private" und "protected".

2. Leg doch die Methode um das Objekt zu erzeugen mit in die Klasse, das ist geschickter, da du die Methode dann im Interface mit angeben kannst, und falls du mal (aus welchen gründen auch immer) mehr als eins dieser Objekte brauchst, hast du kein Problem mit doppelt definierten Funktionen.

Realisieren würde ich das im Interface so:
PHP:
interface iIncludeFunctions {
    public static function factory();
    public function save_values($filename, $file_parameters, $sqlconn, $user, $bbcode);
    public function get_needed_parameters();
    public function get_title_extention();
    public function show_content_of_file();
}

Und dann in der Klasse:
PHP:
class ...
{
    public static function factory()
    {
        return new self();
    }
}

3. Du könntest das Erzeugen der Objekte (Mit oder ohne factory-Methode wie in 2.) auch dem aufrufenden Skript überlassen.

4. Es wäre evtl. sinnvoll das Interface durch eine abstrakte Klasse zu ersetzen, denn dann könntest du die meisten Funktionen und Eigenschaften schon dort definieren und teilweise auch implementieren. Besonders die Methoden "save_values" und "get_needed_parameters" könnten dadurch in der abstrakten Klasse bleiben und müssten nicht mehr in jeder Klasse neu implementiert werden. Das selbe gilt auch für (fast) alle Eigenschaften.

So, dann bin ich auch fertig ;)
Ich hoffe das war in irgendeiner Weise noch hilfreich.
 
Hallo!

Danke für deine konstruktiven Ideen! Meistens kommt man da ja selbst nicht unbedingt so drauf - aber das mit der abstrakten Klasse habe ich mir auch schon überlegt. Ich mache das aber nicht da ich in einigen Klassen die Werte garnicht brauche und nach der Übergabe einfach verfallen lasse. Zum beispiel auf einer About Me seite oder Impressum. Hat alles Vor und Nachteile.

Das mit public und private und var habe ich noch nie in PHP verstanden - Ich dachte eigentlich es gibt keine Modifier und auf einmal doch? na dann mache ich die doch mal direkt alle private ;)

PHP:
function get_inc_object() {
	return new inc_about();
}

class inc_about implements iIncludeFunctions {
	
	public function save_values($filename, $file_parameters, $sqlconn, $user, $bbcode) {
		// Nothing to save
	}
	
	public function get_needed_parameters() {
		return null;
	}
	
	public function get_title_extention() {
		return null;
	}
	
	function show_content_of_file() {
..........
}
}

Allerdings muss ich mir in PHP immer angucken wie das alles in OOP t (schlüsselwörter), da ich mit PHP eigentlich noch nie was objektorientiertes gemacht habe ;)

Zu deiner Factory-Methode:
Ich habe mir extra eine dynamische Lösung überlegt um den Klassennamen nicht unbedingt kennen zu müssen und somit das ganze leicht erweiterbar ist. Bei deiner Lösung müsste ich ja den Klassennamen kennen um ein Objekt daraus zu erzeugen.

Wenn ich nun aber schreibe:
PHP:
function get_inc_object() {
	return new inc_about();
}

class inc_about implements iIncludeFunctions {
...........

oder
PHP:
function get_inc_object() {
	return new inc_videos();
}

class inc_videos implements iIncludeFunctions {

Dann gibt mir die Funktion immer ein Objekt zurück welches eine Instanz von iIncludeFunctions ist. Dadurch muss ich nur prüfen ob die funktion gibt und komme so ohne Klassennamen an ein Objekt. Was genau dahinter steht weiß ich natürlich nicht...
2 Includes werde ich auch nie brauchen ;)

Die Funktion get_needed_parameters brauche ich auf jeden Fall mit anderem Rückgabewert in jeder Klasse - daher würde in der abstrakten Klasse nur die save_values auftauchen. Eigentlich ne gute Idee - mal sehen ob ich das noch umsetze! Das wär ja nicht die meiste Arbeit - erstmal alles anpassen nun.

lg
 
Hi, so geht es natürlich auch :)

Ich programmiere jetzt schon ziemlich lange, auch mit PHP, und das wichtigste ist, dass man gut plant, bevor man überhaupt eine einzige Zeile Quellcode schreibt. Ich würde diese "include-Geschichte" folgendermaßen lösen, wenn ich das programmieren würde:

1. Für jede Seite eine Klasse erstellen.
2. Alle diese Klassen von einer abstrakten Klasse erben lassen.
3. Die Klassen so benennen, wie die Seiten für die sie zuständig sind. z.B. nach dem Schema class Page_News, class Page_News_Comments usw.
4. Die Dateien der Klassen genauso benenen und in Ordnern speichern, für das Beispiel so:

Code:
[Root]
  [include]
    page.php -> Enthält die abstrakte Klasse Page
  [page]
    news.php -> Enthält Klasse Page_News
    [news]
      comments.php -> Enthält Klasse Page_News_Comments
Die eckigen Klammern sollen zeigen, dass es ein Ordner ist, schöner krieg ichs leider nicht hin ;)

5. Durch diese klare Struktur, weisst du immer sofort wie die Klasse heisst, die geladen werden soll.
6. In die abstrakte Klasse Page eine Factory-Methode einbauen, die automatisch die benötigte Klasse lädt und erzeugt, quellcodemäßig sähe das in etwa so aus:
PHP:
abstract class Page
{
...
  public static function factory($page)
  {
    $class = 'Page_' . str_replace('/', '_', $page);
    $file = PAGE_ROOT_DIRECTORY . '/page/' . strtolower($page) . '.php';

    if(!is_readable($file) || (!include_once($file)))
    {
      throw new Exception('Page Klasse konnte nicht geladen werden: ' . $class . ' (' . $file . ')');
    }

    if(!class_exists($class, false))
    {
      throw new Exception('Die Datei ' . $file . ' enthält keine Definition der Klasse ' . $class);
    }

    if(!in_array(__CLASS__, class_parents($class, false)))
    {
      throw new Exception('Alle Page Klassen müssen müssen Kindklassen von ' . __CLASS__ . ' sein.');
    }

    $page = new $class(); // Hier könnten schon Parameter übergeben werden...

    $page->setParameter('irgendwas', 'test'); // Wäre auch eine schöne Möglichkeit so etwas zu realisieren...

    return $class;
  }
}
8. In der index.php, über die ja alles mit GET parametern gesteuert wird, muss man jetzt nur noch den Parameter für die Seite auslesen und das Objekt erzeugen, also z.B. so:
PHP:
require_once 'include/page.php'; // Oder wo auch immer man die Page Klasse speichert...

define('PAGE_ROOT_DIRECTORY', dirname(__FILE__));

if(!empty($_GET['load']))
{
    $page = $_GET['load'];
}
else
{
    $page = 'news';
}

$pageObject = Page::factory($page);

So, das war ja jetzt schon einiges, aber vielleicht bringt dich das ja auf ein paar neue Ideen, was du noch alles machen kannst. Vor allem bei größeren Projekten, bewährt sich ein solcher Aufbau ziemlich gut, da man immer leicht die Übersicht behalten kann.

Was die Parameter-Geschichte angeht, lässt sich das auch super integrieren, z.B. so:
PHP:
abstract class Page
{
...
  protected $parameters = array();

  protected $neededParameters = array();
...
  public function checkParameters(array $check = null)
  {
    foreach($this->neededParameters as $param)
    {
      if(!isset($this->parameters[$param]))
      {
        return false;
      }
    }

    if($check !== null)
    {
      foreach($check as $param)
      {
        if(!isset($this->parameters[$param]))
        {
          return false;
        }
      }
    }

    return true;
  }
...
  public function setParameter($name, $value)
  {
    $this->parameters[$name] = $value;
  }
...
}
und dann neededParameters einfach in jeder Klasse überschreiben, also etwa so:
PHP:
class Page_News_Comments extends Page
{
  protected $neededParameters = array
  (
    'news_id',
    'user_id'
  );
}
und falls du nur in bestimmtem Funktionen Parameter brauchst, dann kannst du die noch zusätzlich mit überprüfen lassem, indem du sie als Array an die Funktion checkParameters() übergibst.

So, das reicht erstmal :)
 
Zuletzt bearbeitet:
Hey vielen dank dafür! Ich werde deine Ratschläge sicherlich umsetzen ;) Das ganze ist so echt ne ganze ecke einfacher zu handhaben - wenn man schon die Möglichkeit hat in PHP objektorientiert zu arbeiten sollte man das auch tun denke ich.

Dauert nun aber erstmal bis ich alle meine alten Inhalte auf das neue Konzept umgestellt habe. Auf die Idee bin ich eigentlich erst gekommen weil ich meine Seite mit mod_rewrite Suchmaschinenfreundlicher gestaltet wollte.

Ich danke dir für deine Mühe ;)

Hast du eine Homepage wo du so ein Konzept schon anwendest? Ich arbeite auch schon relativ lange mit PHP (3-4 Jahre). Aber so eine komplexe Struktur habe ich mir da noch nicht überlegt ;)

Das schöne ist ja nun, dass die Daten für die einzelnen Bereiche auch nochmal gekapselt sind und nicht so ohne weiteres weiterverwendet werden können! Das beugt jeder Menge Fehler vor und macht das ganze viel übersichtlicher - wie du schon sagtest!

lg und schönes Restwochenende!
 
Ja, ich habe dieses Konzept schon ein paar Mal angewendet für Websites, und es funktioniert ausgezeichnet und lässt sich auch sehr schön mit mod_rewrite kombinieren. In diesem Fall müsste die .htaccess ungefähr so aussehen:
Code:
<IfModule rewrite_module>

 RewriteEngine On
 
 Options +FollowSymLinks

 RewriteBase /
 
 RewriteCond %{SCRIPT_FILENAME} !-f
 RewriteCond %{SCRIPT_FILENAME} !-d
 RewriteRule ^(.*)\.html$ index.php?load=$1 [QSA]

</IfModule>

Das schöne ist, das einem ja eigentlich kaum Grenzen gesetzt sind, man kann immer wieder was dazuprogrammieren, da gibt es Möglichkeiten ohne Ende. z.B. kann man ja noch die Datenbankverbindung einbauen, oder Templates usw.
 
Zuletzt bearbeitet:
Hallo zusammen!

Ich hab gerade wieder eine weitere Idee und möchte deswegen nur diesen Thread erweitern und nicht unbedingt einen neuen eröffnen ;) Also es geht um die Suche nach Stichwörtern auf meiner Homepage.

Dazu werde ich erstmal mein bisheriges Konzept (Danke an EvilO) vorstellen:

Abstrakte Klasse von der alle Klassen erben müssen die jemals in Frage kommen irgendwas auf meiner Seite dar stellen zu wollen ;)
PHP:
abstract class IncludeModel {
	
	protected $filename;
	protected $file_parameters;
	protected $sqlconn;
	protected $user;
	protected $bbcode;
	
	public function IncludeModel($filename, $parameters, $sqlconn, $user, $bbcode) {
		$this->filename = $filename;
		$this->convert_parameters($parameters);
		$this->sqlconn = $sqlconn;
		$this->user = $user;
		$this->bbcode = $bbcode;
	}
	
	// Kann überschrieben werden
	protected function get_needed_parameters() {
		return null;
	}
	
	// Kann überschrieben werden
	public function get_title_extention() {
		return null;
	}
	
	// Wandelt die Parameter die eingegeben wurden in lesbare Links
	private function convert_parameters($parameters) {
		$parameterstring = $this->get_needed_parameters();
		if ($parameterstring != null) {
			$exploded_names = explode("|", $parameterstring);
			
			for ($paramcounter=0;$paramcounter<count($parameters);$paramcounter++) {
				
				$this->file_parameters[$exploded_names[$paramcounter]] = $parameters[$paramcounter];
			}
		}
	}
	
	public abstract function show_content_of_file();
	
	// Gibt die Klasse zurück - Statisch
	public static function factory($page)
	{
		$class = 'inc_' . str_replace('/', '_', $page);
		$file = './links/' . strtolower($page) . '.php';
		
		if(!is_readable($file) || (!include_once($file))) {
			throw new Exception('Datei wurde nicht gefunden.');
		}
		
		if(!class_exists($class, false)) {
			throw new Exception('Keine Definition der Klasse '.$class);
		}
		
		if(!in_array(__CLASS__, class_parents($class, false))) {
			throw new Exception('Alle Klassen müssen müssen Kindklassen von ' . __CLASS__ . ' sein');
		}
		
		return $class;
	}
	
}
Die Klassen müssen also folgendermaßen benannt sein: inc_filename. So kann das System beliebig erweitert werden.

Diese Klasse wird in der index.php folgendermaßen verwendet:
PHP:
$page = $_GET['load'];
try {
	$class = IncludeModel::factory($page);
	$titleext = ucfirst($page);
}
catch (Exception $e) {
	$class = null;
	$errorlog = $e->getMessage();
	$titleext = 'Fehler';
}

// Falls die Klasse gefunden wurde
if ($class != null) {
	$include_page = new $class($page, $parameters, $sqlconn, $user, $bbcode);
	
	$newext = $include_page->get_title_extention();
	
	if ($newext != null) {
		$titleext .= ' ('.$newext.')';
	}
}
else {
	$include_page = null;
}

Das kann man sicher noch ein wenig optimieren - aber für eine erste Beta reicht das so ;) Dem Objekt werden also grundsätzlich alle Daten reingereicht die für irgendeine Art von verarbeitung gebraucht werden.

Nun kann ich die Klassen mit dem eigentlichen Inhalt in meinem Ordner platzieren und genauso nennen wie gewünscht. Dabei kann ich die beiden Funktionen get_title_extention und get_needed_parameters überschreiben wenn ich sie brauche. Andernfalls geben sie einfach nu null zurück.

Dabei ist darauf zu achten, dass jede Kindklasse die Abstrakte Funktion
public function show_content_of_file()
implementiert. Diese Funktion wird genutzt um die eigentliche Ausgabe zu erzeugen.

Nun zu meiner eigentlichen Frage:
Wenn ich eine Suche implementieren möchte, müsste ich mir ja eine Klasse inc_search anlegen. Dort könnte ich ein Suchbegriff definieren und wenn ich den abschicke werden alle Dateien in meinem links-Verzeichnis geladen (bzw die enthaltenen Klassen).

Nun erstelle ich aus jeder dieser Klassen ein Objekt und schaue, ob in dem Objekt die Funktion search_string() überschrieben ist. Das geht einfach indem ich die Funktion in der Abstrakten Klasse definiere (die dort null zurück gibt) und nur in den Klassen überschreibe wo sich das suchen lohnt (ein Impressum muss nicht durchsucht werden).

Falls das so ist gebe ich ein Array mit Objekten von Suchergebnissen (welche z.B. Linkname und Link enthalten).

Jemand eine bessere Idee?

lg
 
Zuletzt bearbeitet:
Zurück