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.
This commit is contained in:
Yury Pikhtarev 2025-06-18 01:43:12 +04:00
commit 7fddf2f0fc
No known key found for this signature in database
17 changed files with 237 additions and 33 deletions

View file

@ -5,6 +5,7 @@ This guide helps you upgrade your TorrentPier installation to the latest version
## 📖 Table of Contents ## 📖 Table of Contents
- [Configuration System Migration](#configuration-system-migration) - [Configuration System Migration](#configuration-system-migration)
- [Censor System Migration](#censor-system-migration)
- [Breaking Changes](#breaking-changes) - [Breaking Changes](#breaking-changes)
- [Best Practices](#best-practices) - [Best Practices](#best-practices)
@ -85,6 +86,80 @@ 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
## ⚠️ Breaking Changes ## ⚠️ Breaking Changes
### Deprecated Functions ### Deprecated Functions
@ -92,6 +167,10 @@ if (isset(config()->bt_announce_url)) {
- `set_config()` → Use `config()->set()` - `set_config()` → Use `config()->set()`
- Direct `$bb_cfg` access → Use `config()` methods - Direct `$bb_cfg` access → Use `config()` methods
### Deprecated Patterns
- `new TorrentPier\Censor()` → Use `censor()` global function
- Direct `$wordCensor` access → Use `censor()` methods
### File Structure Changes ### File Structure Changes
- New `/src/` directory for modern PHP classes - New `/src/` directory for modern PHP classes
- Reorganized template structure - Reorganized template structure
@ -123,6 +202,31 @@ 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
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 ### Error Handling
```php ```php
// ✅ Graceful error handling // ✅ Graceful error handling

View file

@ -81,6 +81,7 @@ if ($mode != '') {
} }
$datastore->update('censor'); $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>'); $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); bb_die($message);
@ -95,6 +96,7 @@ if ($mode != '') {
} }
$datastore->update('censor'); $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>')); 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 { } else {

View file

@ -86,7 +86,8 @@ if (is_file(BB_PATH . '/library/config.local.php')) {
require_once 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); $config = \TorrentPier\Config::init($bb_cfg);
/** /**
@ -99,6 +100,16 @@ function config(): \TorrentPier\Config
return \TorrentPier\Config::getInstance(); return \TorrentPier\Config::getInstance();
} }
/**
* Get the Censor instance
*
* @return \TorrentPier\Censor
*/
function censor(): \TorrentPier\Censor
{
return \TorrentPier\Censor::getInstance();
}
/** /**
* Initialize debug * Initialize debug
*/ */

View file

@ -339,7 +339,7 @@ if (config()->get('show_latest_news')) {
$template->assign_block_vars('news', [ $template->assign_block_vars('news', [
'NEWS_TOPIC_ID' => $news['topic_id'], '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_TIME' => bb_date($news['topic_time'], 'd-M', false),
'NEWS_IS_NEW' => is_unread($news['topic_time'], $news['topic_id'], $news['forum_id']), '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', [ $template->assign_block_vars('net', [
'NEWS_TOPIC_ID' => $net['topic_id'], '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_TIME' => bb_date($net['topic_time'], 'd-M', false),
'NEWS_IS_NEW' => is_unread($net['topic_time'], $net['topic_id'], $net['forum_id']), 'NEWS_IS_NEW' => is_unread($net['topic_time'], $net['topic_id'], $net['forum_id']),
]); ]);

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $lang, $userdata, $wordCensor; global $lang, $userdata;
if (!isset($this->request['type'])) { if (!isset($this->request['type'])) {
$this->ajax_die('empty type'); $this->ajax_die('empty type');
@ -80,7 +80,7 @@ switch ($this->request['type']) {
// hide sid // hide sid
$message = preg_replace('#(?<=[\?&;]sid=)[a-zA-Z0-9]#', 'sid', $message); $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']) { if ($post['post_id'] == $post['topic_first_post_id']) {
$message = "[quote]" . $post['topic_title'] . "[/quote]\r"; $message = "[quote]" . $post['topic_title'] . "[/quote]\r";

View file

@ -401,12 +401,12 @@ function add_search_words($post_id, $post_message, $topic_title = '', $only_retu
function bbcode2html($text) function bbcode2html($text)
{ {
global $bbcode, $wordCensor; global $bbcode;
if (!isset($bbcode)) { if (!isset($bbcode)) {
$bbcode = new TorrentPier\Legacy\BBCode(); $bbcode = new TorrentPier\Legacy\BBCode();
} }
$text = $wordCensor->censorString($text); $text = censor()->censorString($text);
return $bbcode->bbcode2html($text); return $bbcode->bbcode2html($text);
} }

View file

@ -379,8 +379,10 @@ require_once INC_DIR . '/functions.php';
config()->merge(bb_get_config(BB_CONFIG)); config()->merge(bb_get_config(BB_CONFIG));
$bb_cfg = config()->all(); $bb_cfg = config()->all();
// wordCensor deprecated, but kept for compatibility with non-adapted code
$wordCensor = censor();
$log_action = new TorrentPier\Legacy\LogAction(); $log_action = new TorrentPier\Legacy\LogAction();
$wordCensor = new TorrentPier\Censor();
$html = new TorrentPier\Legacy\Common\Html(); $html = new TorrentPier\Legacy\Common\Html();
$user = new TorrentPier\Legacy\Common\User(); $user = new TorrentPier\Legacy\Common\User();

View file

@ -83,7 +83,7 @@ if ($watch_count > 0) {
'ROW_CLASS' => (!($i % 2)) ? 'row1' : 'row2', 'ROW_CLASS' => (!($i % 2)) ? 'row1' : 'row2',
'POST_ID' => $watch[$i]['topic_first_post_id'], 'POST_ID' => $watch[$i]['topic_first_post_id'],
'TOPIC_ID' => $watch[$i]['topic_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'], 'FULL_TOPIC_TITLE' => $watch[$i]['topic_title'],
'U_TOPIC' => TOPIC_URL . $watch[$i]['topic_id'], 'U_TOPIC' => TOPIC_URL . $watch[$i]['topic_id'],
'FORUM_TITLE' => $watch[$i]['forum_name'], 'FORUM_TITLE' => $watch[$i]['forum_name'],

View file

@ -472,8 +472,8 @@ if ($refresh || $error_msg || ($submit && $topic_has_new_posts)) {
// hide sid // hide sid
$message = preg_replace('#(?<=[\?&;]sid=)[a-zA-Z0-9]#', 'sid', $message); $message = preg_replace('#(?<=[\?&;]sid=)[a-zA-Z0-9]#', 'sid', $message);
$subject = $wordCensor->censorString($subject); $subject = censor()->censorString($subject);
$message = $wordCensor->censorString($message); $message = censor()->censorString($message);
if (!preg_match('/^Re:/', $subject) && !empty($subject)) { if (!preg_match('/^Re:/', $subject) && !empty($subject)) {
$subject = 'Re: ' . $subject; $subject = 'Re: ' . $subject;

View file

@ -376,8 +376,8 @@ if ($mode == 'read') {
// //
$post_subject = htmlCHR($privmsg['privmsgs_subject']); $post_subject = htmlCHR($privmsg['privmsgs_subject']);
$private_message = $privmsg['privmsgs_text']; $private_message = $privmsg['privmsgs_text'];
$post_subject = $wordCensor->censorString($post_subject); $post_subject = censor()->censorString($post_subject);
$private_message = $wordCensor->censorString($private_message); $private_message = censor()->censorString($private_message);
$private_message = bbcode2html($private_message); $private_message = bbcode2html($private_message);
// //
@ -1044,8 +1044,8 @@ if ($mode == 'read') {
if ($preview && !$error) { if ($preview && !$error) {
$preview_message = bbcode2html($privmsg_message); $preview_message = bbcode2html($privmsg_message);
$preview_subject = $wordCensor->censorString($privmsg_subject); $preview_subject = censor()->censorString($privmsg_subject);
$preview_message = $wordCensor->censorString($preview_message); $preview_message = censor()->censorString($preview_message);
$s_hidden_fields = '<input type="hidden" name="folder" value="' . $folder . '" />'; $s_hidden_fields = '<input type="hidden" name="folder" value="' . $folder . '" />';
$s_hidden_fields .= '<input type="hidden" name="mode" value="' . $mode . '" />'; $s_hidden_fields .= '<input type="hidden" name="mode" value="' . $mode . '" />';
@ -1381,7 +1381,7 @@ if ($mode == 'read') {
$msg_userid = $row['user_id']; $msg_userid = $row['user_id'];
$msg_user = profile_url($row); $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&amp;mode=read&amp;" . POST_POST_URL . "=$privmsg_id"; $u_subject = PM_URL . "?folder=$folder&amp;mode=read&amp;" . POST_POST_URL . "=$privmsg_id";

View file

@ -571,7 +571,7 @@ if ($post_mode) {
'FORUM_ID' => $forum_id, 'FORUM_ID' => $forum_id,
'FORUM_NAME' => $forum_name_html[$forum_id], 'FORUM_NAME' => $forum_name_html[$forum_id],
'TOPIC_ID' => $topic_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), 'TOPIC_ICON' => get_topic_icon($first_post, $is_unread_t),
)); ));
@ -586,7 +586,7 @@ if ($post_mode) {
} }
$message = get_parsed_post($post); $message = get_parsed_post($post);
$message = $wordCensor->censorString($message); $message = censor()->censorString($message);
$template->assign_block_vars('t.p', array( $template->assign_block_vars('t.p', array(
'ROW_NUM' => $row_num, 'ROW_NUM' => $row_num,
@ -787,7 +787,7 @@ else {
'FORUM_NAME' => $forum_name_html[$forum_id], 'FORUM_NAME' => $forum_name_html[$forum_id],
'TOPIC_ID' => $topic_id, 'TOPIC_ID' => $topic_id,
'HREF_TOPIC_ID' => $moved ? $topic['topic_moved_id'] : $topic['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, 'IS_UNREAD' => $is_unread,
'TOPIC_ICON' => get_topic_icon($topic, $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')), 'PAGINATION' => $moved ? '' : build_topic_pagination(TOPIC_URL . $topic_id, $topic['topic_replies'], config()->get('posts_per_page')),

View file

@ -68,7 +68,9 @@ class Ajax
*/ */
public function exec() 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 // Exit if we already have errors
if (!empty($this->response['error_code'])) { if (!empty($this->response['error_code'])) {

View file

@ -10,11 +10,15 @@
namespace TorrentPier; namespace TorrentPier;
/** /**
* Class Censor * Word Censoring System
* @package TorrentPier *
* Singleton class that provides word censoring functionality
* with automatic loading of censored words from the datastore.
*/ */
class Censor class Censor
{ {
private static ?Censor $instance = null;
/** /**
* Word replacements * Word replacements
* *
@ -32,7 +36,34 @@ class Censor
/** /**
* Initialize word 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; global $datastore;
@ -59,4 +90,56 @@ class Censor
{ {
return preg_replace($this->words, $this->replacements, $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.");
}
} }

View file

@ -179,7 +179,7 @@ class Atom
*/ */
private static function create_atom($file_path, $mode, $id, $title, $topics) private static function create_atom($file_path, $mode, $id, $title, $topics)
{ {
global $lang, $wordCensor; global $lang;
$date = null; $date = null;
$time = null; $time = null;
$dir = \dirname($file_path); $dir = \dirname($file_path);
@ -213,7 +213,7 @@ class Atom
if (isset($topic['tor_status'])) { if (isset($topic['tor_status'])) {
$tor_status = " ({$lang['TOR_STATUS_NAME'][$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']; $author_name = $topic['first_username'] ?: $lang['GUEST'];
$last_time = $topic['topic_last_post_time']; $last_time = $topic['topic_last_post_time'];
if ($topic['topic_last_post_edit_time']) { if ($topic['topic_last_post_edit_time']) {

View file

@ -341,7 +341,7 @@ class Post
*/ */
public static function user_notification($mode, &$post_data, &$topic_title, &$forum_id, &$topic_id, &$notify_user) 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')) { if (!config()->get('topic_notify_enabled')) {
return; return;
@ -363,7 +363,7 @@ class Post
"); ");
if ($watch_list) { 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'); $u_topic = make_url(TOPIC_URL . $topic_id . '&view=newest#newest');
$unwatch_topic = make_url(TOPIC_URL . "$topic_id&unwatch=topic"); $unwatch_topic = make_url(TOPIC_URL . "$topic_id&unwatch=topic");

View file

@ -445,7 +445,7 @@ foreach ($topic_rowset as $topic) {
'FORUM_ID' => $forum_id, 'FORUM_ID' => $forum_id,
'TOPIC_ID' => $topic_id, 'TOPIC_ID' => $topic_id,
'HREF_TOPIC_ID' => $moved ? $topic['topic_moved_id'] : $topic['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, 'TOPICS_SEPARATOR' => $separator,
'IS_UNREAD' => $is_unread, 'IS_UNREAD' => $is_unread,
'TOPIC_ICON' => get_topic_icon($topic, $is_unread), 'TOPIC_ICON' => get_topic_icon($topic, $is_unread),

View file

@ -364,7 +364,7 @@ if (!$ranks = $datastore->get('ranks')) {
} }
// Censor topic title // Censor topic title
$topic_title = $wordCensor->censorString($topic_title); $topic_title = censor()->censorString($topic_title);
// Post, reply and other URL generation for templating vars // Post, reply and other URL generation for templating vars
$new_topic_url = POSTING_URL . "?mode=newtopic&amp;" . POST_FORUM_URL . "=$forum_id"; $new_topic_url = POSTING_URL . "?mode=newtopic&amp;" . POST_FORUM_URL . "=$forum_id";
@ -624,8 +624,8 @@ for ($i = 0; $i < $total_posts; $i++) {
$user_sig = str_replace( $user_sig = str_replace(
'\"', '"', '\"', '"',
substr( substr(
preg_replace_callback('#(\>(((?>([^><]+|(?R)))*)\<))#s', function ($matches) use ($wordCensor) { preg_replace_callback('#(\>(((?>([^><]+|(?R)))*)\<))#s', function ($matches) {
return $wordCensor->censorString(reset($matches)); return censor()->censorString(reset($matches));
}, '>' . $user_sig . '<'), 1, -1 }, '>' . $user_sig . '<'), 1, -1
) )
); );
@ -634,8 +634,8 @@ for ($i = 0; $i < $total_posts; $i++) {
$message = str_replace( $message = str_replace(
'\"', '"', '\"', '"',
substr( substr(
preg_replace_callback('#(\>(((?>([^><]+|(?R)))*)\<))#s', function ($matches) use ($wordCensor) { preg_replace_callback('#(\>(((?>([^><]+|(?R)))*)\<))#s', function ($matches) {
return $wordCensor->censorString(reset($matches)); return censor()->censorString(reset($matches));
}, '>' . $message . '<'), 1, -1 }, '>' . $message . '<'), 1, -1
) )
); );