1. Systemübersicht
Widget Storm ist eine modulare Widget-Plattform für das Web, aktiv seit 2008. Widgets sind eigenständige Webkomponenten aus drei Schichten (PHP, CSS, JavaScript), die verschlüsselt auf dem Server gespeichert und über HTTP-Endpunkte ausgeliefert werden.
| Eigenschaft | Wert |
|---|---|
| Version | 4.0.0 |
| Sprache | PHP 8.3 |
| Datenbank | MySQL / MariaDB |
| Verschlüsselung | RIJNDAEL-256 (Legacy), AES-256-CBC (Modern) |
| Standard-Autor | WidgetStormSystem |
| Ausführungskontexte | Lokal (wgclient.php), Remote (wgcall.php), Client-seitig (wghandler.php) |
2. Widget-Struktur
Jedes Widget besteht aus exakt drei Dateien. Alle drei Schichten haben Zugriff auf
die gleichen Parameter ($wgparams) und die Instanz-ID ($wgguid).
| Schicht | Datei | Content-Type | Beschreibung |
|---|---|---|---|
| PHP | widget_name.php | text/html | Serverseitige Logik und HTML-Ausgabe |
| CSS | widget_name.css | text/css | Styling mit optionaler PHP-Bridge |
| JS | widget_name.js | text/javascript | Client-seitige Interaktion |
Verfügbare Variablen im Widget-Code
| Variable | Typ | Beschreibung |
|---|---|---|
| $wgparams | object|array|string | Widget-Parameter (geparst aus JSON, Semikolon-Liste oder String) |
| $widgetparams | object|array|string | Alias für $wgparams |
| $wgguid | string | Instanz-ID für CSS/JS-Scoping — beim Script-Tag gesetzt, im PHP-Include das 5. Argument |
PHP-Schicht — Beispiel
<?php
if(isset($widgetparams->uid)) $wgguid = $widgetparams->uid;
else $wgguid = "";
if(isset($wgparams)){
$text = isset($wgparams->text) ? $wgparams->text : "Standard-Text";
?>
<div id="mein_widget<?= $wgguid;?>" class="mein-widget">
<p><?= $text;?></p>
</div>
<?php
}
?>
Ausgabemodi
| Modus | Ausgabe |
|---|---|
| php | Nur HTML (evalBei Widget-Storm führt eval() ausschließlich Widget-Code aus, der von authentifizierten Autoren in der Datenbank hinterlegt wurde — nicht von Benutzereingaben. Es ist die technische Grundlage der PHP-in-CSS Bridge und der dynamischen Widget-Komposition. Kein User-Input erreicht eval().-Ergebnis) |
| css | Nur CSS (evalDie CSS-Schicht wird durch eval() gerendert, um die PHP-in-CSS Bridge zu aktivieren. Der Code stammt ausschließlich aus der Datenbank — erstellt von authentifizierten Widget-Autoren. Dynamische Selektoren und instanzspezifisches Styling wären ohne diesen Mechanismus nicht möglich.-Ergebnis) |
| js | Nur JavaScript (evalAuch die JS-Schicht nutzt eval() für eingebettete PHP-Variablen (UID-Scoping, Parameter). Wie bei allen Widget-Schichten: der ausgeführte Code ist autorengeprüft und datenbankgespeichert, nicht nutzergeneriert.-Ergebnis) |
| (leer) | <style>CSS</style> PHP <script>JS</script> |
3. Parameter-System
Parameter werden über wgparams an Widgets übergeben.
Das System unterstützt drei Formate:
JSON (empfohlen)
{"text":"Hallo Welt","color":"#ff0000","items":[1,2,3]}
Wird zu einem stdClass-Objekt geparst. Zugriff via $wgparams->text.
Semikolon-getrennt
Wert1;Wert2;Wert3
Wird zu einem Array geparst. Zugriff via $wgparams[0], $wgparams[1], etc.
Einfacher String
Ein beliebiger Text
Wird unverändert als String durchgereicht.
URL-Encoding für HTTP-Requests
wgparams={"text":"Hallo","uid":"abc123"}
// In PHP für URL-Parameter:
$packurl = http_build_query(['wgparams' => $paramsArray]);
4. PHP-in-CSS/JS Bridge
CSS- und JS-Dateien können eingebetteten PHP-Code enthalten. Da PHP-Tags in CSS/JS syntaktisch ungültig sind, werden sie in Kommentare verpackt. Das System konvertiert diese automatisch vor der Ausführung.
Konvertierungsregeln
| Im Quellcode | Wird zu |
|---|---|
| /*<?php | <?php |
| /*<?= | <?= |
| ?>*/ | ?> |
CSS-Beispiel
/*<?php
if(isset($wgparams->uid)) $wgguid = $wgparams->uid;
else $wgguid = "";
?>*/
div#mein_widget/*<?= $wgguid;?>*/ {
background: rgba(170,210,210,0.08);
border: 1px solid rgba(170,210,210,0.2);
padding: 1em;
border-radius: 0.4em;
}
JS-Beispiel
/*<?php
if(isset($wgparams->uid)) $wgguid = $wgparams->uid;
else $wgguid = "";
?>*/
jQuery(document).ready(function(){
jQuery("#mein_widget/*<?= $wgguid;?>*/").on("click", function(){
jQuery(this).toggleClass("active");
});
});
5. Verschachtelung & Marker
Widgets können andere Widgets laden. Dies geschieht über Marker im PHP-Code, die vom Server rekursiv aufgelöst werden.
Marker-Syntax
##wgstart##widgetname::autor::parameter##wgend##
| Segment | Beschreibung |
|---|---|
| widgetname | Name des einzubettenden Widgets |
| autor | Autor des Widgets |
| parameter | Parameter als JSON oder Semikolon-Liste |
Auflösungsprozess
- Server findet alle
##wgstart##...##wgend##Marker im PHP-Code - Extrahiert Name, Autor und Parameter aus dem Marker
- Löst das verschachtelte Widget rekursiv auf
- CSS und JS des verschachtelten Widgets werden an das Eltern-Widget angehängt
- PHP wird Base64-kodiert und über
intern_script_wall()ausgeführt
UID-Scoping & Platzhalter
| Platzhalter | Ersetzung | Verwendung |
|---|---|---|
| ##self## | widgetname + md5($wgguid) | Selektor dieser Instanz (Widgetname + md5 von $wgguid). Script-Tag: jede Einbettung auf einer Seite erhält einen eigenen. PHP-Include: jeder Wiederholung desselben Widgets ein eigenes $wgguid (5. Argument) übergeben. |
| ##wgself## | widgetname + md5($wgguid) | Alias für ##self## |
| ##selfreplace## | ##self## | Escape: literales ##self## im Output |
6. Verfügbarkeitsstufen
| Stufe | Zugriff | Billing |
|---|---|---|
| open | Alle registrierten Nutzer | Kostenlos |
| authenticated | Autor + Nutzer mit expliziter Berechtigung | Optional |
| payment | Autor + Nutzer mit aktivem Billing | Erforderlich |
Entwickler legen die Verfügbarkeit beim Upload fest (Parameter wgrmode).
Die Stufe bestimmt, wer das Widget über seinen wgclient abrufen kann —
und ob eine Abrechnung erforderlich ist.
Zugriffskontrolle
1. Widget.availability == 'open' → Zugriff gewährt
2. Widget.author == RequestUser → Zugriff gewährt (Autor)
3. Berechtigungsprüfung → Prüfung in interner Berechtigungstabelle
- Globale Berechtigung → Gilt für alle Nutzer
- Spezifische Berechtigung → Gilt für einzelne Nutzer
Sicherheit auf Widget-Ebene
Widgets können eigene Sicherheitsmechanismen mitbringen — unabhängig vom
Hostsystem. Das Widget wg_demo_todo demonstriert dieses Prinzip:
AES-256-verschlüsselte Persistenz, schlüsselbasierte Authentifizierung mit
Brute-Force-Schutz und automatische Preflight-Checks für Abhängigkeiten und
Berechtigungen. Weitere Details finden Sie auf der
Widget-Übersicht.
7. HTTP-Endpunkte
wgclient.php — Widget-Auslieferung (lokal)
Dient als HTTP-Endpunkt und als PHP-Include für serverseitige Einbindung.
| Parameter | Pflicht | Beschreibung |
|---|---|---|
| wgname | Ja | Widget-Name |
| wgauthor | Nein | Widget-Autor (Standard: WidgetStormSystem) |
| wgmode | Nein | Ausgabemodus: php, css, js oder leer (alle) |
| wgparams | Nein | Widget-Parameter (JSON, Semikolon-Liste oder String) |
| wgguid | Nein | Instanz-ID für Scoping |
Composer — wgclient & wghandler als Pakete
Beide Client-Dateien lassen sich alternativ über ein privates Composer-Repository
installieren. Die Authentifizierung erfolgt mit Ihrem Station-Login
(HTTP Basic — Composer fragt beim ersten Zugriff danach und speichert die Zugangsdaten in der auth.json).
composer config repositories.widget-storm composer https://widget-storm.de/composer
composer require widget-storm/wgclient:^4.1
vendor/bin/wg-deploy public/
Schlüssel-Semantik: Wiederholte Installationen und composer update
liefern denselben Schlüssel erneut aus — Ihr installierter wgclient bleibt gültig.
Nur die allererste Composer-Installation eines Kontos erzeugt einmalig einen neuen Schlüssel
(ein zuvor über die Station geladener wgclient wird dabei ungültig). Eine bewusste Rotation
ist jederzeit möglich (wgclientcreator.php?rotate=1); danach
composer clear-cache && composer reinstall widget-storm/wgclient ausführen
und neu deployen — ohne clear-cache würde Composers lokaler Paket-Cache
den alten Client wiederherstellen.
wgcall.php — Remote Widget-Auslieferung
Endpunkt für externe Server. Erfordert Authentifizierung und verschlüsselt die Antwort.
| Parameter | Pflicht | Beschreibung |
|---|---|---|
| wgname | Ja | Widget-Name (alphanumerisch + Unterstrich + Bindestrich) |
| wgauthor | Ja | Widget-Autor |
| wgmode | Ja | Erlaubte Werte: php, css, js, go, c, md |
| requestuser | Ja | Benutzername |
| requestkey | Ja | Authentifizierungsschlüssel (MD5-Hash) |
| wgparams | Nein | Widget-Parameter |
| wgguid | Nein | Instanz-ID |
| wgfirst | Nein | true = keine Verschachtelungsauflösung |
wgregistration.php — Widget-Upload
Endpunkt zum Hochladen neuer oder aktualisierter Widgets.
| Parameter | Beschreibung |
|---|---|
| wgrname | Widget-Name |
| wgrparam | Schicht: php, css, js |
| wgrcode | Verschlüsselter Widget-Code |
| wgrmode | Verfügbarkeit: authenticated, open, payment |
| wgrrequestuser | Benutzername |
| wgrrequestkey | Authentifizierungsschlüssel |
wgbuilds/ (Batch-Modus) werden entsprechend nur
Widgets verarbeitet, die dem eigenen Autoren-Konto zugeordnet sind. wgclientcreator.php — Client-Download
Generiert eine personalisierte wgclient.php mit eingebetteten Zugangsdaten.
Erfordert aktive Session (Login über Station). Ihr requestkey bleibt bei
wiederholtem Download erhalten; ein neuer wird nur beim allerersten Download erzeugt —
oder auf Wunsch per ?rotate=1 (Key-Rotation, der alte Schlüssel wird ungültig).
wghandlercreator.php — Handler-Download
Generiert eine portable wghandler.php Klasse mit eingebetteter
RIJNDAEL-256 Implementierung (pure PHP via phpseclib3, kompatibel mit PHP 8+),
lokalem Widget-Caching und Unterstützung für verschachtelte Widgets.
8. Widget-Einbindung
Lokale Einbindung (gleicher Server)
<!-- CSS im <head> -->
<link rel="stylesheet"
href="wgclient.php?wgname=basic&wgauthor=WidgetStormSystem&wgmode=css&wgparams=..."
type="text/css">
<!-- JavaScript vor </body> -->
<script src="wgclient.php?wgname=basic&wgauthor=WidgetStormSystem&wgmode=js&wgparams=..."></script>
<!-- PHP im <body> -->
<?php
require_once 'wgclient.php';
$wg->get('basic', 'WidgetStormSystem', $params, 'php');
?>
Remote-Einbindung (externer Server)
<?php
// wgclient.php und wghandler.php auf eigenem Server platzieren
require_once 'wgclient.php';
// Zugangsdaten sind in wgclient.php eingebettet
$wg->get('basic', 'WidgetStormSystem', $params, 'php');
?>
Support-Chat wg_chat nach dem Kauf einrichten
Der Chat läuft auf Ihrem eigenen Webserver: PHP ab Version 7.0 mit den Erweiterungen openssl und curl. Nach dem Kauf steht dieselbe Anleitung mit Ihrer Domain in der Station unter „Ihre gekauften Widgets“.
- Laden Sie in der Station
wgclient.phpundwghandler.phpund legen Sie beide in das Hauptverzeichnis Ihrer Website. Danach musshttps://ihre-seite.de/wgclient.phperreichbar sein, denn der Chat ruft genau diese Adresse auf. Erneutes Laden behält Ihren Schlüssel. - Bauen Sie den Chat in eine PHP-Seite im selben Verzeichnis ein, zum Beispiel
kontakt.php(Code unten). Die erste Zeile steht ganz am Anfang der Datei, vor jeder anderen Ausgabe. - Öffnen Sie die Seite einmal mit
?wg_chat_key=und einem selbst gewählten Betreiber-Schlüssel von mindestens 12 Zeichen am Ende der Adresse. Danach sehen Sie die Betreiber-Ansicht. Richten Sie den Chat ein, bevor Sie die Seite verlinken: Bis dahin sehen Besucher einen Einrichtungshinweis, und der erste Schlüssel gilt. - Zum Antworten öffnen Sie dieselbe Adresse mit Ihrem Schlüssel. Die Betreiber-Ansicht bleibt bis 12 Stunden nach der letzten Nutzung angemeldet. Besucher öffnen die Seite ohne Schlüssel.
<?php require_once __DIR__ . '/wgclient.php'; ?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="/wgclient.php?wgname=wg_chat&wgauthor=WidgetStormSystem&wgmode=css">
</head>
<body>
<?php $wg->get('wg_chat', 'WidgetStormSystem', ['uid' => 'support'], 'php'); ?>
<script src="/wgclient.php?wgname=wg_chat&wgauthor=WidgetStormSystem&wgmode=js"></script>
</body>
</html>
| Parameter | Bedeutung |
|---|---|
| uid | Name des Chats. Besucher- und Betreiberseite brauchen denselben Wert. Ohne uid gilt der Hostname Ihrer Website, ohne www. und Port. |
| wglang | en für englische Schaltflächen und Hinweise; ohne Angabe Deutsch. |
| title | Überschrift des Chatfensters (Standard: Support-Chat). |
| greeting | Begrüßung über dem Verlauf. |
| poll | Abfrageintervall in Millisekunden, mindestens 1500 (Standard 4000). |
| maxlen | Höchstlänge einer Nachricht, 20 bis 4000 Zeichen (Standard 1000). |
wgdata/wg_chat, eine Ebene über dem Hauptverzeichnis. PHP muss ihn anlegen dürfen; sonst nennt der Chat den Pfad. Im Browser der Besucher liegt nur eine signierte Gesprächs-ID.Neue Fassung: Ihr Server speichert den Widget-Code beim ersten Abruf unter
wgbuilds/WidgetStormSystem/. Mit einem Handler ab Version 4.2 (steht oben in wghandler.php) holt er Korrekturen selbst, spätestens nach einer Stunde. Bei einem älteren Handler laden Sie ihn neu herunter oder verschieben dort wg_chat.php, wg_chat.js und wg_chat.css in einen anderen Ordner; der nächste Seitenaufruf lädt dann die aktuelle Fassung.Schlüssel vergessen: Verschieben Sie in
wgdata/wg_chat die Dateien Ihres Chats (config_…, data_… und rate_… mit derselben Kennung) in einen anderen Ordner und richten Sie den Chat neu ein. Die alten Gespräche bleiben verschlüsselt in den verschobenen Dateien. Ausführungsmodi (wgexecutionmode)
Der Entwickler steuert über den wgexecutionmode, ob ein Widget
lokal aus dem CodeVault gerendert wird
oder live vom Widget-Storm-Server abgerufen werden soll.
Der Standard-Modus nutzt den lokalen Cache — das bedeutet
keine Netzwerk-Latenz und maximale Ladegeschwindigkeit.
| Modus | Verhalten | Einsatz |
|---|---|---|
| (Standard) | Lokaler CodeVault — Widgets aus dem Cache laden | Produktion: schnellste Auslieferung, keine Server-Abhängigkeit |
| live | Remote Fetch — Widget live vom Server laden und ausführen | Entwicklung: immer aktuelle Version, keine lokale Datei nötig. Gewählt im eigenen PHP (sechstes Argument von $wg->get()); über eine URL wird es ignoriert |
| uploadwidgets | Batch-Upload — lokale Widgets in wgbuilds/ hochladen | Deployment: alle eigenen Widgets aus dem lokalen Verzeichnis synchronisieren. Nur auf der Kommandozeile: php wgclient.php uploadwidgets; über eine URL wird es ignoriert |
9. Verschlüsselung
Die Kommunikation zwischen Client und Server ist verschlüsselt.
Das System unterstützt zwei Verschlüsselungsversionen pro Benutzer:
RIJNDAEL-256 (das ursprüngliche Verfahren seit 2008) und
AES-256-CBC (der moderne Standard für neue Nutzer).
Beide koexistieren über einen zentralen EncryptionNegotiator,
der automatisch die passende Version pro Benutzer wählt.
| Version | Algorithmus | Modus | Status |
|---|---|---|---|
| v1_rijndael | RIJNDAEL-256 | ECB | Legacy (seit 2008) |
| v2_aes | AES-256-CBC | CBC mit zufälligem IV | Standard für neue Nutzer |
Beide Richtungen (Server → Client und Client → Server) sind auf Anwendungsebene verschlüsselt. Jeder Nutzer hat einen individuellen Schlüssel, der bei jedem Client-Download rotiert wird.
mcrypt-Extension der Standard-Weg für Kryptografie war und
openssl_encrypt() noch nicht verfügbar war. Die Entscheidung war dem
damaligen Stand der Technik entsprechend. Seit der Modernisierung auf PHP 8.3
wird phpseclib3 als Drop-in-Ersatz für die entfernte mcrypt-Extension verwendet.
Ausführliche Hintergründe dazu finden Sie in den
FAQ. 10. Rate Limiting
| Endpunkt | Limit | Fenster | HTTP-Status bei Überschreitung |
|---|---|---|---|
| wgcall.php | 120 Anfragen | 60 Sekunden | 429 Too Many Requests |
| wgregistration.php | 30 Uploads | 60 Sekunden | 429 Too Many Requests |
Tracking erfolgt pro IP-Adresse und Endpunkt. Zustandsdateien in /tmp/ws-ratelimit/.
11. CodeVault
Der CodeVault ist das Herzstück der Widget-Auslieferung. Er speichert Widget-Code als lokale Dateien auf dem Client-Server — das bedeutet direkten Dateisystem-Zugriff statt Netzwerk-Roundtrips. Widgets werden so schnell geladen wie jede andere lokale PHP-Datei, ohne Abhängigkeit vom zentralen Widget-Storm-Server.
Zweck & Vorteile
| Eigenschaft | Beschreibung |
|---|---|
| Geschwindigkeit | Lokale Datei-Reads statt HTTP-Anfragen — Ladezeiten im Mikrosekundenbereich |
| Unabhängigkeit | Widgets funktionieren auch wenn der Widget-Storm-Server nicht erreichbar ist |
| Redundanz | Doppelte Speicherung: Datenbank (Server) + Dateisystem (Client) |
| Autoren-Isolation | Jeder Autor hat sein eigenes Unterverzeichnis — kein Überschreiben fremder Widgets möglich |
Verzeichnisstruktur
wgbuilds/
├─ {Autor}/
│ ├─ {widget_name}.php
│ ├─ {widget_name}.css
│ └─ {widget_name}.js
└─ WidgetStormSystem/
├─ basic.php
├─ basic.css
├─ basic.js
├─ wg_nav.php
└─ ...
Die Verzeichnisstruktur ist nach Autoren organisiert.
Beim Batch-Upload (uploadwidgets-Modus) scannt der wghandler
das wgbuilds/ Verzeichnis und lädt geänderte Dateien
automatisch auf den Server hoch — dabei werden nur eigene Widgets
verarbeitet. Widgets anderer Autoren bleiben unberührt.
Rendering aus dem CodeVault
Im Standard-Modus liest der wgclient den Widget-Code direkt aus wgbuilds/
und führt ihn lokal aus (evalDer CodeVault-Code wird via eval() im Server-Kontext ausgeführt. Die Dateien stammen aus dem authentifizierten Download oder eigenem Upload — sie sind identisch mit dem Datenbank-Original. Kein externer Input fließt in die Ausführung ein.).
Der Live-Modus umgeht den CodeVault und fragt den Widget-Storm-Server direkt ab —
nützlich während der Entwicklung, aber langsamer als der lokale Cache.