@@ -97,30 +97,32 @@ if (file_exists($path)) {
9797
9898## Reading Configuration
9999
100- ### With Defaults (Recommended Pattern)
100+ ### With ` default.cfg ` (Recommended Pattern)
101101
102102``` php
103103<?
104- // Define defaults
105- $defaults = [
106- 'enabled' => 'no',
107- 'interval' => '60',
108- 'path' => '/mnt/user/appdata',
109- 'notify_level' => 'normal',
110- 'log_enabled' => 'no',
111- 'max_log_size' => '1048576'
112- ];
113-
114- // Read config and merge with defaults
104+ // Keep defaults in:
105+ // /usr/local/emhttp/plugins/yourplugin/default.cfg
106+ // parse_plugin_cfg() merges defaults with user config automatically.
115107$cfg = parse_plugin_cfg("yourplugin");
116- $settings = array_merge($defaults, $cfg);
117108
118- // Now $settings always has all keys
119- $enabled = $settings ['enabled'];
120- $interval = $settings ['interval'];
109+ // $cfg already includes defaults for any missing keys.
110+ $enabled = $cfg ['enabled'] === 'yes' ;
111+ $interval = intval($cfg ['interval']) ;
121112?>
122113```
123114
115+ ``` ini
116+ # /usr/local/emhttp/plugins/yourplugin/default.cfg
117+ enabled =" no"
118+ interval =" 60"
119+ path =" /mnt/user/appdata"
120+ notify_level =" normal"
121+ ```
122+
123+ {: .note }
124+ > Keep defaults in ` default.cfg ` (under ` /usr/local/emhttp/plugins/<plugin>/ ` ) instead of recreating default values in each PHP script.
125+
124126### Type-Safe Access
125127
126128``` php
@@ -143,29 +145,37 @@ if (!is_dir($path)) {
143145
144146## Writing Configuration
145147
148+ When handling form submissions or AJAX writes, validate the CSRF token before saving settings. See [ CSRF Tokens] ( csrf-tokens.md ) for the full pattern and background.
149+
146150### Basic Write Pattern
147151
148152``` php
149153<?
150154// /plugins/yourplugin/update.php
151155
156+ function save_plugin_cfg($plugin, $settings) {
157+ $cfg_file = "/boot/config/plugins/$plugin/$plugin.cfg";
158+ $config = "";
159+
160+ foreach ($settings as $key => $value) {
161+ $config .= "$key=\"$value\"\n";
162+ }
163+
164+ return file_put_contents($cfg_file, $config) !== false;
165+ }
166+
152167// Validate CSRF
153168$var = parse_ini_file('/var/local/emhttp/var.ini');
154169if ($_POST['csrf_token'] !== $var['csrf_token']) {
155170 die("Invalid CSRF token");
156171}
157172
158- // Define config file path
159- $cfg_file = "/boot/config/plugins/yourplugin/yourplugin.cfg";
160-
161- // Build configuration content
162- $config = "";
163- $config .= "enabled=\"{$_POST['enabled']}\"\n";
164- $config .= "interval=\"{$_POST['interval']}\"\n";
165- $config .= "path=\"{$_POST['path']}\"\n";
166-
167- // Write to file
168- file_put_contents($cfg_file, $config);
173+ // Use the helper
174+ save_plugin_cfg("yourplugin", [
175+ 'enabled' => $_POST['enabled'],
176+ 'interval' => $_POST['interval'],
177+ 'path' => $_POST['path']
178+ ]);
169179?>
170180```
171181
@@ -175,11 +185,6 @@ file_put_contents($cfg_file, $config);
175185<?
176186// /plugins/yourplugin/update.php
177187
178- $var = parse_ini_file('/var/local/emhttp/var.ini');
179- if ($_POST['csrf_token'] !== $var['csrf_token']) {
180- die("Invalid CSRF token");
181- }
182-
183188/**
184189 * Escape value for INI file
185190 */
@@ -190,30 +195,43 @@ function escapeIniValue($value) {
190195 return $value;
191196}
192197
193- $cfg_file = "/boot/config/plugins/yourplugin/yourplugin.cfg";
198+ function save_plugin_cfg($plugin, $settings) {
199+ $cfg_file = "/boot/config/plugins/$plugin/$plugin.cfg";
194200
195- // Sanitize and validate inputs
196- $enabled = in_array($_POST['enabled'], ['yes', 'no']) ? $_POST['enabled'] : 'no';
197- $interval = max(1, min(3600, intval($_POST['interval']))); // 1-3600 range
198- $path = trim($_POST['path']);
201+ $config = "";
202+ foreach ($settings as $key => $value) {
203+ $key = preg_replace('/[^a-zA-Z0-9_]/', '', $key);
204+ $config .= "$key=\"" . escapeIniValue($value) . "\"\n";
205+ }
199206
200- // Build config
201- $config = "";
202- $config .= "enabled=\"" . escapeIniValue($enabled) . "\"\n";
203- $config .= "interval=\"" . escapeIniValue($interval) . "\"\n";
204- $config .= "path=\"" . escapeIniValue($path) . "\"\n";
207+ $dir = dirname($cfg_file);
208+ if (!is_dir($dir)) {
209+ mkdir($dir, 0755, true);
210+ }
205211
206- // Ensure directory exists
207- $dir = dirname($cfg_file);
208- if (!is_dir($dir)) {
209- mkdir($dir, 0755, true);
212+ $temp_file = $cfg_file . '.tmp';
213+ if (file_put_contents($temp_file, $config) !== false) {
214+ return rename($temp_file, $cfg_file);
215+ }
216+
217+ return false;
210218}
211219
212- // Write atomically (write to temp, then rename)
213- $temp_file = $cfg_file . '.tmp';
214- if (file_put_contents($temp_file, $config) !== false) {
215- rename($temp_file, $cfg_file);
220+ $var = parse_ini_file('/var/local/emhttp/var.ini');
221+ if ($_POST['csrf_token'] !== $var['csrf_token']) {
222+ die("Invalid CSRF token");
216223}
224+
225+ // Sanitize and validate inputs
226+ $enabled = in_array($_POST['enabled'], ['yes', 'no']) ? $_POST['enabled'] : 'no';
227+ $interval = max(1, min(3600, intval($_POST['interval']))); // 1-3600 range
228+ $path = trim($_POST['path']);
229+
230+ save_plugin_cfg("yourplugin", [
231+ 'enabled' => $enabled,
232+ 'interval' => $interval,
233+ 'path' => $path
234+ ]);
217235?>
218236```
219237
@@ -264,66 +282,40 @@ save_plugin_cfg("yourplugin", [
264282?>
265283```
266284
267- ## Default File Creation
285+ ## Default File Strategy
268286
269- ### Creating Defaults on Install (PLG)
287+ Use two files with clear responsibilities:
270288
271- ``` xml
272- <!-- In your .plg file -->
273- <FILE Name =" /boot/config/plugins/yourplugin/yourplugin.cfg" >
274- <INLINE >
275- enabled="no"
276- interval="60"
277- path="/mnt/user/appdata"
278- notify_level="normal"
279- </INLINE >
280- </FILE >
281- ```
289+ - ` /usr/local/emhttp/plugins/yourplugin/default.cfg ` for shipped defaults
290+ - ` /boot/config/plugins/yourplugin/yourplugin.cfg ` for user overrides
282291
283- {: .note }
284- > This creates the file only if it doesn't exist. Existing files are preserved on plugin updates.
292+ ` parse_plugin_cfg("yourplugin") ` handles merging, so you do not need to rewrite defaults at runtime.
293+
294+ ### Shipping ` default.cfg ` in Your Package
285295
286- ### Creating Defaults via Script
296+ Install ` default.cfg ` as part of your plugin package so it is available in ` /usr/local/emhttp/plugins/yourplugin/ ` .
287297
288- ``` xml
289- <FILE Run =" /bin/bash" >
290- <INLINE >
291- CFG="/boot/config/plugins/yourplugin/yourplugin.cfg"
292- if [ ! -f "$CFG" ]; then
293- mkdir -p /boot/config/plugins/yourplugin
294- cat > "$CFG" << 'EOF'
298+ ``` ini
299+ # /usr/local/emhttp/plugins/yourplugin/default.cfg
295300enabled =" no"
296301interval =" 60"
297302path =" /mnt/user/appdata"
298- EOF
299- fi
300- </INLINE >
301- </FILE >
303+ notify_level =" normal"
302304```
303305
304- ### Runtime Default Creation (PHP)
306+ ### Writing Only User Changes
307+
308+ When saving settings, write only values the user changed into ` yourplugin.cfg ` .
305309
306310``` php
307311<?
308- $cfg_file = "/boot/config/plugins/ yourplugin/yourplugin.cfg" ;
312+ $cfg = parse_plugin_cfg(" yourplugin") ;
309313
310- // Create default config if it doesn't exist
311- if (!file_exists($cfg_file)) {
312- $defaults = <<<EOT
313- enabled =" no"
314- interval =" 60"
315- path =" /mnt/user/appdata"
316- notify_level =" normal"
317- EOT;
318-
319- $dir = dirname($cfg_file);
320- if (!is_dir($dir)) {
321- mkdir($dir, 0755, true);
322- }
323- file_put_contents($cfg_file, $defaults);
324- }
314+ // Update only user-editable values
315+ $cfg['enabled'] = $_POST['enabled'];
316+ $cfg['interval'] = strval(max(1, min(3600, intval($_POST['interval']))));
325317
326- $cfg = parse_plugin_cfg (" yourplugin" );
318+ save_plugin_cfg ("yourplugin", $cfg );
327319?>
328320```
329321
@@ -446,14 +438,14 @@ $email_notify = $cfg['notifications']['email'];
446438
447439## Best Practices
448440
449- 1 . ** Always provide defaults** - Never assume a setting exists
441+ 1 . ** Use ` default.cfg ` for defaults** - Let ` parse_plugin_cfg() ` merge defaults and user overrides
4504422 . ** Validate before writing** - Sanitize all user input
4514433 . ** Use atomic writes** - Write to temp file, then rename
4524444 . ** Escape values properly** - Handle quotes and special characters
4534455 . ** Version your config format** - Track schema changes for migrations
4544466 . ** Backup before migration** - Preserve user settings when upgrading
4554477 . ** Use consistent naming** - ` plugin.cfg ` matches plugin name
456- 8 . ** Document settings** - Comment your default configuration
448+ 8 . ** Document settings** - Keep comments and rationale in ` default.cfg `
457449
458450## Related Topics
459451
0 commit comments