Die EU-Urheberrechtsreform wird das Internet, wie wir es kennen, grundlegend verändern – wenn sie denn in der finalen Abstimmung angenommen wird. Das können wir aber immer noch verhindern!
Weitere Informationen: https://juliareda.eu/2019/02/artikel-13-endgueltig/
PHP/Tutorials/Templates/Dateien mit include nachladen
In diesem Tutorial lernen Sie, wie Sie immer wiederkehrende Elemente Ihrer Webseiten, wie Navigation, Seitenkopf <header> und die Fußzeile <footer> in eigene Dateien auslagern können, die Sie dann problemlos einbinden können. Änderungen, wie das Einfügen eines Links zu einer neuen Seite, müssen dann nur einmal zentral erledigt werden.
Gerade bei mittelgroßen Webprojekten kann die ständige Aktualisierung zahlreicher Seiten hohen Arbeitsaufwand bedeuten. Alternativ käme der Einsatz eines CMS in Frage.
Inhaltsverzeichnis
Kann mein Webspace PHP?[Bearbeiten]
Heutzutage ist PHP Standard bei allen Providern. Sie können dies einfach testen, indem Sie folgendes Programm auf ihren Server laden.
<?php
phpinfo();
Falls PHP installiert ist, gibt Ihnen dieses kleine Programm sehr viele Informationen über ihre PHP-Installation und den Webserver aus, was recht nützlich ist. Merken Sie es sich also gut.
Allerdings kann es sein (und ist nicht wirklich selten!), dass Webhoster in dem (fragwürdigen) Glauben, hierdurch die Sicherheit der Installation zu erhöhen, die Abarbeitung der Funktion phpinfo() verbieten. In dem Fall testen Sie mit:
<?php echo 'PHP? Funktioniert! Version ist: ', phpversion(), "\n";
Wird etwas wie: PHP? Funktioniert! Version ist: 7.1.0
ausgegeben, dann ist PHP ganz offensichtlich tätig geworden, also installiert und konfiguriert.
[Bearbeiten]
Als Beispieldatei verwenden wir unsere Webseite aus dem HTML5-Tutorial.
Da der head neben dem Seitentitel auch weitere Metaangaben hat, die für jede Seite unterschiedlich sein sollen, schneiden wir nur den <header> aus unserem Dokument aus und speichern ihn als header.php ab:
<header>
<img src="logo.gif" alt="logo">
<h1>Titel</h1>
<nav>
<ul>
<li><a href="#link_1.html">Wiki</a></li>
<li><a href="#link_2.html">Blog</a></li>
<li><a href="#link_3.html">Forum</a></li>
</ul>
</nav>
</header>
Mit dem Footer können wir genauso verfahren.
Einbinden in unsere Webseite[Bearbeiten]
Anstelle der ausgeschnittenen Code-Abschnitte fügen wir jeweils ein include()
ein, das die jeweilige Datei aufruft. Damit der PHP-Befehl erkannt und ausgeführt wird, müssen wir unsere HTML-Datei in index.php umbenennen. Die includierten Daten müssen nicht die Endung .php haben, diese ist völlig beliebig! Die Dateien sollten die Endung .php
aber durchaus haben, wenn es Dateien sind, die php-Code enthalten – allein schon weil gute Editoren dann das Syntax-Highlighting entsprechend einschalten. Dann bietet sich ggf. zur Klarheit auch die Kombination mehrerer Endungen an, z. B. datei.inc.php
. Wenn in den includierten Dateien PHP-Code vorkommt, dann muss dieser auch als PHP-Code markiert sein. Also das öffnende <?php
und ggf. schließende ?>
enthalten.
Von außen sieht niemand, dass die Navigation aus einer ausgelagerten Datei stammt, da der Browser nur den ihm gelieferten HTML-Code zu Gesicht bekommt und darstellt.
Die Alternativen include, include_once, require, require_once - Regeln zur Verwendung[Bearbeiten]
- include benutzen Sie, wenn Sie eine Datei (womöglich) mehrfach einbinden wollen und PHP die Verarbeitung des Skriptes nicht abbrechen soll, falls diese Datei nicht vorhanden ist. Ist die Datei nicht vorhanden wird allerdings eine Notiz „geworfen“, welche man mit sinnvollen Einstellungen des Error-Reportings unterbinden kann.
- include_once benutzen Sie, wenn Sie eine Datei genau einmal einbinden wollen und wenn PHP die Verarbeitung des Skriptes nicht abbrechen soll, falls diese Datei nicht vorhanden ist. Einen Versuch, die Datei mehrfach einzubinden, wird PHP schweigend (also ohne Fehlermeldung oder Notiz) übergehen. Ist die Datei nicht vorhanden wird allerdings eine Notiz „geworfen“, welche man mit sinnvollen Einstellungen des Error-Reportings unterbinden kann.
- require benutzen Sie, wenn Sie eine Datei (womöglich) mehrfach einbinden wollen und wenn PHP die Verarbeitung des Skripts definitiv mit einem Fehler abbrechen soll, wenn diese Datei nicht vorhanden oder nicht lesbar ist.
- require_once benutzen Sie, wenn Sie eine Datei genau einmal einbinden wollen und wenn PHP die Verarbeitung des Skripts definitiv mit einem Fehler abbrechen soll, wenn diese Datei nicht vorhanden oder nicht lesbar ist. Einen Versuch, die Datei mehrfach einzubinden, wird PHP schweigend (also ohne Fehlermeldung oder Notiz) übergehen.
Beispiele:[Bearbeiten]
include('datei')[Bearbeiten]
include wird gerne genutzt, um wiederkehrende Abschnitte einzubinden. Das Szenario ist folgendes. Ein Benutzer darf z. B. Dateien mit dem Namen produktversion.VERSION.inc.php bearbeiten, damit nicht etwa ein (teurer) Programmierer inhaltliche Änderungen an der Webseite vornehmen muss. Eine der vorgesehenen Dateien existiert aber (noch) nicht:
<?php
$P_titel = "Toller Teppich";
$P_descr = "Diesen tollen Teppich können Sie nach Belieben ein- oder ausrollen.";
<?php
$P_titel = "Toller roter Teppich";
$P_descr = "Diesen tollen roten Teppich können Sie nach Belieben ein- oder ausrollen.";
In der includierenden PHP-Datei sieht das dann so aus:
<?php
$arVersionen=array('0815', '0815-rot', '0815-gruen');
foreach ($arVersionen as $version) {
include ('produktversion.' . $version . '.inc.php'); # Hier werden die Variablen überschrieben!
echo '<h4>', $P_titel, '</h4><p>', $P_descr, '</p>';
}
Da die Datei „produktversion.0815-gruen.inc.php“ nicht existiert, wird einfach schweigend nochmals auf die durch das Inkludieren der Datei „produktversion.0815-rot.inc.php“ gesetzten Variablen zurückgegriffen. Die Notiz (Warnung) kann man ja unterdrücken. Es würde also (es werden nur die Überschriften gezeigt)
- „Toller Teppich“
- „Toller roter Teppich“
- „Toller roter Teppich“
ausgegeben. Manchmal ist das tatsächlich erwünscht. Allerdings sollte nicht verschwiegen werden, dass dieses „Manchmal“ eine recht seltene Ausnahme ist. Und weil das so ist, gibt es mit require() bzw. require_once() andere Lösungen, aber wir sehen uns erst einmal einen sinnvollen Einsatz von include_once() an:
include_once('datei')[Bearbeiten]
Szenario: In einem größeren Projekt werden Informationen verarbeitet und der Programmierer möchte mit Konstanten arbeiten. Es wird weiter ziemlich viel mit includes gearbeitet. Jetzt kann es aber passieren, dass nicht klar ist, ob eine Datei, nennen wir sie „settings.inc“, bereits inkludiert ist und dass das mehrfache Inkludieren zu einem Fehler führt:
<?php
define ('PROJEKTNAME', 'Fehler vermeiden');
Würde man jetzt diese Datei mehrfach inkludieren, dann würde daraus ein Fehler entstehen:
<?php
include ('settings.inc');
## weiter unten:
include ('settings.inc');
Denn das würde aus Sicht von PHP zu folgendem Resultat führen:
define ('PROJEKTNAME', 'Fehler vermeiden');
## weiter unten:
define ('PROJEKTNAME', 'Fehler vermeiden');
Eine Konstante ist aber eine Konstante und kann ergo im Programmverlauf nur einmal definiert werden! Der Interpreter würde also einen Fehler melden. Deshalb gibt es include_once():
include_once ('settings.inc');
## weiter unten:
include_once ('settings.inc');
echo '<h1>', PROJEKTNAME, '</h1>';
Der Interpreter stellt fest, er soll diese Datei nur dann inkludieren, wenn er das noch nicht getan hat. So ist einerseits (noch nicht ganz, wie wir gleich sehen werden) sicher gestellt, dass die Informationen geladen werden, aber eben auch, dass nicht mehrfach versucht wird, die Konstanten zu definieren. Die Ausgabe wäre im Erfolgsfall:
<h1>Fehler vermeiden</h1>
require('datei') bzw. require_once('datei')[Bearbeiten]
Was aber passiert, wenn durch einen Fehler die angegebene Datei settings.inc
nicht existiert oder nicht lesbar ist? Dann gäbe PHP eine Notiz aus:
<h1>PHP Notice: Use of undefined constant PROJEKTNAME - assumed 'PROJEKTNAME' in - on line 2
PROJEKTNAME</h1>
oder, wenn die Notizen z. B. durch error_reporting(0)
unterdrückt sind:
<h1>PROJEKTNAME</h1>
PROJEKTNAME soll da nicht stehen! Das ist natürlich ein „Kardinalfehler“ – und da kommt require() bzw. require_once() ins Spiel. Denn require() würde einen Fehler melden und PHP würde die Skriptverarbeitung abbrechen:
require_once ('settings.inc');
## weiter unten:
require_once ('settings.inc');
echo '<h1>', PROJEKTNAME, '</h1>';
PHP Fatal error: require(): Failed opening required 'settings.inc' (include_path='.:/usr/share/php:/usr/share/pear') in - on line 1
require_once()
verhindert dann genau so wie include_once(), dass mehrfach (und vergeblich!) versucht wird die Datei zu includieren und damit die Konstante wiederholt zu definieren.
Des Programmierers Bequemlichkeit kann die Sicherheit gefährden![Bearbeiten]
So manchen wird die Möglichkeit, Dateien so einfach einzubinden auf so manche, nur vordergründig gute Idee bringen. Nämlich die, etwas zu schreiben wie:
<?php
include ($_GET['file']);
Sollte das jemandem auffallen - und das wird es, dann dauert es womöglich nur Minuten bis dieser etwas wie das folgende versucht:
http://localhost/selbstmord.php?file=../../../../../etc/passwd
Mit dem Resultat, dass er den Inhalt der Datei /etc/passwd erhält. Und damit auf einem Linux-System eine für ihn wertvolle Datei mit Benutzerinformationen. Solche Angriffsversuche sind nicht etwa selten, sondern tägliches, automatisiert stattfindendes Geschehen und es gibt terabyteweise Benutzernamen/Passwörter und Bankdaten, gestohlen von ziemlich naseweisen Serverbetreibern im Internet, welche beweisen, dass derart grob nachlässig gehandelt wurde.
Sich gegen diesen Angriff absichern[Bearbeiten]
Möglichkeit: Lassen Sie den Angreifer den Name der Datei nicht (vollständig!) bestimmen:
<?php
include ('./includes/' . $_GET['file'] . '.html');
Jetzt wird vor den übertragenen Wert das Verzeichnis gehängt und ans Ende die Dateiendung '.html'. Das kann aber immer noch nicht erwünscht sein, weil Sie womöglich folgendes nicht wollen:
http://localhost/gefaehrlich.php?file=../gesperrte_dateien/foo
$_GET['file']
enthaltenen Daten können so manipuliert sein, dass das Anhängen der Dateiendung keine Wirkung zeigt! Damit gilt das selbe wie schon oben bei include $_GET['file']
!$_GET
, $POST
, $_REQUEST
und $_COOKIE
), sind grundsätzlich misstrauisch zu behandeln!Sehr sicher und vergleichsweise einfach: includieren mit vordefinierten Werten[Bearbeiten]
Nutzen Sie der Sicherheit Ihrer Applikation zuliebe die sehr sichere Möglichkeit, die zu includierenden Dateien über eine Liste vorzudefinieren. Dann muss das Skript nur den richtigen Namen der Datei aus dieser Liste heraussuchen. Das ist sehr leicht zu programmieren:
<?php
## Aufruf mit https://example.org/?f=home
## In der nicht geheimen, aber sicher (ohne Schreibzugriff!) gespeicherten
## Datei 'includes.txt' stehen die möglichen Werte:
define ('INC_TABLE', 'includes.txt');
## So sieht diese z.B. aus:
/*
home
error
links
whatever
*/
## Die Werte aus der Liste müssen später noch mit der Endung ".inc.php" ergänzt
## werden, um die tatsächlich zu inkludierende PHP-Scriptdatei zu adressieren.
## Das Verzeichnis, in welchem diese Dateien stehen:
define ('INC_DIR', __DIR__.'/includes'); # __DIR__ bedeutet "vom Verzeichnis dieses Scripts aus"
# Aufruf:
include_page();
exit;
function include_page () {
# alle möglichen Werte aus Datei einlesen
$list = file(INC_TABLE);
# Standardwert ist die HOME Seite
$inc = 'home';
# soll eine bestimmte Seite eingebunden werden?
if (array_key_exists('f', $_GET)) {
$inc = 'error'; # sollte die geforderte Seite nicht existieren
# gewünschte Datei finden
foreach ($list as $v) {
$v = trim($v); # möglichen Zeilenumbruch am Ende entfernen
# ist das die gewünschte Datei?
if ($v == $_GET['f']) {
# übernehmen
$inc = $v;
}
}
}
# jetzt inkludieren
include INC_DIR."/$inc.inc.php";
}
Will man keine Textdatei pflegen, in der die möglichen include-Dateien aufgelistet sind, sondern möchte das dynamisch haben, so bietet es sich an, eine Dateinamenskonvention zu verwenden, anhand derer die Liste der zu inkludierenden Dateien jedes Mal vom Programm selbst ermittelt wird:
<?php
/**
* Namenskonventionen
*
* Dateien, die immer inkludiert werden müssen, enden auf .inc.php
* Dateien, die Seiten darstellen, enden auf .page.php
*
* Aufruf einer Unterseite: https://example.org/?f=whatever
*
* Alle zu inkludierenden Dateien sind im Unterverzeichnis /includes/ gespeichert.
*/
$includes = glob(__DIR__.'/includes/*.inc.php');
# alle $includes laden
foreach ($includes as $script) {
include $script;
}
# Seiten finden
$pages = array();
# Liste mit Seiten befüllen
foreach (glob(__DIR__.'/includes/*.page.php') as $p) {
# Schlüssel für Dateinamen ermitteln ("home" für "home.page.php")
$v = preg_replace('~(?i)(?:.*/)?([^.]+)\.page\.php$~', '$1', $p);
$pages[$v] = $p;
}
# anzuzeigende Seite ermitteln
$page = 'home'; # Standard
# soll eine bestimmte Seite eingebunden werden?
if (array_key_exists('f', $_GET)) {
$page = 'error'; # sollte die geforderte Seite nicht existieren
# gewünschte Datei finden
foreach (array_keys($pages) as $p) {
# ist das die gewünschte Datei?
if ($p == $_GET['f']) {
# übernehmen
$page = $p;
}
}
}
# Seite inkludieren
include $pages[$page];
Weiterführende Informationen[Bearbeiten]
- Marc Ermshaus: PHP-Includes: Niemals ohne __DIR__
- PHP Manual: include
?>
fehlen.