Prof. Dr.-Ing. Oliver Radfelder
Informatik / Wirtschaftsinformatik
Hochschule Bremerhaven
Kurz und Knapp: Php

Loggen Sie sich auf hopper und dann in Ihrem Docker-Container ein. Gehen Sie in Ihr Webverzeichnis /var/www/html/docker-IHR-ACCOUNT-web/ und öffnen Sie den Editor vim mit dem Dateinamen miniapp.php.

$ vim miniapp.php
und geben Sie langsam und mit korrekter Formatierung den folgenden ersten Programmcode ein:
<?php
echo("Hello PHP-World");
?>

Dann gehen Sie mit Ihrem Browser auf https://informatik.hs-bremerhaven.de/docker-IHR-ACCOUNT-web/miniapp.php

Für das Überprüfen im Web sollten während der Testphase auch immer Fehlermeldungen angeschaltet sein:

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

echo("Moin");

Variable sind in PHP zunächst nicht mit einem Typen versehen. Sie können auf Zeichenketten, Zahlen oder Objekte verweisen. Innerhalb von Zeichenketten lässt sich auf Variableninhalte direkt zugreifen. Zeichenketten werden mit dem Punkt-Operator verkettet:

<?php
// dies ist ein Kommentar
// nun eine Variable - mit $ im Namen:
$name = "Johannes Flattermann";
// Zugriff auf den Inhalt
echo("Hello $name, ");
// mit Integer-Variablen lässt sich wie
// üblich rechnen:
$zahla = 5;
$zahlb = 7;
echo("$zahla * $zahlb ergibt: ".($zahla*$zahlb));
?>

Schleifen gibt es, wie üblich, sowohl als while-Schleifen ...

<?php
$i = 0;
while($i < 10) {
  echo("$i, ");
  $i++;
}
?>

... als auch als for-Schleifen.

<?php
for($i=0; $i < 10; $i++) {
  echo("$i, ");
}
?>

Und auch Bedingungen sind wie gewohnt:

<?php
$zahl = 8;
if($zahl >= 6) {
  echo("groesser gleich 6");
} else {
  echo("kleiner 6");
}
?>

Arrays werden durch array(...) oder [..] erzeugt. Zugegriffen wird mit name[$index].

<?php
$tage = ["montag", "dienstag", "mittwoch"];
echo("der 3. Tag: ".$tage[2]);
?>

Wie so oft, gehören Arrays und Schleifen zueinander. Die Größe eines Arrays liefert die eingebaute Funktion count(...).

<?php
$tage = ["montag", "dienstag", "mittwoch"];
$tage[] = "donnerstag";

for ($i=0; $i < count($tage); $i++ ) {
  echo($tage[$i]." ");
}
// oder mit foreach
foreach($tage as $tag){
  echo($tag);
}
?>

Assoziative Arrays spielen in PHP eine besondere Rolle. Mit ihnen wird ein Wert zu einem Schlüssel assoziiert, so dass über den Schlüssel wieder auf den Wert zugegriffen werden kann.

<?php
$tage =      ["mo" => "montag", 
              "di" => "dienstag", 
              "mi" => "mittwoch"];

$tage["do"] = "donnerstag";
echo($tage["di"]);
unset($tage["di"]);

foreach($tage as $key => $value) {
   echo("$key: $value\n");
}
?>

Das übliche Lesen und Schreiben über Stdin und Stdout:

<?php
$f = fopen( 'php://stdin', 'r' );
while(! feof($f)) {
  echo(fgets($f));
}

Mit assoziativen Arrays werden auch Werte aus Formularen im Web übergeben. GET-Argumente werden als Schlüsselwert-Paar an die URL hinter einem Fragezeichen anghängt und in der Variablen $_GET verfügbar gemacht:

<?php
// https://informatik.hs-bremerhaven.de/docker-IHR-ACCOUNT-web/miniapp.php?name=abcdef
$name = $_GET["name"];
echo "Sie sind $name ?";
?>

Oft werden Eingabefelder im Web aber aus Formularen heraus vermittels POST übertragen. Ein Formular in einer einfachen HTML-Datei eingabe.html könnte so aussehen:

<!doctype html>
<html>
<body>
  <form action="miniapp.php" method="post" >
    Name:  <input type="text" name="name" />
    Alter: <input type="text" name="alter" />
    <input type="Submit" value="Absenden" />
  </form>
</body>
</html>

Der entsprechende Verarbeitungscode auf dem Server liest dann die Werte aus dem assoziativen Post-Array:

<?php
// Rufen Sie 
// https://informatik.hs-bremerhaven.de/IHR-ACCOUNT/eingabe.html
// auf, füllen Sie das Formular aus und senden Sie es ab ...
$name = $_POST["name"];
$alter = $_POST["alter"];
echo("Hallo $name, Sie sind also $alter Jahre alt?");
?>

Um mit curl zu Test- und Validierungszwecken auch Post-Anfragen an den Webserver zu senden, nutzen Sie für die einzelnen Parameter -F.

# von hopper aus
curl -F "name=paul" -F "alter=18" https://informatik.hs-bremerhaven.de/docker-demo-web/demo.php

Der Zugriff auf Dateien funktioniert am einfachsten mit den Funktionen file(...), die ein Array zurückliefert und file_put_contents(...), die einen String schreibt, bzw anhängt. Unsere Umgebung ist so eingerichtet, dass in das Verzeichnis private unter Ihrem Webverzeichnis auch der User www-data, unter dem der Webserver läuft, schreiben darf, wenn das Script aus Ihrem Verzeichnis heraus aufgerufen wurde.

Sollte das Verzeichnis private nicht existieren, legen Sie es wie folgt an:

(umask 007; mkdir /var/www/html/$USER-web/private/)

Sollte es existieren, aber nicht schreibbar sein, passen Sie die Dateirechte entsprechend an:

chown :www-data /var/www/html/$USER-web/private/
chmod g+w /var/www/html/$USER-web/private/
<?php
// Rufen Sie 
// https://informatik.hs-bremerhaven.de/IHR-ACCOUNT/eingabe.html
// auf, füllen Sie das Formular aus und senden Sie es ab ...
$name = $_POST["name"];
$alter = $_POST["alter"];
file_put_contents("private/namen.txt", $name." ".$alter."\n", FILE_APPEND);
$eintraege=file("private/namen.txt");
for($i=0;$i < count($eintraege); $i++){
   echo($i.": ".$eintraege[$i]."<br>");
}
?>

Da nun eine Datei sich mittels file(...) in ein Array lesen lässt, brauchen wir nur noch die Möglichkeit, eine Zeile in Tokens zu zerlegen:

<?php
$line="das:ist:eine:zeichenkette";
$arr=explode(":",$line);
var_dump($arr);
$str=implode(":",$arr);
echo($str);
?>

Das Gegenstück zu explode ist implode und erwartet einen Trenner und ein Array.

Für kleine Experimente ist die Funktion var_dump recht gut geeignet. Sie gibt den Inhalt einer Variablen auf der Standardausgabe aus. Im Übrigen muss ein PHP-Script nicht im Web aufgerufen werden. Auf der Kommandozeile kann dem Interpreter ebenfalls ein Script mitgegeben werden:

php script.php

Das Einzige, dessen es nun noch bedarf, um eine vollständige Webanwendung zu erstellen, ist das Umgehen mit Sessions. Auch Sessions sind assoziative Arrays, in denen sich allerdings über Requests hinweg Werte halten lassen. Dazu muss jeweils einmal die Funktion session_start() aufgerufen werden. Passend dazu gibt es noch eine Funktion session_destroy.

<?php
session_start();
if (! isset($_SESSION['number'])) {
  $_SESSION['number']=0;
}

$_SESSION['number']++;
echo($_SESSION['number']." Zugriffe in dieser Session\n");
// test with :
// curl -c /tmp/$USER.jar -b /tmp/$USER.jar https://informatik.hs-bremerhaven.de/docker-demo-web/miniapp.php?[1-10]
?>

Zur besseren Organisation des Codes kann man sich auch eigene Funktionen schreiben:

<?php
function isValid($name, $alter) {
  if (strlen($name) > 0 && $alter >= 18 ) return TRUE;
  else return FALSE;
}

$name = $_POST["name"];
$alter = $_POST["alter"];

if (isValid($name, $alter)) {
  echo("Das ist in Ordnung...");
} else {
  echo("Das wird nichts...");
}
?>

Und das ergibt natürlich erst so richtig Sinn, wenn Funktionen in Dateien ausgelagert werden, die dann in mehreren anderen Scripten includiert werden. Schreiben Sie die Funktionsdefinition von isValid in eine Datei mit dem Namen private/funktionen.inc.php.

<?php
include("private/funktionen.inc.php");

$name = $_POST["name"];
$alter = $_POST["alter"];

if (isValid($name, $alter)) {
  echo("Das ist in Ordnung...");
} else {
  echo("Das wird nichts...");
}
?>

Bisweilen braucht man noch - jede Scriptausführung ist ein eigener Prozess - einen Mechanismus zum exklusiven Locking. Die Funktion fopen(...) öffnet eine Datei und erzeugt sie gegebenenfalls. Mit der Funktion flock(...) und dem zweiten Parameter LOCK_EX wird ein exklusiver Lock auf diese Datei gelegt. Zum Ende des Scripts wird dieser Lock automatisch wieder freigegeben.

<?php
$lockfile = fopen("lock","c");
$lock = flock($lockfile, LOCK_EX);
usleep(1000000); //microseconds
flock($lockfile, LOCK_UN);
fclose($lockfile);
echo("ok");
?>

Was nicht fehlen darf, ist ein Dateiupload...

<!doctype html>
<html>
<body>
<form action="phpupload.php" method="post" enctype="multipart/form-data">
    Datei auswaehlen:
    <input type="file" name="diedatei" >
    <input type="submit" value="hochladen" name="submit">
</form>
</body>
</html>
Auf Serverseite liegen nach einem Upload die Informationen in dem assoziativen Array $_FILES wiederum als assoziative Arrays. Für einen sicheren Dateiupload wären manche Zeilen mehr zu schreiben: die Dateigröße zu prüfen, der Datentyp, der Dateiname etc. Allerdings sollte ohne spezielle Verarbeitung - gegebenenfalls in einem gesonderten Prozess - eh keineswegs eine Datei wieder ausgeliefert werden oder gar als php-Datei verarbeitet werden.

<?php
  $basename=basename($_FILES["diedatei"]["name"]);
  $filename=str_replace("/","_", $basename); // minimum ...
  move_uploaded_file($_FILES["diedatei"]["tmp_name"], "private/phpupload/".$basename);
?>

Wer das ausführlicher diskutiert haben will, schaue mal bei wiki.selfhtml.org.

Und auch hier ist curl das kleine Wunderkind für Automatisierung und Tests:

curl -F "diedatei=@rabbit.png" https://informatik.hs-bremerhaven.de/docker-demo-web/phpupload.php
Und wenn man mal etwas in php an einen auf einem anderen Server laufenden Dienst schicken möchte, und das Ergebnis soll an den Client zurückgeschickt werden, hilft es, zu wissen, dass sowohl echo als auch stream_get_contents Binärdateien verarbeiten können, womit sich wie in diesem Fall mittels LaTeX, ncat und php ein kleiner Dokumentendienst realisieren lässt. Alternativ zu dem praktischen stream_get_contents kann hier auch mit $line = fgets($client)
<?php
header("Content-type: image/png");
$client = stream_socket_client("tcp://localhost:11000", 
                               $errno, $errorMessage);
fwrite($client,$_GET['doc']."\n");
fwrite($client,"\\\\renewcommand{\\\\slogan}{".$_GET['val']."}\n");

echo(stream_get_contents($client));
fclose($client);
?>
Auf dem anderen Rechner (oder dem selben), dem Server, läuft dann ein kleiner Dienst mit ncat:
ncat -l -k -e ./worker.sh  11000
Dieser wiederum ruft, wenn von dem Web-Server aus Kontakt aufgenommen wird, das Script worker.sh auf:
#!/bin/bash 
read commandline
read content
echo "$content" > data
name=$commandline
pdflatex -interaction=nonstopmode -halt-on-error $name.tex >>log 2>>&1
pdftocairo -r 150 -png -singlefile $name.pdf
cat $name.png
Falls Sie einmal auf Ihre Mariadb-Datenbank zugreifen wollen, sorgen Sie dafür, dass in dem private-Ordner in Ihrem Webverzeichnis eine Datei dbconnection.inc.php liegt, die nur von dem www-data-User und Ihnen lesbar ist:
<?php
$host       = "mysql-server";
$user       = "IHRUSERNAME";
$password   = "IHRPASSWORT";
$database   = "IHRUSERNAME_db";
Die konkreten Daten entnehmen Sie bitte der Datei .my.cnf in Ihrem Homeverzeichnis. Dann können Sie eine PHP-Datei direkt in Ihrem Webverzeichnis ablegen, die wiederum diese private Datei importiert und eine Verbindung zum Datenbankserver öffnet.
<?php
include("private/dbconnection.inc.php");

$conn = mysqli_connect($host, $user, $password, $database);
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}

$sql = "select * from demo";
$result = mysqli_query($conn, $sql);
while($row = mysqli_fetch_assoc($result)) {
  echo($row['demo_id']." ".$row['name']."\n");
}

mysqli_close($conn);
Eine Tabelle in Ihrer Datenbank können Sie einfach auf hopper mit dem Kommandozeilentool mysql erstellen. Legen Sie dazu eine Textdatei mit dem Namen createdemodb.sql an:
DROP TABLE IF EXISTS demo;
CREATE TABLE demo (
  demo_id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(100),
  PRIMARY KEY (demo_id));

INSERT INTO demo (name) VALUES
  ('peter'),('paul'),('mary');
Rufen Sie dazu mysql wie folgt auf:
mysql < createdemodb.sql
Prüfen Sie gegebenenfalls mit mysql und einem kurzen Aufruf, ob die Datenbank korrekt angelegt wurde:
mysql -s -e "SELECT * FROM demo;"

Wenn Sie übergebene Parameter in einer SQL-Anweisung weiterverarbeiten möchten, müssen Sie sie aus Sicherheitsgründen mindestens mit mysqli_real_escape_string escapen:

$sql = sprintf("SELECT * FROM demo WHERE name='%s'",
    mysqli_real_escape_string($conn,$name));

Besser aber noch ist es, gleich mit prepared Statements zu arbeiten:

<?php
include("private/dbconnection.inc.php");

$conn = mysqli_connect($servername, $username, $password, $db);
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}
$sql = "SELECT * FROM demo WHERE demo_id = ? OR name = ?";

$preparedQuery = mysqli_prepare($conn, $sql);
$id=1;
$name="Peter";
mysqli_stmt_bind_param($preparedQuery, 'is',$id,$name);
mysqli_execute($preparedQuery);
$result = mysqli_stmt_get_result($preparedQuery);
while($row = mysqli_fetch_assoc($result)) {
  echo($row['demo_id']." ".$row['name']."\n");
}

mysqli_close($conn);

Neu in php 8.2 ist mysqli_execute_query(), womit sich prepared Statements weit eleganter formulieren lassen:

<?php
$result = mysqli_execute_query($conn, "select * from demo where id=? or name=?", [1,"Peter"]);

Bilder lassen sich auf unterschiedliche Arten erzeugen. Hier die eher klassische Variante mit der GD-Bibliothek. Für das Nutzen einer Schrift können Sie die ttf-Font Datei in das Arbeitsverzeichnis kopieren.

<?php
header("content-type: image/png");
$txt="Hello Wörld";
if(isset($_GET['text']))
  $txt=$_GET['text'];

$im=imagecreatetruecolor(200,200);
$background_color=imagecolorallocate($im,200,200,255);
imagefilledrectangle($im,0,0,200,200,$background_color);
$text_color=imagecolorallocate($im,233,0,0);

imagettftext($im,20,0,25,40,$text_color,"./font.ttf",$txt);
imagepng($im);
imagedestroy($im);

Sonstiges

Wie so oft benötigen wir bisweilen ein Instrument zur Zeitmessung:

<?php
$start=microtime(TRUE);
$result="";
for($i=0;$i<1000000;++$i){
  $result.="x";
}
$ende=microtime(TRUE);
printf("took:%.3F secs\n",($ende-$start));

Filesystemoperationen, die regelmäßig gebraucht werden:

<?php
if(!is_dir("tempdir")){
  mkdir("tempdir");
}

if(!file_exists("tempdir/temp.txt")){
  touch("tempdir/temp.csv");
}

//csv file handling
$file = fopen("tempdir/temp.csv","w");
fputcsv($file,[1,'feldmit"doublequote']);
fputcsv($file,[2,'feldmit""doublequote']);
fclose($file);

$file = fopen("tempdir/temp.csv","r");
while(! feof($file)){
  print_r(fgetcsv($file));
}
fclose($file);


copy("tempdir/temp.csv","result.csv");


//to delete a file
unlink("tempdir/temp.csv");
rmdir("tempdir");
$fn="file-".date(DATE_ISO8601);

printf("%10s %10d %10d\n","result.csv",filesize("result.csv"),filemtime("result.csv"));

In diesen Zeiten bedarf es regelmäßig der Konvertierung von und nach JSON:

<?php
$json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';

$phpvar=json_decode($json, true);
var_dump($phpvar);
echo(json_encode($phpvar));

Weiterführendes