swoolefy

swoolefy是一个基于swoole实现的轻量级、高性能、协程级、开放性的API应用服务框架。「swoolefy is a lightweight, high-performance, concurrent, open API application service framework based on swoole implementation」

Github stars Tracking Chart

swoolefy

  ______                                _           _ _ _ _
 /  ____|                              | |         |  _ _ _|  _   _
|  (__     __      __   ___     ___    | |   ___   | |       | | | |
 \___  \   \ \ /\ / /  / _ \   / _ \   | |  / _ \  | |_ _ _  | | | |
 ____)  |   \ V  V /  | (_) | | (_) |  | | | ___/  |  _ _ _| | |_| |
|_____ /     \_/\_/    \___/   \___/   |_|  \___|  | |        \__, |
                                                   |_|           | |
                                                              __ / |
                                                             |_ _ /

swoolefy是一个基于swoole实现的轻量级高性能的常驻内存型的协程级应用服务框架,
高度支持httpApi,websocket,udp服务器,以及基于tcp实现可扩展的rpc服务,worker多进程消费模型
同时支持composer包方式安装部署项目。基于实用主义设计出发,swoolefy抽象Event事件处理类,
实现与底层的回调的解耦,支持协程单例调度,同步|异步调用,全局事件注册,心跳检查,异步任务,多进程(池),连接池等,
内置log、session、mysql、pgsql、redis、mongodb、kafka、amqp等常用组件等.

建议版本

swoolefy-5.0+ 版本:
目前主分支,最低要求php8.0+,swoole5.0+(或者swoole-cli-v5.0+), 或者也可以使用swoole-cli-v4.8+, 因为其内置php8.1+

swoolefy-4.8-lts 版本:
长期维护分支,最低要求php >= php7.3 && php < php8.0, 推荐直接swoole-v4.8+,需要通过源码编译安装swoole

选择哪个版本?
1、如果确定项目是使用php8+的,那么直接选择 swoole-v5.0+, 以上源码来编译安装或者直接使用swoole-cli-v5.0,然后选择 bingcool/swoolefy:~5.0.14 作为项目分支

2、如果确定项目是使用 php7.3 ~ php7.4 的,那么选择 swoole-v4.8+ 版本来进行编译安装(不能直接使用 swoole-cli-v4.8+ 了, 因为其内置的是php8.1,与你的项目的php7不符合)
所有只能通过编译swoole源码的方式来生成swoole扩展,然后选择 bingcool/swoolefy:^4.8.14 作为项目分支

实现的功能特性

基础特性

  • 支持架手脚一键创建项目
  • 支持分组路由, 路由middleware, 前置路由组件, 后置路由组件,多模块应用
  • 支持自定义注册不同根命名空间,快速多项目部署
  • 支持httpServer,实用轻量Api接口开发
  • 支持多协议websocketServer、udpServer、mqttServer
  • 支持基于tcp实现的rpc服务,开放式的系统接口,可自定义协议数据格式,并提供rpc-client协程组件
  • 支持DI容器,组件IOC、配置化,Channel公共组件池
  • 支持协程单例注册,协程上下文变量寄存
  • 支持mysql、postgreSql、redis协程组件
  • 支持全局logger组件、trace链路追踪
  • 支持mysql协程连接池
  • 支持redis协程池
  • 支持curl协程池
  • 支持protobuf buffer的数据接口结构验证,压缩传输等
  • 支持异步务管理TaskManager
  • 定时器管理TickManager
  • 内存表管理TableManager
  • 支持自定义进程管理ProcessManager,进程池管理PoolsManger
  • 支持底层异常错误的所有日志捕捉,支持全局日志,包括debug、info、notice、warning、error等级
  • 支持自定义进程的redis,rabbitmq,kafka的订阅发布,消息队列等
  • 支持热更新reload worker 监控以及更新
  • 支持定时的系统信息采集,并以订阅发布,udp等方式收集至存贮端
  • 支持命令行形式高度封装启动|停止控制的脚本,简单命令即可管理整个框架

高级特性

  • 支持crontab的local调用和fork独立进程的计划任务

    支持方式 说明
    local 自定义进程内定时执行代码
    fork 自定义进程定时拉起一个新的进程,由新的进程去支持任务,可异步
    url 自定义进程定时发起远程url请求,可设置callback回调处理结果
  • 支持worker下后台daemon模式的多进程协程消费模型,包括进程自动拉起,进程数动态调整,进程健康状态监控

  • 支持console终端脚本模式,跑完脚本自动退出,可用于修复数据、数据迁移等临时脚本功能

  • 支持分布式服务注册(zk,etcd)

适配协程环境组件

组件名称 安装 说明
predis composer require predis/predis:~1.1.7 predis组件、或者Phpredis扩展
mongodb composer require mongodb/mongodb:~1.3 mongodb组件,需要使用mongodb必须安装此组件
rpc-client composer require bingcool/rpc-client:dev-master swoolefy的rpc客户端组件,当与rpc服务端通信时,需要安装此组件,支持在php-fpm中使用
cron-expression composer require dragonmantank/cron-expression:~3.3.0 crontab计划任务组件,类似Linux的crobtab
redis lock composer require malkusch/lock Redis锁组件
amqp composer require php-amqplib/php-amqplib:~3.5.0 amqp php原生实现amqp协议客户端
ffmpeg composer require php-ffmpeg/php-ffmpeg:~1.1.0 php proc-open 调用ffmpeg处理音视频
validate composer require vlucas/valitron validate数据校验组件
guzzlehttp composer require guzzlehttp/guzzle guzzlehttp 组件
oauth 2.0 composer require league/oauth2-server oauth 2.0 授权认证组件
bingcool/library composer require bingcool/library library组件库

bingcool/library 是swoolefy require 内置库,专为swoole协程实现的组件库

实现了包括:

  • Db ORM Model 组件(支持mysql、 postSql、 sqlite、 Oracle)
  • DB Query Builder 链式操作查询组件
  • Kafka Producer Consumer组件
  • Rabbitmq Queue组件
  • Rabbitmq Delay Queue 死信延迟队列组件
  • Redis Cache组件
  • Redis Queue队列组件
  • Redis Delay Queue延迟队列组件
  • RedisLock锁组件
  • RateLimit限流组件
  • Redis Public Subscribe组件
  • Db、Redis、 Curl协程连接池组件
  • UUid 分布式自增id组件
  • Curl基础组件
  • Jwt 组件
  • Validate组件

github: https://github.com/bingcool/library

一、安装

1、先配置环境变量

// 独立物理机或者云主机配置系统环境变量
vi /etc/profile
在/etc/profile末尾添加一行标识环境,下面是支持的4个环境,框架将通过这个环境变量区分环境,加载不同的配置
export SWOOLEFY_CLI_ENV='dev'  // 开发环境
export SWOOLEFY_CLI_ENV='test' // 测试环境
export SWOOLEFY_CLI_ENV='gra'  // 灰度环境
export SWOOLEFY_CLI_ENV='prd'  // 生产环境
// 最后是配置生效
source /etc/profile

// 如果是通过dockerfile 创建容器的, 可以根据不同环境生成的内置环境变量不同镜像,每个不同的环境镜像可以用在不同环境,代码将通过这个环境变量区分环境,加载不同的配置
ENV SWOOLEFY_CLI_ENV dev

2、创建项目

// 下载代码到到你的自定义目录,这里定义为myproject
composer create-project bingcool/swoolefy:~5.0 myproject

二、添加项目入口启动文件cli.php,并定义你的项目目录,命名为App

<?php
// 在myproject目录下添加cli.php, 这个是启动项目的入口文件
include './vendor/autoload.php';

define('IS_DAEMON_SERVICE', 0);
define('IS_CRON_SERVICE', 0);
define('IS_CLI_SCRIPT', 0);
// 应用父目录
defined('ROOT_PATH') or define('ROOT_PATH', __DIR__);
// 启动目录
defined('START_DIR_ROOT') or define('START_DIR_ROOT', __DIR__);

date_default_timezone_set('Asia/Shanghai');

define('APP_NAMES', [
     // 你的项目命名为App,对应协议为http协议服务器,支持多个项目的,只需要在这里添加好项目名称与对应的协议即可
    'App' => 'http', 
    'Test' => 'http
]);

// 启动前处理,比如加载.env
$beforeFunc = function () {
    
};

include './swoolefy';

三、执行创建你定义的App项目

// 你定义的项目目录是App, 在myproject目录下执行下面命令行

swoole-cli cli.php create App 
或者    
php cli.php create App   

// 执行完上面命令行后,将会自动生成App项目目录以及内部子目录
myproject
|—— App  // 应用项目目录
|     |── Config       // 应用配置
|     |   |__ component // 协程单例组件
|     |      |—— database.php //数据库相关组件
|     |      |—— log.php     // 日志相关组件
|     |      |—— cache.php   // 缓存组件,可以继续添加其他组件,命名自由 
|     │   ├── dc.php   //环境配置项
|     │   └── constants.php
|     |   |—— config.php    // 应用层配置
|     |
|     ├── Controller
|     │   └── IndexController.php // 控制器层
|     ├── Model
|     │   └── ClientModel.php
|     ├── Module        // 模块层
|     ├── Protocol      // 协议配置
|     │   ├── conf.php  // 全局配置
|     │
|     ├── Router
|     │   └── Api.php  // 路由文件,不同模块定义不同文件即可
|     |—— Storage
|     |   |—— Logs  // 日志文件目录
|     |   |—— Sql   // sql日志目录
|     |__ .env     // 自动生成环境变量文件
|     │—— autoloader.php // 自定义项目自动加载
|     |—— Event.php      // 事件实现类
|     |—— HttpServer.php // http server
|    
|——— src //源码
|——— cli.php // http应用启动入口文件
|——— cron.php // 定时worker任务的多进程启动入口文件
|——— daemon.php // 守护进程worker的多进程启动入口文件
|——— script.php // 脚本启动入口文件

四、启动应用项目

// 终端启动 ctl+c 停止进程
php cli.php start App
或者    
swoole-cli cli.php start App

// 守护进程方式启动,添加-D参数控制
php cli.php start App --daemon=1
或者  
swooole-cli cli.php start App --daemon=1

// 停止进程
php cli.php stop App
或者   
swooole-cli cli.php stop App --force=1

// 查看进程状态
swooole-cli cli.php status App

五、访问

默认端口是9502,可以通过 http://localhost:9502 访问默认控制器

<?php
namespace App\Controller;

use Swoolefy\Core\Application;
use Swoolefy\Core\Controller\BController;

// 默认生成的IndexController
class IndexController extends BController {

    public function index() {
        Application::getApp()->response->write('<h1>Hello, Welcome to Swoolefy Framework! <h1>');
    }
}

至此一个最简单的http的服务就创建完成了,更多例子请参考项目下Test的demo

定义组件

应用层配置文件:
Config/config.php

<?php

return [

    // db|redis连接池
    'enable_component_pools' => [
        // 取components的`DB`组件名称相对应
        'db' => [
            'max_pool_num' => 5, // db实例数
            'max_push_timeout' => 2, // db实例进入channel池最长等待时间,单位s
            'max_pop_timeout' => 1, // db实例出channel池最长等待时间,单位s.在规定时间内获取不到db对象,将降级为实时创建db实例
            'max_life_timeout' => 10, // db实例的有效期,单位s.过期后将被掉弃,重新创建新DB实例
            'enable_tick_clear_pool' => 0 // 是否每分钟定时清空pool,防止长时间一直占用链接,max_pool_num设置很大的时候需要设置,否则不需要设置
        ],
    
        // 取components的`redis`组件名称相对应
        'redis' => [
            'max_pool_num' => 5,
            'max_push_timeout' => 2,
            'max_pop_timeout' => 1,
            'max_life_timeout' => 10,
            'enable_tick_clear_pool' => 0 // 是否每分钟定时清空pool,防止长时间一直占用链接,max_pool_num设置很大的时候需要设置,否则不需要设置
        ]
    ],
    
     // default_db
    'default_db' => 'db',

    // 加载组件配置
    'components' => \Swoolefy\Core\SystemEnv::loadComponent()
    
    // 其他配置
    ......
]

组件Component.php:


$dc = \Swoolefy\Core\SystemEnv::loadDcEnv();

return [
    // 用户行为记录的日志
    'log' => function($name) {
        $logger = new Log($name);
        $logger->setChannel('application');
        if(SystemEnv::isDaemonService()) {
            $logFilePath = LOG_PATH.'/daemon/info.log';
        }else if (SystemEnv::isScriptService()) {
            $logFilePath = LOG_PATH.'/script/info.log';
        }else if (SystemEnv::isCronService()) {
            $logFilePath = LOG_PATH.'/cron/info.log';
        } else {
            $logFilePath = LOG_PATH.'/cli/info.log';
        }
        $logger->setLogFilePath($logFilePath);
        return $logger;
    },

    // 用户行为记录错误日志
    'error_log' => function($name) {
        $logger = new Log($name);
        $logger->setChannel('application');
        if(SystemEnv::isDaemonService()) {
            $logFilePath = LOG_PATH.'/daemon/error.log';
        }else if (SystemEnv::isScriptService()) {
            $logFilePath = LOG_PATH.'/script/error.log';
        }else if (SystemEnv::isCronService()) {
            $logFilePath = LOG_PATH.'/cron/error.log';
        } else {
            $logFilePath = LOG_PATH.'/cli/error.log';
        }
        $logger->setLogFilePath($logFilePath);
        return $logger;
    },

    // 系统捕捉抛出异常错误日志
    'system_error_log' => function($name) {
        $logger = new \Swoolefy\Util\Log($name);
        $logger->setChannel('application');
        if(SystemEnv::isDaemonService()) {
            $logFilePath = LOG_PATH.'/daemon/system_error.log';
        }else if (SystemEnv::isScriptService()) {
            $logFilePath = LOG_PATH.'/script/system_error.log';
        }else if (SystemEnv::isCronService()) {
            $logFilePath = LOG_PATH.'/cron/system_error.log';
        } else {
            $logFilePath = LOG_PATH.'/cli/system_error.log';
        }
        $logger->setLogFilePath($logFilePath);
        return $logger;
    }
    
    // Redis Cache
    'redis' => function() use($dc) {
        $redis = new \Common\Library\Redis\Redis();
        $redis->connect($dc['redis']['host'], $dc['redis']['port']);
        return $redis;
    },
    
    // Predis Cache
    'predis' => function() use($dc) {
        $predis = new \Common\Library\Redis\predis([
            'scheme' => $dc['predis']['scheme'],
            'host'   => $dc['predis']['host'],
            'port'   => $dc['predis']['port'],
        ]);
        return $predis;
    }
    

使用组件

use Swoolefy\Core\Application;

class TestController extends BController {
    /**
    * 控制器
    */
    public function test() {
        // 获取组件,组件就是配置回调中定义的组件
        $redis = Application::getApp()->redis;
        //或者通过get指明组件名获取(推荐)
        // $redis = Application::getApp()->get('redis');

        // swoole hook 特性,这个过程会发生协程调度
        $redis->set('name', swoolefy);

        // predis组件
        $predis = Application::getApp()->predis;
        //或者通过get指明组件名获取(推荐)
        // $predis = Application::getApp()->get('predis');
        
        // 这个过程会发生协程调度
        $predis->set('predis','this is a predis instance');
        $predis->get('predis');
        
        // PDO的mysql实例,这个过程会发生协程调度
        $db = Application::getApp()->db;
        // 或者
        // $mysql = Application::getApp()->get('db');
        // 添加一条数据
        $sql = "INSERT INTO `user` (`username` ,`sex`) VALUES (:username, :sex)"; 
        $numRows = $db->createCommand($sql)->insert([
            ':username'=>'bingcool-test',
            ':sex' => 1
        ]);
        var_dump($numRows)
        
        // DB Query查询
         $db = Application::getApp()->db;
         $db->newQuery()->table('user')->where([
            'user_id' => 10000
         ])->select()
         
         // DB 插入单条数据
         $data = [
            'username'=>'bingcool-test',
            'sex' => 1
         ]
         $db = Application::getApp()->db;
         $db->newQuery()->table('user')->insert($data);
         
         // DB 插入多条数据
         $data = 
         [
            [
                'username'=>'bingcool-test1111',
                'sex' => 1
            ],
            [
                'username'=>'bingcool-test2222',
                'sex' => 1
            ]
         ]
         $db = Application::getApp()->db;
         $db->newQuery()->table('user')->insertAll($data);
         
         
        // 查询
        $result = $db->createCommand('select * from user where id>:id')->queryOne([':id'=>100]);
        var_dump($result);    

        // pg实例    
        $pg = Application::getApp()->get('pg');   
        // 添加一条数据   
        $sql = "INSERT INTO `user` (username ,sex) VALUES (:username, :sex)"; 
        $pg->createCommand($sql)->insert([
            ':username'=>'bingcool-test',
            ':sex' => 1
        ]);
    }
}

默认协议层全局配置文件 Protocol/conf.php

开发者可以根据实际使用适当调整配置项

$dc = \Swoolefy\Core\SystemEnv::loadDcEnv();

<?php

return [
    // 应用层配置
    'app_conf'                 => \Swoolefy\Core\SystemEnv::loadAppConf(), // 应用层配置
    'application_index'        => '',
    'event_handler'            => \Test\Event::class,
    'exception_handler'        => \Test\Exception\ExceptionHandle::class,
    'response_formatter'       => \Swoolefy\Core\ResponseFormatter::class,
    'master_process_name'      => 'php-swoolefy-http-master',
    'manager_process_name'     => 'php-swoolefy-http-manager',
    'worker_process_name'      => 'php-swoolefy-http-worker',
    'www_user'                 => '',
    'host'                     => '0.0.0.0',
    'port'                     => '9501',
    'time_zone'                => 'PRC',
    'swoole_process_mode'      => SWOOLE_PROCESS,
    'include_files'            => [],
    'runtime_enable_coroutine' => true,

    // swoole setting
	'setting' => [
        'admin_server'           => '0.0.0.0:9503',
        'reactor_num'            => 1,
        'worker_num'             => 4,
        'max_request'            => 10000,
        'task_worker_num'        => 2,
        'task_tmpdir'            => '/dev/shm',
        'daemonize'              => 0,
        'dispatch_mode'          => 3,
        'reload_async'           => true,
        'enable_coroutine'       => 1,
        'task_enable_coroutine'  => 1,
        // 压缩
        'http_compression'       => true,
        // $level 压缩等级,范围是 1-9,等级越高压缩后的尺寸越小,但 CPU 消耗更多。默认为 1, 最高为 9
        'http_compression_level' => 1,
        'log_file'               => \Swoolefy\Core\SystemEnv::loadLogFile('/tmp/' . APP_NAME . '/swoole_log.txt'),
        'pid_file'               => \Swoolefy\Core\SystemEnv::loadPidFile('/data/' . APP_NAME . '/log/server.pid'),
	],

    'coroutine_setting' => [
        'max_coroutine' => 50000
    ],

    // 是否内存化线上实时任务
    'enable_table_tick_task' => true,

    // 内存表定义
    'table' => [
        'table_process' => [
             // 内存表建立的行数,取决于建立的process进程数,最小值64
             'size' => 64,
              // 定义字段
              'fields'=> [
                     ['pid','int', 10],
                     ['process_name','string', 56],
                  ]
               ]
     ],

    // 依赖于EnableSysCollector = true,否则设置没有意义,不生效
    'enable_pv_collector'  => false,
    'enable_sys_collector' => true,
    'sys_collector_conf' => [
        'type'           => SWOOLEFY_SYS_COLLECTOR_UDP,
        'host'           => '127.0.0.1',
        'port'           => 9504,
        'from_service'   => 'http-app',
        'target_service' => 'collectorService/system',
        'event'          => 'collect',
        'tick_time'      => 2,
        'callback'       => function () {
            $sysCollector = new \Swoolefy\Core\SysCollector\SysCollector();
            return $sysCollector->test();
        }
    ],

    // 热更新
    'reload_conf'=> [
        'enable_reload'     => false, // 是否启用热文件更新功能       
        'after_seconds'     => 3, // 检测到只要有文件更新,3s内不在检测,等待重启既可     
        'monitor_path'      => APP_PATH, // 开发者自己定义目录
        'reload_file_types' => ['.php', '.html', '.js'],
        'ignore_dirs'       => [],
        'callback'          => function () {}
    ]
];

路由文件(类似laravel路由)

Router/api.php

<?php

use Swoolefy\Http\Route;
use Swoolefy\Http\RequestInput;

// 直接路由-不分组
Route::get('/index/index', [
    'beforeHandle' => function(RequestInput $requestInput) {
        Context::set('name', 'bingcool');
        $name = $requestInput->getPostParams('name');
    },

    // 这里需要替换长对应的控制器命名空间
    'dispatch_route' => [\Test\Controller\IndexController::class, 'index'],

    'afterHandle' => function(RequestInput $requestInput) {

    },
    'afterHandle1' => function(RequestInput $requestInput) {

    },
]);

// 分组路由
Route::group([
    // 路由前缀
    'prefix' => 'api',
    // 路由中间件,多个按顺序执行
    'middleware' => [
        \Test\Middleware\Route\ValidLoginMiddleware::class,
    ]
], function () {

    Route::get('/', [
        // 前置路由,闭包函数形式
        'beforeHandle' => function(RequestInput $requestInput) {
            var_dump('beforeHandle');
        },

        // 前置路由,中间件类形式(推荐)
        'beforeHandle2' => \Test\Middleware\Route\ValidLoginMiddleware::class,

        // 前置路由,中间件数组类形式(推荐)
        'beforeHandle3' => [
            \Test\Middleware\Route\ValidLoginMiddleware::class,
            \Test\Middleware\Route\ValidLoginMiddleware::class,
        ],

        // 控制器action
        'dispatch_route' => [\Test\Controller\IndexController::class, 'index'],

        // 后置路由
        'afterHandle1' => function(RequestInput $requestInput) {
            var_dump('afterHandle');
        },

        // 前置路由,中间件类形式(推荐)
        'afterHandle2' => \Test\Middleware\Route\ValidLoginMiddleware::class,

        // 前置路由,中间件数组类形式(推荐)
        'afterHandle3' => [
            \Test\Middleware\Route\ValidLoginMiddleware::class,
            \Test\Middleware\Route\ValidLoginMiddleware::class
        ],
    ]);
});

数据库操作


$db = Application::getApp()->get('db');
// 插入单条数据
$db->newQuery()->table('tbl_users')->insert([
            'user_name' => '李四-'.rand(1,9999),
            'sex' => 0,
            'birthday' => '1991-07-08',
            'phone' => 12345678
    ]);

// 批量插入
$db->newQuery()->table('tbl_users')->insertAll([
            [
                'user_name' => '李四-'.rand(1,9999),
                'sex' => 0,
                'birthday' => '1991-07-08',
                'phone' => 12345678
            ],
            [
                'user_name' => '李四-'.rand(1,9999),
                'sex' => 0,
                'birthday' => '1991-07-08',
                'phone' => 12345678
            ]
    ]);


// 查询列表
$db->newQuery()->table('tbl_users')->where('id','>', 1)->field(['id', 'user_name'])->limit(0,10)->select();

// 查询单条
$db->newQuery()->table('tbl_users')->where(['id', '=', 100])->field(['id', 'user_name'])->find();

.....还有很多其他链式操作

协程单例,协程并发

// 协程单例使用goApp直接调用创建, 每个协程的DB,redis,kafka,mq的socket对象相互隔离,互不影响,代码通用

goApp(function() {
    $db = Application::getApp()->get('db');
    // 查询列表
    $db->newQuery()->table('tbl_users')->where('id','>', 1)->field(['id', 'user_name'])->limit(0,10)->select();
    // redis
    $redis = Application::getApp()->get('redis');
    $redis->set('name','bingcool')
})



// 协程并发-协程并发迭代数组处理数据(针对数据量大,无需关注返回数据)

$list = [
    [
        'name' => ’name-1'
    ],
    [
        'name' =>  ’name-2'
    ],
    [
        'name' =>  ’name-3'
    ],
    [
        'name' =>  ’name-4'
    ],
    [
        'name' =>  ’name-5'
    ]
];

// 并发2个协程-循环迭代数组-回调函数处理
Parallel::run(2, $list, function ($item) {
    var_dump($item['name']);
}, 0.01);



// 协程并发-协程并发处理并等待结果(针对并发量少,并且需要返回数据的)

$parallel = new Parallel();
$parallel->add(function () {
    sleep(2);
    return "阿里巴巴";
},'ali');

$parallel->add(function () {
    sleep(2);
    return "腾讯";
},'tengxu');

$parallel->add(function () {
    sleep(2);
    return "百度";
},'baidu');

$parallel->add(function () {
    sleep(5);
    return "字节跳动";
},'zijie');

// 并发等待返回数据
$result = $parallel->runWait(10);
array(4) {
  ["ali"]=>
  string(12) "阿里巴巴"
  ["tengxu"]=>
  string(6) "腾讯"
  ["baidu"]=>
  string(6) "百度"
  ["zijie"]=>
  string(12) "字节跳动"
}


License

MIT
Copyright (c) 2017-2024 zengbing huang

Main metrics

Overview
Name With Ownerbingcool/swoolefy
Primary LanguagePHP
Program languagePHP (Language Count: 5)
Platform
License:MIT License
所有者活动
Created At2017-08-07 10:49:21
Pushed At2024-09-30 10:56:25
Last Commit At2024-06-01 17:05:29
Release Count81
Last Release Namev4.9.3 (Posted on )
First Release NameRC1 (Posted on )
用户参与
Stargazers Count513
Watchers Count27
Fork Count78
Commits Count399
Has Issues Enabled
Issues Count9
Issue Open Count2
Pull Requests Count1
Pull Requests Open Count0
Pull Requests Close Count1
项目设置
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private