修复在子对象中修改this.$global.version的问题

目前我们的代码结构:
God(这是一个母对象)专门负责:1、加工传入的业务对象。2、把重要属性让业务对象可访问。3、把一些属性密封起来

业务子对象,约定通用的data()方法和methods子对象。
可以有n个业务子对象。

这样做的目的?让我们的业务对象具备较强的可编程性、可扩展性、规范性。

前面我们完成了子对象中通过this.$global来访问母对象God中的属性,但是这其中还有一个问题,就是$global可以在子对象中被修改:

    methods:{
        show(){
            this.$global.version = '2.0';
            alert(this.$global.version);
        }
    }

比如这样被修改了。

现在我们来解决这个问题

修改母对象Godinit()方法:

    init(){
        // 把God对象的这2个属性的enumerable设置为false
        // Object.defineProperty(this,'init',{enumerable:false});
        // Object.defineProperty(this,'extends',{enumerable:false});

        // 批量设置方法
        Object.defineProperties(this,{
            'init':{enumerable:false}, // 设置属性不可枚举
            'extends':{enumerable:false}, // 设置属性不可枚举
        });

        var keys = Object.keys(this); // 列举出this当前是所有属性

        this.$global = {}; // 创建一个新对象 叫做 $global
        keys.forEach((key)=>{
            this.$global[key] = this[key];
        });

        Object.freeze(this.$global);  // 冻结对象
    }
#!/bin/bash YFW跨平台IM插件自动化部署脚本 部署路径: yfw_szrengjing_com/wp-content/plugins 数据库配置: 名称=yfw_szrengjing_c, 用户=yfw_szrengjing_c, 密码=GAm2jPL4Dm 配置参数 PLUGIN_DIR=“yfw_szrengjing_com/wp-content/plugins/yfw-im” DB_NAME=“yfw_szrengjing_c” DB_USER=“yfw_szrengjing_c” DB_PASS=“GAm2jPL4Dm” WS_PORT=8080 PHP_VERSION_REQUIRED=“7.4” RATCHET_VERSION=“0.4.4” 颜色输出函数 info() { echo -e “\033[34m[INFO] $1\033[0m”; } success() { echo -e “\033[32m[SUCCESS] $1\033[0m”; } error() { echo -e “\033[31m[ERROR] $1\033[0m” && exit 1; } 1. 环境检查 info “开始环境检查…” 检查PHP版本 PHP_VERSION= ( p h p − r " e c h o P H P M A J O R V E R S I O N ... P H P M I N O R V E R S I O N ; " ) i f [ [ (php−r"echoPHP M ​ AJOR V ​ ERSION...PHP M ​ INOR V ​ ERSION;")if[[(echo “$PHP_VERSION < $PHP_VERSION_REQUIRED” | bc -l) -eq 1 ]]; then error “需要PHP $PHP_VERSION_REQUIRED及以上版本,当前版本: $PHP_VERSION” fi 检查Composer if ! command -v composer &> /dev/null; then error “未安装Composer,请先安装Composer” fi 检查目录是否存在 if [ ! -d “yfw_szrengjing_com/wp-content/plugins” ]; then error “目标路径不存在: yfw_szrengjing_com/wp-content/plugins” fi 2. 创建插件目录 info “创建插件目录结构…” mkdir -p $PLUGIN_DIR/{includes,assets/{js,css}} || error “创建目录失败” 3. 生成核心文件 info “生成插件文件…” 3.1 插件入口文件 (yfw-im.php) cat > $PLUGIN_DIR/yfw-im.php << ‘EOF’ <?php /* Plugin Name: YFW 跨平台IM Description: 基于WebSocket的跨平台即时通讯插件 Version: 1.0 Author: Your Name */ if (!defined('ABSPATH')) exit; // 加载核心文件 require_once plugin_dir_path(__FILE__) . 'includes/class-yfw-im.php'; require_once plugin_dir_path(__FILE__) . 'includes/db.php'; require_once plugin_dir_path(__FILE__) . 'config.php'; // 初始化插件 function yfw_im_init() { $yfw_im = new YFW_IM(); $yfw_im->init(); } add_action('plugins_loaded', 'yfw_im_init'); // 激活插件时创建数据库表 register_activation_hook(__FILE__, 'yfw_im_install'); function yfw_im_install() { require_once plugin_dir_path(__FILE__) . 'includes/db.php'; yfw_im_create_tables(); } EOF # 3.2 数据库操作文件 (includes/db.php) cat > $PLUGIN_DIR/includes/db.php << EOF <?php global \$wpdb; \$table_prefix = \$wpdb->prefix . 'yfw_im_'; // 数据库配置(自动填充) define('YFW_IM_DB_NAME', '$DB_NAME'); define('YFW_IM_DB_USER', '$DB_USER'); define('YFW_IM_DB_PASS', '$DB_PASS'); // 创建消息表和会话表 function yfw_im_create_tables() { global \$wpdb, \$table_prefix; \$charset_collate = \$wpdb->get_charset_collate(); // 会话表 \$sql = "CREATE TABLE IF NOT EXISTS {\$table_prefix}conversations ( id INT AUTO_INCREMENT PRIMARY KEY, user1_id BIGINT NOT NULL, user2_id BIGINT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY user_pair (user1_id, user2_id) ) \$charset_collate;"; // 消息表 \$sql .= "CREATE TABLE IF NOT EXISTS {\$table_prefix}messages ( id INT AUTO_INCREMENT PRIMARY KEY, conversation_id INT NOT NULL, sender_id BIGINT NOT NULL, content TEXT NOT NULL, type ENUM('text', 'image', 'file') DEFAULT 'text', status ENUM('sent', 'delivered', 'read') DEFAULT 'sent', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES {\$table_prefix}conversations(id) ON DELETE CASCADE ) \$charset_collate;"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta(\$sql); } // 存储消息 function yfw_im_save_message(\$conversation_id, \$sender_id, \$content, \$type = 'text') { global \$wpdb, \$table_prefix; return \$wpdb->insert( "{\$table_prefix}messages", [ 'conversation_id' => \$conversation_id, 'sender_id' => \$sender_id, 'content' => \$content, 'type' => \$type ] ); } EOF # 3.3 WebSocket服务端 (includes/websocket.php) cat > $PLUGIN_DIR/includes/websocket.php << 'EOF' <?php use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; require_once plugin_dir_path(__FILE__) . '../../vendor/autoload.php'; class YFW_IM_Socket implements MessageComponentInterface { protected $clients; protected $userConnections; // 用户ID与连接的映射 public function __construct() { $this->clients = new \SplObjectStorage; $this->userConnections = []; } // 新连接建立 public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); error_log("新连接: {$conn->resourceId}"); } // 接收消息 public function onMessage(ConnectionInterface $from, $msg) { $data = json_decode($msg, true); if (!$data) return; // 处理用户认证(首次连接时) if ($data['action'] === 'auth') { $user_id = $data['user_id']; $this->userConnections[$user_id] = $from; $from->send(json_encode(['status' => 'success', 'msg' => '认证成功'])); return; } // 处理消息发送 if ($data['action'] === 'send_message') { $this->handleMessage($data); } } // 处理消息转发 private function handleMessage($data) { $sender_id = $data['sender_id']; $receiver_id = $data['receiver_id']; $content = $data['content']; // 1. 查找或创建会话 $conversation_id = $this->getOrCreateConversation($sender_id, $receiver_id); // 2. 保存消息到数据库 yfw_im_save_message($conversation_id, $sender_id, $content); // 3. 转发消息给接收者 if (isset($this->userConnections[$receiver_id])) { $receiverConn = $this->userConnections[$receiver_id]; $receiverConn->send(json_encode([ 'action' => 'new_message', 'sender_id' => $sender_id, 'content' => $content, 'time' => date('Y-m-d H:i:s') ])); } } // 获取或创建会话 private function getOrCreateConversation($user1, $user2) { global $wpdb, $table_prefix; $min = min($user1, $user2); $max = max($user1, $user2); $conv = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM {$table_prefix}conversations WHERE user1_id = %d AND user2_id = %d", $min, $max ) ); if ($conv) return $conv->id; // 创建新会话 $wpdb->insert( "{$table_prefix}conversations", ['user1_id' => $min, 'user2_id' => $max] ); return $wpdb->insert_id; } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); // 移除用户连接映射 $this->userConnections = array_filter($this->userConnections, function($c) use ($conn) { return $c !== $conn; }); error_log("连接关闭: {$conn->resourceId}"); } public function onError(ConnectionInterface $conn, \Exception $e) { error_log("错误: {$e->getMessage()}"); $conn->close(); } } // 启动WebSocket服务(需通过命令行运行) if (php_sapi_name() === 'cli') { $server = IoServer::factory( new HttpServer( new WsServer( new YFW_IM_Socket() ) ), 8080 // 端口号 ); $server->run(); } EOF # 3.4 核心类文件 (includes/class-yfw-im.php) cat > $PLUGIN_DIR/includes/class-yfw-im.php << 'EOF' <?php class YFW_IM { public function init() { // 注册AJAX接口(加载历史消息) add_action('wp_ajax_yfw_im_load_messages', [$this, 'load_messages']); add_action('wp_ajax_nopriv_yfw_im_load_messages', [$this, 'load_messages_nopriv']); } // 加载历史消息(登录用户) public function load_messages() { check_ajax_referer('yfw_im_nonce', 'nonce'); $current_user = wp_get_current_user(); if (!$current_user->ID) { wp_send_json_error('未登录'); } $receiver_id = intval($_POST['receiver_id']); $messages = $this->get_message_history($current_user->ID, $receiver_id); wp_send_json_success(['messages' => $messages]); } // 未登录用户处理 public function load_messages_nopriv() { wp_send_json_error('请先登录'); } // 获取消息历史 private function get_message_history($user1, $user2) { global $wpdb, $table_prefix; $min = min($user1, $user2); $max = max($user1, $user2); $conv = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM {$table_prefix}conversations WHERE user1_id = %d AND user2_id = %d", $min, $max ) ); if (!$conv) return []; return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table_prefix}messages WHERE conversation_id = %d ORDER BY created_at ASC", $conv->id ), ARRAY_A ); } } EOF # 3.5 前端WebSocket客户端 (assets/js/socket.js) cat > $PLUGIN_DIR/assets/js/socket.js << 'EOF' class YFW_IM_Client { constructor() { this.socket = null; this.userId = yfw_im_config.user_id; this.connect(); } // 连接WebSocket服务 connect() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const host = yfw_im_config.ws_host || `${protocol}//${window.location.hostname}:8080`; this.socket = new WebSocket(host); this.socket.onopen = () => { console.log('WebSocket连接成功'); this.auth(); }; this.socket.onmessage = (event) => { const data = JSON.parse(event.data); this.handleMessage(data); }; this.socket.onerror = (error) => { console.error('WebSocket错误:', error); }; this.socket.onclose = () => { console.log('连接关闭,尝试重连...'); setTimeout(() => this.connect(), 3000); }; } // 认证用户 auth() { this.send({ action: 'auth', user_id: this.userId }); } // 发送消息 sendMessage(receiverId, content) { this.send({ action: 'send_message', sender_id: this.userId, receiver_id: receiverId, content: content }); } // 通用发送方法 send(data) { if (this.socket.readyState === WebSocket.OPEN) { this.socket.send(JSON.stringify(data)); } else { console.error('连接未就绪'); } } // 处理接收的消息 handleMessage(data) { if (data.action === 'new_message') { window.dispatchEvent(new CustomEvent('yfw_im_new_message', { detail: data })); } } } EOF # 3.6 前端聊天界面逻辑 (assets/js/frontend.js) cat > $PLUGIN_DIR/assets/js/frontend.js << 'EOF' // 初始化聊天客户端 document.addEventListener('DOMContentLoaded', () => { const imClient = new YFW_IM_Client(); const chatWindow = document.getElementById('yfw-im-chat'); const messageInput = document.getElementById('yfw-im-input'); const sendBtn = document.getElementById('yfw-im-send'); let currentReceiver = null; // 设置当前聊天对象 document.querySelectorAll('.yfw-im-contact').forEach(contact => { contact.addEventListener('click', () => { currentReceiver = contact.dataset.userId; chatWindow.innerHTML = ''; loadMessages(currentReceiver); }); }); // 发送消息 sendBtn.addEventListener('click', () => { const content = messageInput.value.trim(); if (content && currentReceiver) { imClient.sendMessage(currentReceiver, content); addMessageToUI(content, 'outgoing', yfw_im_config.user_id); messageInput.value = ''; } }); // 回车发送 messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') sendBtn.click(); }); // 接收新消息 window.addEventListener('yfw_im_new_message', (e) => { const data = e.detail; addMessageToUI(data.content, 'incoming', data.sender_id); }); // 加载历史消息 function loadMessages(receiverId) { fetch(yfw_im_config.ajax_url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ action: 'yfw_im_load_messages', receiver_id: receiverId, nonce: yfw_im_config.nonce }) }) .then(res => res.json()) .then(data => { if (data.success) { data.data.messages.forEach(msg => { const type = msg.sender_id == yfw_im_config.user_id ? 'outgoing' : 'incoming'; addMessageToUI(msg.content, type, msg.sender_id); }); } }); } // 添加消息到界面 function addMessageToUI(content, type, senderId) { const msgEl = document.createElement('div'); msgEl.className = `yfw-im-message ${type}`; msgEl.innerHTML = `<div class="content">${content}</div>`; chatWindow.appendChild(msgEl); chatWindow.scrollTop = chatWindow.scrollHeight; } }); EOF # 3.7 样式文件 (assets/css/style.css) cat > $PLUGIN_DIR/assets/css/style.css << 'EOF' .yfw-im-chat { width: 400px; height: 500px; border: 1px solid #ccc; border-radius: 8px; overflow: hidden; display: flex; flex-direction: column; } .yfw-im-messages { flex: 1; padding: 10px; overflow-y: auto; background-color: #f9f9f9; } .yfw-im-message { margin: 5px 0; max-width: 70%; padding: 8px 12px; border-radius: 18px; clear: both; } .yfw-im-message.incoming { background-color: #e9e9eb; float: left; } .yfw-im-message.outgoing { background-color: #0078d7; color: white; float: right; } .yfw-im-input-area { display: flex; padding: 10px; border-top: 1px solid #ccc; } #yfw-im-input { flex: 1; padding: 8px 12px; border: 1px solid #ccc; border-radius: 20px; outline: none; } #yfw-im-send { margin-left: 10px; padding: 8px 16px; background-color: #0078d7; color: white; border: none; border-radius: 20px; cursor: pointer; } .yfw-im-contacts { padding: 10px; border-bottom: 1px solid #ccc; } .yfw-im-contact { padding: 8px 12px; border-radius: 4px; cursor: pointer; margin-bottom: 5px; } .yfw-im-contact:hover { background-color: #f0f0f0; } EOF # 3.8 配置文件 (config.php) cat > $PLUGIN_DIR/config.php << 'EOF' <?php // 跨平台配置 define('YFW_IM_WS_HOST', 'ws://' . $_SERVER['HTTP_HOST'] . ':8080'); define('YFW_IM_ALLOWED_ORIGINS', [ 'https://yfw_szrengjing_com', 'http://yfw_szrengjing_com', 'http://localhost:3000' ]); // 前端配置(通过WP本地化脚本传递) function yfw_im_enqueue_scripts() { wp_enqueue_script('yfw-im-socket', plugins_url('assets/js/socket.js', dirname(__FILE__)), [], '1.0', true); wp_enqueue_script('yfw-im-frontend', plugins_url('assets/js/frontend.js', dirname(__FILE__)), ['yfw-im-socket'], '1.0', true); wp_enqueue_style('yfw-im-style', plugins_url('assets/css/style.css', dirname(__FILE__))); // 传递配置到前端 wp_localize_script('yfw-im-frontend', 'yfw_im_config', [ 'user_id' => get_current_user_id(), 'ws_host' => YFW_IM_WS_HOST, 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('yfw_im_nonce') ]); } add_action('wp_enqueue_scripts', 'yfw_im_enqueue_scripts'); EOF # 4. 安装依赖 info "安装WebSocket依赖..." cd $PLUGIN_DIR || error "进入插件目录失败" composer require cboden/ratchet:$RATCHET_VERSION > /dev/null 2>&1 || error "安装Ratchet失败" # 5. 数据库初始化(通过WordPress CLI触发,若没有WP-CLI则提示手动激活) info "初始化数据库..." if command -v wp &> /dev/null; then # 若安装了WP-CLI,直接触发激活钩子 wp plugin activate yfw-im > /dev/null 2>&1 success "数据库表创建成功" else info "未检测到WP-CLI,请在WordPress后台手动激活插件以创建数据库表" fi # 6. 配置WebSocket服务自启动(使用systemd) info "配置WebSocket服务自启动..." SERVICE_FILE="/etc/systemd/system/yfw-im-websocket.service" if [ -w "/etc/systemd/system/" ]; then cat > $SERVICE_FILE << EOF [Unit] Description=YFW IM WebSocket Service After=network.target [Service] User=$(whoami) WorkingDirectory=$PLUGIN_DIR/includes ExecStart=$(which php) websocket.php Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF # 启动服务 systemctl daemon-reload systemctl start yfw-im-websocket systemctl enable yfw-im-websocket > /dev/null 2>&1 success "WebSocket服务已启动并设置开机自启" else info "无权限配置systemd,手动启动命令: php $PLUGIN_DIR/includes/websocket.php" fi # 7. 完成部署 success "部署完成!" echo "======================================" echo "插件路径: $PLUGIN_DIR" echo "WebSocket端口: $WS_PORT" echo "使用说明:" echo "1. 在WordPress后台激活 'YFW 跨平台IM' 插件" echo "2. 在需要显示聊天的页面添加聊天组件(可通过短代码实现)" echo "3. 确保服务器开放$WS_PORT端口的防火墙权限" echo "======================================" [root@yfw ~]# cd /www/wwwroot/yfw_szrengjing_com/wp-content/plugins [root@yfw plugins]# ./deploy_yfw_im.sh [INFO] 开始环境检查... [ERROR] 目标路径不存在: yfw_szrengjing_com/wp-content/plugins [root@yfw plugins]#修复生成完整版源文件
最新发布
11-15
let setProxyArr = function (proxyObjArr) { for (let i = 0; i < proxyObjArr.length; i++) { const handler = `{ get:function(target,property,receiver){ console.log("方法:","get","对象","${proxyObjArr[i]}","属性:", property,"属性类型:",typeof property,"属性值:",target[property],"属性值类型:",typeof target[property]); return Reflect.get(...arguments) }, set:function(target,property,value,receiver){ console.log("方法:","set","对象:","${proxyObjArr[i]}","属性:", property,"属性类型:",typeof property,"属性值:",value,"属性值类型:",typeof target[property]); return Reflect.set(...arguments); } }`; eval(`try{ ${proxyObjArr[i]}; ${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]},${handler}); } catch (e){ ${proxyObjArr[i]} = {}; ${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]},${handler}); }`); } } function watch(object) { const handler = { get: function (target, property, receiver) { if (property !== 'isNaN' && property !== 'encodeURI' && property !== "Uint8Array" && property !== 'undefined' && property !== 'JSON') { console.log("方法:", "get", "对象", target, "属性:", property, "属性类型:", typeof property, "属性值:", target[property], "属性值类型:", typeof target[property]); } return Reflect.get(...arguments) }, set: function (target, property, value, receiver) { console.log("方法:", "set", "对象:", target, "属性:", property, "属性类型:", typeof property, "属性值:", value, "属性值类型:", typeof target[property]); return Reflect.set(...arguments); } } // return object return new Proxy(object, handler) } const safeFunction = function safeFunction(func) { //处理安全函数 Function.prototype.$call = Function.prototype.call; const $toString = Function.toString; const myFunction_toString_symbol = Symbol('('.concat('', ')')); const myToString = function myToString() { return typeof this === 'function' && this[myFunction_toString_symbol] || $toString.$call(this); } const set_native = function set_native(func, key, value) { Object.defineProperty(func, key, { "enumerable": false, "configurable": true, "writable": true, "value": value }); } delete Function.prototype['toString']; set_native(Function.prototype, "toString", myToString); set_native(Function.prototype.toString, myFunction_toString_symbol, "function toString() { [native code] }"); const safe_Function = function safe_Function(func) { set_native(func, myFunction_toString_symbol, "function" + (func.name ? " " + func.name : "") + "() { [native code] }"); } return safe_Function(func) } //创建函数 const makeFunction = function makeFunction(name) { // 使用 Function 保留函数名 let func = new Function("v_log", ` return function ${name}() { v_log('函数${name}传参-->', arguments); }; `)(v_log); // 传递 v_log 到动态函数 safeFunction(func); func.prototype = myProxy(func.prototype, `方法${name}.prototype`); return func; } //Window Window = function Window() {} Window.prototype = {} safeFunction(Window) window = global; Object.setPrototypeOf(window, Window.prototype) window.top =window.Window = window.self = window window.chrome = {} window.loadts = '1758137904794' MouseEvent = function MouseEvent(){} safeFunction(window.MouseEvent) window.addEventListener = function addEventListener(){} safeFunction(window.addEventListener) window.DeviceMotionEvent = function (){} safeFunction(window.DeviceMotionEvent) delete Buffer delete __dirname delete __filename delete process delete global Document = function Document(){} safeFunction(Document) //Node Node = function Node(){} Node.prototype.removeChild = function (){} safeFunction(Node) safeFunction(Node.prototype.removeChild) //Element Element = function Element(){ Node.call(this) } safeFunction(Element) Element.prototype = Object.create(Node.prototype) Element.prototype.constructor = Element Element.prototype.getAttribute = function getAttribute(){} Element.prototype.removeChild = function removeChild(){} safeFunction(Element.prototype.getAttribute) safeFunction(Element.prototype.removeChild) //HTMLElement HTMLElement = function HTMLElement(){ Element.call(this) //在 HTMLElement 构造函数内部通过 Element.call(this) 调用了父类 Element 的构造函数,并将 this 绑定为 HTMLElement 的实例。 } safeFunction(HTMLElement) HTMLElement.prototype = Object.create(Element.prototype) HTMLElement.prototype.constructor = HTMLElement //HTMLHtmlElement HTMLHtmlElement = function HTMLHtmlElement(){ HTMLElement.call(this) this.tagName = 'HTML' } safeFunction(HTMLHtmlElement) HTMLHtmlElement.prototype = Object.create(HTMLElement.prototype) HTMLHtmlElement.prototype.constructor = HTMLHtmlElement //XMLHttpRequest XMLHttpRequest = function XMLHttpRequest(){} XMLHttpRequest.prototype.open = function (){} XMLHttpRequest.prototype.send = function (){} XMLHttpRequest.prototype.setRequestHeader = function (){} XMLHttpRequest.prototype.addEventListener = function (){} safeFunction(window.XMLHttpRequest) //requestIdleCallback requestIdleCallback = function (){} safeFunction(window.requestIdleCallback) //HtmlCollection function HTMLCollection(){ this._element = [watch(new HTMLHtmlElement())] } HTMLCollection.prototype[Symbol.iterator] = function() { // 获取文档根元素的子元素集合(迭代的目标) const elements = this._element; let index = 0; // 迭代索引 // 返回迭代器对象(必须包含next()方法) return { next: () => { // 当索引小于元素数量时,返回当前元素并递增索引 if (index < elements.length) { return { value: elements[index++], done: false }; } else { // 迭代结束 return { value: undefined, done: true }; } } }; }; safeFunction(HTMLCollection) //HTMLAllCollection function HTMLAllCollection() { this.length = 1048; } safeFunction(HTMLAllCollection) //HTMLBodyElement function HTMLBodyElement(){ HTMLElement.call(this); this.tagName = "BODY" } HTMLBodyElement.prototype = Object.create(HTMLElement.prototype) safeFunction(HTMLBodyElement) //HTMLDocument 用于实现document上的属性 function HTMLDocument() { } safeFunction(HTMLDocument) HTMLDocument.prototype.addEventListener = function addEventListener(){} safeFunction(HTMLDocument.prototype.addEventListener) HTMLDocument.prototype.getElementsByTagName = function getElementsByTagName(tagName){ console.log("HTMLDocument.prototype.getElementsByTagName的参数==>",tagName) if(tagName === "*"){ return watch(new HTMLCollection()) } } safeFunction(HTMLDocument.prototype.getElementsByTagName) HTMLDocument.prototype.all = watch(new HTMLAllCollection()) HTMLDocument.prototype.body = watch(new HTMLBodyElement()) HTMLDocument.prototype.documentElement = watch(new HTMLHtmlElement()) document = new HTMLDocument() document.cookie = "" //Permissions function Permissions (){ function createPermissionStatus(name) { return { name: name, // 权限名称(如 'geolocation') state: 'prompt', // 状态值:'granted' | 'denied' | 'prompt' onchange: null, // 事件回调占位 // 模拟事件监听方法(空实现) addEventListener: function() {}, removeEventListener: function() {} }; } this.query = function(permissionDesc) { const permName = permissionDesc?.name || 'unknown'; // 返回 Promise,resolve 出模拟的状态对象 return Promise.resolve(createPermissionStatus(permName)); }; this.request = function(permissionDesc) { const permName = permissionDesc?.name || 'unknown'; return Promise.resolve(createPermissionStatus(permName)); } } safeFunction(Permissions) //Navigator function Navigator() { } safeFunction(Navigator) Navigator.prototype.webdriver = false; Navigator.prototype.permissions = watch(new Permissions()); Navigator.prototype.userAgentData = watch(new NavigatorUAData()); Navigator.prototype.appName = 'Netscape' Navigator.prototype.userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36' navigator = new Navigator() //NavigatorUAData function NavigatorUAData() { this["brands"] = [ { "brand": "Chromium", "version": "140" }, { "brand": "Not=A?Brand", "version": "24" }, { "brand": "Google Chrome", "version": "140" } ]; this["mobile"] = false; this["platform"] = "Windows" } safeFunction(NavigatorUAData) //Screen function Screen() {} screen = new Screen() safeFunction(Screen) //History function History() { } history = new History() safeFunction(History) //Location function Location() { } location = new Location() location.href = 'https://www.xiaohongshu.com/explore' safeFunction(Location) setProxyArr(["window",'document', 'location', 'history', 'screen', 'navigator']) 这个代码会发生无限递归, 我测试过,setProxyArr([“window”,‘document’, ‘location’, ‘history’, ‘screen’, ‘navigator’]) 里面如果没有window,在这个环境下执行业务代码不会发生无限递归,这是为什么,是不是window的某些属性导致的
09-19
#!/bin/bash # YFW跨平台IM插件自动化部署脚本 # 部署路径: yfw_szrengjing_com/wp-content/plugins # 数据库配置: 名称=yfw_szrengjing_c, 用户=yfw_szrengjing_c, 密码=GAm2jPL4Dm # 配置参数 PLUGIN_DIR="yfw_szrengjing_com/wp-content/plugins/yfw-im" DB_NAME="yfw_szrengjing_c" DB_USER="yfw_szrengjing_c" DB_PASS="GAm2jPL4Dm" WS_PORT=8080 PHP_VERSION_REQUIRED="7.4" RATCHET_VERSION="0.4.4" # 颜色输出函数 info() { echo -e "\033[34m[INFO] $1\033[0m"; } success() { echo -e "\033[32m[SUCCESS] $1\033[0m"; } error() { echo -e "\033[31m[ERROR] $1\033[0m" && exit 1; } # 1. 环境检查 info "开始环境检查..." # 检查PHP版本 PHP_VERSION=$(php -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;") if [[ $(echo "$PHP_VERSION < $PHP_VERSION_REQUIRED" | bc -l) -eq 1 ]]; then error "需要PHP $PHP_VERSION_REQUIRED及以上版本,当前版本: $PHP_VERSION" fi # 检查Composer if ! command -v composer &> /dev/null; then error "未安装Composer,请先安装Composer" fi # 检查目录是否存在 if [ ! -d "yfw_szrengjing_com/wp-content/plugins" ]; then error "目标路径不存在: yfw_szrengjing_com/wp-content/plugins" fi # 2. 创建插件目录 info "创建插件目录结构..." mkdir -p $PLUGIN_DIR/{includes,assets/{js,css}} || error "创建目录失败" # 3. 生成核心文件 info "生成插件文件..." # 3.1 插件入口文件 (yfw-im.php) cat > $PLUGIN_DIR/yfw-im.php << 'EOF' <?php /* Plugin Name: YFW 跨平台IM Description: 基于WebSocket的跨平台即时通讯插件 Version: 1.0 Author: Your Name */ if (!defined('ABSPATH')) exit; // 加载核心文件 require_once plugin_dir_path(__FILE__) . 'includes/class-yfw-im.php'; require_once plugin_dir_path(__FILE__) . 'includes/db.php'; require_once plugin_dir_path(__FILE__) . 'config.php'; // 初始化插件 function yfw_im_init() { $yfw_im = new YFW_IM(); $yfw_im->init(); } add_action('plugins_loaded', 'yfw_im_init'); // 激活插件时创建数据库表 register_activation_hook(__FILE__, 'yfw_im_install'); function yfw_im_install() { require_once plugin_dir_path(__FILE__) . 'includes/db.php'; yfw_im_create_tables(); } EOF # 3.2 数据库操作文件 (includes/db.php) cat > $PLUGIN_DIR/includes/db.php << EOF <?php global \$wpdb; \$table_prefix = \$wpdb->prefix . 'yfw_im_'; // 数据库配置(自动填充) define('YFW_IM_DB_NAME', '$DB_NAME'); define('YFW_IM_DB_USER', '$DB_USER'); define('YFW_IM_DB_PASS', '$DB_PASS'); // 创建消息表和会话表 function yfw_im_create_tables() { global \$wpdb, \$table_prefix; \$charset_collate = \$wpdb->get_charset_collate(); // 会话表 \$sql = "CREATE TABLE IF NOT EXISTS {\$table_prefix}conversations ( id INT AUTO_INCREMENT PRIMARY KEY, user1_id BIGINT NOT NULL, user2_id BIGINT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY user_pair (user1_id, user2_id) ) \$charset_collate;"; // 消息表 \$sql .= "CREATE TABLE IF NOT EXISTS {\$table_prefix}messages ( id INT AUTO_INCREMENT PRIMARY KEY, conversation_id INT NOT NULL, sender_id BIGINT NOT NULL, content TEXT NOT NULL, type ENUM('text', 'image', 'file') DEFAULT 'text', status ENUM('sent', 'delivered', 'read') DEFAULT 'sent', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES {\$table_prefix}conversations(id) ON DELETE CASCADE ) \$charset_collate;"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta(\$sql); } // 存储消息 function yfw_im_save_message(\$conversation_id, \$sender_id, \$content, \$type = 'text') { global \$wpdb, \$table_prefix; return \$wpdb->insert( "{\$table_prefix}messages", [ 'conversation_id' => \$conversation_id, 'sender_id' => \$sender_id, 'content' => \$content, 'type' => \$type ] ); } EOF # 3.3 WebSocket服务端 (includes/websocket.php) cat > $PLUGIN_DIR/includes/websocket.php << 'EOF' <?php use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; require_once plugin_dir_path(__FILE__) . '../../vendor/autoload.php'; class YFW_IM_Socket implements MessageComponentInterface { protected $clients; protected $userConnections; // 用户ID与连接的映射 public function __construct() { $this->clients = new \SplObjectStorage; $this->userConnections = []; } // 新连接建立 public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); error_log("新连接: {$conn->resourceId}"); } // 接收消息 public function onMessage(ConnectionInterface $from, $msg) { $data = json_decode($msg, true); if (!$data) return; // 处理用户认证(首次连接时) if ($data['action'] === 'auth') { $user_id = $data['user_id']; $this->userConnections[$user_id] = $from; $from->send(json_encode(['status' => 'success', 'msg' => '认证成功'])); return; } // 处理消息发送 if ($data['action'] === 'send_message') { $this->handleMessage($data); } } // 处理消息转发 private function handleMessage($data) { $sender_id = $data['sender_id']; $receiver_id = $data['receiver_id']; $content = $data['content']; // 1. 查找或创建会话 $conversation_id = $this->getOrCreateConversation($sender_id, $receiver_id); // 2. 保存消息到数据库 yfw_im_save_message($conversation_id, $sender_id, $content); // 3. 转发消息给接收者 if (isset($this->userConnections[$receiver_id])) { $receiverConn = $this->userConnections[$receiver_id]; $receiverConn->send(json_encode([ 'action' => 'new_message', 'sender_id' => $sender_id, 'content' => $content, 'time' => date('Y-m-d H:i:s') ])); } } // 获取或创建会话 private function getOrCreateConversation($user1, $user2) { global $wpdb, $table_prefix; $min = min($user1, $user2); $max = max($user1, $user2); $conv = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM {$table_prefix}conversations WHERE user1_id = %d AND user2_id = %d", $min, $max ) ); if ($conv) return $conv->id; // 创建新会话 $wpdb->insert( "{$table_prefix}conversations", ['user1_id' => $min, 'user2_id' => $max] ); return $wpdb->insert_id; } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); // 移除用户连接映射 $this->userConnections = array_filter($this->userConnections, function($c) use ($conn) { return $c !== $conn; }); error_log("连接关闭: {$conn->resourceId}"); } public function onError(ConnectionInterface $conn, \Exception $e) { error_log("错误: {$e->getMessage()}"); $conn->close(); } } // 启动WebSocket服务(需通过命令行运行) if (php_sapi_name() === 'cli') { $server = IoServer::factory( new HttpServer( new WsServer( new YFW_IM_Socket() ) ), 8080 // 端口号 ); $server->run(); } EOF # 3.4 核心类文件 (includes/class-yfw-im.php) cat > $PLUGIN_DIR/includes/class-yfw-im.php << 'EOF' <?php class YFW_IM { public function init() { // 注册AJAX接口(加载历史消息) add_action('wp_ajax_yfw_im_load_messages', [$this, 'load_messages']); add_action('wp_ajax_nopriv_yfw_im_load_messages', [$this, 'load_messages_nopriv']); } // 加载历史消息(登录用户) public function load_messages() { check_ajax_referer('yfw_im_nonce', 'nonce'); $current_user = wp_get_current_user(); if (!$current_user->ID) { wp_send_json_error('未登录'); } $receiver_id = intval($_POST['receiver_id']); $messages = $this->get_message_history($current_user->ID, $receiver_id); wp_send_json_success(['messages' => $messages]); } // 未登录用户处理 public function load_messages_nopriv() { wp_send_json_error('请先登录'); } // 获取消息历史 private function get_message_history($user1, $user2) { global $wpdb, $table_prefix; $min = min($user1, $user2); $max = max($user1, $user2); $conv = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM {$table_prefix}conversations WHERE user1_id = %d AND user2_id = %d", $min, $max ) ); if (!$conv) return []; return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table_prefix}messages WHERE conversation_id = %d ORDER BY created_at ASC", $conv->id ), ARRAY_A ); } } EOF # 3.5 前端WebSocket客户端 (assets/js/socket.js) cat > $PLUGIN_DIR/assets/js/socket.js << 'EOF' class YFW_IM_Client { constructor() { this.socket = null; this.userId = yfw_im_config.user_id; this.connect(); } // 连接WebSocket服务 connect() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const host = yfw_im_config.ws_host || `${protocol}//${window.location.hostname}:8080`; this.socket = new WebSocket(host); this.socket.onopen = () => { console.log('WebSocket连接成功'); this.auth(); }; this.socket.onmessage = (event) => { const data = JSON.parse(event.data); this.handleMessage(data); }; this.socket.onerror = (error) => { console.error('WebSocket错误:', error); }; this.socket.onclose = () => { console.log('连接关闭,尝试重连...'); setTimeout(() => this.connect(), 3000); }; } // 认证用户 auth() { this.send({ action: 'auth', user_id: this.userId }); } // 发送消息 sendMessage(receiverId, content) { this.send({ action: 'send_message', sender_id: this.userId, receiver_id: receiverId, content: content }); } // 通用发送方法 send(data) { if (this.socket.readyState === WebSocket.OPEN) { this.socket.send(JSON.stringify(data)); } else { console.error('连接未就绪'); } } // 处理接收的消息 handleMessage(data) { if (data.action === 'new_message') { window.dispatchEvent(new CustomEvent('yfw_im_new_message', { detail: data })); } } } EOF # 3.6 前端聊天界面逻辑 (assets/js/frontend.js) cat > $PLUGIN_DIR/assets/js/frontend.js << 'EOF' // 初始化聊天客户端 document.addEventListener('DOMContentLoaded', () => { const imClient = new YFW_IM_Client(); const chatWindow = document.getElementById('yfw-im-chat'); const messageInput = document.getElementById('yfw-im-input'); const sendBtn = document.getElementById('yfw-im-send'); let currentReceiver = null; // 设置当前聊天对象 document.querySelectorAll('.yfw-im-contact').forEach(contact => { contact.addEventListener('click', () => { currentReceiver = contact.dataset.userId; chatWindow.innerHTML = ''; loadMessages(currentReceiver); }); }); // 发送消息 sendBtn.addEventListener('click', () => { const content = messageInput.value.trim(); if (content && currentReceiver) { imClient.sendMessage(currentReceiver, content); addMessageToUI(content, 'outgoing', yfw_im_config.user_id); messageInput.value = ''; } }); // 回车发送 messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') sendBtn.click(); }); // 接收新消息 window.addEventListener('yfw_im_new_message', (e) => { const data = e.detail; addMessageToUI(data.content, 'incoming', data.sender_id); }); // 加载历史消息 function loadMessages(receiverId) { fetch(yfw_im_config.ajax_url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ action: 'yfw_im_load_messages', receiver_id: receiverId, nonce: yfw_im_config.nonce }) }) .then(res => res.json()) .then(data => { if (data.success) { data.data.messages.forEach(msg => { const type = msg.sender_id == yfw_im_config.user_id ? 'outgoing' : 'incoming'; addMessageToUI(msg.content, type, msg.sender_id); }); } }); } // 添加消息到界面 function addMessageToUI(content, type, senderId) { const msgEl = document.createElement('div'); msgEl.className = `yfw-im-message ${type}`; msgEl.innerHTML = `<div class="content">${content}</div>`; chatWindow.appendChild(msgEl); chatWindow.scrollTop = chatWindow.scrollHeight; } }); EOF # 3.7 样式文件 (assets/css/style.css) cat > $PLUGIN_DIR/assets/css/style.css << 'EOF' .yfw-im-chat { width: 400px; height: 500px; border: 1px solid #ccc; border-radius: 8px; overflow: hidden; display: flex; flex-direction: column; } .yfw-im-messages { flex: 1; padding: 10px; overflow-y: auto; background-color: #f9f9f9; } .yfw-im-message { margin: 5px 0; max-width: 70%; padding: 8px 12px; border-radius: 18px; clear: both; } .yfw-im-message.incoming { background-color: #e9e9eb; float: left; } .yfw-im-message.outgoing { background-color: #0078d7; color: white; float: right; } .yfw-im-input-area { display: flex; padding: 10px; border-top: 1px solid #ccc; } #yfw-im-input { flex: 1; padding: 8px 12px; border: 1px solid #ccc; border-radius: 20px; outline: none; } #yfw-im-send { margin-left: 10px; padding: 8px 16px; background-color: #0078d7; color: white; border: none; border-radius: 20px; cursor: pointer; } .yfw-im-contacts { padding: 10px; border-bottom: 1px solid #ccc; } .yfw-im-contact { padding: 8px 12px; border-radius: 4px; cursor: pointer; margin-bottom: 5px; } .yfw-im-contact:hover { background-color: #f0f0f0; } EOF # 3.8 配置文件 (config.php) cat > $PLUGIN_DIR/config.php << 'EOF' <?php // 跨平台配置 define('YFW_IM_WS_HOST', 'ws://' . $_SERVER['HTTP_HOST'] . ':8080'); define('YFW_IM_ALLOWED_ORIGINS', [ 'https://yfw_szrengjing_com', 'http://yfw_szrengjing_com', 'http://localhost:3000' ]); // 前端配置(通过WP本地化脚本传递) function yfw_im_enqueue_scripts() { wp_enqueue_script('yfw-im-socket', plugins_url('assets/js/socket.js', dirname(__FILE__)), [], '1.0', true); wp_enqueue_script('yfw-im-frontend', plugins_url('assets/js/frontend.js', dirname(__FILE__)), ['yfw-im-socket'], '1.0', true); wp_enqueue_style('yfw-im-style', plugins_url('assets/css/style.css', dirname(__FILE__))); // 传递配置到前端 wp_localize_script('yfw-im-frontend', 'yfw_im_config', [ 'user_id' => get_current_user_id(), 'ws_host' => YFW_IM_WS_HOST, 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('yfw_im_nonce') ]); } add_action('wp_enqueue_scripts', 'yfw_im_enqueue_scripts'); EOF # 4. 安装依赖 info "安装WebSocket依赖..." cd $PLUGIN_DIR || error "进入插件目录失败" composer require cboden/ratchet:$RATCHET_VERSION > /dev/null 2>&1 || error "安装Ratchet失败" # 5. 数据库初始化(通过WordPress CLI触发,若没有WP-CLI则提示手动激活) info "初始化数据库..." if command -v wp &> /dev/null; then # 若安装了WP-CLI,直接触发激活钩子 wp plugin activate yfw-im > /dev/null 2>&1 success "数据库表创建成功" else info "未检测到WP-CLI,请在WordPress后台手动激活插件以创建数据库表" fi # 6. 配置WebSocket服务自启动(使用systemd) info "配置WebSocket服务自启动..." SERVICE_FILE="/etc/systemd/system/yfw-im-websocket.service" if [ -w "/etc/systemd/system/" ]; then cat > $SERVICE_FILE << EOF [Unit] Description=YFW IM WebSocket Service After=network.target [Service] User=$(whoami) WorkingDirectory=$PLUGIN_DIR/includes ExecStart=$(which php) websocket.php Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF # 启动服务 systemctl daemon-reload systemctl start yfw-im-websocket systemctl enable yfw-im-websocket > /dev/null 2>&1 success "WebSocket服务已启动并设置开机自启" else info "无权限配置systemd,手动启动命令: php $PLUGIN_DIR/includes/websocket.php" fi # 7. 完成部署 success "部署完成!" echo "======================================" echo "插件路径: $PLUGIN_DIR" echo "WebSocket端口: $WS_PORT" echo "使用说明:" echo "1. 在WordPress后台激活 'YFW 跨平台IM' 插件" echo "2. 在需要显示聊天的页面添加聊天组件(可通过短代码实现)" echo "3. 确保服务器开放$WS_PORT端口的防火墙权限" echo "======================================" [root@yfw ~]# cd /www/wwwroot/yfw_szrengjing_com/wp-content/plugins [root@yfw plugins]# ./deploy_yfw_im.sh [INFO] 开始环境检查... [ERROR] 目标路径不存在: yfw_szrengjing_com/wp-content/plugins [root@yfw plugins]#
11-15
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值