×

用chatgpt 的4o-mini 模型写Bug !

Falcon 2024-09-18 views:
自动摘要

正在生成中……

晚上因为网站被机器人疯狂爬取,就图方便用chaptgpt写一个控制ip访问频率的wp插件,万没想到 chatgpt 的 4o-mini 果然还是不够聪明,被它坑死了!

一开始是它不懂我用的是phpiredis扩展,一直使用Redis()类,而phpiredis只有函数式命令操作,经过我的再次提醒,并附上文档用法,它总算改成正确的了。

然后它又犯了一个让我debug了半个小时的错误,当检查redis一个key不存在时,会返回NULL,它又将转成int,也就是0,然后它将0用三个等号与 false 对比,也就是类型和值都要相等,这它喵的这永远不可能相等啊!导致设置过期时间的代码不可能执行。

 // 获取当前请求次数
        $request_count_response = phpiredis_command_bs( $redis, array( 'GET', $redis_key ) );
        $request_count = intval( $request_count_response );

        if ( false === $request_count ) {
            // 初始化计数为1,过期时间为60秒
            phpiredis_command_bs( $redis, array( 'SETEX', $redis_key, 60, 1 ) );
        } else {
            if ( $request_count >= $max_requests_per_minute ) {
                $this->block_request();
            } else {
                // 增加计数
                phpiredis_command_bs( $redis, array( 'INCR', $redis_key ) );
            }
			//....

于是key过期时间一直不生效,我开始还以为是phpiredis的问题,还debug了半天,直到我往上看了一眼!这TMD!

cursor 的3.5 sonnet是可以识别到这个bug的,开始用的gpt 4o也可以,后来因为4o达到每日限制,降级回4o-mini,没想到就给我写bug了。😅

尽量如此,展示一下这个WordPress 插件的成果吧。可以用redis或者用wordpress自带的transient缓存系列函数(存储在mysql)来控制ip的访问频率, 我用的是phpiredis扩展,其实可以再兼容主流的redis扩展(phpiredis确实很非主流,操作的方式完全就是redis-cli,没有流行的面向对象那些乱七八糟的东西,我觉得反而很纯粹)或者纯php客户端。

完整代码:

<?php
/**
 * Plugin Name: IP Rate Limiter with Redis
 * Description: 限制每个IP每分钟的访问频率,并支持使用Redis存储访问数据。
 * Version: 1.3
 * Author: Falcon
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // 防止直接访问文件
}

class IP_Rate_Limiter {

    // 初始化插件
    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
        add_action( 'admin_init', array( $this, 'register_settings' ) );
        add_action( 'init', array( $this, 'check_ip_rate_limit' ) );
    }

    // 检查IP访问频率
    public function check_ip_rate_limit() {
        $ip_address = $this->get_ip_address();
        $use_redis = get_option( 'ip_rate_limiter_use_redis', 'no' );


        // 获取用户设置的最大请求次数,默认为10
        $max_requests_per_minute = get_option( 'ip_rate_limiter_max_requests', 10 );
        if ( $use_redis === 'yes' && $this->is_redis_available() ) {
            // 使用Redis进行计数
            $this->check_ip_in_redis( $ip_address, $max_requests_per_minute );
        } else {
            // 使用transient进行计数
            $this->check_ip_in_transient( $ip_address, $max_requests_per_minute );
        }
    }

    // 使用Redis检查和记录访问次数
    private function check_ip_in_redis( $ip_address, $max_requests_per_minute ) {
        $redis = $this->get_redis_connection();
        $redis_key = 'ip_rate_limit_' . $ip_address;

        // 获取当前请求次数
        $request_count_response = phpiredis_command_bs( $redis, array( 'GET', $redis_key ) );
        $request_count = intval( $request_count_response );

        if ( $request_count === 0) {
            // 初始化计数为1,过期时间为60秒            
            $result = phpiredis_command($redis, "SETEX  $redis_key 60 1");
        } else {
            if ( $request_count >= $max_requests_per_minute ) {
                $this->block_request();
            } else {
                // 增加计数
                phpiredis_command_bs( $redis, array( 'INCR', $redis_key ) );
            }
        }
    }

    // 使用transient检查和记录访问次数
    private function check_ip_in_transient( $ip_address, $max_requests_per_minute ) {
        $transient_key = 'ip_rate_limit_' . $ip_address;

        $request_count = get_transient( $transient_key );

        if ( false === $request_count ) {
            set_transient( $transient_key, 1, 60 ); // 初始化计数为1,过期时间为60秒
        } else {
            if ( $request_count >= $max_requests_per_minute ) {
                $this->block_request();
            } else {
                set_transient( $transient_key, $request_count + 1, 60 );
            }
        }
    }

    // 获取用户IP地址
    private function get_ip_address() {
        if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
            return $_SERVER['HTTP_CLIENT_IP'];
        } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
        } else {
            return $_SERVER['REMOTE_ADDR'];
        }
    }

    // 阻止请求并输出错误信息
    private function block_request() {
		// 设置 429 Too Many Requests 状态码
		//http_response_code(429);

		// 设置 Retry-After 头,告知客户端 180 秒后重试
		header('Retry-After: 180');

		// 显示自定义的错误信息
		wp_die( 'Too many requests, please try again later.', 'Too Many Requests', array( 'response' => 429 ) );
    }

    // 获取Redis连接
    private function get_redis_connection() {
        $host = get_option( 'ip_rate_limiter_redis_host', '127.0.0.1' );
        $port = get_option( 'ip_rate_limiter_redis_port', 6379 );

        // 使用 phpiredis 扩展函数连接 Redis
        $redis = phpiredis_connect( $host, $port );

        // 如果有密码,进行身份验证
        $password = get_option( 'ip_rate_limiter_redis_password', '' );
        if ( ! empty( $password ) ) {
            phpiredis_command_bs( $redis, array( 'AUTH', $password ) );
        }

        return $redis;
    }

    // 检查Redis是否可用
    private function is_redis_available() {
        try {
            $redis = $this->get_redis_connection();
            $response = phpiredis_command_bs( $redis, array( 'PING' ) );
            return $response === 'PONG';
        } catch ( Exception $e ) {
            return false;
        }
    }

    // 添加设置页面
    public function add_settings_page() {
        add_options_page(
            'IP Rate Limiter Settings', 
            'IP Rate Limiter', 
            'manage_options', 
            'ip-rate-limiter', 
            array( $this, 'settings_page_html' )
        );
    }

    // 注册设置
    public function register_settings() {
        register_setting( 'ip_rate_limiter_settings', 'ip_rate_limiter_max_requests' );
        register_setting( 'ip_rate_limiter_settings', 'ip_rate_limiter_use_redis' );
        register_setting( 'ip_rate_limiter_settings', 'ip_rate_limiter_redis_host' );
        register_setting( 'ip_rate_limiter_settings', 'ip_rate_limiter_redis_port' );
        register_setting( 'ip_rate_limiter_settings', 'ip_rate_limiter_redis_password' );

        add_settings_section(
            'ip_rate_limiter_section',
            '设置IP访问频率限制',
            null,
            'ip-rate-limiter'
        );

        add_settings_field(
            'ip_rate_limiter_max_requests',
            '每分钟最大访问次数',
            array( $this, 'settings_field_max_requests_html' ),
            'ip-rate-limiter',
            'ip_rate_limiter_section'
        );

        add_settings_field(
            'ip_rate_limiter_use_redis',
            '是否使用Redis存储',
            array( $this, 'settings_field_use_redis_html' ),
            'ip-rate-limiter',
            'ip_rate_limiter_section'
        );

        add_settings_field(
            'ip_rate_limiter_redis_host',
            'Redis主机',
            array( $this, 'settings_field_redis_host_html' ),
            'ip-rate-limiter',
            'ip_rate_limiter_section'
        );

        add_settings_field(
            'ip_rate_limiter_redis_port',
            'Redis端口',
            array( $this, 'settings_field_redis_port_html' ),
            'ip-rate-limiter',
            'ip_rate_limiter_section'
        );

        add_settings_field(
            'ip_rate_limiter_redis_password',
            'Redis密码',
            array( $this, 'settings_field_redis_password_html' ),
            'ip-rate-limiter',
            'ip_rate_limiter_section'
        );
    }

    // 设置页面内容
    public function settings_page_html() {
        ?>
        <div class="wrap">
            <h1>IP Rate Limiter 设置</h1>
            <form action="options.php" method="POST">
                <?php
                settings_fields( 'ip_rate_limiter_settings' );
                do_settings_sections( 'ip-rate-limiter' );
                submit_button();
                ?>
            </form>
        </div>
        <?php
    }

    // 最大访问次数设置字段HTML
    public function settings_field_max_requests_html() {
        $max_requests = get_option( 'ip_rate_limiter_max_requests', 10 );
        ?>
        <input type="number" name="ip_rate_limiter_max_requests" value="<?php echo esc_attr( $max_requests ); ?>" />
        <?php
    }

    // 是否使用Redis设置字段HTML
    public function settings_field_use_redis_html() {
        $use_redis = get_option( 'ip_rate_limiter_use_redis', 'no' );
        ?>
        <select name="ip_rate_limiter_use_redis">
            <option value="no" <?php selected( $use_redis, 'no' ); ?>>否</option>
            <option value="yes" <?php selected( $use_redis, 'yes' ); ?>>是</option>
        </select>
        <?php
    }

    // Redis主机设置字段HTML
    public function settings_field_redis_host_html() {
        $redis_host = get_option( 'ip_rate_limiter_redis_host', '127.0.0.1' );
        ?>
        <input type="text" name="ip_rate_limiter_redis_host" value="<?php echo esc_attr( $redis_host ); ?>" />
        <?php
    }

    // Redis端口设置字段HTML
    public function settings_field_redis_port_html() {
        $redis_port = get_option( 'ip_rate_limiter_redis_port', 6379 );
        ?>
        <input type="number" name="ip_rate_limiter_redis_port" value="<?php echo esc_attr( $redis_port ); ?>" />
        <?php
    }

    // Redis密码设置字段HTML
    public function settings_field_redis_password_html() {
        $redis_password = get_option( 'ip_rate_limiter_redis_password', '' );
        ?>
        <input type="password" name="ip_rate_limiter_redis_password" value="<?php echo esc_attr( $redis_password ); ?>" />
        <?php
    }
}

// 初始化插件
new IP_Rate_Limiter();

本文收录于