Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Formulare-Theorie
Datenbankanbindung
Arbeiten am Projekt
Arbeiten am Projekt
Interaktive Website
Formular-Aufgaben
Testing
Webserver-Setup
Datenverarbeitung in PHP
Erarbeiten: Web Application Security
PHP-Grundlagen
Clientseitige Scriptsprache
🍕
MVC-Grundlagen
JavaScript-Grundlagen
Präsentation: Web Application Security
Arbeiten am Projekt
Arbeiten am Projekt
MVC-Refactoring
Javascript-Aufgaben
Start der Projektarbeit
(Zwischengespräch mit Auftraggeber)
Präsentation
PHP-Aufgaben
Datenvalidierung in JavaScript
Besprechung und Reflexion
Prüfung 1: Theorie
Um eine Ausgabe zu machen, kann das Sprachkonstrukt echo verwendet werden.
Wenn du in einem PHP-Codesegment ausschliesslich eine Ausgabe erzeugen möchtest, kannst du auch die Kurzform für echo verwenden.
PHP-Ausgaben werden direkt in den Output geschrieben. Du kannst also HTML- und PHP-Code mischen, um eine dynamische Seite zu generieren.
Da der PHP-Code immer auf dem Server und nie vom Client verarbeitet wird, erzeugt das Beispiel oben folgende Antwort an den Client:
Erstelle ein neues Projekt in deinem htdocs-Ordner und erstelle darin die Datei index.php.
Erstelle in der Datei index.php ein HTML-Gerüst. Im Body-Bereich soll die Datei ein Echo-Konstrukt enthalten, welches folgende Nachricht ausgibt:
<?php
// Dein PHP-Script
// ...
// Schliessendes Tag ist optional
?><?php
anweisung1;
anweisung2;
anweisung3;
?><?php
// Würde auch funktionieren
anweisung1;anweisung2;anweisung3;
?><?php
echo 'Hallo Welt!';
?><?= 'Hallo Welt!'; ?><!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Meine Seite</title>
</head>
<body>
<?php
echo "Dieser Text ist mit PHP generiert.\n";
echo "Das heutige Datum ist " . date('d.m.Y');
?>
</body>
</html><!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Meine Seite</title>
</head>
<body>
Dieser Text ist mit PHP generiert.
Das heutige Datum ist 15.02.2016
</body>
</html>Heute bin ich mit dem Auto gefahren.Seit 2009 gibt es die Framework Interoperability Group (kurz PHP-FIG), die PHP Standards Recommendations (kurz PSRs) erarbeitet und veröffentlicht.
In diesen PSRs werden Empfehlung für das Arbeiten mit PHP definiert. Ziel der PHP-FIG ist es, einmal erstellte PHP-Komponenten in verschiedenen Projekten und Frameworks wiederverwendbar zu machen. Dazu ist es wichtig, dass der Code nach einheitlichen Regeln geschrieben wird.
In werden die «Basic Coding Standards» definiert. Es wird zum Beispiel festgelegt, welche PHP-Tags verwendet werden oder wie Variablen benannt werden sollen. (ÜK-Relevant: 1. und 2.)
ist der «Coding Style Guide». Hier wird definiert, wie Code formatiert werden soll. (ÜK-Relevant: 1., 2. und 5.). definiert den «Extended Coding Style» und erweitert bzw. ersetzt einzelne Vorgaben aus PSR-2.
Schreibe sauberen Code (Formatierung, Namensgebung, Verständlichkeit). Versuche PSR-1 und PSR-2/12 zu beachten.
Versuche redundanten Code auf einem absoluten Minimum zu halten (). Nutze wenn möglich Funktionen.
Achte auf deine Ordnerstruktur. Benenne deine Dateien und Ordner auf eine verständliche Art und Weise.
PHP (rekursives Akronym und Backronym für «PHP: Hypertext Preprocessor», ursprünglich «Personal Home Page Tools») ist eine Skriptsprache mit einer an C und Perl angelehnten Syntax, die hauptsächlich zur Erstellung dynamischer Webseiten oder Webanwendungen verwendet wird.
PHP wurde erstmals 1995 von seinem Entwickler, Rasmus Lerdorf, veröffentlicht.
Aktuell ist PHP als Version 8 verfügbar.
Erweitere dein Formular mit einer clientseitigen Validierung.
Bonusaufgabe: Findest du einen Weg wie du die Validierungslogik des Servers im Client wiederverwenden kannst?
Tipp: Dies kann z.B. via Ajax und einem entsprechenden Controller und Eintrag in der Routing-Tabelle passieren.
$("form").on('submit', function(){ $.ajax({url: "seite_xy/validator", // Funktion, welche den Submit entweder zulässt (success:) oder die zurückgegebenen Fehlermeldungen platziert (error:). }); });Wende die gelernten Techniken nun auf dein eigenes Formular an.
Nutze die folgenden Test-Eingaben um zu überprüfen, ob dein Formular vor XSS-Attacken geschützt ist. Nutze zum Testen einen anderen Browser als Chrome, da dieser einfache XSS-Attacken automatisch verhindert.
Stelle sicher, dass keine Alert-Boxen erscheinen, also kein JavaScript eingeschleust werden kann.
Stelle sicher, dass kein Teil deiner Eingabe bei der Ausgabe ausserhalb der input Felder erscheint. Es muss alles, was du in ein Feld eingegeben hast, wieder 1:1 darin erscheinen. Dein HTML darf also nicht durch Benutzereingaben verändert werden!
<script>alert("XSS")</script>"><script>alert("XSS")</script>>'>"><img src=x onerror=alert('XSS')>.Füge einen neuen Routes-Eintrag mit Pfad "/" ein:
Erstelle einen Controller NavigationController und eine Methode home() und eine View home.view.php dazu. Erstelle erst mal manuell ein Array $navigation mit allen bisherigen Links, die du als Menü haben willst:
Gib alle Links als Liste in home.view.php aus:
Fülle nun das Array $navigation dynamisch aus der Routes-Tabelle. Für lesbare Beschriftungen kannst du z.B. die Stringfunktionen explode(), str_replace() und ucfirst() benutzen.
Prüfe nun in der View, ob die Links vernünftige Beschriftungen haben und die Links korrekt funktionieren.
Füge einen neuen Routes-Eintrag hinzu und schau, ob das neue Menü korrekt erscheint und funktioniert.
$routes = [
'/' => 'NavigationController@home', /* NEU */
'/aufgabe/spam' => 'AufgabenController@spam',
// ... alle weiteren bestehenden Routes ...
];class NavigationController {
public function home() {
$navigation = [
'/' => 'Navigation - Home',
'/aufgabe/spam' => 'Aufgaben - Spam',
// ... weitere Menüpunkte
];
}
}<ul>
<?php foreach($navigation as $url => $label) : ?>
<li><a href=".<?= $url ?>"><?= $label ?></a></li>
<?php endforeach; ?>
</ul>class NavigationController {
public function home() {
global $routes;
$navigation = [];
foreach ($routes as $url => $ControllerMethod) {
// Aus $ControllerMethod ein lesbares Link-Label erzeugen.
// Aus 'AufgabenController@spam' soll z.B. 'Aufgaben - Spam' werden.
$navigationLabel = '...';
$navigation[$url] = $navigationLabel;
}
}Einige der grössten Websites der Welt...
Facebook (HHVM)
Wikipedia (HHVM)
Flickr
Tumblr
Yahoo
Sourceforge
Mailchimp
Fotolia
Imgur
Uber
...
Und natürlich die grossen CMS...
Wordpress
Drupal
Typo3
Joomla
...
PHP wird auf etwa 244 Millionen Websites eingesetzt (Stand: Januar 2013), wird auf über 78% aller Websites als serverseitige Programmiersprache verwendet (Stand: Februar 2022) und ist damit die am häufigsten verwendete Sprache zum Erstellen von Websites - Tendenz steigend. Die Programmiersprache ist sie bei den meisten Webhostern vorinstalliert.
PHP ist eine serverseitige Scriptsprache und erlaubt dem Entwickler Logik in seine Webpages zu integrieren.
Mit PHP können Daten von verschiedensten Quellen verarbeitet werden:
Datenbanken
Dateisystem
HTTP-Requests
Entfernte APIs
u. v. m.
PHP setzt sich aus der Scriptsprache und dem dazugehörigen Interpreter zusammen.
Der Webserver (in unserem Fall Apache) verfügt über ein PHP-Modul. Dieses Modul versteht PHP-Code, es kann ihn also interpretieren.
Beim Aufruf einer Webpage durch den Besucher lädt der Webserver das gewünschte Script und schickt es an das PHP-Modul. Dort wird der PHP-Code verarbeitet. Dabei können Script-Ausgaben entstehen. Diese Ausgaben werden in die aufgerufene Seite eingefügt und anschliessend an den Browser des Besuchers gesendet.
Hier ein Beispiel eines PHP-Scripts, wie es auf dem Server abgespeichert ist. Mittels dem echo-Konstrukt wird HTML-Code direkt in das Dokument ausgegeben.
Wir rufen das Script über den Link http://localhost/modul-307/script.php im Browser auf. Es wird nun vom PHP-Modul verarbeitet.
Nach der Verarbeitung wird das Dokument ohne den ursprünglichen PHP-Code an den Browser gesendet. Nur dessen Ausgabe ist noch vorhanden.
Das resultierende Dokument wird vom Browser also wie normales HTML behandelt. Dass darin einmal PHP-Code vorhanden war, weiss dieser nicht.
PHP ermöglicht es, die Ausgabe von HTML-Code an Bedingungen zu knüpfen. So können dynamische Dokumente erstellt werden.
In PHP steht eine grosse Auswahl an internen Funktionen zur Verfügung. Eine komplette nach Thema gegliederte Liste befindet sich in der PHP-Dokumentation:
https://secure.php.net/manual/de/funcref.php
Wir werden an dieser Stelle nur auf einige Beispiele eingehen und uns während den Übungsaufgaben mit weiteren Funktionen vertraut machen.
Eine Funktion akzeptiert üblicherweise eines oder mehrere Argumente, verarbeitet diese und gibt anschliessend einen Rückgabewert zurück.
$wert = funktionsname($argument1, $argument2);var_dumpEine nützliche Funktion zur Ausgabe des Wertes und Typs einer Variable ist var_dump. Die Funktion wird in erster Linie zum debuggen verwendet.
Zur Manipulation von Strings stehen zur Verfügung.
Ersetze die Funktion print_r in der Datei index.php mit der Funktion var_dump und beobachte den Unterschied.
Umschliesse nun noch die var_dump Funktion mit der internen Funktion die und beobachte was passiert: die(var_dump($vehicles));
Überlege dir, für was ein solches Konstrukt nützlich sein könnte.
Erstelle eine Website, auf der via Local File Inclusion deine MySQL-Zugangsdaten ausgelesen werden können.
Erstelle eine einfache Website, die über den GET-Parameter content dynamisch Inhalte anzeigt. Bsp: index.php?content=home, index.php?content=contact
Die Inhaltsdateien mit den Texten sollen in einem Ordner contents abgelegt werden. Es existiert also folgende Verzeichnisstruktur:
Erstelle nun einen Ordner geheim und speichere deine MySQL-Zugangsdaten in die Datei mysql.txt ab.
Wie kann ein Angreifer nun an deine Zugangsdaten gelangen (ohne sie direkt im Browser aufzurufen)? Wie kannst du dieses Problem verhindern? Was kannst du über diese Lücke sonst noch anstellen? Welche Dateien auf deinem Computer müssen immer geheim bleiben?
Schreibe ein PHP-Script, welches den Songtext des Liedes «99 Bottles of Beer» ausgibt: http://www.99-bottles-of-beer.net/lyrics.html
Nutze zur Strukturierung des Codes die erstellte MVC-Umgebung:
index.php => Route
BeerController.php => Logik
beer.view.php => Ausgabe
Entwickle das Script immer nur so weit, bis alle Komponenten des jeweiligen Schrittes komplett erfüllt werden. Erweitere es anschliessend, damit der nächste Schritt erfüllt wird.
Das Script soll einen Vers für alle 99 Flaschen ausgeben. Nach der ersten Zeile soll ein Zeilenumbruch ausgegeben werden. Nach der zweiten Zeile sollen zwei Zeilenumbrüche folgen.
99bottles of beer on the wall,99bottles of beer. Take one down and pass it around,98bottles of beer on the wall.
98bottles of beer on the wall,98bottles of beer. Take one down and pass it around,97bottles of beer on the wall. ...
Wenn nur noch eine Flasche übrig ist, soll das Wort bottles in der Einzahl als bottle ausgegeben werden.
... 2
bottlesof beer on the wall, 2bottlesof beer. Take one down and pass it around, 1bottleof beer on the wall.1
bottleof beer on the wall, 1bottleof beer. Take one down and pass it around, 0bottlesof beer on the wall.
Wenn keine Flaschen mehr vorhanden sind, soll der letzte Vers ausgegeben werden:
No more bottles of beer on the wall, no more bottles of beer. Go to the store and buy some more, 99 bottles of beer on the wall.
Lagere die doppelt ausgeführte Abfrage für die Singular-/Plural-Form des Wortes bottle in eine Funktion getWord aus.
Mögliche Lösungen zu den Aufgaben werden dir vom Kursleiter bereitgestellt. Natürlich ist die Ausgabe des Scripts entscheidend, nicht der Code dazu.
Es sind also mehrere Lösungen möglich, solange durch die richtige Logik die gewünschte Ausgabe erzeugt wird.
Die Sicherheit sollte beim Entwickeln einer Webapplikation immer höchste Priorität haben. Als Entwickler hast du die Verantwortung dafür zu sorgen, dass deine Kunden und deren Daten bei dir gut aufgehoben sind.
Macht euch in 2er Gruppen mit einer der untenstehenden Sicherheitslücken vertraut.
Erstellt ein Proof of Concept mit dem ihr aufzeigen könnt, wie man die Sicherheitslücke ausnutzen kann
Erstellt eine kurze Präsentation um eure Sicherheitslücke der Klasse vorzustellen
Zeigt auf, wie man eure Sicherheitslücke schliessen kann
Erstelle ein Login-Formular mit einer SQL-Injection Sicherheitslücke. Ein Angreifer soll die Möglichkeit haben, sich ohne Passwort als admin anmelden zu können.
Nutze als Basis für dein Proof of Concept den Inhalt der sql.zip Datei.
Erstelle eine Datenbank mit einer users Tabelle. Die Tabelle benötigt nur die Spalten id, username und password.
Erstelle in der Datenbank einen Benutzer admin mit Passwort password.
Finde heraus, was ein Angreifer in euer Formular eingeben müsste, um sich ohne korrektes Passwort als Admin anmelden zu können. Wie kannst du das Problem verhindern? Kriegst du es hin, dass sogar die komplette users Tabelle gelöscht werden kann?
Du findest den für das Login zuständige Code in der Datei LoginController.php.
Um gespeicherte Daten in der Datenbank zu löschen, wird die instanzierte Datenbankverbindung (PDO) verwendet.
Aus Gründen der Sicherheit führen wir die Abfrage nie direkt aus, sondern bereiten diese zuerst mit der prepare-Methode vor.
Im Prepare-Statement werden die effektiven Werte noch nicht eingetragen, sondern Platzhalter an deren Stelle positioniert.
$statement = $pdo->prepare('DELETE FROM `users` WHERE id = :id');Erst in einem zweiten Schritt werden mit der bindParam-Methode Werte für die Platzhalter definiert.
$statement->bindParam(':id', 50);Anschliessend kann das fertige Statement nur noch durch die execute-Methode ausgeführt werden, damit der gewünschte Eintrag in der Datenbank gelöscht wird.
$statement->execute();Erstelle nun eigenständig eine Methode im Controller TaskController.php an der eine Task-Id übergeben werden kann. Die Methode im Controller nimmt die Id entgegen und löscht den dazugehörigen Eintrag. Anschliessend wir der Benutzer wieder auf die Task-Übersicht umgeleitet.
Sorge dafür, dass die Id nicht von Hand eingetragen werden muss. Neben dem Bearbeiten-Link soll somit ebenfalls noch ein Löschen-Link positioniert werden.
JavaScript (kurz JS) ist eine Skriptsprache, die ursprünglich für dynamisches HTML in Webbrowsern entwickelt wurde, um Benutzerinteraktionen auszuwerten, Inhalte zu verändern, nachzuladen oder zu generieren und so die Möglichkeiten von HTML und CSS zu erweitern. Heute findet JavaScript auch außerhalb von Browsern Anwendung, so etwa auf Servern und in Microcontrollern.
JavaScript wurde erstmals am 18. September 1995 mit der Vorversion des Navigator 2.0 veröffentlicht (unter dem Namen LiveScript, entwickelt von Brendan Eich).
JavaScript ist eine Implementierung des ECMAScript-Standards und wird von allen gängigen Webbrowsern unterstützt.
ECMAScript-Implementierungen wie JScript oder die Node.js Plattform ermöglichen die Anwendung von JavaScript auch serverseitig.
Die 6. Version des ECMAScript-Standards (ES6 oder ECMAScript 2015) wurde 2015 verabschiedet. Der Browser-Support für die neuen Funktionen hinkt hier jedoch etwas hinterher. Die 5. Version des Standards (ES5) ist . Funktionen und stehen nur teilweise zur Verfügung.
Um gespeicherte Daten in der Datenbank zu ändern, wird ebenfalls die instanzierte Datenbankverbindung (PDO) verwendet.
Aus Gründen der Sicherheit führen wir die Abfrage nie direkt aus, sondern bereiten diese zuerst mit der prepare-Methode vor.
Im Prepare-Statement werden die effektiven Werte noch nicht eingetragen, sondern Platzhalter an deren Stelle positioniert.
Erst in einem zweiten Schritt werden mit der bindParam-Methode Werte für die Platzhalter definiert.
Anschliessend kann das fertige Statement nur noch durch die execute-Methode ausgeführt werden, damit die Änderung in die Datenbank gespeichert werden.
Verbindungen werden durch das Erstellen von Instanzen der PDO-Basisklasse erzeugt. Der Konstruktor der PDO-Klasse erwartet dabei Parameter zur Angabe der Datenbankquelle, den Benutzer und das Passwort (falls vorhanden).
Die Datenbankquelle besteht aus einem definierten Connection-String. Darin wird angegeben, was für eine Art Quelle wir ansprechen möchten (mysql), was der Host dieser Datenbank ist (host=localhost) und wie die Datenbank heisst (meinedatenbank):
Um mit PHP auf eine Datenbank zuzugreifen, verwenden wir (kurz PDO).
Die von PHP zur Verfügung gestellte PDO-Klasse stellt ein einheitliches Interface zur Anbindung unterschiedlicher Datenbanktypen bereit.
Lade das herunter.
Erstelle den neuen Controller TaskController.php und schaue via Routes-Eintrag, dass diese bei der URL
<!DOCTYPE html>
<html>
<head>
<title>Ein PHP-Beispiel</title>
</head>
<body>
<?php
echo '<p>Diese Ausgabe wurde von PHP generiert.</p>';
?>
</body>
</html><!DOCTYPE html>
<html>
<head>
<title>Ein PHP-Beispiel</title>
</head>
<body>
<p>Diese Ausgabe wurde von PHP generiert.</p>
</body>
</html><h2>Seite</h2>
<?php include($_GET['content']); ?>/
|-- contents/
|-- contents/home.txt
|-- contents/contact.txt
|-- index.php- Aufagbe 1 | bearbeiten | löschen
- Aufgabe 2 | bearbeiten | löschen
...Erstelle eine neue View /app/Views/template.view.php mit einem HTML-Gerüst und Ausgabe der Variable $content:
Erstelle eine neue Klasse View in /core/view.php und lade sie in /core/bootstrap.php. Erstelle darin eine Methode View::render($template, $view), welche folgendes tut:
Content der View rendern und nicht direkt ausgeben, sondern in eine Variablen $content zwischenspeichern. Ein Beispiel, wie das geht, findest du z.B. auf https://stackoverflow.com/questions/8510532/using-include-within-ob-start#answer-8510592
Die Template-View laden und von ihr den Content innerhalb des Templates ausgeben lassen.
Vor beiden Schritten solltest du testen, ob es die angegebene Datei auch gibt (file_exists());
Deine bestehenden Aufgaben entsprechend anpassen:
In den bisherigen Views kannst du alles rauslöschen, was nun in template.view.php vorhanden ist.
Im Controller wird nun nicht mehr die View direkt eingefügt (require ...), sondern eine neue View erzeugt und die Methode render() aufgerufen:
Füge in template.view.php VOR dem Content die Navigation aus Aufgabe 7 ein (horizontal).
Klicke alle Aufgaben mal durch und schau, ob alles noch richtig funktioniert.
SpamController.php => Logik
spam.view.php => Ausgabe
Entwickle das Script immer nur so weit, bis alle Komponenten des jeweiligen Schrittes komplett erfüllt werden. Erweitere es anschliessend, damit der nächste Schritt erfüllt wird.
Verwende folgendes Array als Input:
Schreibe ein Script, welches jeden Input-Satz durchläuft und mit Hilfe der Funktion strpos überprüft, ob das Wort SPAM im Satz enthalten ist.
Erzeuge folgende Ausgabe:
Unser Spamfilter scheint den letzten Satz nicht als Spam zu erkennen, obwohl das Wort spam darin vorkommt. Weisst du wieso? Korrigiere dein Script, damit die folgende korrigierte Ausgabe generiert wird.
Tipp: In der PHP-Dokumentation gibt es zu den meisten Funktionen eine nützliche «Siehe auch» Rubrik, in der auf verwandte Funktionen verwiesen wird.
Unser Spamfilter könnte noch etwas besser werden... Angebote von Partnerbörsen interessieren uns nicht und sollen neu auch als Spam eingestuft werden.
Wir erweitern unseren Filter also um das Wort Singles. Folgende neue Ausgabe soll generiert werden:
Mögliche Lösungen zu den Aufgaben werden dir vom Kursleiter bereitgestellt. Natürlich ist die Ausgabe des Scripts entscheidend, nicht der Code dazu.
Es sind also mehrere Lösungen möglich, solange durch die richtige Logik die gewünschte Ausgabe erzeugt wird.
Als nächstes wollen wir eine einen bestehenden Task aus der Datenbank abrufen, ändern und speichern.
Erstelle die neue Controller Action TaskController@edit und schaue via Routes-Eintrag, dass diese bei der URL /edit aufgerufen wird. Ebenfalls kannst du die neue View edittask.view.php erstellen, welche am Schluss des neuen Controllers geladen wird. In dieser View wollen wir den gewünschten Eintrag anpassen.
Um die Daten zu bearbeiten benötigen wir wiederum ein Formular mit dem Feld title in der neuen Edit-Task-View edittask.view.php. In dieses Feld sollen die bestehenden Daten geladen werden, damit diese vom Benutzer bearbeitet werden können.
Die id des zu bearbeitenden Eintrages kannst du vorerst noch manuell per GET-Variable übergeben:
Erweitere nun deinen Controller TaskController, dass der zur Id passende Eintrag abgerufen und der Titel im Feld title in der View angezeigt wird.
Nachdem die Daten durch den Benutzer im Feld bearbeitet wurden, sollen diese in die Datenbank gespeichert werden.
Dazu erstellen wir einen neuen Controller TaskController@update inkl. /update Route, welche wir gleich als Ziel für unser Formular eintragen. Sofern der neue Controller aufgerufen wird und Daten vorhanden sind, soll eine Datenbankverbindung hergestellt und die Daten per Prepare-Statement in die Datenbank übergeben werden.
Nach der erfolgreichen Speicherung des Eintrages, soll der Benutzer wieder zurück auf die Task-Liste gelangen.
Damit die Id nicht immer manuell eingegeben werden muss, erstelle einen Bearbeiten-Link hinter jeder Task auf der Taskübersicht, welche automatisch den einen Link zur URL /edit?id=2 generiert.
Sorge nun dafür, dass nicht nur der Name der Aufgabe bearbeitet werden kann, sondern auch der Status completed.
Wenn ein Fehler bei der Verbindung festgestellt wird, wird eine PDOException ausgeworfen. Diese Ausnahme kann mit dem try-catch-Konstrukt für das Debugging abgefangen und angezeigt werden.
Über den vierten Parameter können weitere Verbindungsattribute definiert werden.
Mit dem Wert PDO::ERRMODE_EXCEPTION für PDO::ATTR_ERRMODE wird sichergestellt, dass bei fehlerhaften Datenbankabfragen eine Exception generiert wird. Wird diese Option nicht gesetzt, werden dir allfällige Fehler nicht angezeigt.
Der unter dieser Option aufgeführte Befehl wird bei jedem Verbindungsaufbau zur Datenbank ausgeführt. Mit SET NAMES utf8; teilen wir dem Server mit, dass unsere Kommunikation im utf8 Zeichensatz erfolgen wird.
Als erstes richten wir in unserem Controller TaskController.php die Verbindung zur eben erstellen Datenbank ein:
/taskstask.view.phpRufe anschliessend phpMyAdmin über folgende URL auf.
Erstelle eine Datenbank mit dem Namen tasklist. Darin wiederum legst du die Tabelle tasks an, mit den folgenden drei Spalten: id, title und completed
Über den Reiter Einfügen im phpMyAdmin, kannst du manuell 2-3 Aufgaben erfassen.
Nun sind wir bereit, um mit dem Projekt Task-Liste zu starten.
new PDO();http://localhost/phpmyadmin/$bool = true;
$integer = 20;
$string = 'Hallo';
var_dump($bool);
// bool(true)
var_dump($integer);
// int(20)
var_dump($string);
// string(5) "Hallo"$string = 'Hallo';
echo strtoupper($string);
// HALLO
echo strtolower($string);
// hallo
echo strlen($string);
// 5
echo substr($string, 2, 2);
// ll
echo strpos($string, 'll');
// 2
echo strrev($string);
// ollaH/
|-- geheim/
|-- geheim/mysql.txt
|-- contents/
|-- contents/home.txt
|-- contents/contact.txt
|-- index.php<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Meine Seite</title>
<!-- <base ...>: Relative urls start at index.php's directory -->
<base href="<?= dirname($_SERVER['SCRIPT_NAME']); ?>/" />
<link rel="stylesheet" href="public/css/app.css">
</head>
<body>
<?= $content ?>
<script src="public/js/app.js"></script>
</body>
</html>class View
{
/**
* Render a view and display the result with the given template
*
* @param $template string - e.g. "app/Views/template.view.php"
* @param $view string - e.g. "app/Views/spam.view.php"
*/
public function render(string $template, string $view)
{
// Render given `$view` and don't send result to client, but store it into the variable `$content`.
// Dein Code hier...
// Load template view
// Dein Code hier...
}
}class XYController
{
public function xy()
{
// Do what ever needs to be done before loading the view..
// Load view and render with template (replaces require 'app/Views/aufgabe-xy.view.php')
$view = new View();
$view->render('app/Views/template.view.php', 'app/Views/aufgabe-xy.view.php');
}
}$input = [
'Neue Aktionen von Ihrem Computerteile-Händler.',
'Vergrössern Sie jetzt ihr SPAM mit SPAM.',
'SPAM kann ihnen dabei helfen wieder ruhig zu schlafen.',
'Kennen Sie Justin Bieber? Finden Sie andere Singles in Ihrer Nähe.',
'Wenn spam zum Problem wird, haben Sie ein Problem.'
];Satz 0 ist OK
Satz 1 ist SPAM
Satz 2 ist SPAM
Satz 3 ist OK
Satz 4 ist OKSatz 0 ist OK
Satz 1 ist SPAM
Satz 2 ist SPAM
Satz 3 ist OK
Satz 4 ist SPAMSatz 0 ist OK
Satz 1 ist SPAM
Satz 2 ist SPAM
Satz 3 ist SPAM
Satz 4 ist SPAM$statement = $pdo->prepare('UPDATE `users` SET role = :role WHERE id = :id');$statement->bindParam(':role', 'admin');
$statement->bindParam(':id', 50);$statement->execute();http://localhost/deinProjekt/tasks/edit?id=3// Datenbankquelle = mysql:host=localhost;dbname=meinedatenbank
// Benutzer = root
// Passwort = ADfk3lox!4foi4hd
$pdo = new PDO('mysql:host=localhost;dbname=meinedatenbank', 'root', 'ADfk3lox!4foi4hd', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
]);mysql:host=localhost;dbname=meinedatenbanktry {
$pdo = new PDO('mysql:host=localhost;dbname=meinedatenbank', 'root', 'ADfk3lox!4foi4hd', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
]);
} catch (PDOException $e) {
die('Keine Verbindung zur Datenbank möglich: ' . $e->getMessage());
}try {
$pdo = new PDO(...);
} catch (PDOException $e) {
die('Keine Verbindung zur Datenbank möglich: ' . $e->getMessage());
}Über sogenannte «Transpiler» wie Babel.js können diese neuen Funktionen jedoch heute schon verwendet werden. Die neuen Funktionen werden dabei vom Transpiler so umgeschreiben, dass sie der «alten» ECMAScript 5 Syntax entsprechen, die in allen Browsern unterstützt wird.
Java is to JavaScript as car is to carpet.
«Java hat mit JavaScript genau so viel zu tun, wie ein Auto mit einem Teppich.»
Trotz der Ähnlichkeit des Namens und der Syntax hat JavaScript nichts mit Java zu tun. Es handeln sich um zwei komplett unterschiedliche Sprachen.
JavaScript ist eine Scriptsprache, die clientseitig oder seit einigen Jahren auch serverseitig Anwendung findet.
Wir werden uns in diesem ÜK in erster Linie auf die clienseitige Anwendung fokussieren.
JavaScript wird im Browser ausgeführt. Dies ermöglicht die Interaktion mit einer Website ohne für alle Änderungen eine neue Version der Seite vom Server anfordern zu müssen. So können zum Beispiel dynamisch Elemente erstellt oder entfernt werden. Eine weitere Anwendungsmöglichkeit ist die Validierung von Formularen, indem die Eingaben validiert werden, bevor das Formular überhaupt abgeschickt wird.
Mit dem AJAX-Konzept wurde zudem die asynchrone Datenübertragung zwischen Client und Server ermöglicht. So kann zum Beispiel von JavaScript eine Anfrage an den Server gesendet werden um Daten zu laden, auf deren Basis dann nur ein Teil der Website verändert wird. So muss für die teilweise Aktualisierung einer Seite nicht die komplette Webpage erneut vom Server heruntergeladen werden.
Mit der Entwicklung von JavaScript-Frameworks wie Angular, React oder Ember wurde das Erstellen von Single-Page-Applications (SPA) populär. Dabei wird nur beim ersten Aufruf einer Webpage die komplette Seite an den Browser gesendet. Alle weiteren Daten werden dann via JavaScript «häppchenweise» vom Server angefordert und meistens clientseitig gerendert. So kann eine komplette Webapplikation in einer einzigen Webpage (Single Page) umgesetzt werden.
JavaScript wird in den meisten Fällen von einer «JavaScript Engine» ausgeführt. Je nach Browser wird eine andere Engine zur Ausführung von JavaScript verwendet. Daher ist es möglich, dass Code, der in Browser A funktioniert, in Browser B Fehler erzeugt.
Die bekanntesten JavaScript Engines sind
V8 (Google Chrome, node.js)
Spidermonkey (Firefox)
JavaScriptCore (Safari)
Chakra (IE, Edge)
Das Document Object Model ist die Schnittstelle, die der Browser für die Interaktion mit dem HTML-Dokument bereitstellt.
Wird HTML-Code an den Browser gesendet, wird dieser Code geparsed. Daraus erstellt der Browser das DOM. Das DOM enthält eine Representation des Dokuments als Baumstruktur.
Wenn du in deinem Browser die Option «Quelltext anzeigen» auswählst, siehst du den HTML-Code, der vom Server an den Browser gesendet wurde. Öffnest du die Entwicklertools, arbeitest du mit dem DOM. Dein Browser versucht Fehler im HTML-Code automatisch zu beheben. Daher ist es möglich, dass sich der HTML-Code vom letzendlichen DOM unterscheidet!
Ein primärer Anwendungszweck von JavaScript im Browser ist die Manipulation des DOM. Via JavaScript können die einzelnen Attribute eines Objektes im DOM ausgelesen und verändert werden. Das DOM enthält Position, Grösse, CSS-Styles und diverse andere Attribute zu jedem Element.
Du hast eine App für Smartphones entwickelt, mit deren Hilfe man eine Todo-Liste führen kann.
Die Todo-Liste wird auf dem Smartphone bearbeitet. Es können Einträge erstellt und gelöscht werden.
Beim Speichern der Änderungen auf dem Smartphone, wird der neue Datenbestand der Todo-Liste an ein PHP-Script auf deinem Server gesendet, welches die zentrale Datenbank aktualisieren soll.
Dieses Script soll nun ermitteln, welche Einträge in der Datenbank neu eröffnet werden müssen und welche Einträge zu löschen sind.
Nutze folgende Arrays als Daten:
Nutze zur Strukturierung des Codes die erstellte MVC-Umgebung:
TodoController.php => Logik todo.view.php => Ausgabe
Entwickle das Script immer nur so weit, bis alle Komponenten des jeweiligen Schrittes komplett erfüllt werden. Erweitere es anschliessend, damit der nächste Schritt erfüllt wird.
Erstelle das PHP-Script, welches die Datenbestände abgleicht und folgende Ausgabe daraus generiert:
Erweitere das Script so, dass es dir zusätzlich zu vorherigem Output auch noch anzeigt, welche Aufgaben unverändert geblieben sind.
Du hast jetzt vermutlich redundanten (wiederholenden) Code für die Ausgabe der Aufgaben in deinem Script. Da für alle drei Ausgaben die gleiche Logik (Titel + Liste) besteht, bietet sich eine Funktion als Lösung an.
Erstelle die Funktion printTasks die über folgende drei Aufrufe die gleiche Ausgabe wie in Schritt 2 erzeugt.
Mögliche Lösungen zu den Aufgaben werden dir vom Kursleiter bereitgestellt. Natürlich ist die Ausgabe des Scripts entscheidend, nicht der Code dazu.
Es sind also mehrere Lösungen möglich, solange durch die richtige Logik die gewünschte Ausgabe erzeugt wird.
Erstelle ein Gästebuch mit einer XSS-Sicherheitslücke. Die Sicherheitslücke soll es einem Angreifer möglich machen, alle Besucher auf google.com umzuleiten, die das Gästebuch ansehen möchten.
Nutze als Basis für dein Proof of Concept den Inhalt der xss.zip Datei.
Versuche herauszufinden, was ein Besucher für eine Nachricht eingeben muss, um alle zukünftigen Besucher des Gästebuchs auf google.com umzuleiten. (Tipp: JavaScript spielt hierbei eine wichtige Rolle, Stichwort )
Wie kannst Du dieses Problem verhindern? Was sind andere «Schäden», die ein Angreifer mit dieser Attacke anrichten kann?
Als Webserver verwenden wir Apache mit PHP 7. Um die Installation zu vereinfachen, greifen wir auf XAMPP zurück.
XAMPP ist ein Software-Paket, welches die Installation von Apache mit PHP auf Linux, Mac und Windows vereinfacht. XAMPP sollte nur für Entwicklungszwecke und nicht für den produktiven Betrieb verwendet werden.
Die Login-Funktion für das CMS deines Lehrbetriebes wurde von einem Partnerunternehmen in Indien entwickelt. Die Funktion tut was sie soll. Die Code-Qualität hingegen lässt zu wünschen übrig.
Die Login-Funktion überprüft, ob eine Kombination aus Benutzername und Passwort zu einem registrierten Benutzer gehört. Zudem wird überprüft, ob der Benutzer eine Rolle als Administrator oder Publisher hat. Nur diese Benutzerrollen dürfen sich einloggen.
Schreibe den Code der Funktion login
Um eine Datenbank-Abfrage auszuführen, können wir nur die instanzierte Datenbankverbindung (PDO) nutzen um eine reguläre Abfrage an die Datenbank zu senden.
Aus Gründen der Sicherheit führen wir die Abfrage nie direkt aus, sondern bereiten diese zuerst mit der prepare-Methode vor.
Erst in einem zweiten Schritt wird der Datenbank-Befehl mit der execute-Methode effektiv ausgeführt.
Um Daten in die Datenbank über ein Formular zu speichern, können wir wiederum die instanzierte Datenbankverbindung (PDO) nutzen.
Aus Gründen der Sicherheit führen wir die Abfrage nie direkt aus, sondern bereiten diese zuerst mit der prepare-Methode vor.
Im Prepare-Statement werden die effektiven Werte noch nicht eingetragen, sondern Platzhalter an deren Stelle positioniert.
Erst in einem zweiten Schritt werden mit der bindParam-Methode Werte für die Platzhalter definiert.
Anschliessend kann das fertige Statement nur noch durch die execute-Methode ausgeführt werden, damit die Daten in die Datenbank geschrieben werden.
Kopiere den Code von Karl's Website auf deinen lokalen Webserver, damit die Website im Browser aufgerufen werden kann.
Sieh dir die Website von Karl an. Das Design und HTML-Markup brauchst du nicht zu beurteilen. Zeige ihm jedoch auf, wie du Sicherheitslücken in seinem Code ausnutzen kannst!
Zeige ihm dann natürlich auch, wie er diese Lücken schliessen kann...
Lies die Zugangsdaten zu Karl's Hosting mit deinem Web-Browser aus.
Leite alle Besucher des Gästebuchs auf http://google.com um. (Tipp: Du kannst die Datei database/guestbook.txt einfach löschen, um das Gästebuch zu leeren)
Zusatzaufgabe: Welchen Link auf seine eigene Website musst du Karl per Email senden, damit du sein Session-Cookie an einen von dir kontrollierten Server senden könntest, sobald er auf den Link klickt? Frage Google, wie man dies tun könnte und für was dies nützlich wäre.
Als nächstes wollen wir eine neue Task erfassen und in die Datenbank speichern. Erstelle dazu ein Formular mit dem Feld title in deiner Task-View.
Erstelle die neue Controller-Methode TaskController@create und schaue via Routes-Eintrag, dass dieser bei der URL /create aufgerufen wird. Die Daten aus deinem Formular sollen per POST übergeben werden und als Ziel die /create URL haben.
Erstelle nun in deinem neuen Controller eine Überprüfung, ob Daten per POST gesendet wurden. Sofern Daten vorhanden sind, soll eine Datenbankverbindung hergestellt und die Daten per Prepare-Statement in die Datenbank gespeichert werden.
Nach der erfolgreichen Speicherung der Daten, soll der Benutzer wieder zurück auf die Task-Liste gelangen.
Unsere kleine Applikation erfüllt zwar ihren Zweck, verstösst jedoch gegen das DRY-Prinzip. So wird die Datenbankverbindung mehrfach im Code aufgeführt. Da es sich um den identischen Code handelt, macht es Sinn, diesen an eine zentrale Funktion auszulagern.
$statement = $pdo->prepare('INSERT INTO `users` (name, role) VALUES (:name, :role)');$statement->bindParam(':name', 'Silvana');
$statement->bindParam(':role', 'admin');fetchAll-Methode genauer beschrieben.Die fetchAll-Methode von PDO gibt ein Array zurück mit sämtlichen Reihen aus dem Resultat der Datenbankabfrage. Wiederum werden die einzelnen Reihen als eigenes Array (verschachtelt im Resultate-Array) zurückgegeben.
Wurde kein Resultat anhand der Datenbank-Abfrage gefunden, wird ein leeres Array zurückgegeben.
Betrachtet man die Array-Struktur, sieht man, dass sämtliche Werte doppelt aufgeführt sind. Einmal als reguläres Array und einmal als assoziatives Array.
Der Grund dafür liegt darin, dass die Standard-Einstellung der fetchAll-Methode auf PDO::FETCH_BOTH ist. Diese gibt die Resultate immer in den beiden Varianten zurück. Möchte man dies ändern, kann die Ausgabe der Resultate mit verschiedenen Einstellungen beeinflusst werden: PHP-Dokumentation.
Als nächstes wollen wir sämtliche Tasks in der Tabelle abrufen und auflisten.
Erstelle nun ein Prepare-Statement, welches sämtliche Tasks aus der Datenbank abruft und mit einem var_dump-Konstrukt im Controller ausgibt.
Anstatt das Array mit dem var_dump-Konstrukt auszugeben, möchten wir diese an die View übergeben und in einer ungeordneten Liste angezeigen.
$datenbank = [
'Rasen mähen',
'Einkaufen',
'Pflanzen giessen',
'Katze füttern',
'Staubsaugen'
];
$smartphone = [
'Staubsaugen',
'Einkaufen',
'Hausaufgaben erledigen',
'Ferien buchen'
];Abgeschlossene Aufgaben:
- Rasen mähen
- Pflanzen giessen
- Katze füttern
Neue Aufgaben:
- Hausaufgaben erledigen
- Ferien buchen...
Unveränderte Aufgaben:
- Staubsaugen
- Einkaufenecho printTasks('Abgeschlossene Aufgaben', $abgeschlossen);
echo printTasks('Neue Aufgaben', $neu);
echo printTasks('Unveränderte Aufgaben', $unbearbeitet);$statement->execute();$statement = $pdo->prepare('SELECT * FROM users');$statement->execute(); // Abfrage wird ausgeführt$result = $statement->fetchAll();
var_dump($result);Array // Array von sämtlichen Resultaten
(
[0] => Array // Verschachteltes Array repräsentiert eine Reihe des Suchresultats
(
[name] => Peter
[0] => Peter
[role] => admin
[1] => admin
)
[1] => Array // Verschachteltes Array repräsentiert eine Reihe des Suchresultats
(
[name] => Petra // Ausgabe als Wert eines assoziativen Arrays
[0] => Petra // Ausgabe als Wert eines regulären Arrays
[role] => user
[1] => user
)
)- Für die Mathe-Prüfung lernen
- PHP repetieren
- JavaScript repetieren
- Einkaufen gehen
...Führe das XAMPP-Setup aus. Du findest den Installer auf dem Austausch-Laufwerk.
Es reicht aus, dass Du nur die folgenden Komponenten installierst: Apache, PHP, MySQL.
Installiere XAMPP ins Verzeichnis C:\xampp
Deaktiviere die Option «Learn more about Bitnami for XAMPP»
Das XAMPP Control Panel startet nach erfolgreicher Installation automatisch. Falls nicht, findest Du es im Startemenü.
Nach dem Starten wird das CP im Systemtray neben der Uhr angezeigt. Klicke das Icon an um das Fenster zu öffnen.
Das CP ermöglicht Dir, die XAMPP-Dienste zu starten und zu stoppen.
Stelle sicher, dass Du den Apache-Dienst erfolgreich starten und stoppen kannst.
Besuche in Deinem Browser die URL http://localhost. Wenn alles funktioniert, solltest Du nun die XAMPP-Begrüssungsseite sehen.
Unter C:\xampp\htdocs befindet sich der so genannte DocumentRoot deines Servers. Alle Dateien aus diesem Verzeichnis sind direkt via http://localhost zugänglich.
Lösche alle mitgelieferten Dateien im htdocs Verzeichnis.
Lade http://localhost in Deinem Browser neu - du solltest nun eine Fehlermeldung erhalten.
Erstelle den Ordner modul-307 und darin die Datei test.txt.
Schreibe etwas in die Textdatei.
Du kannst diese Datei nun via http://localhost/modul-307/test.txt aufrufen.
Wenn Du über die URL ein Verzeichnis aufrufst, ohne eine Datei anzugeben, wird Apache automatisch eine der folgenden Dateien laden, sofern diese existiert:
index.php index.pl index.cgi index.asp index.shtml index.html index.htm default.php default.pl default.cgi default.asp default.shtml default.html default.htm home.php home.pl home.cgi home.asp home.shtml home.html home.htm
Benenne Deine test.txt in index.html um und rufe im Browser http://localhost/modul-307/ auf. Die index.html wird jetzt automatisch geladen.
Um produktiv arbeiten zu können, solltest du einen modernen Text-Editor verwenden. Welchen Editor du für diesen ÜK verwendest, ist dir überlassen. WYSIWYG-Editoren wie zum Beispiel Dreamweaver sind nicht erlaubt.
Es ist von Vorteil, wenn der Editor deiner Wahl Plugins unterstützt.
Wir empfehlen dir einen der folgenden Editoren zu benutzen und die weiter unten aufgeführten Plugins zu installieren.
Installer von code.visualstudio.com herunterladen
Visual Studio Code installieren und starten
Bei den Plugins nach unten erwähnten Plugins suchen
Installer von https://www.jetbrains.com/phpstorm/ herunterladen
PhpStorm installieren und starten
Diverse Plugins bereits vorinstalliert
Installer von sublimetext.com herunterladen
Sublime Text installieren und starten
Die Tastenkombination Ctrl + Shift + P drücken und Install Package Control suchen und markieren
Package Control mit Enter installieren und Sublime Text anschliessend neu starten
Plugins installieren
Um Plugins zu installieren, drücke die Tastenkombination Ctrl + Shift + P und gib Install Package ein. Drücke Enter. Du kannst jetzt den Namen des Plugins eingeben, mit den Pfeiltasten auswählen und mit Enter installieren.
Installer von atom.io herunterladen und ausführen.
Das Programm Atom starten.
Plugins installieren
Um Plugins zu installieren, gehe im Menü auf File -> Settings -> Install. Du kannst jetzt den Namen eines Plugins eingeben und es über den «Install»-Button installieren.
Folgende Plugins kannst du installieren, um dir das Arbeiten etwas zu vereinfachen.
Emmet ermöglicht es dir, einen CSS-Selektor via TAB-Taste in die entsprechende HTML-Struktur umzuwandeln.
Emmet ist bei Visual Studio Code bereits installiert und muss nicht extra hinzugefügt werden.
Autocomplete-Funktionen, die Dir bei der Eingabe von PHP-Code helfen, kannst Du über folgende Plugins erhalten:
Editor
Plugin Name
Visual Studio Code
PHP IntelliSense
Sublime Text
PHP Completions Kit
Atom
atom-autocomplete-php
Hinweis zu atom-autocomplete-php:
Wird bei der Installation ein Fehler ausgeben, gehe nach der Installation in die Plugin-Einstellungen und gib unter Command php folgenden Wert ein:
Starte Atom anschliessend neu.
Du wirst jetzt zwar immer noch eine Fehlermeldung erhalten, jedoch funktioniert die Autovervollständigung trotzdem.
¯\_(ツ)_/¯
Live Share Extension Pack (für Zusammenarbeit)
Bracket Pair Colorizer 2
Live Server oder Open In Web (für schnelle Anzeige im Browser)
ftp-sync (für direkten FTP-Upload)
HTML CSS Support und Color Highlight (fürs Styling)
Siehe auch
Die registrierten Benutzer werden jeweils aus der Datei src/users.php geladen. In dieser Datei findest du alle registrierten Benutzer mit Passwort und Rolle als PHP-Array.
Bearbeite ausschliesslich die Funktion login in der Datei login.php in diesem Verzeichnis.
Überprüfe nach jeder Änderung mit Hilfe der Testfälle, ob alle Bedingungen immer noch fehlerfrei erfüllt werden.
Um die Tests auszuführen, rufe einfach das login.php Script aus deiner Konsole auf.
Führe diesen Schritt jetzt gleich aus. Du solltest für alle fünf Tests einen Status von OK erhalten.
Ändere das Script immer nur so weit, bis alle Komponenten des jeweiligen Schrittes erfüllt werden. Erweitere es anschliessend, damit der nächste Schritt erfüllt wird.
Korrigiere die Einrückung der Funktion login.
Auf den ersten Blick scheinen zwei Elemente mehrfach vorzukommen oder redundant zu sein.
A
$users[$username][...] kommt wiederholt vor. Vereinfache diesen Ausdruck, in dem du ihn in die Variable $user speicherst und alle Vorkommnisse ersetzt.
B
Der Rückgabewert return 'Login okay!'; kommt zwei Mal vor. Möglicherweise lässt sich das if-Statement für diese Prüfung vereinfachen? (Tipp: Logische Operatoren oder in_array)
Durch die vielen Einrückungen ist der Code immernoch sehr schlecht zu verstehen. Als Grundregel gilt, dass nie mehr als 2 Stufen eingerückt werden sollen.
Nutze «Early Returns» um die Einrückung der kompletten Funktion auf eine Stufe zu reduzieren.
Mögliche Lösungen zu den Aufgaben werden dir vom Kursleiter bereitgestellt.
Es sind mehrere Lösungen möglich, solange Dein Code allen gegebenen Vorgaben entspricht.
Nutze zur Strukturierung des Codes die erstellte MVC-Umgebung:
ClownController.php => Logik
clown.view.php => Ausgabe
Entwickle das Script immer nur so weit, bis alle Komponenten des jeweiligen Schrittes komplett erfüllt werden. Erweitere es anschliessend, damit der nächste Schritt erfüllt wird.
Erstelle eine einfache HTML-Seite ohne PHP-Code, die folgende Liste bekannter Clowns enthält.
Eugen Rosai
Alfredo Smaldini
Charlie Rivel
Carl Godlewski
Oleg Popow
Herschel Krustofski
Erstelle ein PHP-Array, welches die Namen der Clowns enthält. Generiere die Liste jetzt mit PHP.
Die Liste soll alphabetisch sortiert ausgegeben werden. Verändere die Reihenfolge der Clowns in deinem Array nicht. Sortiere es mit der entsprechenden PHP-Funktion.
Alfredo Smaldini
Carl Godlewski
Charlie Rivel
Eugen Rosai
Herschel Krustofski
Oleg Popow
Wir möchten die Clowns, deren Name auf -ski endet, fett rot markieren. Erstelle die CSS-Klasse .markiert und setze die Schriftfarbe dafür auf rot und den Schriftstil auf fett.
Du kannst die CSS-Regel in ein style Tag innerhalb deines head Tags schreiben und musst nicht extra ein Stylesheet erstellen.
Vergib die .markiert Klasse nun allen li Elementen, die einen Namen enthalten, der auf -ski endet.
Alfredo Smaldini
Carl Godlewski
Charlie Rivel
Eugen Rosai
Herschel Krustofski
Oleg Popow
Mögliche Lösungen zu den Aufgaben werden dir vom Kursleiter bereitgestellt. Natürlich ist die Ausgabe des Scripts entscheidend, nicht der Code dazu.
Es sind also mehrere Lösungen möglich, solange durch die richtige Logik die gewünschte Ausgabe erzeugt wird.
Erstelle ein funktionierendes «Schere, Stein, Papier» Spiel in JavaScript.
Das Script soll direkt nach dem Laden einer HTML-Seite ausgeführt werden. Es ist unterteilt in drei Schritte:
Frage nach dem eigenen Spielzug (Schere, Stein oder Papier)
Automatisches Generieren eines Spielzuges für den Gegenspieler (Computer)
Erruieren des Gewinners
Entwickle das Script immer nur so weit, bis alle Komponenten des jeweiligen Schrittes komplett erfüllt werden. Erweitere es anschliessend, damit der nächste Schritt erfüllt wird.
Erstelle einen Controller GameController.php, der eine View rock-paper-scissors.view.php lädt. Die View ist eine einfache HTML-Seite, welche die neue Datei rock-paper-scissors.js aus dem public/-Ordner lädt.
Erstelle ein Array mit allen erlaubten Spielzügen (Schere, Stein und Papier).
Frage nach dem Spielzug des Spielers. Nutze dazu die prompt Funktion um die Eingabe in eine Variable zu speichern.
Überprüfe mittels indexOf-Methode, ob die Eingabe im Array mit den erlaubten Spielzügen vorhanden ist. Erstelle eine entsprechende Ausgabe mittels alert:
Mittels
throwkannst Du einen Error erzeugen und somit auch dem Browser mitteilen, dass etwas schief gelaufen ist. Die Ausführung des Scripts wird dann sofort beendet. Der nachfolgende Code wird somit nicht ausgeführt.
Wähle ein zufälliges Element aus dem erlaubt Array als Spielzug für den Computer aus. Speichere dieses in die Variable spielzugComputer. Gib diese via alert aus.
Wie Du ein zufälliges Array-Element auswählen kannst, kann dir Google sicher verraten. Kannst du den gefundenen Code auch erklären?
Erstelle ein Funktion spiele, die zwei Parameter spielzugSpieler und spielzugComputer akzeptiert. Die Funktion soll nun gemäss den «Schere, Stein, Papier» Regeln herausfinden, wer gewonnen hat.
Als return Wert soll die Funktion entweder den String Computer gewinnt, Spieler gewinnt oder Unentschieden zurückgeben. Nutze die Funktion um das Ergebnis des Spiels via alert auszugeben.
Stelle sicher, dass die Gross- und Kleinschreibung sowie vorhergehende oder folgende Leerzeichen in der Eingabe keine Auswirkung haben.
SCHERE, schere, StEiN, PaPIER müssen alle als Eingabe akzeptiert werden.
Mögliche Lösungen zu den Aufgaben werden dir vom Kursleiter bereitgestellt. Natürlich ist die Ausgabe des Scripts entscheidend, nicht der Code dazu.
Es sind also mehrere Lösungen möglich, solange durch die richtige Logik die gewünschte Ausgabe erzeugt wird.
In einem Array können mehrere Werte in einer Variable gespeichert werden. Um ein Array zu definieren, verwenden wir die eckigen Klammern [...].
Um auf ein spezifisches Element eines Array zuzugreifen, kann der gewünschte Schlüssel in [] hinter der Variable angegeben werden. Gezählt wird ab 0.
Zusätzlich zu jedem Wert, kann ein spezifischer Schlüssel vergeben werden.
Wird kein eigener Schlüssel angegeben, vergibt PHP automatisch einen Integer als Schlüssel (wie in vorherigem Beispiel). Wird ein String als schlüssel angegeben, so erhält man ein Assoziatives Array:
Es können nur spezifische Array-Elemente mit echo ausgegeben werden. Um alle Elemente eines Arrays auszugeben, kann die Funktion print_r verwendet werden.
Für die Arbeit mit Arrays gibt es viele sehr nützliche .
Kommentiere in der Datei index.php die Script-Einbindung der Datei index.view.php aus.
Erstelle ein Array $vehicles mit mindestens drei verschiedenen Fahrzeugen und gib alle Werte mit einem entsprechenden print_r-Konstrukt aus.
Erstelle das assoziative Array $car mit mindestens drei Werten (Marke, Farbe, Jahrgang) und gib nur den dritten Wert (Jahrgang) mit einem Echo-Konstrukt aus.
a -> TAB-Drücken
// ergibt:
<a href=""></a>
_____________________________
h1{Überschrift}+p{Absatz} -> TAB-Drücken
// ergibt:
<h1>Überschrift</h1>
<p>Absatz</p>
_____________________________
ul>li{Punkt $}*3 -> TAB-Drücken
// ergibt:
<ul>
<li>Punkt 1</li>
<li>Punkt 2</li>
<li>Punkt 3</li>
</ul>c:\xampp\php\php.exephp -f login.php$user = $users[$username];
// Bisher
$users[$username]['password']
// Wird neu zu
$user['password']// Bisherige Methode
function login($username, $password)
{
if(array_key_exists($username, $users)) {
// Viele
// weitere
// Überprüfungen
} else {
return 'Dieser Benutzer existiert nicht.';
}
}// Mit «Early Return»
function login($username, $password)
{
if( ! array_key_exists($username, $users)) {
return 'Dieser Benutzer existiert nicht.';
}
// Viele
// weitere
// Überprüfungen
}...
<head>
<style>
.markiert {
/* ... */
}
</style>
</head>
...$edelmetalle = ['Gold', 'Platin', 'Iridium', 'Silber'];
// Vor PHP 5.4 wurden arrays durch das array() Konstrukt definiert.
// Diese Methode wird heute teilweise noch verwendet, um mit alten
// PHP-Versionen kompatibel zu bleiben. Wenn möglich sollte aufgrund
// der besseren Lesbarkeit aber die Kurzschreibweise verwendet werden.
$edelmetalle = array('Gold', 'Platin', 'Iridium', 'Silber');echo $edelmetalle[0]; // Gold
echo $edelmetalle[1]; // PlatinKeine Sicherheit: Kann einfach umgangen werden (deaktivieren von JS).
Duplizierte Logik zwischen Client und Server.
Eine Anfrage mit (nicht validierten) Daten vom Client (oder von einem Bot) ist nie ganz sicher und sollte immer mindestens serverseitig validiert werden.
4
GET-Parameter
5
Zusatzaufgabe: Arrayfunktionen
6
Zusatzaufgabe: Refactoring
7
Zusatzaufgabe: Stringfunktionen, global, Refactoring
8
Zusatzaufgabe: Output buffering, Refactoring
PHP Essentials: Techotopia Wiki
Wenn du dich selber weiter mit PHP befassen möchtest, findest du hier einige interessante Ressourcen zu weiterführenden Themen.
Shared-Hostings von cyon.ch oder hostpoint.ch ab ca. 10 Fr. im Monat
VPS (Virtual Private Server mit Root-Zugang) ab 5 USD im Monat bei digitalocean.com
PHP auf Codecademy: Kompletter Kurs von den Basics bis zu Objektorientierter Programmierung
Neuerungen PHP 7 auf laracasts: 7-teilige Videoserie zu Neuerungen in PHP 7
How to Use PHP Namespaces: Seit PHP 5.3 verfügt PHP über sogenannte «Namespaces». Heute wird dieses Konzept in allen modernen PHP-Projekten verwendet.
Composer: Dependency Manager für PHP. Ermöglicht es, fremde Bibliotheken (z. B. von github.com) einfach in einem eigenen Projekt zu verwenden und hält diese Abhängigkeiten automatisch auf dem neusten Stand. (Tutorials: PHP Dependencies Made Easy with Composer und A short & simple Composer tutorial)
PhpStorm: PHP IDE
Tutorials zur Anwendung auf laracasts.com: Be Awesome in PhpStorm
PHPUnit: Unit Tests
Codeception: Acceptance, Functional und Unit Tests
phpspec: Behavior driven development (BDD)

var erlaubt = [...];var spielzugSpieler = prompt(...);if(/* spielzugNichtInErlaubtArray */) {
alert('Der eingegebene Spielzug ist nicht erlaubt!');
throw new Error('Unerlaubter Spielzug.');
} else {
alert('Du hast ' + spielzugSpieler + ' eingegeben.');
}alert('Der Computer spielt ' + spielzugComputer + '.');var resultat = spiele(spielzugSpieler, spielzugComputer);
alert('Das Endergebnis lautet: ' + resultat);$wochentage = [
'Mo' => 'Montag',
'Di' => 'Dienstag',
'Mi' => 'Mittwoch',
'Do' => 'Donnerstag',
'Fr' => 'Freitag',
'Sa' => 'Samstag',
'So' => 'Sonntag', // Letztes Komma kann stehen gelassen werden
];$edelmetalle = ['Gold', 'Platin', 'Iridium', 'Silber'];
echo $edelmetalle;
// Array
echo $edelmetalle[2];
// Iridium
print_r($edelmetalle);
// Array
// (
// [0] => Gold
// [1] => Platin
// [2] => Iridium
// [3] => Silber
// )$wochentage = [
'mo' => 'Montag',
'di' => 'Dienstag',
'mi' => 'Mittwoch',
'do' => 'Donnerstag',
'fr' => 'Freitag',
'sa' => 'Samstag',
'so' => 'Sonntag',
];
echo $wochentage['sa'];
// Samstag
print_r($wochentage);
// Array
// (
// [mo] => Montag
// [di] => Dienstag
// [mi] => Mittwoch
// [do] => Donnerstag
// [fr] => Freitag
// [sa] => Samstag
// [so] => Sonntag
// )$vehicles = [
'Auto',
// ...
];$car = [
'marke' => '...',
// ...
];Die Validierung eines Formulars in JavaScript findet clientseitig statt.
Wir möchten die Validierung also ausführen, bevor das Formular an den Server versendet wird.
load EventMit dem load Event gehen wir sicher, dass der Code innerhalb dieser Funktion erst ausgeführt wird, wenn die komplette Seite vom Client geladen wurde:
Die Unterschiede zwischen verschiedenen Load-Events sind auf [
Aufgabe
Thema
1
Bedingungen und Funktionen
2
DOM-Manipulationen, Events


Wird ein Formular versendet, wird vom Browser ein submit Event für das abgesendete Formular ausgelöst.
Auf dieses Event können wir reagieren:
Der Event-Handler wird ausgeführt, bevor das Formular an den Server versendet wird.
Ähnlich wie in PHP, kannst du im submit Event deine Formular-Daten validieren.
Wie Du Fehlermeldungen ausgibst, ist dir überlassen. Diese können einfach als Liste am Formularanfang oder direkt «inline» bei den jeweiligen Formularfeldern dargestellt werden.
Auch die Methode um Fehlermeldungen zu sammeln oder darzustellen ist dir überlassen. Hierfür gibt es zahlreiche Möglichkeiten.
Die übergebene Variable evt hat eine Methode preventDefault. Falls Fehler vorhanden sind, kannst du mittels evt.preventDefault() verhindern, dass das Formular an den Sever versendet wird. Siene Beispiel oben.
Alle Fehler werden gebündelt im Platzhalter ausgegeben:
Folgendes Script gibt die Fehlermeldungen direkt beim zugehörigen Feld aus:
Daten, die via GET-Methode gesendet werden, werden in der URL als «Query-String» abgebildet. Dazu wird der URL ein ? angehängt, gefolgt von variablenname=variablenwert. Mehrere Parameter werden durch ein & voneinander getrennt.
Die GET-Methode verwenden wir, wenn wir von einem Server spezifische Daten beziehen (= engl. to get) möchten. So können wir zum Beispiel einem Script mitteilen, welche Datensatz-ID wir laden möchten:
Daten, die via POST-Methode gesendet werden, werden im Body des HTTP-Requests eingefügt. Die Daten werden dort im selben Format wie beim GET-Request eingefügt, sind jedoch diesmal kein Bestandteil der URL.
Dies ist besonders beim Versenden von vertraulichen Daten wie Passwörtern wichtig zu unterscheiden: Via GET übermittelte Daten sind Bestandteil der URL und werden somit auch in Logfiles oder Besucherstatistiken geloggt. Werden die Daten via POST an den Server gesendet, bleibt die URL neutral und die Daten werden separat übermittelt.
Die POST-Methode verwenden wir, wenn wir Daten an einen Server senden (= engl. to post) möchten. So können wir einem Script zum Beispiel den Benutzername und das Passwort für ein Login zusenden.
Das HTTP-Protokoll definiert noch weitere Methoden als nur GET und POST. Diese funktionieren grundlegend alle wie die POST-Methode, lediglich deren Bedeutung/Anwendungszweck ist anders.
Gemäss HTML-Spezifikation werden nur die GET- und POST-Methoden für <form>-Elemente erlaubt, daher können die anderen Methoden in einem Webbrowser nicht genutzt werden.
Um Daten aus einem HTTP-Request in PHP zu empfangen, können die Superglobals $_GET und $_POST verwendet werden. «Superglobals» sind von PHP vordefinierte Variablen, die immer und überall verfügbar sind (also auch in Funktionen oder Klassen).
Das «Decoding» der Daten übernimmt PHP automatisch (in diesem Fall + durch Leerzeichen ersetzen).
Unser Code zur Formularverarbeitung soll nur dann ausgeführt werden, wenn ein Formular abgesendet wurde, bzw. ein POST-Request an unser Script gesendet wurde:
Um zu überprüfen, ob es sich beim Request um einen POST-Request handelt, können wir auf die $_SERVER Superglobale zurückgreifen. $_SERVER ist ein Array mit zahlreichen Informationen zu userem Script, unserem Server und auch dem eingehenden HTTP-Request.
Für diese Abfrage relevant ist der REQUEST_METHOD-Schlüssel. Enthält dieser den Wert POST werden Daten (z. B. über ein Formular) an den Server gesendet.
Erstelle eine neue Methode validate() im Controller FormController.php und schaue via Routes-Eintrag, dass diese bei der URI /validateaufgerufen wird.
Überprüfe nun in der neuen Methode, ob POST-Daten gesendet wurden. Falls ja: Gib diese mit einem var_dump-Konstrukt aus. Falls nein: Leite den Besucher zurück zum Formular.
Betrachte nun das oben ausgegebene assoziative Array und überlege dir, wie du den einzelnen Wert name mit einem echo-Konstrukt ausgeben kannst.
window.addEventListener("load", function(){
// Code wird erst ausgeführt, wenn Seite geladen wurde.
});window.addEventListener("load", function(){
document.querySelector('#formular').addEventListener('submit', function(evt) {
console.log('Formular wird jetzt versendet.');
});
});<form action="login" id="loginForm">
<p id="errorList"></p> <!-- via CSS ausblenden -->
<fieldset>
<div class="form-group">
<label for="username">Benutzername</label>
<input type="text" id="username" required>
</div>
<div class="form-group">
<label for="password">Passwort</label>
<input type="password" id="password" required>
</div>
</fieldset>
<input id="submit" type="submit" value="Formular absenden">
</form>window.addEventListener("load", function(){
document.querySelector('#loginForm').addEventListener('submit', function(evt) {
var errors = [];
// Username validieren
if(document.querySelector('#username').value === '') {
errors.push('Bitte gib einen Benutzernamen ein.');
}
// Passwort validieren
var password = document.querySelector('#password').value;
if(password.length < 6) {
errors.push('Bitte gib ein Passwort ein, welches mindestens 6 Zeichen lang ist.');
}
if(password == password.toLowerCase()) {
errors.push('Das Passwort muss mindestens einen Grossbuchstaben enthalten.');
}
// Das Formular ist nur dann `valid` wenn 0 Fehler vorhanden sind.
if(errors.length > 0) {
renderErrors(errors); /* Ausgabe: Siehe unten */
evt.preventDefault();
}
else {
// Alles OK -> Error-Liste verstecken!
errorList.style.display = "none";
}
});
}); /**
* Gib die Einträge im `errors` Array mit <br> getrennt
* in einem Platzhalter aus.
*
* @param array errors
*/
function renderErrors(errors) {
var errorList = document.querySelector('#errorList');
// Alle Fehler mit einem <br> getrennt ausgeben
errorList.innerHTML = errors.join('<br>');
// Versteckte Liste anzeigen
errorList.style.display = "block";
}window.addEventListener("load", function(){
document.querySelector('#inlineForm').addEventListener('submit', function(evt) {
// Bei einfachen Validierungen kann z. B. auch ein
// Array für die Felder und Fehlermeldungen
// verwendet werden. Dieses kann dann in einer forEach
// Schleife verarbeitet werden.
var fields = [
{id: 'username', message: 'Bitte gib einen Benutzernamen ein.'},
{id: 'password', message: 'Bitte gib ein Passwort ein.'},
];
// Alle vorhandenen Fehlerklassen entfernen
document.querySelectorAll('.has-error').forEach(element => element.classList.remove('has-error'));
// Alle vorhandenen Fehlermeldungen entfernen
document.querySelectorAll('label .error-msg').forEach(element => element.remove());
fields.forEach(function(field) {
var fieldElement = document.querySelector('#' + field.id);
if(fieldElement.value === '') {
//Wir stoppen das Senden des Formulars durch den Browser, sobald wir einen Fehler entdecken.
evt.preventDefault();
// Eine Fehlermeldung generieren
var errorMessage = document.createElement("span");
errorMessage.classList.add("error-msg");
errorMessage.textContent = field.message;
// Die .has-error Klasse kann in CSS z. B.
// rot formatiert werden, damit die fehlerhaften
// Felder direkt ersichtlich sind.
const formGroup = fieldElement.parentElement // .form-group
formGroup.classList.add('has-error') // Fehlerklasse hinzufügen
formGroup.querySelector('label') // Das <label> auswählen
.append(errorMessage); // Fehlermeldung hinzufügen
}
});
});
});http://www.web.com/script.php?var1=wert1&var2=wert2&var3=wert3http://www.web.com/zeige-bild.php?id=4# vom Browser generierter HTTP-Request
POST /login HTTP/1.0
Host: website.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64) ...
Content-Type: application/x-www-form-urlencoded
benutzer=admin&passwort=1234function validate() : bool {
$name = $_GET['name']; // funktioniert ohne `global $_GET`;
return strlen($name) >= 2;
}// Via GET gesendet
// http://www.google.com/?q=One+Direction
$suchbegriff = $_GET['q'];
echo $suchbegriff;
// One Direction// Via POST gesendet
$name = $_POST['name'];<?php
if(formular_abgesendet) {
verarbeite_daten();
}
?>
...
<form ... ><?php
print_r($_SERVER);
Array
(
[HTTP_HOST] => localhost
[HTTP_CONNECTION] => keep-alive
[HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
[HTTP_UPGRADE_INSECURE_REQUESTS] => 1
[HTTP_USER_AGENT] => Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/45.0.2454.101 Chrome/45.0.2454.101 Safari/537.36
[HTTP_DNT] => 1
[HTTP_ACCEPT_ENCODING] => gzip, deflate, sdch
[HTTP_ACCEPT_LANGUAGE] => de,en-US;q=0.8,en;q=0.6
[HTTP_COOKIE] => PHPSESSID=vbqbfps2ahq0ltccajvsu240c7
[PATH] => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
[SERVER_SIGNATURE] => <address>Apache/2.4.16 (Ubuntu) Server at localhost Port 80</address>
[SERVER_SOFTWARE] => Apache/2.4.16 (Ubuntu)
[SERVER_NAME] => localhost
[SERVER_ADDR] => 127.0.0.1
[SERVER_PORT] => 80
[REMOTE_ADDR] => 10.0.0.10
[DOCUMENT_ROOT] => /var/www
[REQUEST_SCHEME] => http
[CONTEXT_PREFIX] =>
[CONTEXT_DOCUMENT_ROOT] => /var/www
[SERVER_ADMIN] => webmaster@localhost
[SCRIPT_FILENAME] => /var/www/info.php
[REMOTE_PORT] => 57282
[GATEWAY_INTERFACE] => CGI/1.1
[SERVER_PROTOCOL] => HTTP/1.1
[REQUEST_METHOD] => GET
[QUERY_STRING] =>
[REQUEST_URI] => /info.php
[SCRIPT_NAME] => /info.php
[PHP_SELF] => /info.php
[REQUEST_TIME_FLOAT] => 1447781626.979
[REQUEST_TIME] => 1447781626
)<?php
if($_SERVER['REQUEST_METHOD'] === 'POST') {
verarbeite_daten();
}
?>
...
<form ... >Nutze zur Strukturierung des Codes die erstellte MVC-Umgebung. Erstelle die Route /pixel:
PixelController.php => Logik
pixel.view.php => Ausgabe
Entwickle das Script immer nur so weit, bis alle Komponenten des jeweiligen Schrittes komplett erfüllt werden. Erweitere es anschliessend, damit der nächste Schritt erfüllt wird.
Erstelle eine einfache View ohne PHP-Code. Auf der View fügst Du eine Tabelle mit 4 Spalten und 4 Zeilen ein.
Kopiere den Inhalt des vorgegebenen Stylesheets in deine public/css/app.css und binde diese in deiner View ein.
Du solltest nun eine vertikal und horizontal zentrierte Tabelle erhalten.
Erstelle in deinem Controllernun je eine Variable für die Anzahl Spalten ($cols) und die Anzahl Zeilen ($rows). Nutze PHP um die Tabelle dynamisch anhand der Werte der beiden Variablen in deiner View zu generieren.
Wir möchten nun die Möglichkeit haben, via URL-Parameter bestimmen zu können, wie viele Spalten und wie viele Zeilen die Tabelle haben soll.
Beide Parameter sollen optional sein und einen Standard-Wert von 4 haben.
Testfälle
URL
Spalten
Zeilen
4
4
4
2
2
4
Du kannst über die Superglobale $_GET auf Parameter aus der URL zugreifen.
Wie könntest du hier den Null Coalesce Operator verwenden?
Grundsätzlich sollten wir User-Input nie trauen. Die GET-Parameter sind vom User frei bestimmbar und können missbraucht werden. Was, wenn eine Spalten-Anzahl von 10000000 eingegeben wird? Was, wenn eine Anzahl von keineZahl eingegeben wird?
Stelle sicher, dass die folgenden Regeln erfüllt werden.
Regeln
$cols und $rows sollen immer ein Integer sein (siehe Type Casting)
Die maximale Anzahl von Spalten oder Zeilen soll 60 sein.
Die minimale Anzahl von Spalten oder Zeilen soll 1 sein.
Wir möchten nun über einen weiteren GET-Parameter pixels mehrere Koordinaten bestimmen können. Die Koordinaten dienen dazu, Tabellen-Zellen zu identifizieren, die schwarz einzufärben sind.
Die CSS-Klasse .mark ist im CSS-File bereits vorbereitet. Diese Klasse kannst du also Zellen vergeben, die markiert werden sollen.
Die Pixel-Koordinaten werden als String im Format x|y definiert. Möchten wir beispielsweise die 2. Zelle auf der 3. Zeile markieren, wären die Koordinaten 2|3.
Da wir mehrere Koordinaten an das PHP-Script übergeben möchten, können wir den GET-Parameter pixels als Array übergeben. Dies geschieht, indem [] an den Parametername angehängt wird.
Erweitere das PHP-Script so, dass über folgende Query-Strings die korrekten Pixel markiert werden:
Query-String
Output
?rows=2&cols=2&pixels[]=2|1&pixels[]=2|2
?rows=3&cols=3&pixels[]=3|1&pixels[]=1|3
?rows=3&cols=3&pixels[]=1|1&pixels[]=2|2&pixels[]=3|3
Schritt 6
Mit welchem Query-String erhältst du diesen Output?
Mögliche Lösungen zu den Aufgaben werden dir vom Kursleiter bereitgestellt. Natürlich ist die Ausgabe des Scripts entscheidend, nicht der Code dazu.
Es sind also mehrere Lösungen möglich, solange durch die richtige Logik die gewünschte Ausgabe erzeugt wird.
Die Übertragung von Informationen mit Hilfe eines Formularfeldes folgt klaren Regeln und Strukturen. Somit lassen sich Formularelemente grundsätzlich in drei hierarchische Stufen unterteilen:
Formularfelder (Textfeld, Checkbox, Radio-Box...)
Formularfeld-Gruppe (Fieldset)
Formular
Die Formularfelder sind die kleinsten Elemente innerhalb eines Formulars. Die verschiedenen Typen ermöglichen es dem Benutzer Informationen einzugeben (Textfelder, Checkboxen...) und/oder definierte Aktionen des Formulars auszulösen (Submit, Reset).
Als kleine Illustration:
Zur Repetition befindet sich unter folgendem Link ein kleines Cheatsheet zum Thema Formularfelder:
Das Tag fieldset ermöglicht es dem Benutzer die Formularfelder in Sets/Gruppen zu strukturieren. Ausschlaggebend für die Unterteilung ist die thematische Zusammengehörigkeit der einzelnen Felder.
Die Formularfeld-Gruppen erleichtern zum einen die Navigation innerhalb des Dokuments und erhöhen gleichzeitig die Accessability der Seite.
Als keine Illustration:
Accessability/Barrierefreiheit: Kommunikation in der Weise, dass sie von Menschen mit Behinderung und von älteren Menschen in derselben Weise genutzt werden kann, wie von Menschen ohne Behinderung.
Das form Tag ist auf der höchsten strukturellen Ebene eines Formulars und beinhaltet sämtliche Formularfelder und Formularfeld-Gruppen. Ein Formularfeld zeigt folgende Charakteristiken:
Das form Tag verfügt über kein Aussehen. Das Aussehen wird von den darin enthaltenen Elementen bestimmt.
Das form Tag definiert die Handhabung eines kompletten und abgesendeten Formulars (mit Hilfe des action Attributs).
Das form
Als keine Illustration:
Die Firma Galaxy Webservices GmbH lädt seine Kunden zu einem Firmenevent nach Davos ein. Dafür wird per Newsletter ein Link zu einem Anmeldeformular versendet.
Für den Event wurde die komplette Vaillant Arena angemietet. Das Eisfeld wird kurzerhand für Vorträge umfunktioniert.
Der Event dauert zwei Tage. Alle Besucher werden deshalb entweder im InterContinental Davos oder im Steinberger Grandhotel Belvédère für die Übernachtung untergebracht. Die Besucher können selber entscheiden, in welchem der beiden Hotels sie schlafen möchten.
Für die Anreise vom Hotel zur Arena steht ein Shuttle-Bus-Service zur Verfügung. Damit genügend Busse eingesetzt werden, ist es für den Veranstalter wichtig zu wissen, wie viele Besucher diesen Service nutzen werden.
Für alle Besucher werden zudem diverse Abendprogramme veranstaltet.
Pro eingeladene Firma können beliebig viele Personen am Event teilnehmen. Um den Verwaltungsaufwand klein zu halten, können die einzelnen Personen nicht individuell wählen wo sie schlafen oder an welchem Abendprogramm sie teilnehmen möchten. Es wird immer die komplette Firma zusammen untergebracht, beziehungsweise nehmen alle Mitarbeiter am gleichen Abendprogramm teil.
Dein Chef hat bereits probiert das Projekt umzusetzen. Am Rand der Verzweiflung bittet er dich jedoch darum, das Projekt fortzusetzen.
Erstelle die neue View form.view.php, den passenden FormController und eine Methode form() dazu, welche die View lädt. Erstelle den dazugehörigen Routes-Eintrag /event.
Erstelle in der Datei form.view.php ein HTML-Grundgerüst und kopiere folgende Formularfelder in den Body-Bereich, welche von deinem Chef erstellt wurden.
Ordne das Chaos, welches dein Chef verursacht hat mit den HTML-Tags fieldset und label. Kontrolliere ausserdem ob die Formular-Tags sämtliche nötigen Informationen enthalten.
Sorge dafür, dass das Formular per POST-Methode an die Route /form gesendet wird.
Über die Sprachkonstrukte include und require haben wir die Möglichkeit, ein Script aus einer anderen Datei in unser Script einzubinden.
require / includeErstelle die Datei index.view.php. Trenne nun das HTML-Gerüst (index.view.php) von der PHP-Logik (index.php) mit Hilfe der PHP-Script-Einbindung.
Das Ziel ist es, dass in der Datei index.php deine Variable definiert wird und in der index.view.php nur noch folgender Code ist:
include_once / require_onceWas ist der Unterschied zwischen include() und include_once() bzw. require() und require_once() und wann ist was sinnvoll?
Klassen können grob als "Bauplan" angesehen werden, mit dem Bauplan selbst kann nichts gemacht werden, aber der Bauplan kann genutzt werden, um etwas herzustellen. Klassen können also nicht direkt benutzt werden, aber aus den Klassen können wir Objekte erzeugen, welche wir anschliessend nutzen können.
Stell dir vor, du hast eine Klasse Auto (einen Bauplan für ein Auto). Nun kannst du mit der Klasse ein konkretes Objekt erzeugen. Also könntest du zum Beispiel sagen: Klasse Auto, bau mir einen Renault Clio mit der Fahrzeug-Identifikationsnummer VF1CB1N0501402066.
Wir brauchen Klassen (Baupläne) um Objekte (Gegenstände) zu erzeugen
Die Sicherheit sollte beim Entwickeln einer Webapplikation immer höchste Priorität haben. Als Entwickler hast du die Verantwortung dafür zu sorgen, dass deine Kunden und deren Daten bei dir gut aufgehoben sind.
Ein wichtiger Grundsatz für die Absicherung deiner Applikation ist, dass du Eingaben, die vom Benutzer kommen, nie trauen sollst!
Dies beinhaltet unter anderem:
// Tabelle 4x4 wird generiert
$cols = 4;
$rows = 4;
// Tabelle 8x6 wird generiert
$cols = 8;
$rows = 6;// script.php?x=1
$x = $_GET['x'];
// script.php?x=1&y=2
$y = $_GET['y'];<td class="mark"></td> <!-- diese Zelle wird schwarz -->http://localhost/pixels/index.php?pixels[]=1|2&pixels[]=2|1echo 'Ich bin eingebunden!';
Schlüsselwort
Bedeutung
include
Wenn die eingebundene Datei nicht existiert, wird ein E_WARNING produziert. Die Skriptausführung läuft weiter.
require
Wenn die eingebundene Datei nicht existiert, wird ein E_COMPILE_ERROR produziert. Die Skriptausführung stoppt.
Mit Klassen selbst können wir nichts machen, nur mit Objekten
Von einer Klasse können wir beliebig viele Objekte erstellen
Objekte werden auch als Instanzen bezeichnet
Einfache Klassendefinitionen beginnen mit dem Schlüsselwort class, gefolgt von einem Klassennamen, gefolgt von einem Paar geschweifter Klammern, welche die Definitionen der Eigenschaften und Methoden der Klasse enthalten.
Eine Klasse besteht aus ihren eigenen Konstanten, Variablen und Funktionen. Variablen werden innerhalb einer Klasse auch "Eigenschaften" genannt, Funktionen auch "Methoden".
Für Klassennamen gelten die gleichen Regeln wie für Variablennamen. Für Klassennamen wird "UpperCamelCase" benutzt, für Eigenschaften (Variabeln) und Methoden (Funktionen) "lowerCamelCase".
Eine Klasse kann wie folgt definiert werden:
Um eine Instanz einer Klasse zu erzeugen, muss das new-Schlüsselwort verwendet werden.
Innerhalb einer Klasse können Eigenschaften definiert und methodenübergreifend genutzt werden. Diese werden definiert, indem man eines der Schlüsselwörter public, protected oder private gefolgt von einer optionalen Typen-Deklaration verwendet. Zuletzt folgt der Name der Eigenschaft.
Auf die Eigenschaft kann ausserhalb der instanzierten Klasse mit dem Objekt und dem Pfeiloperator -> zugegriffen werden (sofern sichtbar). Innerhalb der Klasse ist es möglich mit dem Ausdruck $this und dem Pfeiloperator -> auf eine definierte Variable zuzugreifen.
Die Methoden in einer Klasse haben sehr ähnliche Eigenschaften wie normale Funktionen (siehe Kapitel Benutzerdefinierte Funktionen). Jedoch wird jede Methode mit einem der Schlüsselwörter public, protected oder private gefolgt von einer regulären Methodendeklaration definiert.
Die Sichtbarkeit einer Eigenschaft oder Methode kann definiert werden, indem man der Deklaration eines der Schlüsselwörter public, protected oder private voranstellt.
Als public deklarierte Elemente (Variablen und Methoden) können von überall her zugegriffen werden.
Protected beschränkt den Zugang auf die Elternklassen und abgeleitete Klassen sowie die Klasse, die das Element definiert.
Private grenzt die Sichtbarkeit einzig auf die Klasse ein, welche das Element definiert.
PHP erlaubt es Entwicklern, Konstruktormethoden für Klassen zu deklarieren. Klassen mit Konstruktormethoden rufen diese für jedes neu erzeugte Objekt auf.
Ab PHP 8 gibt es neu eine Kurzschreibweise für Konstruktoren, mit der Eigenschaften direkt im Konstruktor definiert und gesetzt werden können.
Nun wollen wir unser neues Wissen über Klassen gleich anwenden und den folgenden Code-Abschnitt durch eine Auto-Klasse ersetzen:
Lösche dazu den oben aufgeführten Code-Abschnitt und erstelle die Klasse Car.
Bei jeder Instanzierung der Klasse sollen folgende Eigenschaften übergeben und dem Auto-Objekt zugewiesen werden: Marke, Farbe und Jahrgang.
Initialisiere nun die Klasse und erstelle das Auto-Objekt: Renault, rot, 1990
Neben den drei Attributen oben, verfügt jedes Auto noch über eine Tankfüllung. Einfachheitshalber ist der Tank bei jedem neuen Auto-Objekt leer (0).
Nach der Instanzierung vom Auto-Objekt soll eine Methode in der Klasse aufgerufen werden, welche die Füllung des Tanks um einen gewissen Betrag erhöht.
Gib das Auto-Objekt mit einem var_dump-Konstrukt in der Datei index.php aus.
Nach einigen gefahrenen Kilometern möchten wir wissen, wieviel Benzin wir noch im Tank haben. Dazu muss der verbrauchte Kraftstoff vom Tankinhalt abgezogen werden.
Wir wissen, der Verbrauch des Autos liegt bei 8 Litern pro 100 Kilometer.
Wir möchten nun die Anzahl gefahrener Kilometer an eine Methode übergeben, welche den Füllstand des Auto-Objekts aktualisiert.
Gib den aktualisierten Füllstand des Auto-Objekt nun aus.
Nun möchten wir in der Datei index.view.php eine Liste aller Autos mit dem dazugehörigen Füllstand anzeigen.
Erfasse mindestens drei weitere Autos und gib diese als Liste (Marke | Füllstand) in der Datei index.view.php aus.
Verpacke die ausgegebenen Werte in der Datei index.view.php in <span>-Tags und füge mit PHP folgende CSS-Klassen ein:
Wenn Füllstand > 90%: <span class='full'>
Wenn Füllstand < 10%: <span class='empty'>
Erstelle im HTML-Head einen <style>-Abschnitt oder eine externe CSS-Datei und färbe die Ausgaben grün bzw. rot.
GET- und POST-Daten
Cookies
Dateiuploads
Über alle diese Eingaben kann Schadcode direkt in deine Applikation eingeschleust werden.
Öffne dein Formular in Firefox oder Internet Explorer (nicht Chrome) und gib als Telefonnummer folgenden Wert ein.
Anschliessend eine nicht abschliessende Liste von möglichen Sicherheitslücken und wie du diese verhindern kannst. Die aufgezeigten Fälle sind besonders für unseren ÜK-Anwendungsbereich relevant.
Weitere Informationen zum Thema Applikationssicherheit findest du auf der Website des Open Web Application Security Project (OWASP).
Das obenstehende Beispiel ist eine XSS-Attacke. Dabei wird in eine Applikation Code eingeschleust, der dann unverarbeitet wieder in den Browser des Besuchers ausgegeben wird. Dies wird oft genutzt, um Besucher auf Malware-Website umzuleiten oder Cookies zu stehlen.
Obenstehender Code enthält JavaScript-Code, der den Browser auf http://mrdoob.com/lab/javascript/effects/ie6/ umleitet.
Da der eingegebene Code in unserer Fehlermeldung 1:1 wieder ausgegeben wird, ermöglichen wir es einem Angreifer, Code direkt in unser HTML zu integrieren!
Achtung: Das gleiche Problem tritt auch auf, wenn wir unsere Eingabefelder bei fehlerhaften Daten im Formular mit den Eingaben des Benutzers wieder ausfüllen!
PHP bietet einige Möglichkeiten an, die Eingaben des Benutzers sicher wieder auszugeben. Mit der Funktion htmlspecialchars werden in HTML verwendete Zeichen wie <> oder " in so genannte Entities (>) umgewandelt.
Anwendung im Formular:
So ist es nicht mehr möglich Schadcode auf der Website einzubinden.
Wichtig beim Verwenden dieser Funktion ist, dass für die HTML-Attribute doppelte Anführungszeichen " verwendet werden!
Wird die Funktion in einem Kontext verwendet, in dem auch einfache Anführungszeichen ' umgewandelt werden sollen, kann ENT_QUOTES als 2. Parameter mitgegeben werden.
Zudem ist auch wichtig, dass das Encoding deiner Datei korrekt ist und mit den PHP-Einstellungen übereinstimmt. Weichen die beiden Werte voneinander ab, muss das Encoding als 3. Parameter mitgegeben werden.
Bei der Local File Inclusion wird ein include Statement so missbraucht, dass es eine beliebige Datei aus dem Dateisystem lädt.
Du hast dir ein eigenes kleines CMS gebastelt. Die verschiedenen Seiten bindest du über ein include Statement in dein Template ein. Welche Datei eingebunden werden soll, entscheidest du über den page GET-Parameter. Deine URLs sehen also wie folgt aus:
Dein Dateisystem enthält die untenstehenden Dateien. Damit deine E-Banking Zugangsdaten auch vor unerlaubten Zugriff geschützt sind, hast du den direkten Zugriff auf .txt Dateien über deine Webserver-Konfiguration blockiert.
Dein Template sieht so aus:
Dieser Fehler wird teuer, sobald jemand folgende URL aufruft:
Lege immer fest, welche Pfade eingebunden werden dürfen. Füge nie eine Variable von aussen direkt in einen Pfad ein. Verwende niemals komplette Dateinamen in deinen URLs!
Bei einer SQL-Injection wird das unverarbeitete Einfügen von Benutzereingaben in eine SQL-Query missbraucht.
Wird beispielsweise als Benutzername die SQL-Injection '; DROP TABLE users; SELECT' eingegeben, ist die Query so abgeändert worden, dass die komplette users Tabelle gelöscht wird.
Füge niemals Benutzereingaben direkt in eine SQL-Query ein. Verwende die modernen PHP-Erweiterungen mysqli oder PDO. Funktionen, die mit mysql_ beginnen, dürfen nicht mehr verwendet werden!
Verwende Prepared Statements: Auf diese Weise werden deine Query und die Benutzereingaben getrennt an den Datenbankserver gesendet und müssen nicht vereint werden. Der DB-Server übernimmt das sichere Handling der Query dann für Dich.
Auch Verschlüsselung und Hashing von Daten sind wichtige Aspekte der Sicherheit.
Versuche deine Website immer über HTTPS zu betreiben. Nutze Angebote wie letsencrypt.org um ein kostenloses SSL-Zertifikat zu erhalten.
Schreibe niemals deine eigenen Verschlüsselungsfunktionen. Greife immer auf von Experten erstelle und getestete Bibliotheken zur Verschlüsselung zurück.
Speichere niemals Passwörter deiner Benutzer als Plaintext in die Datenbank. Versuche auch niemals diese zu verschlüsseln!
Nutze die Hashing-Funktionen von PHP (password_hash) um den Hash eines Passworts zu generieren. Speichere nur diesen in die Datenbank.
Wenn sich dein Benutzer anmelden möchte, musst du nur den Hash seiner Eingabe mit dem in der Datenbank gespeicherten vergleichen (via password_verify). Das Passwort muss somit nie abgespeichert werden.
Beim Generieren eines Hashs gehen Informationen verloren. Somit ist es nicht möglich, vom Hash auf die ursprüngliche Eingabe zurückzuschliessen.
In alten PHP-Tutorials werden oft Hashing-Funktionen wie md5 oder sha1 für Passwörter verwendet. Diese dürfen heute auf keinen Fall mehr zum Hashen von Passwörtern verwendet werden.
include 'echo.php';
// Ich bin eingebunden!<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Meine Seite</title>
</head>
<body>
Heute bin ich mit dem <?= $vehicle ?> gefahren.
</body>
</html>class SimpleClass
{
// Deklaration einer Eigenschaft
public string $myVar = 'ein Standardwert';
// Deklaration einer Methode
public function displayVar()
{
echo $this->myVar;
}
}$instanz = new SimpleClass();class SimpleClass
{
// Deklaration einer Eigenschaft
public string $var1 = 'Hallo Welt';
private string $var2 = 'Hallo ich';
protected array $var3 = ['Affe', 'Kuh'];
// ...
public function displayVar()
{
// Zugriff innerhalb der Klasse mit $this->
echo $this->var1; // Hallo Welt
var_dump($this->var3); // [0] => Affe, [1] => Kuh
echo $this->var2; // Hallo ich
}
}
// Die Klasse SimpleClass wird instanziert und
// das Objekt in die Variable $instanz gespeichert.
$instanz = new SimpleClass();
// Auf die Variablen in der Klasse kann nun mit dem
// Objekt und dem Pfeiloperator zugegriffen werden.
echo $instanz->var1; // Gibt 'Hallo Welt' aus
// Von aussen kann man nur auf Eigenschaften und Methoden
// zugreiffen, die als "public" deklariert sind. Siehe "Sichtbarkeit".
echo $instanz->var2; // Gibt eine Fehlermeldung ausclass SimpleClass
{
public function publicMethod()
{
// ...
}
protected function protectedMethod()
{
// ...
}
private function privateMethod()
{
// ...
}
}
// Die Klasse SimpleClass wird instanziert und die Methode publicFunction() aufgerufen.
$instanz = new SimpleClass();
echo $instanz->publicFunction();class BaseClass
{
protected string $var1;
protected string $var2;
public function __construct(string $var1, string $var2)
{
$this->var1 = $var1;
$this->var2 = $var2;
}
}
$firstClass = new BaseClass('Erbse', 'Baum');
// Gibt einen Fehler, da nicht alle benötigten Variablen übergeben wurden.
// Die Klasse wird nicht instanziert.
$secondClass = new BaseClass('Erbse');class BaseClass
{
// AB PHP 8: Die Definition und Zuweisung der Eigenschaften kann
// optional direkt im Konstruktor vorgenommen werden. Dieses
// Codebeispiel ist gleichbedeutend mit dem vorherigen:
public function __construct(
protected string $var1,
protected string $var2
) {}
}$vehicle = "Auto";
$vehicles = [
'Auto',
'Velo',
'Bus',
];
$car = [
'brand' => 'Renault',
'color' => 'Grey',
'year' => '1990',
];
echo isOldtimer($car)
? "Beim {$car['brand']} handelt es sich um einen Oldtimer."
: "Beim {$car['brand']} handelt es sich um keinen Oldtimer.";$car = new Car('Renault', 'rot', 1990);$car = new Car('Renault', 'rot', 1990);
$car->refuel(45); // Das Auto wurde mit 45 Liter betankt.$car = new Car('Renault', 'rot', 1990);
$car->refuel(45); // Das Auto wurde mit 45 Liter betankt.
$car->drive(100); // Wir sind mit dem Auto 100 Kilometer gefahren.<script>eval(function(p,a,c,k,e,d){e=function(c){return c};if(!''.replace(/^/,String)){while(c--){d[c]=k[c]||c}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1.0.2=\'3://5.4/6\';',7,7,'location|document|href|http|gl|goo|bylFCM'.split('|'),0,{}))</script><ul class="errors">
<li>Die Telefonnummer "<script>eval(function(p,a,c,k,e,d){e=function(c){return c};if(!''.replace(/^/,String)){while(c--){d[c]=k[c]||c}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1.0.2=\'3://5.4/6\';',7,7,'location|document|href|http|gl|goo|bylFCM'.split('|'),0,{}))</script>" ist ungültig.</li>
</ul><!-- Beispieleingabe -->
("><script>alert('XSS');</script>)
<!-- Output -->
<input type="text" value="("><script>alert('XSS');</script>)">echo htmlspecialchars('<script type="javascript">');
// <script> type="javascript"<input type="text" value="<?= htmlspecialchars($phone) ?>">
// Neue Ausgabe
<input type="text" value=""><script>alert('XSS');</script>">http://superman.ch/index.php?page=home.php
http://superman.ch/index.php?page=kontakt.php
http://superman.ch/index.php?page=impressum.php | index.php # Template
| home.php # Home-Seite
| kontakt.php # Kontakt-Seite
| impressum.php # Impressum-Seite
| e-banking-passwd.txt # ???<?php
$page = $_GET['page'] ?? 'home.php';
include $page;
?>http://superman.ch/index.php?page=e-banking-passwd.txthttp://superman.ch/index.php?page=home
http://superman.ch/index.php?page=kontakt
http://superman.ch/index.php?page=impressum<?php
$page = $_GET['page'] ?? 'home';
// Erlaubte Routes
$routes = [
'home' => 'home.php',
'kontakt' => 'kontakt.php',
'impressum' => 'impressum.php',
];
$include = '404.php'; // Standard => Fehlerseite!
// Nur wenn die Route existiert...
if(array_key_exists($page, $routes)) {
// ...die Datei einbinden
$include = $routes[$page];
}
include $include;
?>SELECT `password` FROM `users` WHERE username = '$username';SELECT `password` FROM `users` WHERE username = ''; DROP TABLE users; SELECT '';$statement = $dbo->prepare("SELECT `password` FROM `users` WHERE username = :username");
$statement->bindParam(':username', $username);
$statement->execute();
$user = $statement->fetch();
...<?php
$hash = password_hash('wreckingball', PASSWORD_DEFAULT);
// $2y$10$Wn1yu.o6.zIWejtVYQivlOBBjerpb3cHDVDvK0Az7ox0M.K9P6kyW
if(password_verify('wreckingball', $hash)) {
echo 'Passwort ist korrekt!';
}
?>Die User Experience bei Formularen ist wichtig, da zwischen dem Ziel des Benutzers (Kaufen, Beitreten, Bloggen) und dem Ziel des Unternehmen (Verkaufen, hohe Mitgliederzahl, mehr Content) oft ein Formular steht.
Gebrauch von Formularen, auf Smashing Magazine, gemäss Luke Wroblewski.
Ein Formular macht somit eine Website nutzbar.
Ein Formular muss somit nützlich sein.
Jedes Webformular besteht grundsätzlich aus drei Komponenten. Um eine positive User Experience zu schaffen muss sich die positive Erfahrung bei der Interaktion mit dem Formular durch sämtliche drei Ebenen ziehen.
Beziehung - Formulare ermöglichen eine Beziehung zwischen Benutzer und Unternehmen.
Dialog - Formulare ermögilchen eine Konversation zwischen Benutzer und Unternehmen.
Aussehen - Erst mit dem richtigen Aussehen und einer logischen Struktur ermöglichen das Formular den Aufbau einer Beziehung und eines Dialogs.
Ein Formular kann die Beziehung zwischen Benutzer und Unternehmen aufbauen und verstärken. Dies bedeutet jedoch auch, dass ein schlechtes Formular eine Beziehung beenden oder schwächen kann.
Eine Beziehung basiert auf Vertrauen. Somit muss das Formular vertrauenswürdig sein. Dies kann durch das allgemeine Design, Logo, Fotos, Farben, Typographie oder Wording erreicht werden. Der Benutzer muss sofort wissen, dass das hinter dem Formular eine seriösen Firma steht.
Jede Beziehung hat ein Ziel. Ob in einer romatischen Beziehung oder bei Businesspartnern, jede Beziehung hat ein Ziel. Was hat dein Formular für ein Ziel?
Passt der Name des Formulars zum Inhalt? Die Frage ist hier ob der Inhalt (Formularfelder) mit den Erwartungen des Benutzers übereinstimmen. Die Erwartungen des Benutzers werden im Link, Titel, Email etc. aufgebaut, mit dem er auf das Formular gelangt ist.
Passt das Formular zu meiner Zielgruppe? Es ist wichtig zu wissen, wer das Formular ausfüllt und was für Besonderheiten sich daraus schliessen lassen.
Anmeldeformular auf www.debenhams.com - die Bereiche für die unterschiedlichen Zielgruppen sind klar differenzierbar.
Die Grenzen des Formulars kennen. Das Formular darf keine Fragen enthalten, welche über den Sinn des Formulars hinausgehen - ansonsten wird der Benutzer misstrauisch.
Menschen lieben Konstanz. Darum ist es wichtig keine plötzlichen Änderungen in der Struktur und im Aussehen des Formulars einzubauen. Der Benutzer darf nicht in seinem 'Flow' unterbrochen werden.
Ein Formular ist keine einseitige Kommunikation, sondern ein Gespräch zwischen mehreren Parteien - ein Dialog. Somit muss immer im Hinterkopf behalten werden, dass der Benutzer ein Formlar ausfüllt um ein Gespräch mit dem Unternehmen zu beginnen.
Ein Formular ist somit ein Gespräch und kein Verhör. Aggressives und herrscherisches Wording lösen beim Benutzer eine Abwehrhaltung aus.
Dem logischen Aufbau einer Konversation folgen. Ein Formular muss einen logischen Aufbau aufweisen. Du würdest doch auch nicht in den ersten 5 Sekunden des Kennenlernens gleich nach der Telefonnummer fragen? Als Grundsatz gilt: Das Formular geht von den allgemeine Fragen (low Involvement) langsam über zu spezifischen Fragen (high Involvement).
Formular nach Themen sortieren. Menschen denken in Cluster: Fragt ein Formular beispielsweise Informationen über das Auto des Benutzers ab, ruft dieser das Cluster «Auto» in seine Gedanken auf. Nun kann er sämtliche Informationen über sein Auto schnell abrufen und das Formular ausfüllen.
Formularstruktur auf www.comparis.ch - Die Informationsgruppen sind optisch und inhaltlich klar aufgeteilt.
Pausen einbauen. Viele Benutzer fühlen sich vor allem von grossen Formularen überrumpelt. Dem kann entgegengewirkt werden, indem gezielt Abstände oder Seitenumbrüche in das Formular eingebaut werden.
Ablenkungen entfernen. Wie bei einem richtigen Dialog, stören Ablenkungen den Gesprächsfluss. Darum ist es wichtig den Fokus auf das Formular zu setzen und unnötige Ablenkungen zu eliminieren.
Anmeldeformular für Unternehmen auf www.dropbox.com - Die gesamte Seite wirkt sehr reduziert. So ist es möglich den Fokus komplett auf das Kaufformular zu richten.
Das Aussehen eines Formulars ist entscheidend für dessen Benutzerfreundlichkeit. Es gibt verschiedene Grundsätze und Tipps um das Aussehen eines Formulars zu gestalten:
Wörter vs. Sätze. Sätze machen es dem Benutzer bei komplexeren Fragestellungen einfacher das Feld auszufüllen. Hingegen kann der Benutzer sich bei einfachen Wörtern besser orientieren. Damit gibt es hier keine Grundregel, das Label muss je nach Situation angepasst werden.
Anmeldeformular bei Amazon www.amazon.ch - Die Anmeldeseite von Amazon nutzt ganze Sätze, obwohl einzelne Wörter absolut ausreichend sind.
Position des Labels. Auch hier gibt es keine abschliessende Regel und die unterschiedlichen Positionen des Labels bieten verschiedene Vor- und Nachteile. Hier findest du eine kleine Übersicht über die verschiedenen Positionen des Labels: Überblick über Label-Positionierung
Nur eine Kolonne. Der Aufbau eines Formulars sollte sich nicht über mehrere Spalten ziehen, sondern klar dem Verlauf von oben nach unten folgen.
Zweispaltiges Layout für Kontaktfomular www.steinegger-elektro.ch
Feldtypen richtig einsetzen. Beim Erstellen eines Formularfeldes sollte man sich stets folgende Frage stellen: Welches Feld eignet sich am besten für die gewünschte Information?
Gestaltung der Formularfelder. HTML5 und CSS3 bieten eine Vielfalt von verschiedenen Varianten um ein Formularfeld zu gestalten. Diese sollten jedoch nur beschränkt eingesetzt werden. Es ist wichtiger dem Benutzer eine einfache und intuitive Oberfläche zu bieten als ein schön gestaltetes aber benutzerunfreundliches Formular zu designen (Beispiel Pflichtfeld mit *).
Ein zu kreatives Kontaktfomular www.2advanced.com - Kreativität nie vor Funktionalität.
Primäre und Sekundäre Aktionen unterscheiden. Die primäre und die sekundäre Aktion eines Formulares müssen optisch klar unterschieden werden. So wird der Button für das Versenden des Formulars anders dargestellt als der Button für das Abbrechen.
Klare Unterscheidung der Aktionen www.hooklinemusic.de. Die Aktionen auf den Buttons sind optisch nicht in eine primäre und eine sekundäre Aktion differenzierbar.
Aktionen benennen. Aktionen wie «Submit» oder «Reset» sollten nach den eigentlichen Aktionen beschriftet werden. Beispielsweise «Registrieren», «Anfrage senden» etc.
Tausche den Platz mit deinem/r Nachbar/in und schau sein/ihr Formular im Browser an (nicht den Code). Vergleiche es mit den obigen Empfehlungen und schreibe ein paar Feedback-Punkte auf:
2-3 Aspekte, die du benutzerfreundlich findest, die mit den Empfehlungen (oben) übereinstimmen.
2-3 Empfehlungen, was noch verbessert werden kann.
Wenn du fertig bist, tauscht eure Feedbacks aus.
Zeit: 10 Minuten inkl. Austausch.
2
2
Das Validieren oder «Prüfen» der empfangenen Daten ist enorm wichtig, da unerwartete oder nicht vorhandene Daten zu fehlerhaften oder unbrauchbaren Resultaten führen.
Grundsätzlich gilt es die Daten auf folgende Punkte zu überprüfen:
Sind alle benötigten Daten vorhanden (alle Pflichtfelder ausgefüllt)?
Sind die Daten logisch, liegen sie in einem gültigen Bereich (Geburtsdatum in der Vergangenheit)?
Eine Variable wird in PHP durch das Dollar-Zeichen $ gefolgt vom Namen der Variablen dargestellt. Bei Variablen-Namen wird zwischen Gross- und Kleinschreibung unterschieden (case-sensitive).
Ein gültiger Variablen-Name beginnt mit einem Buchstaben oder einem Unterstrich, gefolgt von einer beliebigen Anzahl von Buchstaben, Zahlen oder Unterstrichen.
Dies ist der einfachste Typ. Ein boolean Ausdruck ist ein Wahrheitswert der entweder TRUE (wahr) oder FALSE (falsch) sein kann.
<label for="firstname">Vorname</label>
<input type="text" id="firstname" name="firstname">
<label for="lastname">Nachname</label>
<input type="text" id="lastname" name="lastname">
<label for="work">Beruf</label>
<input type="text" id="work" name="work">
<label for="workplace">Arbeitgeber</label>
<input type="text" id="workplace" name="workplace"><fieldset>
<legend>Informationen zur Person</legend>
<label for="firstname">Vorname</label>
<input type="text" id="firstname" name="firstname">
<label for="lastname">Nachname</label>
<input type="text" id="lastname" name="lastname">
</fieldset>
<fieldset>
<legend>Informationen zum Beruf</legend>
<label for="work">Beruf</label>
<input type="text" id="work" name="work">
<label for="workplace">Arbeitgeber</label>
<input type="text" id="workplace" name="workplace">
</fieldset><form action="https://mydomain.xyz/process.php" method="post">
<fieldset>
<legend>Informationen zur Person</legend>
<label for="firstname">Vorname</label>
<input type="text" id="firstname" name="firstname">
<label for="lastname">Nachname</label>
<input type="text" id="lastname" name="lastname">
</fieldset>
<fieldset>
<legend>Informationen zum Beruf</legend>
<label for="work">Beruf</label>
<input type="text" id="work" name="work">
<label for="workplace">Arbeitgeber</label>
<input type="text" id="workplace" name="workplace">
</fieldset>
<button type="submit" name="form-submit">Anmeldung einreichen</button>
</form> Wir möchten den Shuttle-Bus-Service beanspruchen:
<input value="1" type="checkbox" name="shuttlebus"><br>
Name: <input type="text"><br>
Wie viele Personen werden von Ihrer Firma teilnehmen?:
<input min="0" type="number" name="num_persons"><br>
Telefon: <input type="text" name="phone"><br>
Haben Sie sonst noch einen Wunsch oder eine Bemerkung?
<textarea name="note" id="note" rows="3"></textarea><br>
In welchem Hotel möchten Sie übernachten?
<input type="radio" name="hotel" value="InterContinental Davos">
<input type="radio" name="hotel" value="Steinberger Grandhotel Belvédère"><br>
Email: <input type="email"><br>
<input type="submit" name="submit" value="Anmelden">
Was möchten Sie am Abend unternehmen?
<select name="activity">
<option value="">Kein Abendprogramm</option>
<option value="Billardturnier">Billardturnier</option>
<option value="Bowlingturnier">Bowlingturnier</option>
<option value="Weindegustation">Weindegustation</option>
<option value="Asiatischer Kochkurs">Asiatischer Kochkurs</option>
<option value="Tanzkurs für Webentwickler">Tanzkurs für Webentwickler</option>
<option value="Ying & Yang Yoga Einsteigerkurs">Ying & Yang Yoga Einsteigerkurs</option>
</select><br>Als Integer werden ganze, natürliche Zahlen bezeichnet.
Eine Gleitkommazahl bezeichnen wir als Float.
Ein String stellt eine Kette von Zeichen dar. Ein String muss mit doppelten "oder einfachen Anführungszeichen ' umschlossen werden.
Strings können konkateniert (=verkettet) werden. Dafür verwenden wir den . Operator.
Es ist auch möglich Variablen zu verketten.
Variablen können direkt in einen String eingefügt werden, sofern dieser mit " deklariert wird.
Es kann auch die alternative Schreibweise mit geschweiften Klammern verwendet werden.
... oder einfacher gesagt: Rechnen.
Um den Wert einer Variable um eins zu erhöhen oder zu reduzieren, kann der Inkrement- ++ oder Dekrementoperator -- verwendet werden.
Gehe zur bereits erstellten Datei index.php zurück. Lagere das Fahrzeug Auto deines Echo-Konstrukts in eine Variable aus.
Ersetze nun den Wert Auto mit einem anderen Fahrzeug (Velo|Bus|Roller|Töffli) und überprüfe, ob dein Echo-Konstrukt noch funktioniert.
$variable; // Gültig
$_variable; // Gültig
$2ql4sql; // Ungültig
$sonder!zeichen; // Ungültig
$variable_mit_umläut; // Gültig, jedoch nicht empfohlen$ichBinWahr = true;
$ichBinFalsch = false;$alter = 17;$einViertel = 0.25;
$mwst = 8.0;echo 'Ein einfacher String';
echo "Ein einfacher String";echo 'Ein' . ' verketteter ' . 'String';
// Verkettungen sind auch über mehrere Zeilen möglich
echo 'Ein sehr langer unleserlicher '
. 'und deshalb über mehrere Zeilen verketteter '
. 'String als Beispiel.';$popstar = 'Mariah Carey';
echo 'Ich liebe die Musik von ' . $popstar;
// Ich liebe die Musik von Mariah Carey$popstar = 'Mariah Carey';
echo "Ich liebe die Musik von $popstar.";
// Ich liebe die Musik von Mariah Carey.
echo 'Ich liebe die Musik von $popstar.';
// Ich liebe die Musik von $popstar.echo "Ich liebe die Musik von ${popstar}.";
echo "Ich liebe die Musik von {$popstar}.";$a = 2;
$b = 4;
echo $a + $b; // Addition
// 6
echo $a - $b; // Subtraktion
// -2
echo $a * $b; // Multiplikation
// 8
echo $a / $b; // Divison
// 0.5
echo $a % $b; // Modulus (Rest)
// 2$a = 5;
$a++;
echo $a;
// 6
$a--;
echo $a;
// 5Damit nicht mit der Superglobalen $_POST gearbeitet werden muss, bietet es sich an, die Werte in eigene Variablen zu übernehmen und bei nicht vorhandenen Feldern ein eigener Standard-Wert zu setzen.
Um zu überprüfen, ob ein Formularfeld in einem POST-Request vorhanden ist, muss die Existenz des gewünschten Schlüssels in der $_POST Variable überprüft werden.
Eine Möglichkeit dies zu tun ist mittels isset(). isset() überprüft, ob eine Variable vorhanden und nicht null ist. Es wird nicht überprüft, ob die Variable leer ist.
Der Null Coalesce Operator bietet uns eine einfache Möglichkeit, obenstehenden Code zu vereinfachen.
Bevor die Daten weiter validiert werden, sollten sie noch bereinigt werden. Leerzeichen am Anfang und Ende einer Eingabe, möchten wir zum Beispiel entfernen. Dafür können wir die trim-Funktion verwenden.
So können wir verhindern, dass ein Pflichtfeld nur mit Leerzeichen gefüllt wird. Auch Leerzeichen am Ende von Email-Adressen, wie sie von Smartphone-Tastaturen gerne automatisch hinzugefügt werden, führen für den Besucher so nicht zu einer «Ungültige Email-Adresse»-Fehlermeldung.
Leere Eingaben sind für Pflichtfelder nicht erwünscht. Wir können überprüfen, ob die Variable einem leeren String '' entspricht oder die Länge 0 Zeichen beträgt.
Die logische Überprüfung variiert je nach Feld. Ein Geburtsjahr darf beispielsweise nie in der Zukunft liegen. Bei einem Mindestalter muss das eingegebene Datum maximal X Jahre in der Vergangenheit liegen. Nutze dazu einfache Vergleiche.
(Bitte beachte, dass im letzten Beispiel natürlich das gesamte Geburtsdatum beachtet werden sollte, nicht nur das Jahr. Hier wurde das Beispiel vereinfacht.)
PHP bietet zur Überprüfung von einigen Formaten die Funktion filter_var an.
Die Syntax ist wie folgt:
filter_var liefert bei einer ungültigen Eingabe einen Wert von false zurück. Andernfalls werden die Eingabe-Daten zurückgegeben. Es ist also wichtig einen typenstarken Vergleich mit false zu verwenden.
Als Filter-Konstante kann einer der folgenden Werte verwendet werden:
FILTER_VALIDATE_BOOLEAN
FILTER_VALIDATE_EMAIL
FILTER_VALIDATE_FLOAT
FILTER_VALIDATE_INT
FILTER_VALIDATE_IP
FILTER_VALIDATE_URL
Um zu überprüfen, ob eine gültige URL eingegeben wurde, könnte also folgender Code verwendet werden:
Bitte beachte, dass FILTER_VALIDATE_EMAIL bei Eingaben mit Umlauten false zurück gibt. Eine Email mit Umlauten ist jedoch theoretisch gültig (kontakt@höhenluft.ch). Die Überprüfung von Email-Adressen ist ohnehin ein heikles Thema. Am besten wird nur überprüft, ob das @-Symbol vorhanden ist. Alle anderen Regeln könnten sonst ungewöhnliche aber dennoch gültige Email-Adressen als ungültig erkennen.
Formate, die mit filter_var nicht überprüft werden können, lassen sich mit regulären Ausdrücken validieren.
Mit der Funktion preg_match und dem regulären Ausdruck /^\d{2}\.\d{2}\.\d{2,4}$/ lässt sich beispielsweise das Format eines Datums überprüfen.
Dieser Ausdruck würde auch das Datum 40.80.8000 zulassen, für eine grobe Überprüfung reicht er jedoch aus.
Eine genauere Überprüfung wurde folgender Ausdruck bringen.
Wie Du siehst, wird es sehr schnell sehr komplex. Reguläre Ausdrücke sind daher ein Thema für sich und werden in diesem ÜK nur teilweise behandelt.
Siehe auch PHP: Reguläre Ausdrücke auf wikibooks.org
Tool zum Testen von Ausdrücken: PHP Live Regex
Alle Felder, ausser das für die Bemerkung, sind Pflichtfelder.
Für die Validierung der Email-Adresse überprüfen wir, ob es sich um eine gültige Email-Adresse handelt.
Für die Telefonnummer dürfen nur Zahlen, Leerzeichen und das + Symbol eingegeben werden. Alle anderen Eingaben sind ungültig.
Wenn keine Fehler vorhanden sind, gib einfach nur den String OK nach der Validierung der Daten aus.
Die folgenden Testfälle sollte dein Formular erfüllen:
Feld
Input
Fehlermeldung
Name
'' (leer)
Bitte geben Sie einen Namen ein.
''
Bitte geben Sie eine Email ein.
Telefon
''
Bitte geben Sie eine Telefonnummer ein.
Anzahl Personen
Speichere alle gefundenen Fehler in ein$errors Array.
Sofern ein Fehler gefunden wurde, soll in der form.view.php eine Liste sämtlicher angezeigt werden:
Falls kein Fehler gefunden wurde, wird die neue View success.view.php geladen.
Damit der Benutzer bei einem Fehler nicht wieder alle Daten einfüllen muss. Fülle die eingetragenen Felder wieder in das Formular ab.
Hier ein Beispiel dazu:

Ein Server ist ein Programm, das mit einem anderen Programm kommuniziert, dem Client. Dabei verschafft der Server dem Client Zugang zu einem Dienst. Hierbei muss abgrenzend beachtet werden, dass es sich bei «Server» um eine Rolle handelt, nicht um einen Computer an sich. Ein Computer kann dementsprechend ein Server und Client zugleich sein.
Ein Client kann einen Dienst bei einem Server anfordern, welcher diesen Dienst bereitstellt.
Zusätzlich zu den internen Funktionen, können wir auch eigene Funktionen definieren.
Eine Funktion kann wie folgt definiert werden:
Für die Funktionsnamen gelten die gleichen Regeln wie für .
Einer Funktion können Argumente mitgegeben werden, auf die innerhalb der Funktion zugegriffen werden können.
Einem Argument kann ein Standardwert zugewiesen werden. Somit ist die Eingabe dieses Arguments optional.
if($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = ''; // Standard-Wert für die Variable setzen
if(isset($_POST['name'])) {
// Das Feld wurde mitgesendet, wir können den Wert also übernehmen
$name = $_POST['name'];
}
}if($_SERVER['REQUEST_METHOD'] === 'POST') {
// $_POST['name'] in die Variable übernehmen,
// falls vorhanden. Ansonsten '' als Standardwert,
// in die Variable speichern.
$name = $_POST['name'] ?? '';
}// ' Franz ' wird zu 'Franz'
// ' ' wird zu ''
$name = trim($name);if($name === '') {
echo 'Bitte geben Sie einen Namen ein.';
}
if(strlen($email) < 5) {
echo 'Bitte geben Sie eine Email ein.';
}if($geburtsjahr > date('Y')) { // date('Y') = aktuelles Jahr (z. B. 2016)
echo 'Geburtsjahr muss in der Vergangenheit liegen.';
}
if($geburtsjahr > (date('Y') - 18)) { // Geburtsjahr > 1998 -> Minderjährig
echo 'Du musst volljährig sein! ಠ_ಠ';
}filter_var($variable, FILTER_KONSTANTE);if(filter_var($url, FILTER_VALIDATE_URL) === false) {
echo 'URL ist ungültig!';
} else {
echo 'URL ist gültig!';
}$datum = '24.11.1990';
if(preg_match('/^\d{2}\.\d{2}\.\d{2,4}$/', $datum)) {
echo 'Datum ist gültig!';
} else {
echo 'Datum ist ungültig!';
}^(0[1-9]|[12][0-9]|3[01])\.(0[1-9]|1[012])\.(19|20)[0-9]{2}$<ul>
<li>Fehler #1</li>
<li>Fehler #2</li>
...
</ul><input type="text" id="name" name="name" value="<?= $name ?? '' ?>">''
Bitte geben Sie die Anzahl teilnehmender Personen ein.
Hotel
''
Bitte wählen Sie ein Hotel für die Übernachtung aus.
'google.com'
Die Email-Adresse "google.com" ist ungültig.
Telefon
'phone'
Die Telefonnummer "phone" ist ungültig.
Anzahl Personen
'Acht'
Bitte geben Sie für die Anzahl Personen nur Zahlen ein.
keine
keine
Telefon
'+41 260 30 39'
keine
Name
'Peter'
keine
Anzahl Personen
'5'
keine
Bemerkung
''
keine
Eine Funktion kann über das return Konstrukt einen Wert zurückgeben.
Ein return beendet die Ausführung der Funktion. Code, der nach einem return steht wird nicht ausgeführt.
Seit PHP 7 können für die Funktionsargumente sowie für den Rückgabewert einer Funktion Typen definiert werden.
Der Geltungsbereich einer Variablen ergibt sich aus dem Zusammenhang, in dem sie definiert wurde. Anders ausgedrückt: Globale Variablen sind in Funktionen nicht zugänglich. Variablen die in Funktionen definiert wurden, sind ausserhalb der Funktion nicht zugänglich.
Über das global Schlüsselwort können Variablen aus dem globalen Geltungsbereich in einer Funktion zugänglich gemacht werden.
Erstelle eine Funktion dumpAndDie, welche einen $wert ausgibt und die Script-Ausführung beendet (var_dump($wert); die();).
Lagere die Funktion in eine separate Datei functions.php aus und binde diese in die Datei index.php ein. Teste die Funktionsweise indem du das Array $vehicles an die Funktion übergibst.
Erstelle eine benutzerdefinierte Funktion mit folgenden Kriterien:
An die Funktion wird das assoziative Array $car mit den Attributen Marke, Farbe und Jahrgang übergeben.
Die Funktion überprüft, ob das Auto ein Oldtimer ist.
Oldtimer sind Autos, welche vor dem Jahr 1990 gebaut wurden.
Die Funktion gibt je nach Prüfergebnis ein true oder false zurück.
War das Prüfergebnis positiv, wird folgender String ausgegeben: "Beim Renault handelt es sich um einen Oldtimer."
War das Prüfergebnis negativ, wird folgender String ausgegeben: "Beim Renault handelt es sich um keinen Oldtimer."
(Die Ausgaben können provisorisch in der Datei index.php ausgeführt werden und müssen nicht an die HTML-Datei übergeben werden.)
function funktionsname($argument1, $argument2) {
echo $argument1 . ' ' . $argument2;
}
// Funktion aufrufen
funktionsname('Hallo', 'Welt');
// Hallo Weltfunction sagwas($wort1, $wort2 = 'Welt') {
echo $wort1 . ' ' . $wort2;
}
sagwas('Hallo');function sagwas($wort1, $wort2) {
return $wort1 . ' ' . $wort2;
}
echo sagwas('Hallo', 'Welt');
// Hallo Welt
$wert = sagwas('Hallo', 'ÜK');
echo $wert;
// Hallo ÜK;function sagwas($wort1, $wort2) {
return $wort1 . ' ' . $wort2;
echo 'Wird nie ausgeführt.';
}function addiere(int $zahl1, int $zahl2) : int {
return $zahl1 + $zahl2;
}
$resultat = addiere(3, 7);
var_dump($resultat);
// int(10)$zahl = 20; // Global
function demo() {
return $zahl;
}
echo demo();
// PHP Notice: Undefined variable: zahlfunction demo() {
$zahl = 20; // In Funktion, aber nicht zurückgegeben via return
echo $zahl;
}
demo();
// 20
echo $zahl;
// PHP Notice: Undefined variable: zahl$zahl = 20; // Global
function demo() {
global $zahl;
return $zahl;
}
echo demo();
// 20 dumpAndDie($vehicles);function isOldtimer(array $car): bool
{
// if ...
}
if (isOldtimer($car)) {
echo "Beim " . $car['brand'] . " handelt es sich um einen Oldtimer.";
} else {
// ...
}Hyper Text Transfer Protocol (HTTP) ist ein Protokoll für den Transfer von Daten, in unserem Fall Websites (wie die Website, welche du gerade betrachtest). Ein Protokoll ist nichts anders als ein Standard etwas zu tun.
Angenommen du trifft den Präsident der Vereinigten Staaten oder die Queen von England. In diesen beiden Fällen gibt es bestimmte Prozeduren, welchen du befolgen musst. Du kannst nicht einfach zu dieser Person hinlaufen und sagen «Hey Alter!» oder «Was geht ab, Brudi!». Es gibt bestimmte Vorschriften beim Gehen, beim Sprechen, bei der Begrüssung und auch bei der Verabschiedung.
Teil
Bezeichnung
Bedeutung
http://
Protokoll
Sagt dem Browser, welches Protokoll er zum Aufruf der URL verwenden soll. Andere Protokolle sind z. B. HTTPS oder FTP.
www.website.com
Domainname
Sagt dem Browser, zu welchem Server er die Verbindung aufbauen soll. Der Domainname wird via DNS in eine IP-Adresse umgewandelt.
/suchen.html
Dateipfad
Sagt dem Browser, welche Datei er ab dem Server aufrufen soll.
?q=Suchbegriff&page=1
Ein HTTP-Request besteht aus einem Header und einem Body. Im Header werden Informationen wie die HTTP-Methode (POST) oder die zu ladende Datei definiert. Es können auch weitere Details zum Inhalt des Requests angegeben werden (beispielsweise ein Content-Type).
Im Body eines HTTP-Requests können Daten an den Server mitgesendet werden. Die Daten werden hier im gleichen Query-String-Format übergeben, wie man sie auch direkt in der URL übergeben kann.
Der Benutzer öffnet seinen Browser (den Client).
Der Benutzer öffnet die Website http://google.ch/.
Der Client (auf Befehl des Benutzers), sendet eine Anfrage zum Server http://google.ch/ für die Website.
Der Server erkennt die Anfrage und antwortet dem Client mit den Meta-Daten, gefolgt vom Sourcecode der Website.
Der Client empfängt den Sourcecode der Website und übersetzt diesen in eine für den Benutzer lesbare Website.
Der Benutzer tippt das Suchwort ICT Berufsbildung in das Suchfeld und drückt Enter.
Der Client übermittelt die Daten (das Suchwort) zum Server.
Der Server verarbeitet diese Daten und sendet die Resultate am Client zurück.
Wieder übersetzt der Client den erhaltenen Sourcecode vom Server zu einer für den Benutzer lesbaren Website.
Wie bereits erwähnt besteht ein Aufruf immer aus einem Server und einem Client
Der Server und der Client kommunizieren mit dem HTTP Protokoll untereinander. Damit ist es für den Client möglich Anfragen an den Server zu senden und die Antwort darauf zu erhalten.
Der Client und Server müssen keine getrennten Harware-Systeme sein, sondern können auf dem gleichen Rechner installiert werden. Dabei besteht der Client aus dem Browser und der Server aus einem Webserver.
Der Client schickt einen HTTP-Request http://google.ch [1].
Der Server empfängt den Request und stellt darauf die Daten bereit, sprich den Sourcecode [2]. Diese Daten schickt er anschliessend dem Client zurück [3].
Der Client empfängt die HTTP-Response des Servers und wandelt den Sourcecode in eine für den Menschen lesbare Form um: Die Website [4].
Bestimmte Code-Schnippsel müssen aber nicht einfach nur abgerufen und angezeigt werden, sonder ebenfalls noch interpretiert. Serverseitig hält deshalb ein PHP-Interpretator Ausschau nach PHP-Code, um diesen auszuführen.
Das gleiche passiert nach der Übertragung zum Client durch den JavaScript-Interpretator.
Die umhergereichten Daten können beispielsweise im Browser in den Developer Tools (F12) unter "Network" betrachtet werden.
Achtung!!! Sei vorsichtig, wenn du eine solche Datei auf einem Webserver veröffentlichst, da die angezeigten Infos sehr viel über dein System verraten und für Angriffe ausgenutzt werden können.
Dir wird nun eine Übersicht aller für PHP relevanten Einstellungen gezeigt.
Sieh dir die Apache Environment und HTTP Headers Information Sektionen einmal an. Was für Informationen werden angezeigt?
Für die Konfiguration deiner PHP-Installation stehen diverse Einstellungen zur Verfügung, sogenannte Direktiven.
Die wichtigste Sektion in der phpinfo()-Ausgabe ist die Core-Sektion. Diese Einstellungen gelten für den PHP-Core selbst, also nicht für eines der installierten Module (wie z. B. mysql oder session).
Eine Direktive ist eine einzelne Einstellung. Sie setzt sich immer aus einem Namen und einem Wert zusammen.
Direktive
Bedeutung
display_errors
Legt fest, ob Fehlerausgaben angezeigt werden sollen.
log_errors
Legt fest, ob Fehler in eine Datei geloggt werden sollen.
error_log
Den Pfad zur Datei, in der Fehler geloggt werden.
error_reporting
Legt fest, welche Fehler beachtet werden sollen, und welche nicht.
max_execution_time
Legt fest, wie viele Sekunden ein PHP-Script maximal für seine Ausführung Zeit hat, bevor die Ausführung abgebrochen und ein Fehler generiert wird.
Alle Einstellungen für Deine PHP-Installation lassen sich in der php.ini bearbeiten. Diese befindet sich bei XAMPP unter C:\xampp\php\php.ini.
Öffne die Datei in Deinem Texteditor.
In dieser Datei befinden sich alle Direktiven, einschliesslich einer kurzen Info dazu. Zeilen, die mit einem ; beginnen sind auskommentiert und werden nicht beachtet.
Das Format für jede Direktive ist
Du kannst in dieser Datei alle Einstellungen bearbeiten. Wenn Du eine Änderung an der Konfiguration vorgenommen hast, musst Du anschliessend den Apache-Dienst neu starten.
Die wichtigste Einstellung, welche wir jetzt gleich zusammen festlegen möchten, ist das Error-Reporting.
In PHP gibt es mehrere Error-Levels. PHP versucht stehts, gefundene Fehler in Scripts selber zu beheben. Je nach Error-Level generiert PHP dabei eine Fehlerausgabe oder bricht bei gravierenden Fehlern die Ausführung des Scripts komplett ab.
Error-Level
Beschreibung
E_ERROR
Fatale Laufzeit-Fehler. Dies zeigt Fehler an, die nicht behoben werden können. Die Ausführung des Scripts wird abgebrochen.
E_WARNING
Warnungen (keine fatalen Fehler) zur Laufzeit des Scripts. Das Script wird nicht abgebrochen.
E_PARSE
Parser-Fehler während der Übersetzung. Das auszuführende Script enthält Syntax-Fehler. Das Script wird abgebrochen.
E_NOTICE
Eine mögliche Fehlerquelle wurde entdeckt (z. B. undefinierte Variable). Das Script wird trotzdem ausgeführt.
Hier befindet sich eine komplette Liste der vordefinierten Fehler-Konstanten.
Egal ob deine Scripts in einer Produktiv- oder Entwicklungsumgebung ausgeführt werden, es ist immer wichtig, über alle Fehler informiert zu werden. Unsere error_reporting Direktive setzen wir also auf E_ALL (es werden somit alle auftretenden Fehler gemeldet.)
In deiner Entwicklungsumgebung ist es nützlich auftretende Fehler direkt im Output anzuzeigen. Auf einer Produktivumgebung hingegen möchten wir für die Öffentlichkeit nichts von unserem System preisgeben und zeigen die internen PHP-Fehlermeldungen nicht an. Im Fehlerfall werden wir für unsere Besucher eigene Fehlerseiten erstellen.
Für unsere Entwicklungsumgebung lassen wir uns also alle Fehler anzeigen:
In einer Produktivumgebung solltest Du diesen Wert immer auf Off setzen. Nutze dafür die log_errors Direktive und lasse Dir die Fehlermeldungen in einer Datei (definiert via error_log) protokollieren.
Speichere deine php.ini ab, starte Apache neu und aktualisiere deine info.php im Browser.
Stelle sicher, dass error_reporting einen Wert von 32767 (=E_ALL) hat, und display_errors auf On ist.

Selbst mit unserem relativ einfachen Tool wird sehr schnell klar, dass beim Platzieren des kompletten Codes in einem Ordner die Übersicht verloren geht.
Die Strukturierung einer Webapplikation ist ein wichtiger Bestandteil und sollte nicht vernachlässigt werden. Wie du die Applikation strukturierst, ist dir überlassen. Es gibt jedoch klare Komponenten, die jede Webapplikation besitzt und die irgendwo untergebracht werden müssen.
(Zum schrittweisen Aufbau der Applikations-Struktur)
Ein Router delegiert eine Anfrage oft an einen Controller. Dieser verwaltet dann den Datenaustausch zwischen einem Model und einer View. Das Model dient als Schnittstelle zu einer Datenquelle (z. B. einer Datenbank). Die View dient zur Darstellung der Daten.
Ein Benutzer interagiert mit einer View - in dem er auf einen Link klickt oder ein Formular absendet.
Ein Controller bearbeitet den Benutzer-Input und übersetzt die Daten für das Model.
Das Model empfängt die Daten, verändert diese (schreibt sie in eine Datenbank, macht eine Kalkulation oder löscht etwas).
Diese drei Komponenten werden in der für Webapplikationen sehr beliebten MVC-Architektur verwendet. Dies ist jedoch nur eine von vielen gängigen Architekturen.
Als Front-Controller bezeichnen wir den «Einstiegspunkt» unserer Webapplikation. Über diesen wird jeder Seitenaufruf entgegengenommen und dann verarbeitet.
Wenn der Front-Controller einen Request entgegennimmt, muss dieser herausfinden, was damit geschehen soll. Dafür wird oft ein Router erstellt. Dieser definiert, wie eine spezieifsche Anfrage/URL verarbeitet werden soll.
Für den ÜK steht ein einfaches MVC-Framework zur Verfügung:
Lade das herunter.
Erstelle einen Ordner aufgaben in C:\xampp\htdocs\modul-307\ und extrahiere das Framework da.
Ergänze später je Aufgabe die benötigten Routes-Einträge in index.php, sowie die nötigen Controller, Models und Views.
Die index.php ist der Einstiegspunkt für alle Anfragen an unser Framework. Hier wird das Framework geladen (core/bootstrap.php), $routes werden definiert und anschliessend ausgeführt.
Über die $routes Variable in der index.php lassen sich beliebig viele Routes definieren:
Im obenstehenden Beispiel wird beim Aufruf von http://localhost/modul-307/aufgaben/hallo/welt die index Methode auf dem WelcomeController ausgeführt.
Die Controller@Methode Schreibweise ist vom Framework vorgegeben. Der Router kann anhand dieser Schreibweise entscheiden, welche Controller-Methode aufgerufen werden soll.
Im Ordner app/Controllers werden alle Controller-Klassen abgelegt. In den einzelnen Methoden können Variablen definiert werden. Am Ende einer Controller-Methode wird üblicherweise via require eine View geladen.
Im app/Views Ordner können einzelne View-Dateien abgelegt werden. In diesen sollte nur noch sehr wenig PHP-Code vorhanden sein! Es sollten lediglich einfache if und foreach Statements verwendet werden. Aufwändigere Logik gehört in den Controller oder separate Klassen.
In der Datei core/helpers.php gibt es ein paar nützliche Funktionen. Schaue diese an und versuche sie zu verstehen!
Alle CSS, JS und Bild-Dateien gehören in den public Ordner.

Erstelle das Reaktionsspiel «Whack-a-Button» in JS.
Ein Button verschiebt sich nach einer gewissen Zeit, nachdem man mit dem Mauscursor darüber fährt, an eine neue Stelle auf dem Bildschirm. Das Ziel das Spielers ist es, den Button möglichst oft anzuklicken.
Nach jedem erfolgreichen Klick verschiebt sich der Button schneller an eine neue Stelle, was das Klicken erschwert.
http://www.website.com/suchen.html?q=Suchbegriff&page=1#treffer-5POST /pfad/zu/script.php HTTP/1.0
User-Agent: Chrome/31.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
suchbegriff=test&encoding=utf8<?php
phpinfo();
?>direktiven_name = werterror_reporting = E_ALLdisplay_errors = Onmemory_limit
Legt fest, wie viele MB RAM PHP maximal für die Ausführung eines Scripts belegen darf.
Der Controller nimmt die Daten wieder entgegen und gibt diese falls nötig an eine View weiter.
Die View wartet wiederum für die nächste Interaktion mit dem Benutzer.
Entwickle das Script immer nur so weit, bis alle Komponenten des jeweiligen Schrittes komplett erfüllt werden. Erweitere es anschliessend, damit der nächste Schritt erfüllt wird.
Erstelle im Controller GameController.php eine zweite Methode mit dazugehöriger Route, welche die View whack-a-button.view.php lädt. Die View ist eine einfache HTML-Seite, welche die neue Datei whack-a-button.js aus dem public/-Ordner lädt.
Füge auf dieser Seite einen Button hinzu und positioniere diesen via CSS absolut im Body (position: absolute).
Erstelle ein Code-Block der ausgeführt wird, sobald das DOM ready ist:
Erstelle im Code-Block einen querySelector, der dein Button auswählt und speichere diesen in die Variable button.
Erstelle ein Event-Listener für den mouseenter Event. Dieser wird ausgeführt, sobald der Mauszeiger sich über den Button bewegt.
Vorübergehend soll eine Meldung in der Konsole ausgegeben werden, sobald der Event auftritt.
Stelle sicher, dass in deiner Konsole nur die gewünschte Ausgabe erscheint und keine Fehler vorhanden sind.
Sorge dafür, dass der Button beim mouseenter Event automatisch an die Position left: 10% und top: 15% verschoben wird. Dies kannst Du mit Hilfe der style Property lösen.
Anstelle der fixen Position, möchten wir diese nun mit Hilfe von Math.random()zufällig neu generieren.
Erweitere dein Script so, dass der Button beim mouseenter immer an eine neue Stelle springt.
Momentan ist der Button noch viel zu schnell um ihn überhaupt 1x anklicken zu können. Daher müssen wir das Verschieben des Buttons etwas verzögern. Dazu können wir die Funktion setTimeout verwenden.
Sorge dafür, dass der Button sich erst 1 Sekunde nach dem mouseenter Event verschiebt.
Probleme mit this?
setTimeout ändert die Bedeutung des this Schlüsselwortes. Wenn du also den Button vorhin über this.style neu positioniert hast, wird dies innerhalb von setTimeout nicht mehr funktionieren, da this nicht mehr auf den Button verweist. Dieses Problem kannst du einfach lösen, in dem du this vor dem setTimeout in eine Variable speicherst und diese verwendest:
Alternativ kann mittels bind das this Schlüsselwort neu definiert werden.
Der Button bewegt sich nun wie gewünscht. Was wir noch benötigen ist eine Möglichkeit, den Klick auf den Button zu registrieren.
Gib den Text Angeklickt! in der Konsole aus, sobald auf den Button geklickt wird.
Es wird Zeit, Punkte zu zählen!
Erstelle eine Variable points und erhöhe deren Wert jeweils um 1, sobald auf den Button geklickt wurde. Gib den neuen Punktestand in der Konsole aus.
Unser Spiel funktioniert schon fast wie gewünscht. Einzig die Verzögerung ist derzeit noch fix auf 1 Sekunde eingestellt.
Ersetze die setTimeout-Verzögerung von derzeit 1000 Millisekunden mit einer neuen Variable delay.
Reduziere delay nach jedem Klick auf den Button um 25 Millisekunden.
Gib den aktuellen Wert von delay zusammen mit dem Punktestand in der Konsole aus.
Anstatt den Spielstand in der Konsole auszugeben, füge einen Status-Block in die View ein und setze die aktuellen Werte mit JS:
Wenn das Spiel fertig ist, zeige im "GAME OVER!" an (auch mittels ´innerHtml´)
Uns wurde ein Bug im Spiel gemeldet!
Wenn der Spieler den Mauszeiger innerhalb einer Sekunde mehrmals über den Button und wieder davon weg bewegt, verschiebt sich der Button anschliessend mehrere Male.
Was ist hier das Problem? Wie kannst Du es beheben?
Mögliche Lösungen zu den Aufgaben werden dir vom Kursleiter bereitgestellt. Natürlich ist die Ausgabe des Scripts entscheidend, nicht der Code dazu.
Es sind also mehrere Lösungen möglich, solange durch die richtige Logik die gewünschte Ausgabe erzeugt wird.
Query-String/Parameter
Parameter, die der Datei übergeben werden. Beginnend mit ? werden sie jeweils als schlüssel=wert-Paare getrennt von & der URL angefügt.
#treffer-5
Anker
Verweist auf eine bestimmte Stelle innerhalb des Dokuments. Diese Stelle kann z. B. mit einem id="treffer-5" Attribut in HTML definiert werden.






Die Syntax ähnelt sehr jener von PHP. Daher wird diese Übersicht etwas kürzer gehalten.
Wie in PHP werden alle Anweisungen durch ein Semikolon ; beendet. Zeilenumbrüche und Leerzeichen sind nicht relevant. Die Gross- und Kleinschreibung wird in JavaScript berücksichtigt! (Case-Sensitive)
Ein Softwaretest prüft und bewertet Software auf Erfüllung der für ihren Einsatz definierten Anforderungen und misst ihre Qualität. Die gewonnenen Erkenntnisse werden zur Erkennung und Behebung von Softwarefehlern genutzt. Tests während der Softwareentwicklung dienen dazu, die Software möglichst fehlerfrei in Betrieb zu nehmen.
Softwaretests geben dem Entwickler die Gewissheit, dass bestimmte Komponenten seines Projekts wie von ihm getestet funktionieren. Sie erbringen aber keinen allgemeinen Nachweis, dass keine Programmfehler vorhanden sind.
$routes = [
'/hallo/welt' => 'WelcomeController@index',
];<?php
class WelcomeController
{
public function index()
{
$hello = 'Viel Spass beim Programmieren!';
require 'app/Views/welcome.view.php';
}
}<button id="button">Whack me!</button><script>
document.addEventListener('DOMContentLoaded', function() {
// DOM ist ready!
});
</script>var button = document.querySelector('...');button.addEventListener('mouseenter', function() {
// Wird ausgeführt, sobald die Maus sich
// über den Button bewegt
console.log('Maus ist darüber!');
});var button = this;
setTimeout(function() {
button.style...
}, 500);setTimeout(function() {
this.style...
}.bind(this), 500); ....
<style>
#status {
position: fixed;
top: 10px;
right: 10px;
}
</style>
</head>
<body>
<div id="status">
Dein Punktestand: <span id="status-points">0</span><br>
Aktuelle Verzögerung: <span id="status-delay">1000</span>ms<br>
<span id="status-game"></span>
</div>
.... document.querySelector('#status-points').innerHtml = ....
etc.
Möchtest du JavaScript in HTML-Code verwenden, umschliesse es mit einem script-Tag. Das script-Tag ist innerhalb des body oder head Tags zugelassen.
Die Eingabe des type Attributs ist seit HTML5 optional. Du wirst online jedoch noch zahlreiche Beispiele finden, bei denen dieses vorhanden ist.
Du kannst über das script-Tag via src-Attribut auch JavaScript aus externen Dateien einbinden. Der Code in reinen JavaScript-Dateien muss nicht von Tags umschlossen werden.
Da das Ausführen von JS im Browser das Rendern der Website blockieren kann, sollte alles JS so weit wie möglich ans Ende des Dokuments verschoben werden (optimalerweise direkt vor </body>).
So kann der Browser die Website zuerst rendern und muss sich erst dann mit der Ausführung des JS-Codes befassen.
Um eine Ausgabe in die Entwicklerkonsole deines Browser zu machen, nutze console.log.
console ist hier das Konsolen-Objekt, welches mehrere Methoden zur Verfügung stellt. Via . kannst du auf diese Methoden zugreifen.
Variablen werden via var oder seit ES6 mit den Keywords let/const definiert. Anders als bei PHP müssen die Variablen nicht mit einem Dollarzeichen beginnen.
Da die let/const Keywords noch nicht in allen Browsern unterstützt werden (Stand November 2015), werden wir in unseren Beispielen einfachheitshalber immer das var Keyword verwenden.
Mit let definierte Variablen haben einen andern Geltungsbereich wie mit var definierte. Siehe «Geltungsbereich von Variablen» weiter unten.
Mit const definierte Variablen können nach ihrer Definition nicht mehr geändert werden (=Konstante).
Ein String muss mit doppelten "oder einfachen Anführungszeichen ' umschlossen werden. Anders als bei PHP gibt es in JS keine Unterschiede zwischen beiden Methoden.
In JS wird für die Konkatenation von Strings der + Operator verwendet.
Verkettungen sind auch über mehrere Zeilen möglich. Eine String-Deklaration darf jedoch keine Zeilenumbrüche enthalten.
Seit ES6 gibt es zudem die sogenannten Template-Strings mit deren Hilfe mehrzeilige Strings einfacher deklariert werden können. Zur Deklaration verwendet man hier Backticks (`).
Variablen können anders als in PHP nicht direkt in normale Strings eingebettet werden. Es muss immer konkateniert werden. Alternativ müssen Template-Strings verwendet werden.
In JavaScript gibt es genau genommen keine Arrays sondern nur Objekte. Dies wird deutlich, sobald ein assoziatives Array mit Schlüssel-Wert-Paaren definiert werden soll.
Anders als bei Arrays werden hier {} zur Definition verwendet. Schlüssel und Werte werden via : getrennt.
Der Schlüssel kann hier auch in '' geschrieben werden. Dies ist jedoch nicht zwingend notwendig.
Der Zugriff auf die einzelnen Objekt-Properties kann jetzt mit . oder []-Syntax geschehen.
In JavaScript stehen diverse Standardobjekte zur Verfügung. Diese sind das Pendant zu den internen Funktionen von PHP.
Wird beispielsweise ein String definiert, wird dieser als Instanz eines String Objekts erstellt. Dadurch hat man Zugriff auf die diversen Properties und Methoden dieses Objekts.
Funktionen in JavaScript werden wie in PHP definiert.
Type-Hints existieren in JS nicht. Standardwerte für optionale Argumente können erst seit ES6 definiert werden.
Deklarierte Variablen sind immer in dem Kontext gültig, in dem sie deklariert wurden.
Global definierte Variablen stehen also überall (auch in untergeordneten Funktionen) zur Verfügung.
In Funktionen definierte Variablen sind ausserhalb der Funktion nicht verfügbar.
Wir eine Variable global definiert (via var) und in einer Funktion verändert, ist die Variable im globalen Geltungsbereich ebenfalls angepasst.
Variablendeklarationen werden von der JS-Engine vor dem Ausführen des eigentlichen Codes durchgeführt. Dieser Umstand nennt sich «Hoisting». So kann eine Variable in JS verwendet werden, bevor sie deklariert wurde.
Dies kann in spezifischen Situationen zu unerwartetem Verhalten führen.
Seit ES6 steht neben var auch das let Schlüsselwort zur Deklaration von Variablen zur Verfügung. Je nach Schlüsselwort haben die Variablen andere Geltungsbereiche.
var ist immer für den umgebenden Funktionsblock gültig. let gilt immer nur für den nächsten «umschliessenden» Block.
Wie in PHP kann auch in JS ein typenschwacher == und typenstarker === Vergleich durgeführt werden. Die Vergleichsregeln unterscheiden sich jedoch von PHP.
Die Arithmetischen Operatoren funktionieren gleich wie in PHP.
Inkrement- bzw. Dekrementoperatoren
Die Inkrement- und Dekrementoperatoren funktionieren gleich wie in PHP.
Die if/then/else Konstrukte funktionieren gleich wie in PHP. Einziger Unterschied ist die Schreibweise von else if in zwei separaten Wörtern.
Logische Operatoren
Es stehen die gleichen logischen Operatoren wie in PHP zur Verfügung. Die Schreibweisen or, and und xor existieren jedoch nicht.
&&
||
!
Ternärer Operator
Der Ternäre Operator steht auch in JS zur Verfügung.
Die Klammern ( ) sind optional.
for und while funktionieren gleich wie in PHP.
forEach steht in JS seit ES5.1 zur Verfügung und ist somit in allen modernern Browsern und IE >= 9 vorhanden.
forEach wird als Methode auf einem Array-Objekt ausgeführt. Dabei muss eine sogenannte callback Funktion definiert werden, die für jedes Element aufgerufen wird.
Objekte verfügen über keine forEach Methode. Hier muss auf die for...in Syntax zurückgegriffen werden.
Zugriff auf DOM-Elemente
«Program testing can be used to show the presence of bugs, but never show their absence!» - Edsger W. Dijkstra
Angenommen der Code deines Online-Shops wird zu einem grossen Teil durch automatisierte Tests geprüft.
Ein spezifischer Testfall prüft, ob sich Kunden erfolgreich einloggen können.
Wenn du jetzt eines Tages den Code für dein Login komplett neu schreiben möchtest, kannst du dank den vorhandenen Tests jederzeit überprüfen, ob trotz deiner getätigen Änderungen das Login noch korrekt funktioniert.
Löschst du zum Beispiel versehentlich das Passwort-Eingabefeld, wird dein Test fehlschlagen und du weisst sofort, dass ein Problem existiert.
Angenommen du hast einen schwerwiegenden Bug in deinem Shop: Die Funktion, die für die Überprüfung des Kundenpasswortes zuständig ist, gibt fälschlicherweise für jedes eingegebene Passwort immer true zurück. Die Funktion wird durch keinen Testfall überprüft.
Dieser Bug bleibt von deinem Testfall unbemerkt, da das Login grundsätzlich funktioniert.
Es gibt viele verschiedene Arten und Methoden des Testings. Die Definition und Namensgebung dieser Methoden ist nicht immer ganz einheitlich definiert und kann je nach Anwendungsbereich abweichen.
Grundsätzlich können folgende Testarten unterschieden werden:
Unittests dienen dem Testen einzelner abgrenzbarer Komponenten einer Software. Diese Komponenten werden dabei isoliert und unabhängig vom System in dem sie verwendet werden getestet.
Das beliebteste Testing Framework für Unittests in PHP ist phpunit.
Beispiel
Testen einer PHP-Funktion (die Funktion ist in diesem Fall eine Unit).
Beim Integrationstest wird überprüft, ob die Zusammenarbeit voneinander abhängiger Komponenten (Units) wie erwünscht funktioniert.
Integrationstests können in PHP mit einem Testing Framework wie Codeception umgesetzt werden.
Beispiel
In einem Online-Shop wird nach einer getätigten Bestellung automatisch eine PDF-Rechnung generiert und per Mail versendet. Dafür nutzen wir zwei Komponenten/Units: Eine InvoiceGenerator-Klasse und eine CustomerMailer-Klasse.
Wir überprüfen mit diesem Integrationstest lediglich, ob wir der Email unsere Rechnung anhängen können. Wir testen also die Schnittstelle zwischen InvoiceGenerator und CustomerMailer.
Wir möchten nicht testen, ob die Email versendet werden kann. Wir testen auch nicht, ob die Rechnung korrekt generiert wird. Dies wäre ein Unittest.
Bei Systemtests wird überprüft, ob unser System im Ganzen funktioniert. Sie unterscheiden sich gegenüber den Integrationstests indem hier auch externe Systeme miteinbezogen werden. Dafür wird meistens eine Testumgebung mit Testdaten verwendet. Die Testumgebung sollte immer die Zielumgebung 1:1 simulieren (gleiche Software-Versionen, etc).
Systemtests können in PHP mit einem Testing Framework wie Codeception umgesetzt werden.
Beispiel
Unser Online-Shop verwendet für den Email-Versand der Rechnungen einen lokal installierten Mail-Server. Mit folgendem Test überprüfen wir, ob wir Emails aus unserem PHP-Script über diesen Server versenden können und unser System somit funktioniert.
Ein Abnahmetest ist das Testen der gelieferten Software durch den Kunden. Hierbei orientiert man sich nicht mehr am Code sondern nur noch am Verhalten der Software.
Dazu werden Test-Szenarien definiert, die die genaue Funktionsweise der Software beschreiben.
Für die Definition eines Test-Szenarios können verschiedene Methoden verwendet werden. Eine gängige Methode ist die Given - When - Then Schreibweise.
Da in den meisten Testing Frameworks solche Given - When - Then Szenarios automatisch in ausführbaren Code umgewandelt werden können, werden sie üblicherweise auf Englisch verfasst.
Im Deutschen gibt es keinen offiziellen Standard diese Schreibweise anzuwenden. Dennoch spricht nichts dagegen, die Szenarien sinngemäss auf Deutsch zu übersetzen:
Beim manuellen Testing werden die spezifischen Testfälle von einer Person in Handarbeit durchgeführt.
Wenn du deine Website in deinem Browser aufrufst, ist dies zum Beispiel ein manuell ausgeführter Systemtest.
Beim automatisierten Testing können mehrere definierte Testfälle automatisch ausgeführt werden. Dazu müssen die Testfälle im Normalfall als ausführbarer Code vorhanden sein.
Der Vorteil hierbei ist ganz klar die Zeitersparnis beim mehrfachen Ausführen der Tests. So können beispielsweise vor der Veröffentlichung eines neuen Software-Releases automatisch alle Tests ausgeführt werden. Schlägt einer der Tests fehl, wird der neue Release nicht veröffentlicht.
Der Nachteil gegenüber dem manuellen Testing ist, dass alle Tests, zusätzlich zur eigentlichen Software, ebenfalls programmiert werden müssen.
Testing Frameworks wie PHPUnit oder Codeception vereinfachen diese Arbeit bei der Entwicklung mit PHP.
Eine weit verbreitete Art der Entwicklung ist das so genannte «Test-Driven-Development». Dabei wird vor der Entwicklung eines neuen Features ein Test definiert, der dieses noch nicht existierende Feature überprüft und darum fehlschlägt.
Anschliessend wird das neue Feature entwickelt, damit der Test erfolgreich ausgeführt werden kann.
Mit dieser Methode kann theoretisch sichergestellt werden, dass keine Funktion existiert, die nicht durch mindestens einen Test überprüft wird.
Hier einige Wörter, die im Zusammenhang mit Testing verwendet werden:
Wort
Bedeutung
Test case
Testfall: Kann mehrere Tests enthalten. Ist unabhängig von anderen Testfällen.
Assertion
Aussage/Annahme: Dient dazu zu überprüfen, ob ein Test fehlschlägt. Dabei wir das Ergebnis aus einem Test mit einem erwarteten Ergebnis verglichen. Beispiel: assert(sum(1, 2) === 3)
Szenario
Wir besonders bei Akzeptanztests verwendet. Das Szenario definiert die Situation, in der die Software getestet werden soll.
Schreibe für dein Task-Listen-Projekt mindestens 6 Testfälle eines «Acceptance Test» auf.
Gib die Liste anschliessend einem Kameraden, welcher dein Tool nach den vorgegeben Tests überprüft.
Zum Schluss gebt ihr euch gegenseitig eine Rückmeldung über die Testresultate und die Verständlichkeit der Tests.
<div>
<p>Etwas HTML...</p>
</div>
<script>
var name = 'Robert Pattinson';
</script><script type="text/javascript">...</script>// vars.js
var name = 'Robert Pattinson';<!-- index.html -->
<script src="vars.js"></script>console.log('Hallo Welt!');console.error('Fehler, den ich in die Konsole logge');
console.info('Information, die ich in die Konsole logge');var alter = 17;
let istLernender = true;var istWahr = true;
var istFalsch = false;var alter = 17;var einViertel = 0.25;
var MwSt = 8.0;var name = 'Robert Pattinson';
var name = "Robert Pattinson";var name = 'Robert Pattinson';
console.log('Mein Lieblingsschauspieler ist ' + name);var nichtMoeglich = 'Ein String über
mehrere Zeilen';
var korrektIst = 'Ein String über'
+ 'mehrere Zeilen';var mehrzeilig = `Ein String
über
mehrere
Zeilen`;var film = 'Twilight';
var satz = 'Mein Lieblingsfilm ist ' + film;
var satz = `Mein Lieblingsfilm ist ${film}`;var edelmetalle = ['Gold', 'Platin', 'Iridium', 'Silber'];
edelmetalle[0]; // Gold
edelmetalle[1]; // Platinvar wochentage = {
mo: 'Montag',
di: 'Dienstag',
mi: 'Mittwoch',
do: 'Donnerstag',
fr: 'Freitag',
sa: 'Samstag',
so: 'Sonntag'
};wochentage.mo; // Montag
wochentage['mo']; // Montagvar spruch = 'Hasta la vista, Baby';
spruch.length; // 20
spruch.toUpperCase(); // "HASTA LA VISTA, BABY"
spruch.replace('Baby', 'Justin'); // "Hasta la vista, Justin"function sagwas(wort1, wort2) {
console.log(wort1 + ' ' + wort2);
}
sagwas('Hallo', 'Welt');
// Hallo Welt
// Erst seit ES6
function sagwas(wort1, wort2 = 'Welt') {
console.log(wort1 + ' ' + wort2);
}
sagwas('Hallo');
// Hallo Weltvar zahl = 20;
function demo() {
return zahl;
}
demo();
// 20function demo() {
var zahl = 20;
}
demo();
console.log(zahl)
// undefinedvar zahl;
function demo() {
zahl = 20;
}
demo();
console.log(zahl)
// 20console.log(zahl);
// 20
var zahl = 20;// Resultierender Code für die JS-Engine
// Die Deklaration wird an den Anfang "geschoben"
var zahl = 20;
console.log(zahl);
// 20// Definition mit `var` ist für komplette Funktion gültig
function demo() {
// i existiert (wegen hoisting)
for(var i = 0; i < 10; i++) {
// i existiert
}
// i existiert
}
// Definition mit `let` ist nur für `for` Block gültig
function demo() {
// i ist nicht definiert
for(let i = 0; i < 10; i++) {
// i exisitert
}
// i ist nicht definiert
}console.log(5 + 6);
// 11
console.log(12 / 6);
// 2var i = 1;
i++;
console.log(i);
// 2
i--;
console.log(i);
// 1if(a > b) {
console.log('a ist grösser als b');
} else if(a < b) {
console.log('a ist kleiner als b');
} else {
console.log('a und b sind gleich gross');
}if(a == 1 && b == 2) {
console.log('a ist 1 und b ist 2');
}
if(a == 1 || b == 2) {
console.log('a ist 1 oder b ist 2');
}
if( ! a == 1) {
console.log('a ist nicht 1');
}var text = (alter >= 18) ? 'Volljährig' : 'Minderjährig';var zahl = 1;
while(zahl <= 10) {
console.log(zahl++);
}var werktage = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag'];
werktage.forEach(function(element, index) {
console.log(element, index);
});
// Montag 0
// Dienstag 1
// Mittwoch 2
// Donnerstag 3
// Freitag 4var wochentage = {
'mo': 'Montag',
'di': 'Dienstag',
'mi': 'Mittwoch',
'do': 'Donnerstag',
'fr': 'Freitag',
'sa': 'Samstag',
'so': 'Sonntag'
};
for(var key in wochentage) {
console.log(key + ' ist kurz für ' + wochentage[key]);
}
// mo ist kurz für Montag
// di ist kurz für Dienstag
// mi ist kurz für Mittwoch
// do ist kurz für Donnerstag
// fr ist kurz für Freitag
// sa ist kurz für Samstag
// so ist kurz für Sonntag// Wähle ein Element mit der ID "submit" aus (<button id="submit"></button>)
var button = document.querySelector('#submit');
// Wähle ein Element mit der Klasse "submit" aus (<button class="submit"></button>)
var button = document.querySelector('.submit');
// Wähle alle Buttons aus
var buttons = document.querySelectorAll('button');function add($a, $b)
{
return $a + $b;
}
// Unit-Tests
test('Addiert positive Zahlen korrekt',
add(1, 2) === 3
);
test('Addiert negative Zahlen korrekt',
add(-1, -5) === -6
);function testInvoiceMailer()
{
// Rechnung für Bestellung #100 generieren
$invoice = new InvoiceGenerator(100);
// Mail vorbereiten
$mailer = new CustomerMailer('[email protected]');
// PDF anhängen
$mailer->addAttachment($invoice);
// Überprüfen, ob die Rechnung erfolgreich an die Mail angehängt wurde
test('Email hat PDF im Anhang.',
$mailer->hasAttachment === true
);
}function testMailer()
{
// Mail vorbereiten
$mailer = new CustomerMailer('[email protected]');
// Betreff setzen
$mailer->setSubject('Ihre Bestellung #100');
// Mail versenden
$mailer->send();
// Unser Postfach öffnen
$inbox = new MailInbox('[email protected]');
// Überprüfen, ob die Email im Postfach liegt
test('Die Email wurde korrekt versendet.',
$inbox->seeMessage('Ihre Bestellung #100');
);
}GIVEN I am logged in as an administrator
WHEN I delete a user
THEN the user is removed from the database
GIVEN I am logged in as a unprivileged user
WHEN I try to delete a user
THEN the error message "you have no rights to delete this user"
is shown and the user is NOT removed from the databaseGEGEBEN SEI Ich bin als Administrator eingeloggt
WENN ich einen Benutzer lösche
DANN wird dieser Benutzer aus der Datenbank gelöscht












Das if-Konstrukt ist eines der wichtigsten Features vieler Programmiersprachen, so auch in PHP. Das Konstrukt ermöglicht die bedingte Ausführung von Codefragmenten.
Nur wenn ausdruck den Wert true ergibt, wird anweisung ausgeführt.
Um zwei Werte in PHP zu vergleichen, gibt es die Vergleichsoperatoren == und ===. Ein Vergleichs-Ausdruck gibt immer einen boolschen Wert von true oder false zurück.
Typenschwache und typenstarke Vergleiche
In PHP können typenschwache oder typenstarke Vergleiche durchgeführt werden. Bei typenschwachen Vergleichen (==, !=) wird der Wert der Variablen unabhängig von ihren Typen verglichen. Bei typenstarken Vergleichen (===, !==) müssen Wert und Typ beider Variablen identisch sein.
Der typenstarke Ungleich-Operator (!==) ist wahr, wenn der Wert oder der Typ unterschiedlich ist. So kann z.B. unterschieden werden, ob ein Teil-String in einem Text am Anfang (die Funktion strpos() liefert 0) oder gar nicht (strpos() liefert false) vorkommt.
Um mehrere Vergleiche zu verbinden oder einen Vergleich umzukehren, können logische Operatoren verwendet werden.
and
or
xor
&&
Oft will man eine Anweisung ausführen, wenn eine bestimmte Bedingung erfüllt ist, und eine andere Anweisung, wenn dies nicht der Fall ist. Dies ist der Einsatzzweck von else.
Um eine einfache if/else-Bedingung zu erstellen, kann auch der Ternäre Operator verwendet werden. Dies ist besonders bei der Zuweisung von bedingten Werten an eine Variable eine leserliche Alternative.
Die Klammern ( ) sind optional.
elseif, wie der Name schon sagt, ist eine Kombination aus if und else. Wie else erweitert es eine if-Kontrollstruktur, um alternative Befehle auszuführen, wenn die ursprüngliche if-Bedingung nicht zutrifft.
Die Bedeutung einer while-Schleife ist simpel. Die Schleife weist PHP an, die untergeordnete Anweisung wiederholt auszuführen, solange die while-Bedingung zutrifft. Die Bedingung wird jedes Mal am Anfang der Schleife überprüft.
for-Schleifen sind die komplexesten Schleifen in PHP. Die Syntax einer for-Schleife ist:
ausdruck1 wird vor Ausführung der Schleife ausgeführt.
ausdruck2 wird am Anfang jedes Schleifendurchlaufs ausgeführt. Wenn diese true ergibt, wird die Schleife fortgesetzt. Ergibt sie false, wird die Ausführung der Schleife abgebrochen.
ausdruck3 wird am Ende jedes Schleifendurchlaufs ausgeführt.
Das foreach-Konstrukt bietet eine einfache Möglichkeit durch Arrays zu iterieren (=schrittweise durchlaufen).
Es existiert eine zweite Schreibweise für foreach, bei der zusätzlich der Array-Schlüssel in eine Variable geschrieben wird.
Für Kontrollstrukturen gibt es eine alternative Schreibweise, die besonders beim Generieren von HTML oft besser lesbar ist.
Bei der alternativen Schreibweise werden keine { } verwendet:
Anwendungsbeispiel beim Generieren von HTML:
Herkömmliche Schreibweise...
Kommentiere nun sämtliche Ausgabefunktionen in der Datei index.php aus. Kommentiere in der Datei index.php die Script-Einbindung der Datei index.view.php wieder ein.
Ändere die Datei index.view.php so ab, dass alle Fahrzeuge aufgelistet werden, welche du heute benutzt hast:
if(ausdruck) {
anweisung;
}if($a > $b) {
echo '$a ist grösser als $b';
}Gibt true zurück, wenn $a ungleich $b ist.
$a !== $b
Nicht identisch
Gibt true zurück, wenn $a ungleich $b ist oder nicht vom gleichen Typ sind.
$a < $b
Kleiner als
Gibt true zurück, wenn $a kleiner als $b ist.
$a > $b
Grösser als
Gibt true zurück, wenn $a grösser als $b ist.
$a <= $b
Kleiner Gleich
Gibt true zurück, wenn $a kleiner oder gleich $b ist.
$a >= $b
Grösser Gleich
Gibt true zurück, wenn $a grösser oder gleich $b ist.
||
!
Beispiel
Name
Ergebnis
$a == $b
Gleich
Gibt true zurück, wenn $a gleich $b ist.
$a === $b
Identisch
Gibt true zurück, wenn $a gleich $b ist und beide vom gleichen Typ sind.
$a != $b
Ungleich
$a = 1; // Integer
$b = "1"; // String
var_dump($a == $b); // 1 == "1"
// true
var_dump($a === $b); // 1 == "1"
// false
// -------------------------------------
$a = 1; // Integer
$b = true; // Boolean
var_dump($a == $b); // 1 == true
// true
var_dump($a === $b); // 1 === true
// falsevar_dump(strpos('hallo welt', 'hallo') != false); // 0 != false
// false
var_dump(strpos('hallo welt', 'hallo') !== false); // 0 !== false
// trueif($a == 1 && $b == 2) { // $a == 1 and $b == 2
echo '$a hat einen Wert von 1, $b hat einen Wert von 2.';
}
if($a == 1 || $b == 2) { // $a == 1 or $b == 2
echo '$a hat einen Wert von 1 oder $b hat einen Wert von 2.';
}
if($a == 1 xor $b == 2) {
echo '$a hat einen Wert von 1 oder $b hat einen Wert von 2, jedoch nicht beides.';
}
if( ! $a == 1) {
echo '$a hat nicht einen Wert von 1.';
}if($a > $b) {
echo '$a ist grösser als $b';
} else {
echo '$a ist nicht grösser als $b';
}// (if) ? then : else;
$text = ($alter >= 18) ? 'volljährig' : 'minderjährig';
// Entspricht
if($alter >= 18) {
$text = 'volljährig';
} else {
$text = 'minderjährig';
}if($a > $b) {
echo '$a ist grösser als $b';
} elseif($a == $b) {
echo '$a ist gleich gross wie $b';
} else {
echo '$a ist nicht grösser als $b';
}// Zahlen von 1 bis 10 ausgeben
$zahl = 1;
while ($zahl <= 10) {
echo $zahl++; // Wert ausgeben, dann um 1 erhöhen
}for (ausdruck1; ausdruck2; ausdruck3) {
anweisung;
}// Zahlen von 1 bis 10 ausgeben
for ($i = 1; $i <= 10; $i++) {
echo $i;
}// Zahlen von 1 bis 10 ausgeben
$zahlen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // = range(1, 10)
foreach($zahlen as $zahl) {
echo $zahl;
}$wochentage = [
'Mo' => 'Montag',
'Di' => 'Dienstag',
'Mi' => 'Mittwoch',
'Do' => 'Donnerstag',
'Fr' => 'Freitag',
'Sa' => 'Samstag',
'So' => 'Sonntag',
];
foreach($wochentage as $abkuerzung => $wochentag) {
echo "{$wochentag} kürzt man ab als {$abkuerzung}.<br>";
// Montag kürzt man ab als Mo.
}if($x == 1): // Doppelpunkt anstelle von {
echo 'x = 1';
endif; // `endif` anstelle von }<?php if(count($options) > 0): ?>
<select name="auswahl">
<?php foreach($options as $value => $option): ?>
<option value="<?= $value ?>"><?= $option ?></option>
<?php endforeach; ?>
</select>
<?php endif; ?><?php if(count($options) > 0) { ?>
<select name="auswahl">
<?php foreach($options as $value => $option) { ?>
<option value="<?= $value ?>"><?= $option ?></option>
<?php } ?>
</select>
<?php } ?>Heute bin ich mit dem Auto gefahren.
Heute bin ich mit dem Velo gefahren.
Heute bin ich mit dem Bus gefahren.

Im Moment werden sämtliche Aufgaben des Tools von den Controllern und den Views ausgeführt. Die Logik ist zwar von der Darstellung getrennt, jedoch ist die Datenverarbeitung noch ein integrativer Bestandteil der Logik, beziehungsweise des Controllers.
Dies führt früher oder später zu folgenden Problemen:
Einige Code-Elemente müssen doppelt aufgeführt werden (DRY)
Änderungen der Infrastruktur (z. B. Datenbank-Technologie) führenr zu einem grossen Umstrukturierungsaufwand.
Die Code-Struktur im Controller wird unübersichtlich.
Der Code ist schlecht lesbar.
Um genau diese Problemstellungen zu umgehen, wird mit einem Model gearbeitet. Das Model ist für die Entgegennahme, Bearbeitung und Speicherung der Daten zuständig. Für unser Beispiel, hat dies die folgenden Auswirkungen:
Erstelle nun das Model app/Models/Task.php und binde dieses in der bootstrap.php ein.
Erstelle die Klasse Task. Unser Ziel ist es nun, dass wir den Code in den einzelnen Controller lesbarer machen, Wiederholungen vermeiden (DRY) und fast die komplette Datenbank-Kommunikation in das Model Task auslagern.
Als erstes Erstellen wir einen Konstruktor für die Klasse. Dieser soll sämtliche Attribute bei der Instanzierung einer Klasse entgegennehmen und im erstellten Objekt abspeichern.
Der Controller soll so angepasst werden, dass sich nur noch die Logik darin befindet. Alles andere soll durch das Model Task ausgeführt werden. Gehe dabei immer in zwei Schritten vor:
Schreibe den Code, wie du ihm im Controller haben möchtest.
Ergänze den Code im Model so, dass er gemäss deinen Vorstellungen funtkioniert.
Versuche nun eigenständig noch die Logik und Datenverarbeitung für das Löschen vom TaskController zu trennen.
Findest Du eine einfache Möglichkeit, den PDO-Verbindungsaufbau in eine einfache Helper-Funktion auszulagern?
class Task
{
public $id;
public $title;
public $completed;
function __construct($title, $completed)
{
$this->title = $title;
$this->completed = $completed;
}
}$task = new Task("Titel des Tasks", false);
$task->create();$task = (new Task)->find($id);$task = new Task($_POST['title'], $completed);
$task->update($_GET['id']);









