PHP für Anfänger. Die Sitzung

ElePHPant. PHP für Anfänger. Sitzung

Einen schönen Tag noch. Hier ist der erste Artikel in der PHP-Reihe für Anfänger. Dies wird eine ungewöhnliche Artikelserie sein, es wird kein echo "Hello World" , es wird Hardcore aus dem Leben von PHP-Programmierern mit einer kleinen Mischung aus "Hausaufgaben" geben, um das Material zu konsolidieren.

Ich beginne mit Sitzungen - dies ist eine der wichtigsten Komponenten, mit denen Sie arbeiten müssen. Die Prinzipien seiner Arbeit nicht verstehen - Geschäfte machen. Um Probleme zu vermeiden, werde ich versuchen, über alle möglichen Nuancen zu sprechen.

Um zu verstehen, warum wir eine Sitzung benötigen, wenden wir uns zunächst den Ursprüngen zu - dem HTTP-Protokoll.

HTTP-Protokoll


Das HTTP-Protokoll ist das HyperText-Übertragungsprotokoll - das "Hypertext-Übertragungsprotokoll" - d.h. in der Tat - ein Textprotokoll, und zu verstehen, ist es nicht schwierig.
Anfangs war klar, dass unter diesem Protokoll nur HTML übertragen wird, die Adresse und der Name, aber jetzt werden sie nicht gesendet und = ^. ^ = Und (• _ ㅅ _ •)

Um nicht um den heißen Brei herumzureden, möchte ich Ihnen ein Beispiel für die Kommunikation über das HTTP-Protokoll geben.
Hier ist ein Beispiel für die Anfrage, wie Ihr Browser sie sendet, wenn Sie die Seite http://example.com anfordern:

 GET / HTTP/1.1 Host: example.com Accept: text/html < > 

Und hier ist eine Beispielantwort:

 HTTP/1.1 200 OK Content-Length: 1983 Content-Type: text/html; charset=utf-8 <html> <head>...</head> <body>...</body> </html> 

Dies sind sehr vereinfachte Beispiele, aber auch hier können Sie sehen, woraus die HTTP-Anforderung und -Antwort bestehen:

  1. Startzeile - für eine Anforderung enthält die Methode und den Pfad der angeforderten Seite, für eine Antwort - die Protokollversion und den Antwortcode
  2. Überschriften - haben ein Schlüsselwertformat, das durch einen Doppelpunkt getrennt ist. Jede neue Überschrift wird aus einer neuen Zeile geschrieben
  3. Nachrichtentext - entweder direkt HTML oder Daten werden durch zwei Zeilenumbrüche von den Headern getrennt, kann fehlen, wie in der obigen Anforderung

Wir haben also das Protokoll herausgefunden - es ist einfach, es führt seine Geschichte seit 1992 an, also nennen Sie es nicht ideal, aber was es ist - senden Sie eine Anfrage - erhalten Sie eine Antwort, und das war's, der Server und der Client sind nicht mehr verbunden. Ein solches Szenario ist jedoch keineswegs das einzig mögliche. Wir können eine Autorisierung haben. Der Server muss irgendwie verstehen, dass diese Anforderung von einem bestimmten Benutzer stammt, d. H. Der Client und der Server müssen innerhalb einer bestimmten Sitzung kommunizieren. Und ja, dafür wurde folgender Mechanismus erfunden:

  1. Wenn ein Benutzer autorisiert, generiert und merkt sich der Server einen eindeutigen Schlüssel - die Sitzungskennung - und meldet ihn an den Browser
  2. Der Browser speichert diesen Schlüssel und sendet ihn bei jeder nachfolgenden Anforderung

Um diesen Mechanismus zu implementieren, wurden Cookies (Cookies, Cookies) erstellt - einfache Textdateien auf Ihrem Computer nach Datei für jede Domain (obwohl einige Browser fortgeschrittener sind und eine Datenbank zum Speichern von SQLite verwenden), während der Browser die Anzahl der Datensätze begrenzt und die Größe der gespeicherten Daten (für die meisten Browser sind dies 4096 Bytes, siehe RFC 2109 von 1997)
Das heißt, Wenn Sie ein Cookie aus Ihrem Browser stehlen, können Sie in Ihrem Namen auf Ihre Facebook-Seite gehen? Seien Sie nicht beunruhigt, dies ist zumindest mit Facebook nicht möglich. Dann zeige ich Ihnen eine der Möglichkeiten, sich vor solchen Angriffen auf Ihre Benutzer zu schützen.

Nun wollen wir sehen, wie sich unsere Anfrage-Antwort ändert.

Anfrage
 POST /login/ HTTP/1.1 Host: example.com Accept: text/html login=Username&password=Userpass 


Unsere Methode wurde in POST geändert und der Login und das Passwort werden im Anfragetext übertragen. Wenn Sie die GET-Methode verwenden, enthält die Abfragezeichenfolge einen Benutzernamen und ein Kennwort, die aus ideologischer Sicht nicht sehr korrekt sind und eine Reihe von Nebenwirkungen in Form der Protokollierung (z. B. im selben access.log ) und des Zwischenspeicherns von Kennwörtern in klarer Form haben.

Antwort
 HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Set-Cookie: KEY=VerySecretUniqueKey <html> <head>...</head> <body>...</body> </html> 

Die Antwort des Servers enthält den Set-Cookie: KEY=VerySecretUniqueKey Header Set-Cookie: KEY=VerySecretUniqueKey , wodurch der Browser Set-Cookie: KEY=VerySecretUniqueKey wird, diese Daten in Cookies zu speichern. Beim nächsten Zugriff auf den Server werden sie vom Server gesendet und erkannt:

Anfrage
 GET / HTTP/1.1 Host: example.com Accept: text/html Cookie: KEY=VerySecretUniqueKey < > 

Wie Sie sehen können, unterscheiden sich die vom Browser (Anforderungsheader) und vom Server (Antwortheader) gesendeten Header, obwohl sowohl Anforderungen als auch Antworten (Allgemeine Header) gemeinsam sind.

Der Server hat unseren Benutzer an dem gesendeten Cookie erkannt und wird ihm weiterhin Zugriff auf persönliche Informationen gewähren. Nun, wenn Sitzungen und HTTP aussortiert sind, können Sie jetzt zu PHP und seinen Funktionen zurückkehren.

PHP und Sitzung


Ich hoffe, Sie haben bereits PHP auf Ihrem Computer installiert, als Weiter werde ich Beispiele geben, und sie müssen ausgeführt werden

Die PHP-Sprache wurde erstellt, um mit dem HTTP-Protokoll übereinzustimmen - d. H. Seine Hauptaufgabe besteht darin, eine Antwort auf die HTTP-Anfrage zu geben und Speicher und Ressourcen freizugeben. Daher funktioniert der Sitzungsmechanismus in PHP nicht im automatischen Modus, sondern im manuellen Modus, und Sie müssen wissen, was und in welcher Reihenfolge aufgerufen werden soll.
Hier ist ein Artikel zum Thema PHP soll sterben , oder hier ist es auf Russisch , aber es ist besser, ihn "für später" in die Lesezeichen zu setzen.

Zunächst müssen Sie die Sitzung "starten". Dazu verwenden wir die Funktion session_start () und erstellen eine session.start.php- Datei mit folgendem Inhalt:

 <?php session_start(); 

Führen Sie den integrierten PHP -Webserver in dem Ordner mit Ihrem Skript aus:

 php -S 127.0.0.1:8080 

Starten Sie den Browser und öffnen Sie die Entwicklertools (oder was auch immer ). Gehen Sie dann zur Seite http://127.0.0.1:8080/session.start.php - Sie sollten nur eine leere Seite sehen, aber nicht schnell zum Schließen - schauen zu den Headern, die der Server uns gesendet hat:

Keks

Es wird eine Menge Dinge geben, wir sind nur an dieser Zeile in der Antwort des Servers interessiert (saubere Cookies, wenn es keine solche Zeile gibt, und aktualisieren Sie die Seite):

 Set-Cookie: PHPSESSID=dap83arr6r3b56e0q7t5i0qf91; path=/ 

Sobald dies angezeigt wird, speichert der Browser ein Cookie mit dem Namen "PHPSESSID":

Browser-Sitzungscookie

PHPSESSID - Der Standard-Sitzungsname wird aus der php.ini-Konfiguration mit der Anweisung session.name angepasst. Falls erforderlich, kann der Name in der Konfigurationsdatei selbst oder mithilfe der Funktion session_name () geändert werden

Und jetzt - wir aktualisieren die Seite und wir sehen, dass der Browser dieses Cookie an den Server sendet. Sie können versuchen, die Seite ein paar Mal zu aktualisieren. Das Ergebnis ist identisch:

Browseranfrage mit Cookie

Die Summe, die wir haben - die Theorie stimmte mit der Praxis überein, und das ist in Ordnung.

Der nächste Schritt besteht darin, einen beliebigen Wert in der Sitzung zu speichern. $_SESSION in PHP die $_SESSION Variable $_SESSION verwendet. Wir speichern die aktuelle Zeit. Rufen Sie dazu die Funktion date () auf :

 session_start(); $_SESSION['time'] = date("H:i:s"); echo $_SESSION['time']; 

Wir aktualisieren die Seite und sehen die Serverzeit, aktualisieren sie erneut - und die Zeit wurde aktualisiert. Stellen wir jetzt sicher, dass sich die eingestellte Zeit nicht bei jeder Seitenaktualisierung ändert:

 session_start(); if (!isset($_SESSION['time'])) { $_SESSION['time'] = date("H:i:s"); } echo $_SESSION['time']; 

Wir aktualisieren - die Zeit ändert sich nicht, was benötigt wird. Gleichzeitig erinnern wir uns daran, dass PHP im Sterben liegt, was bedeutet, dass diese Sitzung irgendwo gespeichert wird und wir diesen Ort finden werden ...

Alles Geheimnis wird klar


Standardmäßig speichert PHP die Sitzung in Dateien - die Direktive session.save_handler ist dafür verantwortlich, sucht nach dem Pfad, unter dem die Dateien in der Direktive session.save_path gespeichert werden, oder verwendet die Funktion session_save_path () , um den erforderlichen Pfad abzurufen .
In Ihrer Konfiguration wird der Pfad zu den Dateien möglicherweise nicht angegeben. Anschließend werden die Sitzungsdateien in temporären Dateien Ihres Systems gespeichert. Rufen Sie die Funktion sys_get_temp_dir () auf und finden Sie heraus, wo sich dieser versteckte Ort befindet.

Also gehen wir diesen Weg und suchen Ihre Sitzungsdatei (ich habe diese Datei sess_dap83arr6r3b56e0q7t5i0qf91 ), öffnen sie in einem Texteditor:

 time|s:8:"16:19:51"; 

Wie Sie sehen, ist dies unsere Zeit, dies ist das schwierige Format, in dem unsere Sitzung gespeichert ist, aber wir können Änderungen vornehmen, die Zeit ändern oder einfach eine beliebige Zeile eingeben, warum nicht:

 time|s:13:"\m/ (@.@) \m/"; 

Um diese Zeichenfolge in ein Array zu konvertieren, müssen Sie die Funktion session_decode () für die umgekehrte Konvertierung verwenden - session_encode () - dies wird als Serialisierung bezeichnet, nur in PHP für Sitzungen - es ist eine eigene - spezielle Funktion, obwohl Sie die Standard- PHP-Serialisierung verwenden können - schreiben Sie in die Sitzungskonfigurationsanweisung Der Wert von .serialize_handler ist php_serialize und Sie werden glücklich sein, und $_SESSION kann ohne Einschränkungen verwendet werden - jetzt können Sie Zahlen und Sonderzeichen als Index | und ! im Namen (für alle 10+ Jahre Arbeit musste ich nie :)

Aufgabe
Schreiben Sie Ihre Funktion, ähnlich wie session_decode() . Hier haben Sie einen Testdatensatz für die Sitzung (es ist nicht erforderlich, die Kenntnisse über reguläre Ausdrücke zu lösen). Nehmen Sie den Text zur Konvertierung aus der Datei Ihrer aktuellen Sitzung:

 $_SESSION['integer var'] = 123; $_SESSION['float var'] = 1.23; $_SESSION['octal var'] = 0x123; $_SESSION['string var'] = "Hello world"; $_SESSION['array var'] = array('one', 'two', [1,2,3]); $object = new stdClass(); $object->foo = 'bar'; $object->arr = array('hello', 'world'); $_SESSION['object var'] = $object; $_SESSION['integer again'] = 42; 


Was haben wir noch nicht versucht? Das ist richtig - um Cookies zu stehlen, starten wir einen anderen Browser und fügen dieselben Cookies hinzu. Dazu habe ich ein einfaches Javascript für Sie geschrieben, es in die Browserkonsole kopiert und ausgeführt. Denken Sie daran, die Sitzungskennung in Ihre eigene zu ändern:

 javascript:(function(){document.cookie='PHPSESSID=dap83arr6r3b56e0q7t5i0qf91;path=/;';window.location.reload();})() 

Jetzt sehen sich beide Browser dieselbe Sitzung an. Ich habe oben erwähnt, dass ich über Schutzmethoden sprechen werde. Betrachten Sie den einfachsten Weg. Wir binden die Sitzung an den Browser, genauer gesagt, wie der Browser auf dem Server angezeigt wird. Wir werden uns an den User-Agent erinnern und ihn jedes Mal überprüfen:

 session_start(); if (!isset($_SESSION['time'])) { $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT']; $_SESSION['time'] = date("H:i:s"); } if ($_SESSION['ua'] != $_SERVER['HTTP_USER_AGENT']) { die('Wrong browser'); } echo $_SESSION['time']; 

Es ist schwieriger zu fälschen, aber es ist immer noch möglich, das Speichern und Überprüfen von $_SERVER['REMOTE_ADDR'] und $_SERVER['HTTP_X_FORWARDED_FOR'] , und dies wird mehr oder weniger wie ein Schutz vor Eindringlingen aussehen, die in unsere Cookies eindringen.

Das Schlüsselwort im vorherigen Absatz sieht so aus, als würden Cookies in realen Projekten schon lange über das HTTPS-Protokoll ausgeführt, sodass niemand sie stehlen kann, ohne physischen Zugriff auf Ihren Computer oder Ihr Smartphone zu haben


Erwähnenswert ist die Anweisung session.cookie-httponly , dank der auf das Sitzungscookie über JavaScript nicht zugegriffen werden kann. Wenn Sie sich das Funktionshandbuch von setcookie () ansehen, werden Sie außerdem feststellen, dass der letzte Parameter auch für HttpOnly verantwortlich ist. Denken Sie daran - mit dieser Einstellung können Sie XSS-Angriffe in fast allen Browsern effektiv verarbeiten.

Aufgabe
Fügen Sie der IP des Benutzers im Code eine Prüfung hinzu. Wenn die Prüfung fehlschlägt, löschen Sie die gefährdete Sitzung.

Schritt für Schritt


Und jetzt werde ich in Schritten den Algorithmus erklären, wie eine Sitzung in PHP funktioniert, am Beispiel des folgenden Codes (Standardeinstellungen):

 session_start(); $_SESSION['id'] = 42; 

  1. Nach dem Aufruf von session_start() durchsucht PHP das Cookie nach der Sitzungskennung unter dem in session.name angegebenen session.name - dies ist PHPSESSID
  2. Wenn keine Kennung vorhanden ist, wird diese erstellt (siehe session_id () ) und eine leere Sitzungsdatei entlang des Pfads session.save_path mit dem Namen sess_{session_id()} Der Antwort des Servers werden Header hinzugefügt, um Cookie {session_name()}={session_id()}
  3. Wenn der Bezeichner vorhanden ist, suchen Sie nach der Sitzungsdatei im Ordner session.save_path :
    • wir finden es nicht - wir erstellen eine leere Datei mit dem Namen sess_{$_COOKIE[session_name()]} (der Bezeichner darf nur Zeichen aus den Bereichen az , AZ , 0-9 , ein Komma und ein Minuszeichen enthalten)
    • Suchen, lesen Sie die Datei und entpacken Sie die Daten (siehe session_decode () ) in die $_SESSION Variable $_SESSION (die Datei ist zum Lesen / Schreiben gesperrt).
  4. Wenn das Skript seine Arbeit beendet hat, werden alle Daten von $_SESSION mit session_encode() in eine Datei entlang des Pfads session.save_path Namen sess_{session_id()} (die Sperre wird sess_{session_id()} ).

Aufgabe
PHPSESSID Sie in Ihrem Browser einen beliebigen Cookie-Wert mit dem Namen PHPSESSID , lassen Sie ihn 1234567890 , PHPSESSID Sie die Seite und überprüfen Sie, ob Sie eine neue Datei sess_1234567890

Gibt es ein Leben ohne Kekse?


PHP kann mit der Sitzung arbeiten, auch wenn Cookies im Browser deaktiviert sind. Dann enthalten alle URLs auf der Website einen Parameter mit der Kennung Ihrer Sitzung. Ja, müssen Sie dies noch konfigurieren, aber benötigen Sie ihn? Ich musste es nicht benutzen, aber wenn ich es wirklich will, sage ich einfach, wo ich graben soll:


Und wenn Sie eine Sitzung in einer Datenbank speichern müssen?


Um eine Sitzung in der Datenbank zu speichern, müssen Sie den Sitzungsspeicher ändern und PHP mitteilen, wie er verwendet werden soll. Zu diesem Zweck wurden die SessionHandlerInterface- Schnittstelle und die Funktion session_set_save_handler erstellt.
Unabhängig davon stelle ich fest, dass Sie keine eigenen Sitzungshandler für Redis und Memcache schreiben müssen. Wenn Sie diese Erweiterungen installieren, werden auch die entsprechenden Handler mitgeliefert, sodass RTFM alles ist. Nun ja, der Handler muss angegeben werden, bevor session_start()

Aufgabe
Implementieren Sie SessionHandlerInterface , um die Sitzung in MySQL zu speichern, und überprüfen Sie, ob sie funktioniert.
Dies ist eine Sternchenaufgabe für diejenigen, die sich bereits mit Datenbanken vertraut gemacht haben.


Wann stirbt die Sitzung?


Die Anweisung session.gc_maxlifetime ist für die Lebensdauer einer Sitzung verantwortlich. Standardmäßig entspricht diese Anweisung 1440 Sekunden (24 Minuten). Wenn eine Sitzung für eine bestimmte Zeit nicht aufgerufen wurde, wird die Sitzung als "faul" betrachtet und wartet, bis sie an der Reihe ist.

Eine andere Frage ist interessant. Können Sie sie reifen Entwicklern stellen? Wann löscht PHP Dateien abgelaufener Sitzungen? Die Antwort ist im offiziellen Leitfaden, aber nicht explizit - denken Sie also daran:

Die session_start() kann gestartet werden, wenn die Funktion session_start() aufgerufen wird. Die Wahrscheinlichkeit des Startens hängt von zwei Anweisungen ab. Session.gc_probability und session.gc_divisor , die erste fungiert als Dividende, die zweite fungiert als Divisor, und standardmäßig sind diese Werte 1 und 100 usw. e. Die Wahrscheinlichkeit, dass der Collector gestartet und die Sitzungsdateien gelöscht werden, beträgt ca. 1%.

Aufgabe
Ändern Sie den Wert der Anweisung session.gc_divisor so, dass der Garbage Collector jedes Mal session.gc_divisor wird. Überprüfen Sie, ob dies geschieht.


Der trivialste Fehler


Ein Fehler mit mehr als einer halben Million führt zu den Ergebnissen von Google:

Sitzungscookie kann nicht gesendet werden - Header, die bereits von gesendet wurden
Sitzungscache-Limiter kann nicht gesendet werden - Header wurden bereits gesendet

Um eine zu erhalten, erstellen Sie eine session.error.php- Datei mit den folgenden Inhalten:

 echo str_pad(' ', ini_get('output_buffering')); session_start(); 

In der zweiten Zeile ist eine seltsame "Magie" ein Fokus mit einem Ausgabepuffer. Ich werde in einem der folgenden Artikel darauf eingehen. Bisher ist dies nur eine Zeichenfolge mit einer Länge von 4096 Zeichen. In diesem Fall sind es nur Leerzeichen

Beginnen Sie mit dem Löschen des Cookies im Voraus, und Sie erhalten die oben genannten Fehler, obwohl der Fehlertext unterschiedlich ist, aber das Wesentliche ist dasselbe: Der Zug ist abgereist - der Server hat den Seiteninhalt bereits an den Browser gesendet und es ist zu spät, die Header zu senden. Dies funktioniert nicht in Cookies, und die geschätzte Sitzungskennung wird in Cookies nicht angezeigt. Wenn Sie auf diesen Fehler <?php - suchen Sie nach einer Stelle, an der der Text vorab angezeigt wird. Möglicherweise befindet sich in einer der verbundenen Dateien ein Leerzeichen vor den Zeichen <?php oder danach ?> Wenn es sich um ein Leerzeichen handelt, ist möglicherweise ein Thread nicht druckbar Seien Sie also vorsichtig, und diese Infektion wirkt sich nicht auf Sie aus (schließlich ... homerisches Lachen).

Aufgabe
Um dieses Wissen zu testen, möchte ich, dass Sie Ihren eigenen Sitzungsmechanismus implementieren und den obigen Code zum Laufen bringen:

 require_once 'include/sess.php'; sess_start(); if (isset($_SESS["id"])) { echo $_SESS["id"]; } else { $_SESS["id"] = 42; } 

Um Ihren Plan umzusetzen, benötigen Sie die Funktion register_shutdown_function ()



Sperren


Ein weiterer häufiger Fehler bei Anfängern ist der Versuch, die Sitzungsdatei zu lesen, während sie von einem anderen Skript gesperrt wird. Eigentlich ist das kein Fehler, das ist ein Missverständnis des Blockierungsprinzips :)

Aber machen wir noch einmal die Schritte:

  1. session_start() erstellt / liest nicht nur eine Datei, sondern sperrt sie auch, sodass zum Zeitpunkt der Ausführung des Skripts niemand Änderungen vornehmen oder nicht konsistente Daten aus der Sitzungsdatei lesen kann
  2. Die Sperre wird am Ende des Skripts aufgehoben


Das Festhalten an diesem Fehler ist sehr einfach. Erstellen Sie zwei Dateien:

 // start.php session_start(); echo "OK"; 


 // lock.php session_start(); sleep(10); echo "OK"; 


Wenn Sie nun die Seite lock.php im Browser öffnen und dann start.php in einem neuen Tab öffnen, wird die zweite Seite erst geöffnet, nachdem das erste Skript ausgeführt wurde, wodurch die Sitzungsdatei für 10 Sekunden blockiert wird.

Es gibt verschiedene Möglichkeiten, ein solches Phänomen zu vermeiden - „ungeschickt“ und „nachdenklich“.

"Axt"
Verwenden Sie einen benutzerdefinierten Sitzungshandler, in dem Sie das Sperren "vergessen" können :)
Eine etwas bessere Option ist es, eine fertige zu nehmen und die Sperre zu deaktivieren (zum Beispiel hat memcached eine solche Option - memcached.sess_locking ) O_o
Verbringen Sie Stunden damit, Code zu debuggen, um nach einem seltenen Popup-Fehler zu suchen ...

"Nachdenklich"
Wo ist der beste Weg - um die Sitzungssperre selbst zu überwachen und zu entfernen, wenn sie nicht benötigt wird:

- Wenn Sie sicher sind, dass Sie keine Änderungen an den Sitzungsdaten vornehmen müssen, verwenden read_and_close beim Starten der Sitzung die Option read_and_close :

 session_start([ 'read_and_close' => true ]); 


Somit wird die Sperre sofort nach dem Lesen der Sitzungsdaten aufgehoben.

- Wenn Sie noch Änderungen an der Sitzung vornehmen müssen, schließen Sie die Sitzung nach dem Aufnehmen von der Aufzeichnung:

 session_start(); // some changes session_write_close(); 


Aufgabe
Die Auflistung der beiden Dateien start.php und lock.php war etwas höher. Erstellen Sie weitere Dateien read-close.php und write-close.php , in denen Sie das Sperren auf die aufgeführten Arten steuern können. Überprüfen Sie, wie das Schloss funktioniert (oder nicht funktioniert).


Abschließend


In diesem Artikel wurden Ihnen sieben Aufgaben zugewiesen, die sich nicht nur auf die Arbeit mit Sitzungen beziehen , sondern Sie auch mit MySQL- und Zeichenfolgenfunktionen vertraut machen . Für die Assimilation dieses Materials - Sie benötigen keinen separaten Artikel, nur das Handbuch zu den bereitgestellten Links reicht aus - wird niemand es für Sie lesen. Mach es!

PS Wenn du etwas Neues aus dem Artikel gelernt hast - danke dem Autor - teile den Artikel in sozialen Netzwerken;)
PPS Ja, dies ist ein Cross-Post-Artikel aus meinem Blog , aber er ist immer noch relevant :)

Eine Artikelserie "PHP für Anfänger":

Source: https://habr.com/ru/post/de437972/


All Articles