How to Set Up a PHP Workerman MySQL Forward Proxy for Seamless Database Access

This guide explains how to configure and run a Workerman‑based PHP forward proxy that forwards MySQL connections, enabling smooth access to internal databases via Navicat Premium or other clients, with detailed code snippets, startup commands, and Windows usage instructions.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
How to Set Up a PHP Workerman MySQL Forward Proxy for Seamless Database Access

Overview

The forward proxy is designed to relay MySQL connections, allowing tools like Navicat Premium to connect without latency or blockage, and can also be used to reach internal databases or services.

Project repository: https://gitee.com/windthesky/forward_proxy.git

Configuration

All settings are defined in the start.php file.

<?php
/** @noinspection PhpUnused */
/** @noinspection PhpUndefinedFieldInspection */
/** @noinspection PhpObjectFieldsAreOnlyWrittenInspection */
ini_set('memory_limit', '512M');

use Workerman\Connection\AsyncTcpConnection;
use Workerman\Worker;
use Workerman\Connection\TcpConnection;

// Autoload classes
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/common.php';

// Logging options
const LOG_WRITE = true;
const SHOW_LOG = true;
const TIMEOUT_CLOSE = true;
const TIME = 300; // seconds before timeout

// Buffer sizes (default 2M)
TcpConnection::$defaultMaxSendBufferSize = 2*1024*1024;
AsyncTcpConnection::$defaultMaxSendBufferSize = 2*1024*1024;

$proxy_list = [
    [
        'type' => 'tcp',
        'host' => '127.0.0.1',
        'port' => 3306,
        'local_port' => 33060,
    ],
];

$app_list = [];
$connect_list = [];
$client_list = [];

/**
 * App receives data
 * @param AsyncTcpConnection $app_connection
 * @param mixed $data
 */
function app_message(AsyncTcpConnection $app_connection, $data): void {
    try {
        write_log('app收到:开始');
        $client_id = $app_connection->client_id;
        global $connect_list;
        write_log(['app收到:', $data]);
        write_log(['app收到-client_id:', $client_id]);
        $connect_list[$client_id]->send($data);
        write_log('app收到-处理完:');
    } catch (Throwable $e) {
        write_log(['app收到:异常==》', $e->getMessage()]);
        $app_connection->close();
    }
}

/**
 * App error handling
 */
function app_error(AsyncTcpConnection $app_connection): void {
    try {
        write_log('app错误:');
        $client_id = $app_connection->client_id;
        global $connect_list;
        $connect_list[$client_id]->close();
        write_log('app错误:执行完');
    } catch (Throwable $e) {
        write_log(['app错误:异常==》', $e->getMessage()]);
    }
}

/**
 * App close handling
 */
function app_close(AsyncTcpConnection $app_connection): void {
    try {
        write_log('app关闭:');
        $client_id = $app_connection->client_id;
        global $connect_list, $app_list;
        $connect_list[$client_id]->close();
        unset($app_list[$client_id]);
        write_log('app关闭:执行完');
    } catch (Throwable $e) {
        write_log(['app关闭:异常==》', $e->getMessage()]);
    }
}

/**
 * Client connection handling
 */
function handle_connection(TcpConnection $connection): void {
    try {
        write_log('客户端连接:');
        $connection->lastMessageTime = time();
        global $app_list, $connect_list;
        $client_id = 'client-' . session_create_id();
        $connection->client_id = $client_id;
        $connect_list[$client_id] = $connection;
        $proxy_url = $connection->worker->proxy_url;
        write_log(['客户端连接:开始==》', $connection->client_id]);
        $app_list[$client_id] = new AsyncTcpConnection($proxy_url);
        $app_list[$client_id]->client_id = $client_id;
        $app_list[$client_id]->onMessage = 'app_message';
        $app_list[$client_id]->onError = 'app_error';
        $app_list[$client_id]->onClose = 'app_close';
        $app_list[$client_id]->connect();
        write_log(['客户端连接:完成==》', $connection->client_id]);
    } catch (Throwable $e) {
        write_log(['客户端连接:异常==》', $connection->client_id, $e->getMessage()]);
    }
}

/**
 * Message handling
 */
function handle_message(TcpConnection $connection, $data): void {
    try {
        write_log(['收到消息:', $data]);
        global $app_list;
        if (TIMEOUT_CLOSE && time() - $connection->lastMessageTime > TIME) {
            write_log(['收到消息:超时关闭']);
            $connection->close();
            return;
        }
        $app_list[$connection->client_id]->send($data);
        $connection->lastMessageTime = time();
        write_log(['收到消息:完']);
    } catch (Throwable $e) {
        write_log(['收到消息:异常==》', $connection->client_id, $e->getMessage()]);
    }
}

/**
 * Error handling for client connections
 */
function handle_error(TcpConnection $connection): void {
    try {
        write_log(['处理错误:开始==》', $connection->client_id]);
        $client_id = $connection->client_id;
        global $app_list;
        if (empty($app_list[$client_id])) return;
        $app_list[$client_id]->close();
        write_log(['处理错误:完成==》', $client_id]);
    } catch (Throwable $e) {
        write_log(['处理错误:异常==》', $e->getMessage()]);
    }
}

/**
 * Close handling for client connections
 */
function handle_close(TcpConnection $connection): void {
    try {
        write_log(['处理关闭:开始==》', $connection->client_id]);
        $client_id = $connection->client_id;
        global $app_list, $connect_list;
        if (empty($app_list[$client_id])) return;
        $app_list[$client_id]->close();
        unset($connect_list[$client_id]);
        write_log(['处理关闭:完成==》', $client_id]);
    } catch (Throwable $e) {
        write_log(['处理关闭:异常==》', $e->getMessage()]);
    }
}

$worker = new Worker();
$worker->onWorkerStart = function () {
    global $client_list, $proxy_list;
    foreach ($proxy_list as $k => $v) {
        $url = $v['type'] . '://0.0.0.0:' . $v['local_port'];
        $proxy_url = $v['type'] . '://' . $v['host'] . ':' . $v['port'];
        echo '监听地址:' . $url . PHP_EOL;
        echo '代理地址:' . $proxy_url . PHP_EOL;
        $client_list[$k] = new Worker($url);
        $client_list[$k]->proxy_key = $k;
        $client_list[$k]->proxy_info = $v;
        $client_list[$k]->proxy_url = $proxy_url;
        $client_list[$k]->onConnect = 'handle_connection';
        $client_list[$k]->onMessage = 'handle_message';
        $client_list[$k]->onError = 'handle_error';
        $client_list[$k]->onClose = 'handle_close';
        $client_list[$k]->listen();
    }
};
Worker::runAll();

Startup Commands

Run the proxy in different modes using the following commands:

Debug mode: php start.php start Daemon mode: php start.php start -d Stop: php start.php stop Restart: php start.php restart Graceful reload: php start.php reload Check status:

php start.php status

Windows Startup

On Windows, simply double‑click the batch file start_for_win.bat to launch the proxy.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Proxydatabase
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.