1. System Overview
Widget Storm is a modular widget platform for the web, active since 2008. Widgets are self-contained web components made of three layers (PHP, CSS, JavaScript), stored encrypted on the server and delivered over HTTP endpoints.
| Property | Value |
|---|---|
| Version | 4.0.0 |
| Language | PHP 8.3 |
| Database | MySQL / MariaDB |
| Encryption | RIJNDAEL-256 (Legacy), AES-256-CBC (Modern) |
| Default author | WidgetStormSystem |
| Execution contexts | Local (wgclient.php), Remote (wgcall.php), Client-side (wghandler.php) |
2. Widget Structure
Every widget consists of exactly three files. All three layers have access to the same parameters ($wgparams) and the instance ID ($wgguid).
| Layer | File | Content-Type | Description |
|---|---|---|---|
| PHP | widget_name.php | text/html | Server-side logic and HTML output |
| CSS | widget_name.css | text/css | Styling with an optional PHP bridge |
| JS | widget_name.js | text/javascript | Client-side interaction |
Variables available in the widget code
| Variable | Type | Description |
|---|---|---|
| $wgparams | object|array|string | Widget parameters (parsed from JSON, a semicolon list, or a string) |
| $widgetparams | object|array|string | Alias for $wgparams |
| $wgguid | string | Unique instance ID for CSS/JS scoping |
PHP layer — example
<?php
if(isset($widgetparams->uid)) $wgguid = $widgetparams->uid;
else $wgguid = "";
if(isset($wgparams)){
$text = isset($wgparams->text) ? $wgparams->text : "Default text";
?>
<div id="mein_widget<?= $wgguid;?>" class="mein-widget">
<p><?= $text;?></p>
</div>
<?php
}
?>
Output modes
| Mode | Output |
|---|---|
| php | HTML only (evalAt Widget Storm, eval() executes exclusively widget code that authenticated authors have stored in the database — never user input. It is the technical foundation of the PHP-in-CSS bridge and of dynamic widget composition. No user input ever reaches eval(). result) |
| css | CSS only (evalThe CSS layer is rendered through eval() to enable the PHP-in-CSS bridge. The code comes exclusively from the database — created by authenticated widget authors. Dynamic selectors and instance-specific styling would not be possible without this mechanism. result) |
| js | JavaScript only (evalThe JS layer also uses eval() for embedded PHP variables (UID scoping, parameters). As with all widget layers: the executed code is author-verified and stored in the database, not user-generated. result) |
| (empty) | <style>CSS</style> PHP <script>JS</script> |
3. Parameter System
Parameters are passed to widgets via wgparams. The system supports three formats:
JSON (recommended)
{"text":"Hello World","color":"#ff0000","items":[1,2,3]}
Parsed into a stdClass object. Access via $wgparams->text.
Semicolon-separated
Value1;Value2;Value3
Parsed into an array. Access via $wgparams[0], $wgparams[1], etc.
Simple string
Any arbitrary text
Passed through unchanged as a string.
URL encoding for HTTP requests
wgparams={"text":"Hello","uid":"abc123"}
// In PHP, for URL parameters:
$packurl = http_build_query(['wgparams' => $paramsArray]);
4. PHP-in-CSS/JS Bridge
CSS and JS files can contain embedded PHP code. Because PHP tags are syntactically invalid in CSS/JS, they are wrapped in comments. The system converts them automatically before execution.
Conversion rules
| In the source code | Becomes |
|---|---|
| /*<?php | <?php |
| /*<?= | <?= |
| ?>*/ | ?> |
CSS example
/*<?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 example
/*<?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. Nesting & Markers
Widgets can load other widgets. This is done via markers in the PHP code, which the server resolves recursively.
Marker syntax
##wgstart##widgetname::author::parameter##wgend##
| Segment | Description |
|---|---|
| widgetname | Name of the widget to embed |
| author | Author of the widget |
| parameter | Parameters as JSON or a semicolon list |
Resolution process
- The server finds all
##wgstart##...##wgend##markers in the PHP code - Extracts the name, author, and parameters from the marker
- Resolves the nested widget recursively
- The nested widget's CSS and JS are appended to the parent widget
- PHP is Base64-encoded and executed via
intern_script_wall()
UID Scoping & Placeholders
| Placeholder | Replacement | Usage |
|---|---|---|
| ##self## | widgetname + md5($wgguid) | Unique selector per instance |
| ##wgself## | widgetname + md5($wgguid) | Alias for ##self## |
| ##selfreplace## | ##self## | Escape: literal ##self## in the output |
6. Availability Levels
| Level | Access | Billing |
|---|---|---|
| open | All registered users | Free |
| authenticated | Author + users with explicit permission | Optional |
| payment | Author + users with active billing | Required |
Developers set the availability at upload time (parameter wgrmode). The level determines who can retrieve the widget through their wgclient — and whether billing is required.
Access control
1. Widget.availability == 'open' → Access granted
2. Widget.author == RequestUser → Access granted (author)
3. Permission check → Checked against the internal permission table
- Global permission → Applies to all users
- Specific permission → Applies to individual users
Widget-level security
Widgets can bring their own security mechanisms — independent of the host system. The wg_demo_todo widget demonstrates this principle: AES-256-encrypted persistence, key-based authentication with brute-force protection, and automatic preflight checks for dependencies and permissions. You can find more details on the widget overview.
7. HTTP Endpoints
wgclient.php — widget delivery (local)
Serves both as an HTTP endpoint and as a PHP include for server-side integration.
| Parameter | Required | Description |
|---|---|---|
| wgname | Yes | Widget name |
| wgauthor | No | Widget author (default: WidgetStormSystem) |
| wgmode | No | Output mode: php, css, js, or empty (all) |
| wgparams | No | Widget parameters (JSON, semicolon list, or string) |
| wgguid | No | Instance ID for scoping |
wgcall.php — remote widget delivery
Endpoint for external servers. Requires authentication and encrypts the response.
| Parameter | Required | Description |
|---|---|---|
| wgname | Yes | Widget name (alphanumeric + underscore + hyphen) |
| wgauthor | Yes | Widget author |
| wgmode | Yes | Allowed values: php, css, js, go, c, md |
| requestuser | Yes | Username |
| requestkey | Yes | Authentication key (MD5 hash) |
| wgparams | No | Widget parameters |
| wgguid | No | Instance ID |
| wgfirst | No | true = no nesting resolution |
wgregistration.php — widget upload
Endpoint for uploading new or updated widgets.
| Parameter | Description |
|---|---|
| wgrname | Widget name |
| wgrparam | Layer: php, css, js |
| wgrcode | Encrypted widget code |
| wgrmode | Availability: authenticated, open, payment |
| wgrrequestuser | Username |
| wgrrequestkey | Authentication key |
wgbuilds/ (batch mode), only widgets assigned to your own author account are processed. wgclientcreator.php — client download
Generates a personalized wgclient.php with embedded credentials. Requires an active session (login via the Station). A new requestkey is generated on every download (key rotation via md5(username + uniqid())).
wghandlercreator.php — handler download
Generates a portable wghandler.php class with an embedded RIJNDAEL-256 implementation (pure PHP via phpseclib3, compatible with PHP 8+), local widget caching, and support for nested widgets.
8. Widget Integration
Local integration (same server)
<!-- CSS in the <head> -->
<link rel="stylesheet"
href="wgclient.php?wgname=basic&wgauthor=WidgetStormSystem&wgmode=css&wgparams=..."
type="text/css">
<!-- JavaScript before </body> -->
<script src="wgclient.php?wgname=basic&wgauthor=WidgetStormSystem&wgmode=js&wgparams=..."></script>
<!-- PHP in the <body> -->
<?php
require_once 'wgclient.php';
$wg->get('basic', 'WidgetStormSystem', $params, 'php');
?>
Remote integration (external server)
<?php
// Place wgclient.php and wghandler.php on your own server
require_once 'wgclient.php';
// The credentials are embedded in wgclient.php
$wg->get('basic', 'WidgetStormSystem', $params, 'php');
?>
Execution modes (wgexecutionmode)
The developer uses wgexecutionmode to control whether a widget is rendered locally from the CodeVault or fetched live from the Widget Storm server. The default mode uses the local cache — which means no network latency and maximum loading speed.
| Mode | Behavior | Use case |
|---|---|---|
| (Default) | Local CodeVault — load widgets from the cache | Production: fastest delivery, no server dependency |
| live | Remote fetch — load and execute the widget live from the server | Development: always the latest version, no local file needed |
| uploadwidgets | Batch upload — upload local widgets in wgbuilds/ | Deployment: sync all your own widgets from the local directory |
9. Encryption
Communication between client and server is encrypted. The system supports two encryption versions per user: RIJNDAEL-256 (the original method, in use since 2008) and AES-256-CBC (the modern standard for new users). Both coexist through a central EncryptionNegotiator that automatically selects the appropriate version per user.
| Version | Algorithm | Mode | Status |
|---|---|---|---|
| v1_rijndael | RIJNDAEL-256 | ECB | Legacy (since 2008) |
| v2_aes | AES-256-CBC | CBC with a random IV | Standard for new users |
Both directions (server → client and client → server) are encrypted at the application level. Each user has an individual key, which is rotated on every client download.
mcrypt extension was the standard way to do cryptography and openssl_encrypt() was not yet available. The decision matched the state of the art at the time. Since the modernization to PHP 8.3, phpseclib3 is used as a drop-in replacement for the removed mcrypt extension. You can find detailed background on this in the FAQ. 10. Rate Limiting
| Endpoint | Limit | Window | HTTP status when exceeded |
|---|---|---|---|
| wgcall.php | 120 requests | 60 seconds | 429 Too Many Requests |
| wgregistration.php | 30 uploads | 60 seconds | 429 Too Many Requests |
Tracking is done per IP address and endpoint. State files in /tmp/ws-ratelimit/.
11. CodeVault
The CodeVault is the heart of widget delivery. It stores widget code as local files on the client server — which means direct file-system access instead of network round-trips. Widgets load as fast as any other local PHP file, without any dependency on the central Widget Storm server.
Purpose & benefits
| Property | Description |
|---|---|
| Speed | Local file reads instead of HTTP requests — load times in the microsecond range |
| Independence | Widgets keep working even when the Widget Storm server is unreachable |
| Redundancy | Dual storage: database (server) + file system (client) |
| Author isolation | Each author has their own subdirectory — overwriting other authors' widgets is not possible |
Directory structure
wgbuilds/
├─ {Author}/
│ ├─ {widget_name}.php
│ ├─ {widget_name}.css
│ └─ {widget_name}.js
└─ WidgetStormSystem/
├─ basic.php
├─ basic.css
├─ basic.js
├─ wg_nav.php
└─ ...
The directory structure is organized by author. During batch upload (uploadwidgets mode), the wghandler scans the wgbuilds/ directory and automatically uploads changed files to the server — processing only your own widgets. Widgets from other authors remain untouched.
Rendering from the CodeVault
In the default mode, the wgclient reads the widget code directly from wgbuilds/ and executes it locally (evalThe CodeVault code is executed via eval() in the server context. The files come from the authenticated download or your own upload — they are identical to the database original. No external input flows into the execution.). The live mode bypasses the CodeVault and queries the Widget Storm server directly — useful during development, but slower than the local cache.