Журнал изменений
Addresses multiple correctness and safety issues in the compression and error-logging pipeline discovered during systematic code review.
Core changes:
- addCompress() (core/system.php):
Added bool \$bak = false parameter for .bak fallback on no-compression
- When \$algo === 'none' and \$bak=true: rename source to name.bak
- Replaces broken array_intersect_key logic in callers
Replaced temp-file ZIP string path with addFromString()
- Eliminates temp file creation, write, and cleanup risk
Added unlink() result check in ZIP file and gz/bz2 delete branches
- Logs _ERR_DELETE on failure instead of silently ignoring
- addErrorFile() (core/system.php):
Added static \$running recursion guard
- Prevents addCompress->addErrorFile->addCompress infinite loop
- Falls back to error_log() on recursive call
- Replaced hardcoded 10485760 with \$conf['security']['log_size']
Replaced broken checkCompress()/array_intersect_key rotation block
- New: addCompress(..., 'auto', true, true) with .bak fallback
- addFile() (core/system.php):
Fixed bool-to-int coercion: return addCompress() ? 0 : 3
- Previously false coerced to 0, masking compression errors
- addBackupDb() (core/system.php):
Return value of addCompress() now checked
- Returns false on compression failure instead of silently succeeding
Benefits:
- Eliminates infinite recursion risk in error logging
- Consistent error codes in addFile() (0=ok, 1=read, 2=write, 3=compress)
- No temp file leaks in ZIP string compression path
- .bak fallback guaranteed when no compression extension available
Technical notes:
- \$bak parameter default false: backward compatible for all existing callers
- Recursion guard uses static variable: resets correctly after each call
Replaces the unconditional chmod() calls with an ownership-aware guard using posix_geteuid(). This prevents the function from silently failing or changing permissions on files owned by another process, and eliminates string-based octal literals in favor of proper octdec() conversion for reliable permission mode handling.
Core changes:
- Permission checker (core/system.php):
Replaced string octal '0'.\$chm with octdec((string)\$chm)
- Ensures valid integer mode is passed to chmod()
- Added posix_geteuid() call with graceful fallback (-1) when unavailable
- Added file_put_contents() return value check before proceeding
Added fileowner() comparison against current process UID
- chmod() on temp probe file only when owned by current process
- Falls back to is_writable() when posix functions unavailable
Applied same ownership check before chmod() on target directory
- \$cdir guards chmod(\$dir, \$mode) analogously to probe file guard
Moved unlink() inside the file_put_contents() success branch
- Prevents unlink() attempt when file creation failed
Benefits:
- Eliminates silent chmod() failures on foreign-owned files
- Correct permission integer conversion via octdec()
- Robust probe-file lifecycle (create, test, cleanup on success only)
Technical notes:
- posix_geteuid() may not be available on Windows; -1 signals fallback
- Behavior unchanged when process owns all files and chmod is supported
Prevents undefined index notices when \$confst or \$confr are not initialized or incomplete. The shop module similarly guards against a missing 'defis' key in \$confso by falling back to \$conf['defis'] and then a safe default. Both fixes align with the project's pattern of defensive config access.
Core changes:
- Admin info panel (core/admin.php):
Added \$confst to global variable declaration in admininfo()
- Was missing, causing potential undefined variable notice
Guarded \$confst['stat'] with is_array() + isset() check
- Falls back to 0 if key is absent
Guarded \$confr['refer'] with is_array() + isset() check
- Falls back to 0 if key is absent
- Shop module (modules/shop/index.php):
Replaced direct \$confso['defis'] with null-coalescing expression
- Falls back to \$conf['defis'] then '-' as safe default
- Applied consistently in shop() and view() functions
Benefits:
- Eliminates undefined index PHP notices in admin dashboard
- Safe fallback for missing shop separator config
- Consistent defensive config access pattern
Technical notes:
- No functional behavior change when config keys are present
- Backward compatible with existing config structures
Replaces silent error-suppressed @file_get_contents('/proc/...') calls with an explicit is_proc_readable() check that respects open_basedir restrictions. This eliminates suppressed errors and avoids undefined behavior when PHP runs with restricted filesystem access.
Core changes:
- Monitor module (admin/modules/monitor.php):
Added is_proc_readable(string \$path): bool helper function
- Validates path starts with /proc/
- Checks against open_basedir entries if set
- Returns is_readable() result only when access is permitted
Replaced @file_get_contents('/proc/meminfo') with guarded call
- Suppressor removed; function handles restriction transparently
- Replaced @file_get_contents('/proc/net/dev') with guarded call
- Replaced @file_get_contents('/proc/uptime') with guarded call
Added missing ':' presence check before explode() in meminfo parser
- Prevents "Undefined offset" warnings on empty/malformed lines
Benefits:
- Eliminates error suppression antipattern for /proc reads
- Correct behavior under open_basedir=restricted environments
- Prevents PHP warnings from malformed /proc/meminfo lines
Technical notes:
- open_basedir path separator is PATH_SEPARATOR (OS-aware)
- Fallback: if open_basedir is empty, only is_readable() is checked
Prevents false positives when scandir() returns non-directory entries (e.g. files or symlinks) inside the modules/ directory. Without the is_dir() check, is_file() on a path like modules/file.txt/index.php could cause unexpected behavior or warnings.
Core changes:
- Module scanner (admin/modules/lang.php):
Added is_dir() check before is_file() in the modules loop
- Ensures only real subdirectories are treated as modules
- Eliminates potential warnings from invalid path constructions
Align documentation, static analysis config, and tests with the renamed config files and the \$afile variable that replaced \$aroute.
Core changes:
- CONTRIBUTING.md / UPGRADING.md:
- Replace all \$aroute references with \$afile (current variable name)
- .gitignore:
- Remove /config/lang.php exclusion (lang config is now tracked)
- phpstan.neon:
- Remove outdated ignore rule for config/config_uploads.php path
- templates/index.html:
- Update meta-refresh URL: http://www.slaed.net -> https://slaed.net
- tests/ConfigValidationTest.php:
- Rename camelCase properties/methods to snake_case (\$basePath -> \$base_path)
Update scanConfigFiles -> scanconfig_files; testRequiredConfigFilesExist -> testRequiredconfig_filesExist (PSR-consistent project style)
- config/security.php / config/users.php:
- Cosmetic alignment of => operators for readability
Benefits:
- Docs reflect the actual current variable name (\$afile)
- PHPStan no longer reports false positives for removed paths
- Tests use consistent naming convention with the rest of the project
Technical notes:
- No functional changes; docs, config cosmetics, and test naming only
Add missing global config aliases to security.php bootstrap and overhaul the PHP error log rotation to use addCompress() with the new bak fallback. Update doSitemap() to read config via return value and fix all remaining config_rewrite.php path references.
Core changes:
- core/security.php:
Add \$confup (uploads), \$conftp (filetype), \$confla (lang) aliases so modules can rely on these without separate includes
- Update comment: 'set once here; include becomes a no-op' -> clearer
- error_reporting_log(): switch from global \$confs to \$conf['security']
- Log path: config/logs/error.txt -> LOGS_DIR/error_php.log
- Use addCompress(... true, true) for log rotation with .bak fallback
- Add clearstatcache() before filesize(); re-open handle after rotation
- core/system.php:
addCompress(): add bool \$bak = false parameter; when compression unavailable and \$bak is true, rename source to .bak instead of error
doSitemap(): capture return value of include('config/sitemap.php') and extract \$confma from it instead of relying on global pollution
- Fix empty mod check: use ($modules_raw === '') guard
- Replace 3x include('config/config_rewrite.php') with 'config/rewrite.php'
- addErrorFile(): use addCompress(... true, true) — drop inline fallback
Benefits:
- Eliminates global variable leakage from include() in doSitemap
- Log rotation is now atomic and compression-aware
- Modules for uploads/filetype/lang no longer need separate includes
Technical notes:
- addCompress() signature: (dir, src, name, mode, del, bak) — backward compat
- .bak fallback only triggers when algo === 'none' (no compressor available)
The legacy end_chmod() helper performed chmod tests by writing a test file to config/chmod.php, which relied on a dedicated config entry and Linux-specific uname/chmod logic. It has been replaced by the modern checkPerms() function that handles permission checks uniformly.
Core changes:
- core/admin.php:
- Delete end_chmod() function (~20 lines) marked # DELETE OLD
Update fav_aliste(): replace include('config/config_media.php') with $confm = $conf['media'] ?? [] (uses global $conf)
- admin/modules/blocks.php:
Replace 3 end_chmod() calls with checkPerms() equivalents: fileadd(): end_chmod('blocks/', 777) -> checkPerms('blocks/', 1) filecode(): same for directory and per-file permission check
Benefits:
- Removes OS-specific chmod probe that relied on config/chmod.php
- checkPerms() provides consistent, testable permission reporting
- Reduces dead code surface in core/admin.php
Technical notes:
- config/chmod.php remains as an empty return [] placeholder
- No user-facing behaviour change; only internal permission checking
Replace all per-module include('config/config_*.php') and include('config/*.php') calls with global config aliases that are already populated by the unified bootstrap in core/security.php. Also update save_conf() calls to setConfigFile() with the new paths.
Core changes:
- All modules/*/admin/index.php (17 files):
- Remove top-level include('config/config_X.php')
- Replace end_chmod/save_conf with checkPerms/setConfigFile
- Update config file path arguments (config_X.php -> X.php)
- All modules/*/index.php (17 files) + blocks/block-auto_links.php:
- Remove include('config/config_X.php') — alias already set globally
- core/user.php:
- Remove include('config/config_shop.php') in navi()
- Remove include('config/config_forum.php') in editpost()
- Use $conf['shop'] and $conf['forum'] via global $conf
- Fix uninitialised $fstatus variable; fix $_GET isset check
- index.php:
Update require_once paths: config_shop.php -> shop.php, config_uploads.php -> uploads.php
- admin/modules/privat.php:
- Update checkPerms() and setConfigFile() path: privat.php
Benefits:
- Eliminates ~40 redundant file-system reads per request
- Single source of truth: config loaded once in bootstrap
- Consistent use of setConfigFile() for config persistence
Technical notes:
- All config aliases ($confn, $conffo, etc.) remain set in core/security.php
- No functional behaviour change; pure include removal
Drop the redundant config_ prefix from all module config files, aligning naming with the unified config bootstrap introduced in core/system.php. The new names match the module names directly.
Core changes:
- Deleted (23 files) (config/config_*.php):
config_auto_links, config_comments, config_contact, config_content, config_faq, config_files, config_forum, config_global, config_help, config_jokes, config_links, config_media, config_money, config_news, config_order, config_pages, config_privat, config_rewrite, config_shop, config_templ, config_users, config_voting, config_whois
- Added (20 files) (config/*.php):
auto_links, contact, content, faq, files, forum, help, jokes, lang, links, media, money, news, order, pages, privat, rewrite, shop, voting, whois
Benefits:
- Simpler, predictable naming: config/{module}.php
- Eliminates the inconsistent config_ prefix across all modules
- lang.php added as a new first-class config file
Technical notes:
- Git detected renames automatically (rename detection enabled)
config_global, config_templ, config_users, config_comments removed (merged into global config or no longer needed as separate files)