PHP code example of bingcool / swoolefy

1. Go to this page and download the library: Download bingcool/swoolefy library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

bingcool / swoolefy example snippets

8.1+,swoole > 5.1.x, 推荐 swoole-6.x.x+ 以上最新版本
7.3 ~ php7.4, swoole-4.8.x, 推荐直接swoole-v4.8.13
7.3 ~ php7.4

// 独立物理机或者云主机配置系统环境变量
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



// 在myproject目录下添加cli.php, 这个是启动项目的入口文件

pName);
// 启动目录
defined('START_DIR_ROOT') or define('START_DIR_ROOT', __DIR__);
// 应用父目录
defined('ROOT_PATH') or define('ROOT_PATH',__DIR__);
// 应用目录
defined('APP_PATH') or define('APP_PATH',__DIR__.'/'.$appName);

registerNamespace(APP_PATH);

define('IS_WORKER_SERVICE', 0);
define('IS_DAEMON_SERVICE', 0);
define('IS_SCRIPT_SERVICE', 0);
define('IS_CRON_SERVICE', 0);
define('PHP_BIN_FILE','/usr/bin/php');

define('WORKER_START_SCRIPT_FILE', str_contains($_SERVER['SCRIPT_FILENAME'], $_SERVER['PWD']) ? $_SERVER['SCRIPT_FILENAME'] : $_SERVER['PWD'].'/'.$_SERVER['SCRIPT_FILENAME']);
define('WORKER_SERVICE_NAME', makeServerName($appName));
define('WORKER_PID_FILE_ROOT', '/tmp/workerfy/log/'.WORKER_SERVICE_NAME);
define('SERVER_START_LOG', WORKER_PID_FILE_ROOT.'/start.log');

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

// 启动前处理,比如加载.env
//$beforeFunc = function () {
//    try {
//        \Test\LoadEnv::load('192.168.1.101:8848','swoolefy','test','nacos-test','123456');
//    }catch (\Throwable $exception) {
//
//    }
//};


// 终端启动 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

// 完全重启服务
php cli.php restart App    
或者    
swooole-cli cli.php restart App


// 创建生成Cron定时计划任务服务,默认生成WorkerCron目录

php script.php start App --c=gen:cron:service

// 启动Cron服务, CTRL+C 停止进程

php cron.php start App

// --daemon=1 以守护进程启动

php cron.php start App --daemon=1

// 重启Cron服务

php cron.php restart App



// 停止Cron服务,终端交互询问需要输入`yes` or `no` 再次确认是否需要停止服务

php cron.php stop App

// --force=1 强制停止Cron服务,不询问直接停止服务

php cron.php stop App --force=1


// 创建生成Daemon常驻进程消费服务,默认生成WorkerDaemon目录

php script.php start App --c=gen:daemon:service

// 启动Daemon服务,CTRL+C 停止进程

php daemon.php start App 

// --daemon=1 以守护进程启动

php daemon.php start App --daemon=1

// 重启Daemon服务

php daemon.php restart App




// 停止Daemon服务, 终端交互询问是否需要输入`yes` or `no` 再次确认是否需要停止服务

php daemon.php stop App

// --force=1 强制停止Daemon服务,不询问直接停止服务

php daemon.php stop App --force=1




namespace App\Controller;

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

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

    public function index() {
        // 最简单的协程单例,goApp()即可创建一个协程,在单例中的db,redis等其他注册的组件都是单例的,不同协程单例相互隔离  
        goApp(function() {
            var_dump('this is a coroutine single app test');
        });
        
        Application::getApp()->response->write('<h1>Hello, Welcome to Swoolefy Framework! <h1>');
    }
}




return [

    // db|redis连接池
    '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()
    
    // 其他配置
    ......
]



$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
        ]);
    }
}




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
        ],
    ]);
});


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

$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) "字节跳动"
}




namespace Test\Module\Order\Validation;

use OpenApi\Attributes as OA;
use Test\Module\Swag;

class UserOrderValidation
{
    // Post请求的参数
    #[OA\Post(
        path: '/user/user-order/userList',
        summary:'订单保存',
        description:'保存订单',
        tags: [Swag::MODULE_TAG_ORDER], // 根据Swag.php的注册的tag值来设置,相同的tag的接口将汇集在同一个模块下
        security: [['apiKeyAuth' => []], ['appId' => []]], //指定了在哪些接口上应用SecurityScheme中已经定义的安全方案
        requestBody: new OA\RequestBody(
                    // 整型
                        new OA\Property(property: 'product_num', type: 'integer', description:'产品数量'
                        ),
                        // 数组 phone => [1111, 22222]
                        new OA\Property(property: 'phone', type: 'array', description:'电话', items: new OA\Items(
                            type: 'integer'
                        )),

                        // 一维关联数组(对象) address = ['sheng' => '广东省', 'city' => '深圳市','area'=>'宝安区'],
                        new OA\Property(property: 'address', type: 'object', description:'居住地址',
                            properties:[
                                // sheng
                                new OA\Property(property: 'sheng', type: 'string', description:'省份'
                                ),
                                // city
                                new OA\Property(property: 'city', type: 'string', description:'城市'
                                ),
                                // area
                                new OA\Property(property: 'area', type: 'string', description:'县/区'
                                ),
                            ]
                        ),

                        // 二维关联数组 addressList => [
                        //      ['sheng' => '广东省', 'city' => '深圳市','area'=>'宝安区'],
                        //      ['sheng' => '广东省', 'city' => '深圳市','area'=>'宝安区']
                        //  ]
                        new OA\Property(property: 'addressList', type: 'array', description:'地址列表', items: new OA\Items(
                            type: 'object',
                            properties:[
                                // sheng
                                new OA\Property(property: 'sheng', type: 'string', description:'省份'
                                ),
                                // city
                                new OA\Property(property: 'city', type: 'string', description:'城市'
                                ),
                                // area
                                new OA\Property(property: 'area', type: 'string', description:'县/区'
                                ),
                            ]
                        )),
                    ]
                )
            )
        )
    )]

    #[OA\Response(
        response: 200,
        description: '操作成功'
    )]

    public function userList(): array
    {
        return [
            'rules' => [
                    'name' => '