Stand: 29. Februar 2024
$item = [
'name' => 'paper',
'price' => 1.95
];
foreach ($item as $key => $value) {
echo "{$key} of item is {$value}\n";
}
Produziert folgende Ausgabe:
name of item is paper
price of item is 1.95
Das funktioniert auch mit numerischen Arrays:
$array = [ 'foo', 'bar', 'baz' ];
foreach ($array as $key => $value) {
echo "array[{$key}] = {$value}\n";
}
array[0] = foo
array[1] = bar
array[2] = baz
Schließende ?>
Tags sollten ausschließlich gesetzt werden, wenn sich hinter dem PHP Tag noch HTML befindet. In allen anderen Fällen sollte der PHP-Tag nicht geschlossen werden.
Das wird unter anderem auch von der offiziellen PHP Dokumentation empfohlen:
If a file contains only PHP code, it is preferable to omit the PHP closing tag at the end of the file. This prevents accidental whitespace or new lines being added after the PHP closing tag, which may cause unwanted effects because PHP will start output buffering when there is no intention from the programmer to send any output at that point in the script.
Für Templates ist der <?=
Short-Tag hilfreich:
<?=$name?>
// ist äquivalent zu
<?php echo $name; ?>
In einen Short-Tag kann jeder mögliche Ausdruck, der als String interpretiert werden kann, geschrieben werden.
Strings in doppelten Anführungsstrichen ("
) werden vom Interpreter ausgewertet.
Sie sollten prinzipiell nicht verwendet werden, wenn es nicht explizit erforderlich ist.
echo "hello world"; // falsch
echo 'hello world'; // richtig
Doppelte Anführungsstriche können dazu führen, dass ein String ungewollt als Code interpretiert wird. Wie mir Sicherheitsforscher gesagt haben, wäre das „wahrscheinlich schlecht“.[12] [13] [14]
Auch wenn es formal nicht erforderlich ist (PHP ist aus den 90ern, da hatten die es anscheinend noch nicht so mit Sicherheit), sollten um Variablen in Template-Strings immer geschweifte Klammern gesetzt werden.
$name = 'joe';
echo "hello $name"; // falsch
echo "hello {$name}"; // richtig
echo 'hello ' . $name; // auch okay
echo "hello " . $name; // ganz falsch
Das ganze ist eine vorbeugende Maßnahme. Alle Beispiele oben funktionieren in diesem Fall wie erwartet, aber es ist trotzdem eine gute Idee, sich an diese Praktiken zu halten.
Zusätzlich ermöglicht die geklammerte Syntax auch solche Dinge:
$n = 5;
echo "das {$n}te Element";
PHP kennt vier verschiedene Ausdrücke, um Dateien einzubinden. Diese unterscheiden sich in der Frage, ob die selbe Datei mehrmals eingebunden werden darf, und ob das Skript abbricht, wenn beim Einbinden ein Fehler auftritt.
einmaliges Einbinden | mehrmaliges Einbinden | |
---|---|---|
Warnung | require |
include |
Abbruch | require_once |
include_once |
In 99% der Fälle[Wert geraten] verwendet man include
, wenn etwas ausgeführt wird, und require_once
, wenn etwas definiert wird.
require
und include_once
sind in der Regel überflüssig.
All das sind Schlüselwörter, keine Funktionen. Daher werden um den Dateinamen keine Klammern gesetzt.
include 'logo.svg';
include 'footer.html';
include 'post.php';
footer.html:
<footer>
<a href="/impressum">Impressum</a>
</footer>
Die Datei beinhaltet nur statisches HTML, kann also beliebig oft eingebunden werden; im Zweifelsfall fehlt halt ein Teil der Seite.
post.php:
<article class="post">
<?=htmlspecialchars(get_post_content())?>
</article>
Hier wird zwar PHP ausgeführt, aber wahrscheinlich kommt dabei immer die gleiche Ausgabe raus.
require_once 'Database.php';
require_once 'login-form.php';
Database.php:
<?php
class Database {
public static function get($query) {
return 42;
}
}
Diese Datei erzeugt gar keine Ausgabe. Allerdings ist davon auszugehen, dass nach dem Einbinden jemand versuchen wird, auf die Klasse Database zuzugreifen.
login-form.php:
<?php
function print_input($type) {
echo "<input type=\"{$type}\">";
}
print_input('text');
print_input('password');
Diese Datei erzeugt zwar statisches HTML, allerdings wird eine Funktion definiert. Die Datei kann also nicht mehrmals eingebunden werden, da sonst mehrmals eine Funktion mit dem gleichen Namen definiert werden würde.
$array = [ '', 'hello world', 123, '', null];
$array = array_filter($array);
var_dump($array);
array_filter
wird eigentlich als zweites Argument eine anonyme Funktion (oder Lambda-Funktion) übergeben, um nach bestimmten Kriterien zu filtern. Wird keine Funktion übergeben, werden leere Werte rausgefiltert
array(2) {
[1] => string(11) "hello world"
[2]=> int(123)
}
Im array_filter
Beispiel werden aus einem Array einige Werte entfernt:
$array = [ '', 'hello world', '', 123, null];
$array = array_filter($array);
Das erzeugt ein Array mit den folgenden Indizes:
{
1 => 'hello world',
3 => 123
}
Das Gleiche passiert auch, wenn einzelne Werte mit unset
entfernt werden.
In einigen Fällen kann das Probleme verursachen, da zwischendurch einige Schlüssel fehlen (hier zum Beispiel $array[2]
; führt potenziell zu Problemen mit for
-Schleifen), und das Array nicht immer als numerisches Array behandelt werden kann (zum Beispiel wird so ein Array [ ]
beim Encodieren mit json_encode
in ein JSON-Objekt { }
umgewandelt, da die Indizes in JSON nicht anders dargestellt werden können. Das führt wiederum zu Problemen bei der Verarbeitung des JSONs, beispielsweise kann über Objekte nicht wie über Arrays iteriert werden).
Zum Glück lässt sich das Problem relativ einfach mit array_values
beheben:
// [...]
$array = array_values($array);
var_dump($array);
array(2) {
[0] => string(11) "hello world"
[1] => int(123)
}
Soll unformatierter Text ausgegeben werden, kann das dem Browser über einen HTTP-Header mitgeteilt werden:
header('Content-Type: text/plain');
Aufgrund der Struktur von HTTP Responses kann diese Funktion nur verwendet werden, bevor Text z.B. mit echo
ausgegeben wird.
Das ist besonders hilfreich, wenn die Ausgabe mit Zeilenumbrüchen oder Leerzeichen formatiert wurde, da der Browser diese so vollständig darstellt.
Analog dazu haben die meisten Browser eine praktische Listenansicht für JSON:
header('Content-Type: application/json');
foreach (scandir('directory') as $file) {
echo "{$file}\n";
}
(Ausgabe variiert je nach Inhalt)
.
..
index.php
public
style.css
Hier werden Verzeichnisse und Dateien ausgegeben (yay, UNIX). Die Reihenfolge hängt (wahrscheinlich) vom Dateisystem ab, und ist daher nicht verlässlich. (Quelle: der PHP-Interpreter auf meinem System sortiert – soweit ich das beurteilen kann – nicht alphabetisch, sondern nach Unicode, also A-Z vor a-z vor ÄÖÜ vor äöü etc.)
Um nur Dateien rauszufiltern, kann folgender Code verwendet werden:
foreach (scandir('directory') as $file) {
if (is_dir("directory/{$file}")) continue;
echo "{$file}\n";
}
Unter der Annahme, dass public
ein Verzeichnis ist:
index.php
style.css
$dir = 'my_directory';
foreach (scandir($dir) as $file) {
if (is_dir("{$dir}/{$file}")) continue;
$content = file_get_contents("{$dir}/{$file}");
// [...]
}
file_get_contents
arbeitet auf dem gesamten Dateisystem UND allen URLs.
Alle nicht-konstanten Argumente müssen IMMER in irgendeiner Form gefiltert werden.
Noch besser werden einfach keine vom Client übergebenen Werte als Argumente genutzt.
Das beinhaltet unter anderem auch $_POST
, $_GET
, $_SERVER
, Cookies, Sessiondaten und HTTP-Requestdaten allgemein.
Dieser Code gibt dynamisch das letzte Änderungsdatum einer Datei aus. Er kann in einer PHP-Datei in einem Fließtext eingefügt werden.
<?=date('j.n.Y',filemtime(__FILE__))?>
Analog zum Änderungsdatum kann filemtime
verwendet werden, um Cachingprobleme mit CSS zu umgehen:
<link rel="stylesheet" href="style.css?<?=filemtime('style.css')?>">
Hinweis: highlight.js
hat wohl Probleme damit, PHP-Tags in HTML-Attributwerten zu erkennen, deswegen ist hier der PHP-Code auch grün. PHP selbst interessiert sich nicht für Anführungsstriche um Tags, und kann diesen Code probblemlos ausführen.
Dieser Short-Tag generiert einen UNIX-Timestamp als GET Parameter:
style.css?1670089360
Das ist ein sehr schöner und eleganter Hack, der auch noch einwandfrei funktioniert.
Hier noch mal das ganze Ding in einer Funktion:
function stylesheet($file) {
echo "{$file}?" . filemtime($file);
}