mirror of
https://github.com/torrentpier/torrentpier
synced 2025-07-05 12:36:12 -07:00
refactor(censor): migrate Censor class to singleton pattern (#1954)
* refactor(censor): migrate Censor class to singleton pattern - Convert TorrentPier\Censor to singleton pattern following Config class design - Add global censor() helper function for consistent API access - Replace all global $wordCensor declarations and usage across 12 files - Implement automatic reload functionality in admin panel - Add enhanced methods: isEnabled(), addWord(), getWordsCount(), reload() Files updated: - src/Legacy/Atom.php, src/Legacy/Post.php - viewforum.php, posting.php, search.php, index.php, viewtopic.php, privmsg.php - library/ajax/posts.php, library/includes/bbcode.php, library/includes/ucp/topic_watch.php - admin/admin_words.php, library/includes/init_bb.php - common.php (added global helper) - UPGRADE_GUIDE.md (documentation) Benefits: - Single instance shared across application for better performance - Memory efficient word loading only when censoring enabled - Consistent API pattern matching config() singleton - Automatic word reloading when admin updates censored words - Enhanced developer experience with new utility methods BREAKING CHANGE: None - full backward compatibility maintained. The global $wordCensor variable continues to work as before. New censor() function is the recommended approach going forward. * refactor(censor): add enable check to censorString method * refactor(dev): convert Dev class to singleton pattern (#1955) * refactor(dev): convert Dev class to singleton pattern - Convert TorrentPier\Dev class from direct instantiation to singleton pattern - Add getInstance() method and private constructor for singleton implementation - Introduce new instance methods with improved naming: * getSqlDebugLog() (replaces getSqlLog()) * checkSqlDebugAllowed() (replaces sqlDebugAllowed()) * formatShortQuery() (replaces shortQuery()) - Add dev() global helper function for consistent access pattern - Maintain full backward compatibility with existing static method calls - Update all internal usage across 18 files to use new singleton pattern: * src/Ajax.php, src/Legacy/SqlDb.php * All Cache classes (APCu, File, Memcached, Redis, Sqlite, Common) * All Datastore classes (APCu, File, Memcached, Redis, Sqlite, Common) * library/includes/page_footer_dev.php - Implement lazy initialization consistent with Config and Censor singletons - Add comprehensive migration guide in UPGRADE_GUIDE.md This refactoring improves resource management, provides consistent API patterns across all singleton classes, and maintains zero breaking changes for existing code. * refactor(dev): Added missing `\TorrentPier\Dev::init()` --------- Co-authored-by: Roman Kelesidis <roman25052006.kelesh@gmail.com>
This commit is contained in:
parent
b1b2618757
commit
74a564d795
32 changed files with 540 additions and 70 deletions
217
UPGRADE_GUIDE.md
217
UPGRADE_GUIDE.md
|
@ -5,6 +5,8 @@ This guide helps you upgrade your TorrentPier installation to the latest version
|
|||
## 📖 Table of Contents
|
||||
|
||||
- [Configuration System Migration](#configuration-system-migration)
|
||||
- [Censor System Migration](#censor-system-migration)
|
||||
- [Development System Migration](#development-system-migration)
|
||||
- [Breaking Changes](#breaking-changes)
|
||||
- [Best Practices](#best-practices)
|
||||
|
||||
|
@ -85,6 +87,171 @@ if (isset(config()->bt_announce_url)) {
|
|||
}
|
||||
```
|
||||
|
||||
## 🛡️ Censor System Migration
|
||||
|
||||
The word censoring system has been refactored to use a singleton pattern, similar to the Configuration system, providing better performance and consistency.
|
||||
|
||||
### Quick Migration Overview
|
||||
|
||||
```php
|
||||
// ❌ Old way (still works, but not recommended)
|
||||
global $wordCensor;
|
||||
$censored = $wordCensor->censorString($text);
|
||||
|
||||
// ✅ New way (recommended)
|
||||
$censored = censor()->censorString($text);
|
||||
```
|
||||
|
||||
### Key Censor Changes
|
||||
|
||||
#### Basic Usage
|
||||
```php
|
||||
// Censor a string
|
||||
$text = "This contains badword content";
|
||||
$censored = censor()->censorString($text);
|
||||
|
||||
// Check if censoring is enabled
|
||||
if (censor()->isEnabled()) {
|
||||
$censored = censor()->censorString($text);
|
||||
} else {
|
||||
$censored = $text;
|
||||
}
|
||||
|
||||
// Get count of loaded censored words
|
||||
$wordCount = censor()->getWordsCount();
|
||||
```
|
||||
|
||||
#### Advanced Usage
|
||||
```php
|
||||
// Add runtime censored words (temporary, not saved to database)
|
||||
censor()->addWord('badword', '***');
|
||||
censor()->addWord('anotherbad*', 'replaced'); // Wildcards supported
|
||||
|
||||
// Reload censored words from database (useful after admin updates)
|
||||
censor()->reload();
|
||||
|
||||
// Check if censoring is enabled
|
||||
$isEnabled = censor()->isEnabled();
|
||||
```
|
||||
|
||||
### Backward Compatibility
|
||||
|
||||
The global `$wordCensor` variable is still available and works exactly as before:
|
||||
|
||||
```php
|
||||
// This still works - backward compatibility maintained
|
||||
global $wordCensor;
|
||||
$censored = $wordCensor->censorString($text);
|
||||
|
||||
// But this is now preferred
|
||||
$censored = censor()->censorString($text);
|
||||
```
|
||||
|
||||
### Performance Benefits
|
||||
|
||||
- **Single Instance**: Only one censor instance loads words from database
|
||||
- **Automatic Reloading**: Words are automatically reloaded when updated in admin panel
|
||||
- **Memory Efficient**: Shared instance across entire application
|
||||
- **Lazy Loading**: Words only loaded when censoring is enabled
|
||||
|
||||
### Admin Panel Updates
|
||||
|
||||
When you update censored words in the admin panel, the system now automatically:
|
||||
1. Updates the datastore cache
|
||||
2. Reloads the singleton instance with fresh words
|
||||
3. Applies changes immediately without requiring page refresh
|
||||
|
||||
## 🛠️ Development System Migration
|
||||
|
||||
The development and debugging system has been refactored to use a singleton pattern, providing better resource management and consistency across the application.
|
||||
|
||||
### Quick Migration Overview
|
||||
|
||||
```php
|
||||
// ❌ Old way (still works, but not recommended)
|
||||
$sqlLog = \TorrentPier\Dev::getSqlLog();
|
||||
$isDebugAllowed = \TorrentPier\Dev::sqlDebugAllowed();
|
||||
$shortQuery = \TorrentPier\Dev::shortQuery($sql);
|
||||
|
||||
// ✅ New way (recommended)
|
||||
$sqlLog = dev()->getSqlDebugLog();
|
||||
$isDebugAllowed = dev()->checkSqlDebugAllowed();
|
||||
$shortQuery = dev()->formatShortQuery($sql);
|
||||
```
|
||||
|
||||
### Key Development System Changes
|
||||
|
||||
#### Basic Usage
|
||||
```php
|
||||
// Get SQL debug log
|
||||
$sqlLog = dev()->getSqlDebugLog();
|
||||
|
||||
// Check if SQL debugging is allowed
|
||||
if (dev()->checkSqlDebugAllowed()) {
|
||||
$debugInfo = dev()->getSqlDebugLog();
|
||||
}
|
||||
|
||||
// Format SQL queries for display
|
||||
$formattedQuery = dev()->formatShortQuery($sql, true); // HTML escaped
|
||||
$plainQuery = dev()->formatShortQuery($sql, false); // Plain text
|
||||
```
|
||||
|
||||
#### New Instance Methods
|
||||
```php
|
||||
// Access Whoops instance directly
|
||||
$whoops = dev()->getWhoops();
|
||||
|
||||
// Check debug mode status
|
||||
if (dev()->isDebugEnabled()) {
|
||||
// Debug mode is active
|
||||
}
|
||||
|
||||
// Check environment
|
||||
if (dev()->isLocalEnvironment()) {
|
||||
// Running in local development
|
||||
}
|
||||
```
|
||||
|
||||
### Backward Compatibility
|
||||
|
||||
All existing static method calls continue to work exactly as before:
|
||||
|
||||
```php
|
||||
// This still works - backward compatibility maintained
|
||||
$sqlLog = \TorrentPier\Dev::getSqlLog();
|
||||
$isDebugAllowed = \TorrentPier\Dev::sqlDebugAllowed();
|
||||
$shortQuery = \TorrentPier\Dev::shortQuery($sql);
|
||||
|
||||
// But this is now preferred
|
||||
$sqlLog = dev()->getSqlDebugLog();
|
||||
$isDebugAllowed = dev()->checkSqlDebugAllowed();
|
||||
$shortQuery = dev()->formatShortQuery($sql);
|
||||
```
|
||||
|
||||
### Performance Benefits
|
||||
|
||||
- **Single Instance**: Only one debugging instance across the entire application
|
||||
- **Resource Efficiency**: Whoops handlers initialized once and reused
|
||||
- **Memory Optimization**: Shared debugging state and configuration
|
||||
- **Lazy Loading**: Debug features only activated when needed
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
```php
|
||||
// Access the singleton directly
|
||||
$devInstance = \TorrentPier\Dev::getInstance();
|
||||
|
||||
// Initialize the system (called automatically in common.php)
|
||||
\TorrentPier\Dev::init();
|
||||
|
||||
// Get detailed environment information
|
||||
$environment = [
|
||||
'debug_enabled' => dev()->isDebugEnabled(),
|
||||
'local_environment' => dev()->isLocalEnvironment(),
|
||||
'sql_debug_allowed' => dev()->sqlDebugAllowed(),
|
||||
];
|
||||
```
|
||||
|
||||
## ⚠️ Breaking Changes
|
||||
|
||||
### Deprecated Functions
|
||||
|
@ -92,6 +259,12 @@ if (isset(config()->bt_announce_url)) {
|
|||
- `set_config()` → Use `config()->set()`
|
||||
- Direct `$bb_cfg` access → Use `config()` methods
|
||||
|
||||
### Deprecated Patterns
|
||||
- `new TorrentPier\Censor()` → Use `censor()` global function
|
||||
- Direct `$wordCensor` access → Use `censor()` methods
|
||||
- `new TorrentPier\Dev()` → Use `dev()` global function
|
||||
- Static `Dev::` methods → Use `dev()` instance methods
|
||||
|
||||
### File Structure Changes
|
||||
- New `/src/` directory for modern PHP classes
|
||||
- Reorganized template structure
|
||||
|
@ -123,6 +296,50 @@ class TrackerService {
|
|||
}
|
||||
```
|
||||
|
||||
### Censor Management
|
||||
```php
|
||||
// ✅ Check if censoring is enabled before processing
|
||||
function processUserInput(string $text): string {
|
||||
if (censor()->isEnabled()) {
|
||||
return censor()->censorString($text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
// ✅ Use the singleton consistently
|
||||
$censoredText = censor()->censorString($input);
|
||||
```
|
||||
|
||||
### Development and Debugging
|
||||
```php
|
||||
// ✅ Use instance methods for debugging
|
||||
if (dev()->checkSqlDebugAllowed()) {
|
||||
$debugLog = dev()->getSqlDebugLog();
|
||||
}
|
||||
|
||||
// ✅ Access debugging utilities consistently
|
||||
function formatSqlForDisplay(string $sql): string {
|
||||
return dev()->formatShortQuery($sql, true);
|
||||
}
|
||||
|
||||
// ✅ Check environment properly
|
||||
if (dev()->isLocalEnvironment()) {
|
||||
// Development-specific code
|
||||
}
|
||||
class ForumPost {
|
||||
public function getDisplayText(): string {
|
||||
return censor()->censorString($this->text);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Add runtime words when needed
|
||||
function setupCustomCensoring(): void {
|
||||
if (isCustomModeEnabled()) {
|
||||
censor()->addWord('custombad*', '[censored]');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
```php
|
||||
// ✅ Graceful error handling
|
||||
|
|
|
@ -81,6 +81,7 @@ if ($mode != '') {
|
|||
}
|
||||
|
||||
$datastore->update('censor');
|
||||
censor()->reload(); // Reload the singleton instance with updated words
|
||||
$message .= '<br /><br />' . sprintf($lang['CLICK_RETURN_WORDADMIN'], '<a href="admin_words.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
|
||||
|
||||
bb_die($message);
|
||||
|
@ -95,6 +96,7 @@ if ($mode != '') {
|
|||
}
|
||||
|
||||
$datastore->update('censor');
|
||||
censor()->reload(); // Reload the singleton instance with updated words
|
||||
|
||||
bb_die($lang['WORD_REMOVED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_WORDADMIN'], '<a href="admin_words.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
|
||||
} else {
|
||||
|
|
25
common.php
25
common.php
|
@ -86,7 +86,8 @@ if (is_file(BB_PATH . '/library/config.local.php')) {
|
|||
require_once BB_PATH . '/library/config.local.php';
|
||||
}
|
||||
|
||||
// Initialize Config singleton
|
||||
/** @noinspection PhpUndefinedVariableInspection */
|
||||
// Initialize Config singleton, bb_cfg from global file config
|
||||
$config = \TorrentPier\Config::init($bb_cfg);
|
||||
|
||||
/**
|
||||
|
@ -99,6 +100,26 @@ function config(): \TorrentPier\Config
|
|||
return \TorrentPier\Config::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Censor instance
|
||||
*
|
||||
* @return \TorrentPier\Censor
|
||||
*/
|
||||
function censor(): \TorrentPier\Censor
|
||||
{
|
||||
return \TorrentPier\Censor::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Dev instance
|
||||
*
|
||||
* @return \TorrentPier\Dev
|
||||
*/
|
||||
function dev(): \TorrentPier\Dev
|
||||
{
|
||||
return \TorrentPier\Dev::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize debug
|
||||
*/
|
||||
|
@ -108,7 +129,7 @@ if (APP_ENV === 'local') {
|
|||
} else {
|
||||
define('DBG_USER', isset($_COOKIE[COOKIE_DBG]));
|
||||
}
|
||||
(new \TorrentPier\Dev());
|
||||
(\TorrentPier\Dev::init());
|
||||
|
||||
/**
|
||||
* Server variables initialize
|
||||
|
|
|
@ -339,7 +339,7 @@ if (config()->get('show_latest_news')) {
|
|||
|
||||
$template->assign_block_vars('news', [
|
||||
'NEWS_TOPIC_ID' => $news['topic_id'],
|
||||
'NEWS_TITLE' => str_short($wordCensor->censorString($news['topic_title']), config()->get('max_news_title')),
|
||||
'NEWS_TITLE' => str_short(censor()->censorString($news['topic_title']), config()->get('max_news_title')),
|
||||
'NEWS_TIME' => bb_date($news['topic_time'], 'd-M', false),
|
||||
'NEWS_IS_NEW' => is_unread($news['topic_time'], $news['topic_id'], $news['forum_id']),
|
||||
]);
|
||||
|
@ -362,7 +362,7 @@ if (config()->get('show_network_news')) {
|
|||
|
||||
$template->assign_block_vars('net', [
|
||||
'NEWS_TOPIC_ID' => $net['topic_id'],
|
||||
'NEWS_TITLE' => str_short($wordCensor->censorString($net['topic_title']), config()->get('max_net_title')),
|
||||
'NEWS_TITLE' => str_short(censor()->censorString($net['topic_title']), config()->get('max_net_title')),
|
||||
'NEWS_TIME' => bb_date($net['topic_time'], 'd-M', false),
|
||||
'NEWS_IS_NEW' => is_unread($net['topic_time'], $net['topic_id'], $net['forum_id']),
|
||||
]);
|
||||
|
|
|
@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
|
|||
die(basename(__FILE__));
|
||||
}
|
||||
|
||||
global $lang, $userdata, $wordCensor;
|
||||
global $lang, $userdata;
|
||||
|
||||
if (!isset($this->request['type'])) {
|
||||
$this->ajax_die('empty type');
|
||||
|
@ -80,7 +80,7 @@ switch ($this->request['type']) {
|
|||
// hide sid
|
||||
$message = preg_replace('#(?<=[\?&;]sid=)[a-zA-Z0-9]#', 'sid', $message);
|
||||
|
||||
$message = $wordCensor->censorString($message);
|
||||
$message = censor()->censorString($message);
|
||||
|
||||
if ($post['post_id'] == $post['topic_first_post_id']) {
|
||||
$message = "[quote]" . $post['topic_title'] . "[/quote]\r";
|
||||
|
|
|
@ -401,12 +401,12 @@ function add_search_words($post_id, $post_message, $topic_title = '', $only_retu
|
|||
|
||||
function bbcode2html($text)
|
||||
{
|
||||
global $bbcode, $wordCensor;
|
||||
global $bbcode;
|
||||
|
||||
if (!isset($bbcode)) {
|
||||
$bbcode = new TorrentPier\Legacy\BBCode();
|
||||
}
|
||||
$text = $wordCensor->censorString($text);
|
||||
$text = censor()->censorString($text);
|
||||
return $bbcode->bbcode2html($text);
|
||||
}
|
||||
|
||||
|
|
|
@ -379,8 +379,10 @@ require_once INC_DIR . '/functions.php';
|
|||
config()->merge(bb_get_config(BB_CONFIG));
|
||||
$bb_cfg = config()->all();
|
||||
|
||||
// wordCensor deprecated, but kept for compatibility with non-adapted code
|
||||
$wordCensor = censor();
|
||||
|
||||
$log_action = new TorrentPier\Legacy\LogAction();
|
||||
$wordCensor = new TorrentPier\Censor();
|
||||
$html = new TorrentPier\Legacy\Common\Html();
|
||||
$user = new TorrentPier\Legacy\Common\User();
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ if (!empty($_COOKIE['explain'])) {
|
|||
}
|
||||
}
|
||||
|
||||
$sql_log = !empty($_COOKIE['sql_log']) ? \TorrentPier\Dev::getSqlLog() : false;
|
||||
$sql_log = !empty($_COOKIE['sql_log']) ? dev()->getSqlDebugLog() : false;
|
||||
|
||||
if ($sql_log) {
|
||||
echo '<div class="sqlLog" id="sqlLog">' . $sql_log . '</div><!-- / sqlLog --><br clear="all" />';
|
||||
|
|
|
@ -83,7 +83,7 @@ if ($watch_count > 0) {
|
|||
'ROW_CLASS' => (!($i % 2)) ? 'row1' : 'row2',
|
||||
'POST_ID' => $watch[$i]['topic_first_post_id'],
|
||||
'TOPIC_ID' => $watch[$i]['topic_id'],
|
||||
'TOPIC_TITLE' => str_short($wordCensor->censorString($watch[$i]['topic_title']), 70),
|
||||
'TOPIC_TITLE' => str_short(censor()->censorString($watch[$i]['topic_title']), 70),
|
||||
'FULL_TOPIC_TITLE' => $watch[$i]['topic_title'],
|
||||
'U_TOPIC' => TOPIC_URL . $watch[$i]['topic_id'],
|
||||
'FORUM_TITLE' => $watch[$i]['forum_name'],
|
||||
|
|
|
@ -472,8 +472,8 @@ if ($refresh || $error_msg || ($submit && $topic_has_new_posts)) {
|
|||
// hide sid
|
||||
$message = preg_replace('#(?<=[\?&;]sid=)[a-zA-Z0-9]#', 'sid', $message);
|
||||
|
||||
$subject = $wordCensor->censorString($subject);
|
||||
$message = $wordCensor->censorString($message);
|
||||
$subject = censor()->censorString($subject);
|
||||
$message = censor()->censorString($message);
|
||||
|
||||
if (!preg_match('/^Re:/', $subject) && !empty($subject)) {
|
||||
$subject = 'Re: ' . $subject;
|
||||
|
|
10
privmsg.php
10
privmsg.php
|
@ -376,8 +376,8 @@ if ($mode == 'read') {
|
|||
//
|
||||
$post_subject = htmlCHR($privmsg['privmsgs_subject']);
|
||||
$private_message = $privmsg['privmsgs_text'];
|
||||
$post_subject = $wordCensor->censorString($post_subject);
|
||||
$private_message = $wordCensor->censorString($private_message);
|
||||
$post_subject = censor()->censorString($post_subject);
|
||||
$private_message = censor()->censorString($private_message);
|
||||
$private_message = bbcode2html($private_message);
|
||||
|
||||
//
|
||||
|
@ -1044,8 +1044,8 @@ if ($mode == 'read') {
|
|||
|
||||
if ($preview && !$error) {
|
||||
$preview_message = bbcode2html($privmsg_message);
|
||||
$preview_subject = $wordCensor->censorString($privmsg_subject);
|
||||
$preview_message = $wordCensor->censorString($preview_message);
|
||||
$preview_subject = censor()->censorString($privmsg_subject);
|
||||
$preview_message = censor()->censorString($preview_message);
|
||||
|
||||
$s_hidden_fields = '<input type="hidden" name="folder" value="' . $folder . '" />';
|
||||
$s_hidden_fields .= '<input type="hidden" name="mode" value="' . $mode . '" />';
|
||||
|
@ -1381,7 +1381,7 @@ if ($mode == 'read') {
|
|||
|
||||
$msg_userid = $row['user_id'];
|
||||
$msg_user = profile_url($row);
|
||||
$msg_subject = $wordCensor->censorString($row['privmsgs_subject']);
|
||||
$msg_subject = censor()->censorString($row['privmsgs_subject']);
|
||||
|
||||
$u_subject = PM_URL . "?folder=$folder&mode=read&" . POST_POST_URL . "=$privmsg_id";
|
||||
|
||||
|
|
|
@ -571,7 +571,7 @@ if ($post_mode) {
|
|||
'FORUM_ID' => $forum_id,
|
||||
'FORUM_NAME' => $forum_name_html[$forum_id],
|
||||
'TOPIC_ID' => $topic_id,
|
||||
'TOPIC_TITLE' => $wordCensor->censorString($first_post['topic_title']),
|
||||
'TOPIC_TITLE' => censor()->censorString($first_post['topic_title']),
|
||||
'TOPIC_ICON' => get_topic_icon($first_post, $is_unread_t),
|
||||
));
|
||||
|
||||
|
@ -586,7 +586,7 @@ if ($post_mode) {
|
|||
}
|
||||
|
||||
$message = get_parsed_post($post);
|
||||
$message = $wordCensor->censorString($message);
|
||||
$message = censor()->censorString($message);
|
||||
|
||||
$template->assign_block_vars('t.p', array(
|
||||
'ROW_NUM' => $row_num,
|
||||
|
@ -787,7 +787,7 @@ else {
|
|||
'FORUM_NAME' => $forum_name_html[$forum_id],
|
||||
'TOPIC_ID' => $topic_id,
|
||||
'HREF_TOPIC_ID' => $moved ? $topic['topic_moved_id'] : $topic['topic_id'],
|
||||
'TOPIC_TITLE' => $wordCensor->censorString($topic['topic_title']),
|
||||
'TOPIC_TITLE' => censor()->censorString($topic['topic_title']),
|
||||
'IS_UNREAD' => $is_unread,
|
||||
'TOPIC_ICON' => get_topic_icon($topic, $is_unread),
|
||||
'PAGINATION' => $moved ? '' : build_topic_pagination(TOPIC_URL . $topic_id, $topic['topic_replies'], config()->get('posts_per_page')),
|
||||
|
|
|
@ -68,7 +68,9 @@ class Ajax
|
|||
*/
|
||||
public function exec()
|
||||
{
|
||||
global $lang;
|
||||
/** @noinspection PhpUnusedLocalVariableInspection */
|
||||
// bb_cfg deprecated, but kept for compatibility with non-adapted ajax files
|
||||
global $bb_cfg, $lang;
|
||||
|
||||
// Exit if we already have errors
|
||||
if (!empty($this->response['error_code'])) {
|
||||
|
@ -194,8 +196,8 @@ class Ajax
|
|||
];
|
||||
}
|
||||
|
||||
if (Dev::sqlDebugAllowed()) {
|
||||
$this->response['sql_log'] = Dev::getSqlLog();
|
||||
if (dev()->checkSqlDebugAllowed()) {
|
||||
$this->response['sql_log'] = dev()->getSqlDebugLog();
|
||||
}
|
||||
|
||||
// sending output will be handled by $this->ob_handler()
|
||||
|
|
|
@ -10,11 +10,15 @@
|
|||
namespace TorrentPier;
|
||||
|
||||
/**
|
||||
* Class Censor
|
||||
* @package TorrentPier
|
||||
* Word Censoring System
|
||||
*
|
||||
* Singleton class that provides word censoring functionality
|
||||
* with automatic loading of censored words from the datastore.
|
||||
*/
|
||||
class Censor
|
||||
{
|
||||
private static ?Censor $instance = null;
|
||||
|
||||
/**
|
||||
* Word replacements
|
||||
*
|
||||
|
@ -32,11 +36,38 @@ class Censor
|
|||
/**
|
||||
* Initialize word censor
|
||||
*/
|
||||
public function __construct()
|
||||
private function __construct()
|
||||
{
|
||||
$this->loadCensoredWords();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton instance of Censor
|
||||
*/
|
||||
public static function getInstance(): Censor
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the censor system (for compatibility)
|
||||
*/
|
||||
public static function init(): Censor
|
||||
{
|
||||
return self::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load censored words from datastore
|
||||
*/
|
||||
private function loadCensoredWords(): void
|
||||
{
|
||||
global $datastore;
|
||||
|
||||
if (!config()->get('use_word_censor')) {
|
||||
if (!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,6 +88,62 @@ class Censor
|
|||
*/
|
||||
public function censorString(string $word): string
|
||||
{
|
||||
if (!$this->isEnabled()) {
|
||||
return $word;
|
||||
}
|
||||
|
||||
return preg_replace($this->words, $this->replacements, $word);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload censored words from datastore
|
||||
* Useful when words are updated in admin panel
|
||||
*/
|
||||
public function reload(): void
|
||||
{
|
||||
$this->words = [];
|
||||
$this->replacements = [];
|
||||
$this->loadCensoredWords();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if censoring is enabled
|
||||
*/
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return config()->get('use_word_censor', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a censored word (runtime only)
|
||||
*
|
||||
* @param string $word
|
||||
* @param string $replacement
|
||||
*/
|
||||
public function addWord(string $word, string $replacement): void
|
||||
{
|
||||
$this->words[] = '#(?<![\p{Nd}\p{L}_])(' . str_replace('\*', '[\p{Nd}\p{L}_]*?', preg_quote($word, '#')) . ')(?![\p{Nd}\p{L}_])#iu';
|
||||
$this->replacements[] = $replacement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all censored words count
|
||||
*/
|
||||
public function getWordsCount(): int
|
||||
{
|
||||
return count($this->words);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent cloning of the singleton instance
|
||||
*/
|
||||
private function __clone() {}
|
||||
|
||||
/**
|
||||
* Prevent unserialization of the singleton instance
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \Exception("Cannot unserialize a singleton.");
|
||||
}
|
||||
}
|
||||
|
|
173
src/Dev.php
173
src/Dev.php
|
@ -26,11 +26,15 @@ use jacklul\MonologTelegramHandler\TelegramFormatter;
|
|||
use Exception;
|
||||
|
||||
/**
|
||||
* Class Dev
|
||||
* @package TorrentPier
|
||||
* Development and Debugging System
|
||||
*
|
||||
* Singleton class that provides development and debugging functionality
|
||||
* including error handling, SQL logging, and debugging utilities.
|
||||
*/
|
||||
class Dev
|
||||
{
|
||||
private static ?Dev $instance = null;
|
||||
|
||||
/**
|
||||
* Whoops instance
|
||||
*
|
||||
|
@ -39,9 +43,9 @@ class Dev
|
|||
private Run $whoops;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Initialize debugging system
|
||||
*/
|
||||
public function __construct()
|
||||
private function __construct()
|
||||
{
|
||||
$this->whoops = new Run;
|
||||
|
||||
|
@ -60,6 +64,25 @@ class Dev
|
|||
$this->whoops->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton instance of Dev
|
||||
*/
|
||||
public static function getInstance(): Dev
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the dev system (for compatibility)
|
||||
*/
|
||||
public static function init(): Dev
|
||||
{
|
||||
return self::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* [Whoops] Bugsnag handler
|
||||
*
|
||||
|
@ -164,44 +187,44 @@ class Dev
|
|||
}
|
||||
|
||||
/**
|
||||
* Get SQL debug log
|
||||
* Get SQL debug log (instance method)
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getSqlLog(): string
|
||||
public function getSqlLogInstance(): string
|
||||
{
|
||||
global $DBS, $CACHES, $datastore;
|
||||
|
||||
$log = '';
|
||||
|
||||
foreach ($DBS->srv as $srv_name => $db_obj) {
|
||||
$log .= !empty($db_obj->dbg) ? self::getSqlLogHtml($db_obj, "database: $srv_name [{$db_obj->engine}]") : '';
|
||||
$log .= !empty($db_obj->dbg) ? $this->getSqlLogHtml($db_obj, "database: $srv_name [{$db_obj->engine}]") : '';
|
||||
}
|
||||
|
||||
foreach ($CACHES->obj as $cache_name => $cache_obj) {
|
||||
if (!empty($cache_obj->db->dbg)) {
|
||||
$log .= self::getSqlLogHtml($cache_obj->db, "cache: $cache_name [{$cache_obj->db->engine}]");
|
||||
$log .= $this->getSqlLogHtml($cache_obj->db, "cache: $cache_name [{$cache_obj->db->engine}]");
|
||||
} elseif (!empty($cache_obj->dbg)) {
|
||||
$log .= self::getSqlLogHtml($cache_obj, "cache: $cache_name [{$cache_obj->engine}]");
|
||||
$log .= $this->getSqlLogHtml($cache_obj, "cache: $cache_name [{$cache_obj->engine}]");
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($datastore->db->dbg)) {
|
||||
$log .= self::getSqlLogHtml($datastore->db, "cache: datastore [{$datastore->db->engine}]");
|
||||
$log .= $this->getSqlLogHtml($datastore->db, "cache: datastore [{$datastore->db->engine}]");
|
||||
} elseif (!empty($datastore->dbg)) {
|
||||
$log .= self::getSqlLogHtml($datastore, "cache: datastore [{$datastore->engine}]");
|
||||
$log .= $this->getSqlLogHtml($datastore, "cache: datastore [{$datastore->engine}]");
|
||||
}
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sql debug status
|
||||
* Sql debug status (instance method)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function sqlDebugAllowed(): bool
|
||||
public function sqlDebugAllowedInstance(): bool
|
||||
{
|
||||
return (SQL_DEBUG && DBG_USER && !empty($_COOKIE['sql_log']));
|
||||
}
|
||||
|
@ -215,13 +238,13 @@ class Dev
|
|||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function getSqlLogHtml(object $db_obj, string $log_name): string
|
||||
private function getSqlLogHtml(object $db_obj, string $log_name): string
|
||||
{
|
||||
$log = '';
|
||||
|
||||
foreach ($db_obj->dbg as $i => $dbg) {
|
||||
$id = "sql_{$i}_" . random_int(0, mt_getrandmax());
|
||||
$sql = self::shortQuery($dbg['sql'], true);
|
||||
$sql = $this->shortQueryInstance($dbg['sql'], true);
|
||||
$time = sprintf('%.3f', $dbg['time']);
|
||||
$perc = '[' . round($dbg['time'] * 100 / $db_obj->sql_timetotal) . '%]';
|
||||
$info = !empty($dbg['info']) ? $dbg['info'] . ' [' . $dbg['src'] . ']' : $dbg['src'];
|
||||
|
@ -238,13 +261,13 @@ class Dev
|
|||
}
|
||||
|
||||
/**
|
||||
* Short query
|
||||
* Short query (instance method)
|
||||
*
|
||||
* @param string $sql
|
||||
* @param bool $esc_html
|
||||
* @return string
|
||||
*/
|
||||
public static function shortQuery(string $sql, bool $esc_html = false): string
|
||||
public function shortQueryInstance(string $sql, bool $esc_html = false): string
|
||||
{
|
||||
$max_len = 100;
|
||||
$sql = str_compact($sql);
|
||||
|
@ -257,4 +280,120 @@ class Dev
|
|||
|
||||
return $esc_html ? htmlCHR($sql, true) : $sql;
|
||||
}
|
||||
|
||||
// Static methods for backward compatibility (proxy to instance methods)
|
||||
|
||||
/**
|
||||
* Get SQL debug log (static)
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
* @deprecated Use dev()->getSqlLog() instead
|
||||
*/
|
||||
public static function getSqlLog(): string
|
||||
{
|
||||
return self::getInstance()->getSqlLogInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sql debug status (static)
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated Use dev()->sqlDebugAllowed() instead
|
||||
*/
|
||||
public static function sqlDebugAllowed(): bool
|
||||
{
|
||||
return self::getInstance()->sqlDebugAllowedInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Short query (static)
|
||||
*
|
||||
* @param string $sql
|
||||
* @param bool $esc_html
|
||||
* @return string
|
||||
* @deprecated Use dev()->shortQuery() instead
|
||||
*/
|
||||
public static function shortQuery(string $sql, bool $esc_html = false): string
|
||||
{
|
||||
return self::getInstance()->shortQueryInstance($sql, $esc_html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SQL debug log (for dev() singleton usage)
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getSqlDebugLog(): string
|
||||
{
|
||||
return $this->getSqlLogInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if SQL debugging is allowed (for dev() singleton usage)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkSqlDebugAllowed(): bool
|
||||
{
|
||||
return $this->sqlDebugAllowedInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format SQL query for display (for dev() singleton usage)
|
||||
*
|
||||
* @param string $sql
|
||||
* @param bool $esc_html
|
||||
* @return string
|
||||
*/
|
||||
public function formatShortQuery(string $sql, bool $esc_html = false): string
|
||||
{
|
||||
return $this->shortQueryInstance($sql, $esc_html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Whoops instance
|
||||
*
|
||||
* @return Run
|
||||
*/
|
||||
public function getWhoops(): Run
|
||||
{
|
||||
return $this->whoops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if debug mode is enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDebugEnabled(): bool
|
||||
{
|
||||
return DBG_USER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if application is in local environment
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocalEnvironment(): bool
|
||||
{
|
||||
return APP_ENV === 'local';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent cloning of the singleton instance
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent unserialization of the singleton instance
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \Exception("Cannot unserialize a singleton.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ class Atom
|
|||
*/
|
||||
private static function create_atom($file_path, $mode, $id, $title, $topics)
|
||||
{
|
||||
global $lang, $wordCensor;
|
||||
global $lang;
|
||||
$date = null;
|
||||
$time = null;
|
||||
$dir = \dirname($file_path);
|
||||
|
@ -213,7 +213,7 @@ class Atom
|
|||
if (isset($topic['tor_status'])) {
|
||||
$tor_status = " ({$lang['TOR_STATUS_NAME'][$topic['tor_status']]})";
|
||||
}
|
||||
$topic_title = $wordCensor->censorString($topic['topic_title']);
|
||||
$topic_title = censor()->censorString($topic['topic_title']);
|
||||
$author_name = $topic['first_username'] ?: $lang['GUEST'];
|
||||
$last_time = $topic['topic_last_post_time'];
|
||||
if ($topic['topic_last_post_edit_time']) {
|
||||
|
|
|
@ -61,7 +61,7 @@ class APCu extends Common
|
|||
}
|
||||
$this->apcu = new Apc();
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -82,7 +82,7 @@ class Common
|
|||
switch ($mode) {
|
||||
case 'start':
|
||||
$this->sql_starttime = utime();
|
||||
$dbg['sql'] = Dev::shortQuery($cur_query ?? $this->cur_query);
|
||||
$dbg['sql'] = dev()->formatShortQuery($cur_query ?? $this->cur_query);
|
||||
$dbg['src'] = $this->debug_find_source();
|
||||
$dbg['file'] = $this->debug_find_source('file');
|
||||
$dbg['line'] = $this->debug_find_source('line');
|
||||
|
|
|
@ -61,7 +61,7 @@ class File extends Common
|
|||
$filesystem = new Filesystem($adapter);
|
||||
$this->file = new Flysystem($filesystem);
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -85,7 +85,7 @@ class Memcached extends Common
|
|||
$this->client = new MemcachedClient();
|
||||
$this->cfg = $cfg;
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -85,7 +85,7 @@ class Redis extends Common
|
|||
$this->client = new RedisClient();
|
||||
$this->cfg = $cfg;
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -71,7 +71,7 @@ class Sqlite extends Common
|
|||
$client = new PDO('sqlite:' . $dir . $this->dbExtension);
|
||||
$this->sqlite = new SQLiteCache($client);
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -54,7 +54,7 @@ class APCu extends Common
|
|||
}
|
||||
$this->apcu = new Apc();
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -157,7 +157,7 @@ class Common
|
|||
switch ($mode) {
|
||||
case 'start':
|
||||
$this->sql_starttime = utime();
|
||||
$dbg['sql'] = Dev::shortQuery($cur_query ?? $this->cur_query);
|
||||
$dbg['sql'] = dev()->formatShortQuery($cur_query ?? $this->cur_query);
|
||||
$dbg['src'] = $this->debug_find_source();
|
||||
$dbg['file'] = $this->debug_find_source('file');
|
||||
$dbg['line'] = $this->debug_find_source('line');
|
||||
|
|
|
@ -54,7 +54,7 @@ class File extends Common
|
|||
$filesystem = new Filesystem($adapter);
|
||||
$this->file = new Flysystem($filesystem);
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,7 +78,7 @@ class Memcached extends Common
|
|||
$this->client = new MemcachedClient();
|
||||
$this->cfg = $cfg;
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -78,7 +78,7 @@ class Redis extends Common
|
|||
$this->client = new RedisClient();
|
||||
$this->cfg = $cfg;
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -64,7 +64,7 @@ class Sqlite extends Common
|
|||
$client = new PDO('sqlite:' . $dir . $this->dbExtension);
|
||||
$this->sqlite = new SQLiteCache($client);
|
||||
$this->prefix = $prefix;
|
||||
$this->dbg_enabled = Dev::sqlDebugAllowed();
|
||||
$this->dbg_enabled = dev()->checkSqlDebugAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -341,7 +341,7 @@ class Post
|
|||
*/
|
||||
public static function user_notification($mode, &$post_data, &$topic_title, &$forum_id, &$topic_id, &$notify_user)
|
||||
{
|
||||
global $lang, $userdata, $wordCensor;
|
||||
global $lang, $userdata;
|
||||
|
||||
if (!config()->get('topic_notify_enabled')) {
|
||||
return;
|
||||
|
@ -363,7 +363,7 @@ class Post
|
|||
");
|
||||
|
||||
if ($watch_list) {
|
||||
$topic_title = $wordCensor->censorString($topic_title);
|
||||
$topic_title = censor()->censorString($topic_title);
|
||||
|
||||
$u_topic = make_url(TOPIC_URL . $topic_id . '&view=newest#newest');
|
||||
$unwatch_topic = make_url(TOPIC_URL . "$topic_id&unwatch=topic");
|
||||
|
|
|
@ -60,7 +60,7 @@ class SqlDb
|
|||
global $DBS;
|
||||
|
||||
$this->cfg = array_combine($this->cfg_keys, $cfg_values);
|
||||
$this->dbg_enabled = (Dev::sqlDebugAllowed() || !empty($_COOKIE['explain']));
|
||||
$this->dbg_enabled = (dev()->checkSqlDebugAllowed() || !empty($_COOKIE['explain']));
|
||||
$this->do_explain = ($this->dbg_enabled && !empty($_COOKIE['explain']));
|
||||
$this->slow_time = SQL_SLOW_QUERY_TIME;
|
||||
|
||||
|
@ -839,7 +839,7 @@ class SqlDb
|
|||
$msg[] = sprintf('%-6s', $q_time);
|
||||
$msg[] = sprintf('%05d', getmypid());
|
||||
$msg[] = $this->db_server;
|
||||
$msg[] = Dev::shortQuery($this->cur_query);
|
||||
$msg[] = dev()->formatShortQuery($this->cur_query);
|
||||
$msg = implode(LOG_SEPR, $msg);
|
||||
$msg .= ($info = $this->query_info()) ? ' # ' . $info : '';
|
||||
$msg .= ' # ' . $this->debug_find_source() . ' ';
|
||||
|
@ -948,7 +948,7 @@ class SqlDb
|
|||
</tr>
|
||||
<tr><td colspan="2">' . $this->explain_hold . '</td></tr>
|
||||
</table>
|
||||
<div class="sqlLog"><div id="' . $htid . '" class="sqlLogRow sqlExplain" style="padding: 0;">' . Dev::shortQuery($dbg['sql'], true) . ' </div></div>
|
||||
<div class="sqlLog"><div id="' . $htid . '" class="sqlLogRow sqlExplain" style="padding: 0;">' . dev()->formatShortQuery($dbg['sql'], true) . ' </div></div>
|
||||
<br />';
|
||||
break;
|
||||
|
||||
|
|
|
@ -445,7 +445,7 @@ foreach ($topic_rowset as $topic) {
|
|||
'FORUM_ID' => $forum_id,
|
||||
'TOPIC_ID' => $topic_id,
|
||||
'HREF_TOPIC_ID' => $moved ? $topic['topic_moved_id'] : $topic['topic_id'],
|
||||
'TOPIC_TITLE' => $wordCensor->censorString($topic['topic_title']),
|
||||
'TOPIC_TITLE' => censor()->censorString($topic['topic_title']),
|
||||
'TOPICS_SEPARATOR' => $separator,
|
||||
'IS_UNREAD' => $is_unread,
|
||||
'TOPIC_ICON' => get_topic_icon($topic, $is_unread),
|
||||
|
|
|
@ -364,7 +364,7 @@ if (!$ranks = $datastore->get('ranks')) {
|
|||
}
|
||||
|
||||
// Censor topic title
|
||||
$topic_title = $wordCensor->censorString($topic_title);
|
||||
$topic_title = censor()->censorString($topic_title);
|
||||
|
||||
// Post, reply and other URL generation for templating vars
|
||||
$new_topic_url = POSTING_URL . "?mode=newtopic&" . POST_FORUM_URL . "=$forum_id";
|
||||
|
@ -624,8 +624,8 @@ for ($i = 0; $i < $total_posts; $i++) {
|
|||
$user_sig = str_replace(
|
||||
'\"', '"',
|
||||
substr(
|
||||
preg_replace_callback('#(\>(((?>([^><]+|(?R)))*)\<))#s', function ($matches) use ($wordCensor) {
|
||||
return $wordCensor->censorString(reset($matches));
|
||||
preg_replace_callback('#(\>(((?>([^><]+|(?R)))*)\<))#s', function ($matches) {
|
||||
return censor()->censorString(reset($matches));
|
||||
}, '>' . $user_sig . '<'), 1, -1
|
||||
)
|
||||
);
|
||||
|
@ -634,8 +634,8 @@ for ($i = 0; $i < $total_posts; $i++) {
|
|||
$message = str_replace(
|
||||
'\"', '"',
|
||||
substr(
|
||||
preg_replace_callback('#(\>(((?>([^><]+|(?R)))*)\<))#s', function ($matches) use ($wordCensor) {
|
||||
return $wordCensor->censorString(reset($matches));
|
||||
preg_replace_callback('#(\>(((?>([^><]+|(?R)))*)\<))#s', function ($matches) {
|
||||
return censor()->censorString(reset($matches));
|
||||
}, '>' . $message . '<'), 1, -1
|
||||
)
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue