[TUTORIAL] PB-CGI und PHP-Desktop: Routing

Hier kannst du häufig gestellte Fragen/Antworten und Tutorials lesen und schreiben.
Benutzeravatar
TroaX
Beiträge: 659
Registriert: 08.03.2013 14:27
Computerausstattung: PC: Ryzen 9 3950X, 96 GB RAM, RX6800XT, 2.5 TB SSD, 21:9 Display, Pop_OS! | Lappi: Ryzen 7 5800H, 16 GB RAM, 1 TB SSD, Pop_OS!
Wohnort: NRW
Kontaktdaten:

[TUTORIAL] PB-CGI und PHP-Desktop: Routing

Beitrag von TroaX »

Folgetutorial von: http://www.purebasic.fr/german/viewtopi ... =9&t=30554

Das Problem besteht jetzt, das PHP-Desktop nur eine einzige CGI-Anwendung verwalten kann. Das bedeutet, das Unterroutinen mit Kniffen genauer spezifiziert werden müssen. Denn alle Routinen sind in einer einzigen Anwendung implementiert. Wir brauchen jetzt eine Möglichkeit, über das Frontend die passende Backend-Routine auswählen zu können. Dafür gibt es mehrere Möglichkeiten.

Variante 1: Der Query-String
Im HTTP-Protokoll dient der Query-String dazu, um innerhalb von dynamischen Webanwendung innerhalb einer Ressource genauere Optionen für die Verarbeitung der Anwendung zu definieren. Nehmen wir mal an, wir wollen innerhalb eines Forums einen bestimmten Beitrag lesen, so könnte die URL so aussehen:

Code: Alles auswählen

http://example.de/forum.php?thread=1
Alles, was hinter dem Fragezeichen steht, ist der Query-String. Genauso könnte sich aber auch über diesen die Routine auswählen lassen, die bei dem Request ausgeführt werden soll.

In Purebasic bekommt man den Query-String über CGIVariable(#PB_CGI_QueryString). Nun könnte man diesen händisch parsen. Aber mit einem kleinen Trick kann man sich dies auch sparen. Mit der Prozedur GetURLPart kann eine komplette URL in seine Teile zerlegt und Daten daraus abgefragt werden. Übergibt man dieser Funktion aber nur den Query-String, so wird das ganze nicht funktionieren, da es sich dabei um keine gültige URL handelt. Für eine gültige URL benötigt man das Protokoll, die Domain mit TLD-Endung und den Query-String. Wir haben aber nur den letzten Teil. Dieser Aufwand ist aber nötig, um genau die Schlüsselwerte zu erhalten, mit der wir die Route definieren. Denn es sollen auch weitere Parameter damit abgefragt werden können.

Wir erschummeln uns also eine gültige URL, in dem wir den Query-String an eine Fake-URL anhängen:

Code: Alles auswählen

FakeURL.s = "a://b.c/?" + CGIVariable(#PB_CGI_QueryString)
Mit dieser URL können wir nun gezielt nach bestimmten Informationen des Strings suchen:

Code: Alles auswählen

SelectedApp.s = GetURLPart(FakeURL,"app")
Dies liefert uns den Wert des Schlüssels app zurück.

Für Kleinere Projekte kann nun über eine klassische Fallunterscheidung der Wert geprüft und dem entsprechend eine Routine ausgeführt werden:

Code: Alles auswählen

If Not InitCGI() Or Not ReadCGI()
  End
EndIf

WriteCGIHeader(#PB_CGI_HeaderContentType, "text/html", #PB_CGI_LastHeader) ; Write the headers to inform the browser of the content format

WriteCGIString("<html><title>PureBasic CGI</title><body>")  

FakeURL.s = "a://b.c/?" + CGIVariable(#PB_CGI_QueryString)
SelectedApp.s = GetURLPart(FakeURL,"app")
SelectedSubApp.s = GetURLPart(FakeURL,"subapp")

If(SelectedApp = "test" And SelectedSubApp = "1")
  WriteCGIString("<p>Routine 1</p>") 
ElseIf(SelectedApp = "test" And SelectedSubApp = "2")
  WriteCGIString("<p>Routine 2</p>") 
Else
  WriteCGIString("<p>Routine Default</p>")
EndIf

WriteCGIString("</body></html>")
Ruft man innerhalb von PHP-Desktop über einen Link oder Ajax-Request jetzt cgi.ini?app=test&subapp=1 auf, dann werden wir dn Text Routine 1 sehen. Bei subapp=2 würden wir Routine 2 sehen. Lassen wir den Query-String weg, sehen wir Routine Default.

Bei größeren Projekte empfiehlt es sich aber, das ganze über Callbacks zu realisieren!

Variante 2: Der Request-URI
Beim Request-URI erhält man relativ zum Server die URL der Datei, die aufgerufen wurde. Bei der URL http://example.de/test/test.html wäre der Request-URI /test/test.html. Die URI wird auch von PHP verwendet, um die gewählte PHP-Datei zu laden. Die Request-URI erhält man über CGIVariable(#PB_CGI_RequestURI). Hier muss man aber darauf achten, das auch der Query-String mitgeliefert wird. Man sollte mit Stringfield den String am Fragezeichen auftrennen und nur den ersten Teil verwenden. Mit diesem String kann man natürlich die Routine direkt identifizieren. Aber am intelligentesten ist es, die INI-Datei, auf die gezeigt wird, auszulesen und in dieser auch die Routine zu speichern, die ausgeführt werden soll.

Zum Beispiel:
frontend/test/test.ini

Code: Alles auswählen

app=Selected App
PB-Code

Code: Alles auswählen

If Not InitCGI() Or Not ReadCGI()
  End
EndIf

WriteCGIHeader(#PB_CGI_HeaderContentType, "text/html", #PB_CGI_LastHeader) ; Write the headers to inform the browser of the content format

WriteCGIString("<html><title>PureBasic CGI</title><body>")  
File.s = GetCurrentDirectory() + "frontend" + StringField(CGIVariable(#PB_CGI_RequestURI),1,"?")
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  File = ReplaceString(File,"/","\")
CompilerEndIf
OpenPreferences(File)
WriteCGIString("<html><title>PureBasic CGI</title><body>")
WriteCGIString(ReadPreferenceString("app","default"))
WriteCGIString("</body></html>")
Wird mit PHP-Desktop Über einen Link die URL test/test.ini aufgerufen, wird der String Selected App ausgegeben. Bei allen anderen leeren INI's würde default ausgegeben werden.

Wie auch bei Variante 1 kann man den String nutzen, die gewählte Routine zu nutzen. Ein weiterer Vorteil dieser Vorgehensweise ist aber auch, das man auf diesem weg einfach Routinen abschalten kann, in dem man im laufenden Betrieb die ini-Datei löscht. Dadurch ist die Routine nicht mehr erreichbar. Ist aber auch eher für den betrieb auf dem Server interessant.

Nachwort:
Das waren jetzt die 2 interessantesten Varianten, Daten über die gewählte Ressource zu erfahren und somit die Richtung der CGI-Anwendung zu steuern. Zum routen kann für eine kleine Anwendung natürlich ein Baum mit Fallunterscheidungen verwendet werden. Für größere Projekte ist natürlich die Nutzung einer Callback-Liste zu empfehlen.
PC: Ryzen 9 3950X | 96 GB RAM | RX6800XT | 2,5 TB NVMe | Pop_OS!
Notebook: 16" 3:2 | Ryzen 7 5800H | 16 GB RAM | Radeon Vega | 1TB NVMe | Pop_OS!
NAS: Fritz.Box :lol:
Coding: Purebasic 6.04 | PHP | HTML | CSS | Javascript