This commit is contained in:
lq 2024-06-08 15:54:41 +08:00
parent ef6da84deb
commit 154acfa622
72 changed files with 5886 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
/runtime
/.idea
/.vscode
/vendor
*.log
.env
/tests/tmp
/tests/.phpunit.result.cache

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/webman/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

59
README.md Normal file
View File

@ -0,0 +1,59 @@
<div style="padding:18px;max-width: 1024px;margin:0 auto;background-color:#fff;color:#333">
<h1>webman</h1>
基于<a href="https://www.workerman.net" target="__blank">workerman</a>开发的超高性能PHP框架
<h1>学习</h1>
<ul>
<li>
<a href="https://www.workerman.net/webman" target="__blank">主页 / Home page</a>
</li>
<li>
<a href="https://www.workerman.net/doc/webman" target="__blank">文档 / Document</a>
</li>
<li>
<a href="https://www.workerman.net/doc/webman/install.html" target="__blank">安装 / Install</a>
</li>
<li>
<a href="https://www.workerman.net/questions" target="__blank">问答 / Questions</a>
</li>
<li>
<a href="https://www.workerman.net/apps" target="__blank">市场 / Apps</a>
</li>
<li>
<a href="https://www.workerman.net/sponsor" target="__blank">赞助 / Sponsors</a>
</li>
<li>
<a href="https://www.workerman.net/doc/webman/thanks.html" target="__blank">致谢 / Thanks</a>
</li>
</ul>
<div style="float:left;padding-bottom:30px;">
<h1>赞助商</h1>
<h4>特别赞助</h4>
<a href="https://www.crmeb.com/?form=workerman" target="__blank">
<img src="https://www.workerman.net/img/sponsors/6429/20230719111500.svg" width="200">
</a>
<h4>铂金赞助</h4>
<a href="https://www.fadetask.com/?from=workerman" target="__blank"><img src="https://www.workerman.net/img/sponsors/1/20230719084316.png" width="200"></a>
<a href="https://www.yilianyun.net/?from=workerman" target="__blank" style="margin-left:20px;"><img src="https://www.workerman.net/img/sponsors/6218/20230720114049.png" width="200"></a>
<h4>金牌赞助</h4>
</div>
<div style="clear: both">
<h1>LICENSE</h1>
The webman is open-sourced software licensed under the MIT.
</div>
</div>

View File

@ -0,0 +1,26 @@
<?php
namespace app\controller;
use app\service\DepartmentService;
use app\util\Result;
use support\Request;
class DepartmentController
{
/**
* @Inject
* @var Result
*/
private $result;
/**
* @Inject
* @var DepartmentService
*/
private $departmentService;
public function list(Request $request)
{
$info = $this->departmentService->allList();
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace app\controller;
use app\util\Result;
use support\Request;
class FileController
{
/**
* @Inject
* @var Result
*/
private $result;
public function upload(Request $request)
{
$file = $request->file('file');
if ($file && $file->isValid()) {
$name = md5(uniqid());
$file->move(public_path() . '/upload/' . $name . '.' . $file->getUploadExtension());
$data = [
'id' => $name,
'src' => $request->header('x-forwarded-proto') ?? 'http' . '://' . $request->host() . '/upload/' . $name . '.' . $file->getUploadExtension(),
'fileName' => $name
];
return json(retData($this->result::SUCCESS, 'upload success', $data));
}
return json(retData($this->result::ERROR, 'file not found'));
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace app\controller;
use support\Request;
use think\facade\Db;
class IndexController
{
protected $noNeedLogin = ['json'];
public function index(Request $request)
{
static $readme;
if (!$readme) {
$readme = file_get_contents(base_path('README.md'));
}
return $readme;
}
public function view(Request $request)
{
return view('index/view', ['name' => 'webman']);
}
public function json(Request $request)
{
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace app\controller;
use app\service\MenuService;
use app\util\Result;
use support\Request;
use DI\Annotation\Inject;
class MenuController
{
/**
* @Inject
* @var Result
*/
private $result;
/**
* @Inject
* @var MenuService
*/
private $menuService;
public function getMenu(Request $request)
{
$info = $this->menuService->getList();
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
public function my(Request $request)
{
$info = $this->menuService->getList();
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
public function list(Request $request)
{
$info = $this->menuService->getList($request->uid);
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace app\controller;
use app\service\RoleService;
use app\util\Result;
use support\Request;
class RoleController
{
/**
* @Inject
* @var RoleService
*/
private $roleService;
/**
* @Inject
* @var Result
*/
private $result;
/**
* User: 高军
* Date: 2024/5/8
* QQ: 2926804347
* @param Request $request
* @return \support\Response
* Notes:
*/
public function list(Request $request)
{
$info = $this->roleService->allList();
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
/**
* User: 高军
* Date: 2024/5/8
* QQ: 2926804347
* @param array $param
* @return array
* Notes:
*/
public function getList(array $param):array
{
}
public function save(Request $request)
{
$param = $request->post();
$info = $this->roleService->save($param);
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace app\controller;
use app\service\RoleMenuService;
use app\util\Result;
use support\Request;
class RoleMenuController
{
/**
* @Inject
* @var Result
*/
private $result;
/**
* @Inject
* @var RoleMenuService
*/
private $roleMenuService;
protected $noNeedLogin = [];
public function save(Request $request)
{
$param = $request->post();
$info = $this->roleMenuService->save($param);
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
public function test()
{
return json(retData(0, 'ahahah'));
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace app\controller;
use app\service\UserService;
use app\util\Result;
use support\Request;
class UserController
{
/**
* @Inject
* @var Result
*/
private $result;
/**
* @Inject
* @var UserService
*/
private $userService;
protected $noNeedLogin = ['token', 'test'];
public function token(Request $request)
{
$param = $request->post();
if (empty($param['username']) || empty($param['password'])) return json(retData($this->result::ERROR, '账号密码不能为空'));
$info = $this->userService->login($param['username'], $param['password']);
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
public function list(Request $request)
{
$param = $request->get();
$info = $this->userService->getList($param);
if ($info['result']) return json(retData($this->result::SUCCESS, $info['msg'], $info['data']));
return json(retData($this->result::ERROR, $info['msg']));
}
public function test()
{
return json(retData(0, 'ahahah'));
}
}

14
app/functions.php Normal file
View File

@ -0,0 +1,14 @@
<?php
/**
* Here is your custom functions.
*/
function show(bool $bool, string $msg, array $data = []):array
{
return ['result' => $bool, 'msg' => $msg, 'data' => $data];
}
function retData(int $code, string $msg, array $data = []):array
{
return ['code' => $code, 'msg' => $msg, 'data' => $data];
}

View File

@ -0,0 +1,25 @@
<?php
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class AccessControl implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
// 如果是options请求则返回一个空响应否则继续向洋葱芯穿越并得到一个响应
$response = $request->method() == 'OPTIONS' ? response('') : $handler($request);
// 给响应添加跨域相关的http头
$response->withHeaders([
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Origin' => $request->header('origin', '*'),
'Access-Control-Allow-Methods' => $request->header('access-control-request-method', '*'),
'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'),
]);
return $response;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace app\middleware;
use ReflectionClass;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
class AuthCheck implements MiddlewareInterface
{
public function process(Request $request, callable $handler) : Response
{
// 通过反射获取控制器哪些方法不需要登录
$controller = new ReflectionClass($request->controller);
$noNeedLogin = $controller->getDefaultProperties()['noNeedLogin'] ?? [];
// 访问的方法需要登录
if (!in_array($request->action, $noNeedLogin)) {
try {
$uid = \Tinywan\Jwt\JwtToken::getCurrentId();
if ($uid != 0) {
$request->uid = $uid;
return $handler($request);
}
}catch (\Exception $e) {
return Response(json_encode(['code' => 0, 'msg' => $e->getMessage()], 256));
}
}else{
return $handler($request);
}
// 不需要登录,请求继续向洋葱芯穿越
return $handler($request);
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
/**
* Class StaticFile
* @package app\middleware
*/
class StaticFile implements MiddlewareInterface
{
public function process(Request $request, callable $next): Response
{
// Access to files beginning with. Is prohibited
if (strpos($request->path(), '/.') !== false) {
return response('<h1>403 forbidden</h1>', 403);
}
/** @var Response $response */
$response = $next($request);
// Add cross domain HTTP header
/*$response->withHeaders([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
]);*/
return $response;
}
}

18
app/model/Department.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace app\model;
use think\Model;
class Department extends Model
{
protected $table = 'eb_department';
protected $primaryKey = 'id';
public $timestamps = false;
public function allList():array
{
$info = $this->field('id, name, parent_id')->select()->toArray();
if ($info) return show(true, '获取成功', ['rows' => $info]);
return show(false, '暂无查询到数据');
}
}

21
app/model/Menu.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace app\model;
use think\Model;
class Menu extends Model
{
/**
* @var string
*/
protected $table = 'eb_menu';
protected $primaryKey = 'id';
public $timestamps = false;
public function getList()
{
$info = $this->field('id, parent_id, title, path, name, type, icon, component')->where('delete_time', 0)->where('type', 'menu')->select()->toArray();
if ($info) return show(true, '获取成功', $info);
return show(false, '暂无查询到的数据');
}
}

39
app/model/Role.php Normal file
View File

@ -0,0 +1,39 @@
<?php
namespace app\model;
use think\Model;
class Role extends Model
{
protected $table = 'eb_role';
protected $primaryKey = 'id';
public $timestamps = false;
/**
* User: 高军
* Date: 2024/5/8
* QQ: 2926804347
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* Notes:
*/
public function allList():array
{
$info = $this->select()->toArray();
if ($info) return show(true, '获取成功', ['rows' => $info]);
return show(false, '暂无数据');
}
public function saveData(array $param):array
{
if (!empty($param['id'])) {
$info = $this->update($param);
}else{
$param['create_time'] = date('Y-m-d H:i:s', time());
$info = $this::create($param);
}
if ($info) return show(true, '保存成功');
return show(false, '保存失败,请重试');
}
}

25
app/model/RoleMenu.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace app\model;
use think\Model;
class RoleMenu extends Model
{
protected $table = 'eb_role_menu';
protected $primaryKey = 'id';
public $timestamps = false;
public function saveData(array $param):array
{
$info = $this->saveAll($param);
if ($info) return show(true, '保存成功');
return show(false, '保存失败,请重试');
}
public function delByRoleId(int $roleId):array
{
$info = $this->where('role_id', $roleId)->where('delete_time', 0)->update(['delete_time' => time()]);
if ($info) return show(true, '保存成功');
return show(false, '保存失败,请重试');
}
}

29
app/model/Test.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace app\model;
use support\Model;
class Test extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'test';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
}

49
app/model/User.php Normal file
View File

@ -0,0 +1,49 @@
<?php
namespace app\model;
use think\Model;
class User extends Model
{
protected $table = 'eb_user';
protected $primaryKey = 'id';
public $timestamps = false;
/**
* User: 高军
* Date: 2024/5/8
* QQ: 2926804347
* @param string $userName
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* Notes:
*/
public function getInfoByUserName(string $userName):array
{
$info = $this->where('delete_time', 0)->where('user_name', $userName)->find();
if ($info) return show(true, '获取成功', $info->toArray());
return show(false, '暂无查询到的数据');
}
/**
* User: 高军
* Date: 2024/5/8
* QQ: 2926804347
* @param array $param
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* Notes:
*/
public function getList(array $param):array
{
$offset = ($param['page'] - 1) * $param['pageSize'];
$data = $this->limit($offset, $param['pageSize'])->select()->toArray();
$count = $this->count();
if ($data) return show(true, '获取成功', ['rows' => $data, 'total' => $count]);
return show(false, '暂无数据');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace app\service;
use app\model\Department;
class DepartmentService
{
/**
* @Inject
* @var Department
*/
private $department;
public function allList():array
{
return $this->department->allList();
}
}

110
app/service/MenuService.php Normal file
View File

@ -0,0 +1,110 @@
<?php
namespace app\service;
use app\model\Menu;
class MenuService
{
/**
* @Inject
* @var Menu
*/
private $menu;
public function getList(int $userId = 0)
{
$info = $this->menu->getList();
if ($info['result']) {
$data = $this->package($info['data']);
$arr = $this->buildTree($data);
$tempArr['menu'] = $arr;
}
$tempArr['permissions'] = ["list.add", "list.delete", "user.edit", "user.delete"];
$tempArr['dashboardGrid'] = ["welcome", "ver", "time", "progress", "echarts", "about"];
$info['data'] = $tempArr;
return $info;
}
public function myMenu(int $userId = 0):array {
$info = $this->menu->getList();
return $info;
$arr = [];
if ($info['result']) {
$data = $this->package($info['data']);
$arr = $this->buildTree($data);
}
$info['data'] = $arr;
return $info;
}
public function package(array $data): array
{
$arr = [];
foreach ($data as $v) {
$arr[] = [
'id' => $v['id'],
'parent_id' => $v['parent_id'],
'name' => $v['name'],
'path' => $v['path'],
'meta' => [
'title' => $v['title'],
'icon' => $v['icon'],
'type' => 'menu'
],
'component' => $v['component']
];
}
return $arr;
}
public function buildTree(array $array, int $parentId = 0): array
{
$tree = [];
foreach ($array as $element) {
if ($element['parent_id'] == $parentId) {
$children = $this->buildTree($array, $element['id']);
if ($children) {
$element['children'] = $children;
}
$tree[] = $element;
}
}
return $tree;
}
public function add($data)
{
$data = json_decode($data, true);
foreach ($data as $k => $v) {
$arr = [
'parent_id' => 0,
'name' => $v['name'],
'path' => $v['path'],
'title' => $v['meta']['title'],
'icon' => $v['meta']['icon'],
'type' => 'menu',
'component' => $v['component'] ?? ''
];
$id = $this->menu->insertGetId($arr);
if (isset($v['children'])) $this->buld($id, $v['children']);
}
}
public function buld($pid, $data)
{
foreach ($data as $v) {
$arr = [
'parent_id' => $pid,
'name' => $v['name'],
'path' => $v['path'],
'title' => $v['meta']['title'],
'icon' => $v['meta']['icon'] ?? '',
'type' => 'menu',
'component' => $v['component'] ?? ''
];
$id = $this->menu->insertGetId($arr);
if (isset($v['children'])) $this->buld($id, $v['children']);
}
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace app\service;
use app\model\RoleMenu;
class RoleMenuService
{
/**
* @Inject
* @var RoleMenu
*/
private $roleMenu;
public function save(array $param): array
{
$arr = [];
foreach ($param['menu_ids'] as $v) {
$arr[] = [
'role_id' => $param['role_id'],
'menu_id' => $v,
'create_time' => time()
];
}
$this->roleMenu->delByRoleId($param['role_id']);
return $this->roleMenu->saveData($arr);
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace app\service;
use app\model\Role;
class RoleService
{
/**
* @Inject
* @var Role
*/
private $role;
public function allList():array
{
return $this->role->allList();
}
public function save(array $param):array
{
return $this->role->saveData($param);
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace app\service;
use app\model\User;
class UserService
{
/**
* @Inject
* @var User
*/
private $user;
/**
* User: 高军
* Date: 2024/5/8
* QQ: 2926804347
* @param string $userName
* @param string $pwd
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* Notes:
*/
public function login(string $userName, string $pwd): array
{
$userInfo = $this->user->getInfoByUserName($userName);
if (!$userInfo['result']) return $userInfo;
if (!password_verify($pwd, $userInfo['data']['pass_word'])) return show(false, '用户名或密码错误');
$tokenData = [
'id' => $userInfo['data']['id'],
'name' => $userInfo['data']['nickname'],
'email' => $userInfo['data']['email']
];
$token = \Tinywan\Jwt\JwtToken::generateToken($tokenData);
unset($userInfo['data']['pass_word']);
$data = [
'token' => $token,
'userInfo' => $userInfo['data']
];
return show(true, '登录成功', $data);
}
/**
* User: 高军
* Date: 2024/5/8
* QQ: 2926804347
* @param array $param
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* Notes:
*/
public function getList(array $param):array
{
return $this->user->getList($param);
}
}

8
app/util/Result.php Normal file
View File

@ -0,0 +1,8 @@
<?php
namespace app\util;
class Result
{
const SUCCESS = 200;
const ERROR = 0;
}

14
app/view/index/view.html Normal file
View File

@ -0,0 +1,14 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/favicon.ico"/>
<title>webman</title>
</head>
<body>
hello <?=htmlspecialchars($name)?>
</body>
</html>

BIN
build/php8.2.micro.sfx Normal file

Binary file not shown.

BIN
build/php8.2.micro.sfx.zip Normal file

Binary file not shown.

BIN
build/webman.phar Normal file

Binary file not shown.

63
composer.json Normal file
View File

@ -0,0 +1,63 @@
{
"name": "workerman/webman",
"type": "project",
"keywords": [
"high performance",
"http service"
],
"homepage": "https://www.workerman.net",
"license": "MIT",
"description": "High performance HTTP Service Framework.",
"authors": [
{
"name": "walkor",
"email": "walkor@workerman.net",
"homepage": "https://www.workerman.net",
"role": "Developer"
}
],
"support": {
"email": "walkor@workerman.net",
"issues": "https://github.com/walkor/webman/issues",
"forum": "https://wenda.workerman.net/",
"wiki": "https://workerman.net/doc/webman",
"source": "https://github.com/walkor/webman"
},
"require": {
"php": ">=7.2",
"workerman/webman-framework": "^1.5.0",
"monolog/monolog": "^2.0",
"webman/think-orm": "^1.1",
"psr/container": "1.1.1",
"php-di/php-di": "6",
"doctrine/annotations": "1.14",
"tinywan/jwt": "^1.9",
"webman/console": "^1.3",
"vlucas/phpdotenv": "^5.6"
},
"suggest": {
"ext-event": "For better performance. "
},
"autoload": {
"psr-4": {
"": "./",
"app\\": "./app",
"App\\": "./app",
"app\\View\\Components\\": "./app/view/components"
},
"files": [
"./support/helpers.php"
]
},
"scripts": {
"post-package-install": [
"support\\Plugin::install"
],
"post-package-update": [
"support\\Plugin::install"
],
"pre-package-uninstall": [
"support\\Plugin::uninstall"
]
}
}

2383
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

26
config/app.php Normal file
View File

@ -0,0 +1,26 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use support\Request;
return [
'debug' => true,
'error_reporting' => E_ALL,
'default_timezone' => 'Asia/Shanghai',
'request_class' => Request::class,
'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public',
'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime',
'controller_suffix' => 'Controller',
'controller_reuse' => false,
];

21
config/autoload.php Normal file
View File

@ -0,0 +1,21 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'files' => [
base_path() . '/app/functions.php',
base_path() . '/support/Request.php',
base_path() . '/support/Response.php',
]
];

19
config/bootstrap.php Normal file
View File

@ -0,0 +1,19 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
support\bootstrap\Session::class,
support\bootstrap\LaravelDb::class,
Webman\ThinkOrm\ThinkOrm::class,
];

19
config/container.php Normal file
View File

@ -0,0 +1,19 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
$builder = new \DI\ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
$builder->useAnnotations(true);
return $builder->build();
//return new Webman\Container;

15
config/database.php Normal file
View File

@ -0,0 +1,15 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [];

15
config/dependence.php Normal file
View File

@ -0,0 +1,15 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [];

17
config/exception.php Normal file
View File

@ -0,0 +1,17 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'' => support\exception\Handler::class,
];

32
config/log.php Normal file
View File

@ -0,0 +1,32 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'default' => [
'handlers' => [
[
'class' => Monolog\Handler\RotatingFileHandler::class,
'constructor' => [
runtime_path() . '/logs/webman.log',
7, //$maxFiles
Monolog\Logger::DEBUG,
],
'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [null, 'Y-m-d H:i:s', true],
],
]
],
],
];

20
config/middleware.php Normal file
View File

@ -0,0 +1,20 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'' => [
\app\middleware\AccessControl::class,
\app\middleware\AuthCheck::class
]
];

View File

@ -0,0 +1,83 @@
<?php
return [
'enable' => true,
'jwt' => [
/** 算法类型 HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、Ed25519 */
'algorithms' => 'HS256',
/** access令牌秘钥 */
'access_secret_key' => '2022d3d3LmJq',
/** access令牌过期时间单位秒。默认 2 小时 */
'access_exp' => 7200,
/** refresh令牌秘钥 */
'refresh_secret_key' => '2022KTxigxc9o50c',
/** refresh令牌过期时间单位秒。默认 7 天 */
'refresh_exp' => 604800,
/** refresh 令牌是否禁用,默认不禁用 false */
'refresh_disable' => false,
/** 令牌签发者 */
'iss' => 'webman.tinywan.cn',
/** 某个时间点后才能访问单位秒。30 表示当前时间30秒后才能使用 */
'nbf' => 0,
/** 时钟偏差冗余时间,单位秒。建议这个余地应该不大于几分钟 */
'leeway' => 60,
/** 是否允许单设备登录,默认不允许 false */
'is_single_device' => false,
/** 缓存令牌时间,单位:秒。默认 7 天 */
'cache_token_ttl' => 604800,
/** 缓存令牌前缀,默认 JWT:TOKEN: */
'cache_token_pre' => 'JWT:TOKEN:',
/** 缓存刷新令牌前缀,默认 JWT:REFRESH_TOKEN: */
'cache_refresh_token_pre' => 'JWT:REFRESH_TOKEN:',
/** 用户信息模型 */
'user_model' => function ($uid) {
return [];
},
/** 是否支持 get 请求获取令牌 */
'is_support_get_token' => false,
/** GET 请求获取令牌请求key */
'is_support_get_token_key' => 'authorization',
/** access令牌私钥 */
'access_private_key' => <<<EOD
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
EOD,
/** access令牌公钥 */
'access_public_key' => <<<EOD
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
EOD,
/** refresh令牌私钥 */
'refresh_private_key' => <<<EOD
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
EOD,
/** refresh令牌公钥 */
'refresh_public_key' => <<<EOD
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
EOD,
],
];

View File

@ -0,0 +1,24 @@
<?php
return [
'enable' => true,
'build_dir' => BASE_PATH . DIRECTORY_SEPARATOR . 'build',
'phar_filename' => 'webman.phar',
'bin_filename' => 'webman.bin',
'signature_algorithm'=> Phar::SHA256, //set the signature algorithm for a phar and apply it. The signature algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512, or Phar::OPENSSL.
'private_key_file' => '', // The file path for certificate or OpenSSL private key file.
'exclude_pattern' => '#^(?!.*(composer.json|/.github/|/.idea/|/.git/|/.setting/|/runtime/|/vendor-bin/|/build/|/vendor/webman/admin/))(.*)$#',
'exclude_files' => [
'.env', 'LICENSE', 'composer.json', 'composer.lock', 'start.php', 'webman.phar', 'webman.bin'
],
'custom_ini' => '
memory_limit = 256M
',
];

42
config/process.php Normal file
View File

@ -0,0 +1,42 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
global $argv;
return [
// File update detection and automatic reload
'monitor' => [
'handler' => process\Monitor::class,
'reloadable' => false,
'constructor' => [
// Monitor these directories
'monitorDir' => array_merge([
app_path(),
config_path(),
base_path() . '/process',
base_path() . '/support',
base_path() . '/resource',
base_path() . '/.env',
], glob(base_path() . '/plugin/*/app'), glob(base_path() . '/plugin/*/config'), glob(base_path() . '/plugin/*/api')),
// Files with these suffixes will be monitored
'monitorExtensions' => [
'php', 'html', 'htm', 'env'
],
'options' => [
'enable_file_monitor' => !in_array('-d', $argv) && DIRECTORY_SEPARATOR === '/',
'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/',
]
]
]
];

22
config/redis.php Normal file
View File

@ -0,0 +1,22 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'default' => [
'host' => '127.0.0.1',
'password' => null,
'port' => 6379,
'database' => 0,
],
];

21
config/route.php Normal file
View File

@ -0,0 +1,21 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use Webman\Route;

31
config/server.php Normal file
View File

@ -0,0 +1,31 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'listen' => 'http://0.0.0.0:8787',
'transport' => 'tcp',
'context' => [],
'name' => 'webman',
'count' => cpu_count() * 4,
'user' => '',
'group' => '',
'reusePort' => false,
'event_loop' => '',
'stop_timeout' => 2,
'pid_file' => runtime_path() . '/webman.pid',
'status_file' => runtime_path() . '/webman.status',
'stdout_file' => runtime_path() . '/logs/stdout.log',
'log_file' => runtime_path() . '/logs/workerman.log',
'max_package_size' => 10 * 1024 * 1024
];

65
config/session.php Normal file
View File

@ -0,0 +1,65 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use Webman\Session\FileSessionHandler;
use Webman\Session\RedisSessionHandler;
use Webman\Session\RedisClusterSessionHandler;
return [
'type' => 'file', // or redis or redis_cluster
'handler' => FileSessionHandler::class,
'config' => [
'file' => [
'save_path' => runtime_path() . '/sessions',
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'auth' => '',
'timeout' => 2,
'database' => '',
'prefix' => 'redis_session_',
],
'redis_cluster' => [
'host' => ['127.0.0.1:7000', '127.0.0.1:7001', '127.0.0.1:7001'],
'timeout' => 2,
'auth' => '',
'prefix' => 'redis_session_',
]
],
'session_name' => 'PHPSID',
'auto_update_timestamp' => false,
'lifetime' => 7*24*60*60,
'cookie_lifetime' => 365*24*60*60,
'cookie_path' => '/',
'domain' => '',
'http_only' => true,
'secure' => false,
'same_site' => '',
'gc_probability' => [1, 1000],
];

23
config/static.php Normal file
View File

@ -0,0 +1,23 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* Static file settings
*/
return [
'enable' => true,
'middleware' => [ // Static file Middleware
//app\middleware\StaticFile::class,
],
];

36
config/thinkorm.php Normal file
View File

@ -0,0 +1,36 @@
<?php
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => getenv('DB_HOST'),
// 数据库名
'database' => getenv('DB_NAME'),
// 数据库用户名
'username' => getenv('DB_USER'),
// 数据库密码
'password' => getenv('DB_PASSWORD'),
// 数据库连接端口
'hostport' => getenv('DB_PORT'),
// 数据库连接参数
'params' => [
// 连接超时3秒
\PDO::ATTR_TIMEOUT => 3,
],
// 数据库编码默认采用utf8
'charset' => 'utf8',
// 数据库表前缀
'prefix' => '',
// 断线重连
'break_reconnect' => true,
// 关闭SQL监听日志
'trigger_sql' => false,
// 自定义分页类
'bootstrap' => ''
],
],
];

25
config/translation.php Normal file
View File

@ -0,0 +1,25 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* Multilingual configuration
*/
return [
// Default language
'locale' => 'zh_CN',
// Fallback language
'fallback_locale' => ['zh_CN', 'en'],
// Folder where language files are stored
'path' => base_path() . '/resource/translations',
];

22
config/view.php Normal file
View File

@ -0,0 +1,22 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use support\view\Raw;
use support\view\Twig;
use support\view\Blade;
use support\view\ThinkPHP;
return [
'handler' => Raw::class
];

803
menu.json Normal file
View File

@ -0,0 +1,803 @@
[
{
"name": "home",
"path": "/home",
"meta": {
"title": "首页",
"icon": "el-icon-eleme-filled",
"type": "menu"
},
"children": [
{
"name": "dashboard",
"path": "/dashboard",
"meta": {
"title": "控制台",
"icon": "el-icon-menu",
"affix": true
},
"component": "home"
},
{
"name": "userCenter",
"path": "/usercenter",
"meta": {
"title": "帐号信息",
"icon": "el-icon-user",
"tag": "NEW"
},
"component": "userCenter"
}
]
},
{
"name": "vab",
"path": "/vab",
"meta": {
"title": "组件",
"icon": "el-icon-takeaway-box",
"type": "menu"
},
"children": [
{
"path": "/vab/mini",
"name": "minivab",
"meta": {
"title": "原子组件",
"icon": "el-icon-magic-stick",
"type": "menu"
},
"component": "vab/mini"
},
{
"path": "/vab/iconfont",
"name": "iconfont",
"meta": {
"title": "扩展图标",
"icon": "el-icon-orange",
"type": "menu"
},
"component": "vab/iconfont"
},
{
"path": "/vab/data",
"name": "vabdata",
"meta": {
"title": "Data 数据展示",
"icon": "el-icon-histogram",
"type": "menu"
},
"children": [
{
"path": "/vab/chart",
"name": "chart",
"meta": {
"title": "图表 Echarts",
"type": "menu"
},
"component": "vab/chart"
},
{
"path": "/vab/statistic",
"name": "statistic",
"meta": {
"title": "统计数值",
"type": "menu"
},
"component": "vab/statistic"
},
{
"path": "/vab/video",
"name": "scvideo",
"meta": {
"title": "视频播放器",
"type": "menu"
},
"component": "vab/video"
},
{
"path": "/vab/qrcode",
"name": "qrcode",
"meta": {
"title": "二维码",
"type": "menu"
},
"component": "vab/qrcode"
}
]
},
{
"path": "/vab/form",
"name": "vabform",
"meta": {
"title": "Form 数据录入",
"icon": "el-icon-edit",
"type": "menu"
},
"children": [
{
"path": "/vab/tableselect",
"name": "tableselect",
"meta": {
"title": "表格选择器",
"type": "menu"
},
"component": "vab/tableselect"
},
{
"path": "/vab/formtable",
"name": "formtable",
"meta": {
"title": "表单表格",
"type": "menu"
},
"component": "vab/formtable"
},
{
"path": "/vab/selectFilter",
"name": "selectFilter",
"meta": {
"title": "分类筛选器",
"type": "menu"
},
"component": "vab/selectFilter"
},
{
"path": "/vab/filterbar",
"name": "filterBar",
"meta": {
"title": "过滤器v2",
"type": "menu"
},
"component": "vab/filterBar"
},
{
"path": "/vab/upload",
"name": "upload",
"meta": {
"title": "上传",
"type": "menu"
},
"component": "vab/upload"
},
{
"path": "/vab/select",
"name": "scselect",
"meta": {
"title": "异步选择器",
"type": "menu"
},
"component": "vab/select"
},
{
"path": "/vab/iconselect",
"name": "iconSelect",
"meta": {
"title": "图标选择器",
"type": "menu"
},
"component": "vab/iconselect"
},
{
"path": "/vab/cron",
"name": "cron",
"meta": {
"title": "Cron规则生成器",
"type": "menu"
},
"component": "vab/cron"
},
{
"path": "/vab/editor",
"name": "editor",
"meta": {
"title": "富文本编辑器",
"type": "menu"
},
"component": "vab/editor"
},
{
"path": "/vab/codeeditor",
"name": "codeeditor",
"meta": {
"title": "代码编辑器",
"type": "menu"
},
"component": "vab/codeeditor"
}
]
},
{
"path": "/vab/feedback",
"name": "vabfeedback",
"meta": {
"title": "Feedback 反馈",
"icon": "el-icon-mouse",
"type": "menu"
},
"children": [
{
"path": "/vab/drag",
"name": "drag",
"meta": {
"title": "拖拽排序",
"type": "menu"
},
"component": "vab/drag"
},
{
"path": "/vab/contextmenu",
"name": "contextmenu",
"meta": {
"title": "右键菜单",
"type": "menu"
},
"component": "vab/contextmenu"
},
{
"path": "/vab/cropper",
"name": "cropper",
"meta": {
"title": "图像剪裁",
"type": "menu"
},
"component": "vab/cropper"
},
{
"path": "/vab/fileselect",
"name": "fileselect",
"meta": {
"title": "资源库选择器(弃用)",
"type": "menu"
},
"component": "vab/fileselect"
},
{
"path": "/vab/dialog",
"name": "dialogExtend",
"meta": {
"title": "弹窗扩展",
"type": "menu"
},
"component": "vab/dialog"
}
]
},
{
"path": "/vab/others",
"name": "vabothers",
"meta": {
"title": "Others 其他",
"icon": "el-icon-more-filled",
"type": "menu"
},
"children": [
{
"path": "/vab/print",
"name": "print",
"meta": {
"title": "打印",
"type": "menu"
},
"component": "vab/print"
},
{
"path": "/vab/watermark",
"name": "watermark",
"meta": {
"title": "水印",
"type": "menu"
},
"component": "vab/watermark"
},
{
"path": "/vab/importexport",
"name": "importexport",
"meta": {
"title": "文件导出导入",
"type": "menu"
},
"component": "vab/importexport"
}
]
},
{
"path": "/vab/list",
"name": "list",
"meta": {
"title": "Table 数据列表",
"icon": "el-icon-fold",
"type": "menu"
},
"children": [
{
"path": "/vab/table/base",
"name": "tableBase",
"meta": {
"title": "基础数据列表",
"type": "menu"
},
"component": "vab/table/base"
},
{
"path": "/vab/table/thead",
"name": "tableThead",
"meta": {
"title": "多级表头",
"type": "menu"
},
"component": "vab/table/thead"
},
{
"path": "/vab/table/column",
"name": "tableCustomColumn",
"meta": {
"title": "动态列",
"type": "menu"
},
"component": "vab/table/column"
},
{
"path": "/vab/table/remote",
"name": "tableRemote",
"meta": {
"title": "远程排序过滤",
"type": "menu"
},
"component": "vab/table/remote"
}
]
},
{
"path": "/vab/workflow",
"name": "workflow",
"meta": {
"title": "工作流设计器",
"icon": "el-icon-share",
"type": "menu"
},
"component": "vab/workflow"
},
{
"path": "/vab/formrender",
"name": "formRender",
"meta": {
"title": "动态表单(Beta)",
"icon": "el-icon-message-box",
"type": "menu"
},
"component": "vab/form"
}
]
},
{
"name": "template",
"path": "/template",
"meta": {
"title": "模板",
"icon": "el-icon-files",
"type": "menu"
},
"children": [
{
"path": "/template/layout",
"name": "layoutTemplate",
"meta": {
"title": "布局",
"icon": "el-icon-grid",
"type": "menu"
},
"children": [
{
"path": "/template/layout/blank",
"name": "blank",
"meta": {
"title": "空白模板",
"type": "menu"
},
"component": "template/layout/blank"
},
{
"path": "/template/layout/layoutTCB",
"name": "layoutTCB",
"meta": {
"title": "上中下布局",
"type": "menu"
},
"component": "template/layout/layoutTCB"
},
{
"path": "/template/layout/layoutLCR",
"name": "layoutLCR",
"meta": {
"title": "左中右布局",
"type": "menu"
},
"component": "template/layout/layoutLCR"
}
]
},
{
"path": "/template/list",
"name": "list",
"meta": {
"title": "列表",
"icon": "el-icon-document",
"type": "menu"
},
"children": [
{
"path": "/template/list/crud",
"name": "listCrud",
"meta": {
"title": "CRUD",
"type": "menu"
},
"component": "template/list/crud",
"children": [
{
"path": "/template/list/crud/detail/:id?",
"name": "listCrud-detail",
"meta": {
"title": "新增/编辑",
"hidden": true,
"active": "/template/list/crud",
"type": "menu"
},
"component": "template/list/crud/detail"
}
]
},
{
"path": "/template/list/tree",
"name": "listTree",
"meta": {
"title": "左树右表",
"type": "menu"
},
"component": "template/list/tree"
},
{
"path": "/template/list/tab",
"name": "listTab",
"meta": {
"title": "分类表格",
"type": "menu"
},
"component": "template/list/tab"
},
{
"path": "/template/list/son",
"name": "listSon",
"meta": {
"title": "子母表",
"type": "menu"
},
"component": "template/list/son"
},
{
"path": "/template/list/widthlist",
"name": "widthlist",
"meta": {
"title": "定宽列表",
"type": "menu"
},
"component": "template/list/width"
}
]
},
{
"path": "/template/other",
"name": "other",
"meta": {
"title": "其他",
"icon": "el-icon-folder",
"type": "menu"
},
"children": [
{
"path": "/template/other/stepform",
"name": "stepform",
"meta": {
"title": "分步表单",
"type": "menu"
},
"component": "template/other/stepform"
}
]
}
]
},
{
"name": "other",
"path": "/other",
"meta": {
"title": "其他",
"icon": "el-icon-more-filled",
"type": "menu"
},
"children": [
{
"path": "/other/directive",
"name": "directive",
"meta": {
"title": "指令",
"icon": "el-icon-price-tag",
"type": "menu"
},
"component": "other/directive"
},
{
"path": "/other/viewTags",
"name": "viewTags",
"meta": {
"title": "标签操作",
"icon": "el-icon-files",
"type": "menu"
},
"component": "other/viewTags",
"children": [
{
"path": "/other/fullpage",
"name": "fullpage",
"meta": {
"title": "整页路由",
"icon": "el-icon-monitor",
"fullpage": true,
"hidden": true,
"type": "menu"
},
"component": "other/fullpage"
}
]
},
{
"path": "/other/verificate",
"name": "verificate",
"meta": {
"title": "表单验证",
"icon": "el-icon-open",
"type": "menu"
},
"component": "other/verificate"
},
{
"path": "/other/loadJS",
"name": "loadJS",
"meta": {
"title": "异步加载JS",
"icon": "el-icon-location-information",
"type": "menu"
},
"component": "other/loadJS"
},
{
"path": "/link",
"name": "link",
"meta": {
"title": "外部链接",
"icon": "el-icon-link",
"type": "menu"
},
"children": [
{
"path": "https://baidu.com",
"name": "百度",
"meta": {
"title": "百度",
"type": "link"
}
},
{
"path": "https://www.google.cn",
"name": "谷歌",
"meta": {
"title": "谷歌",
"type": "link"
}
}
]
},
{
"path": "/iframe",
"name": "Iframe",
"meta": {
"title": "Iframe",
"icon": "el-icon-position",
"type": "menu"
},
"children": [
{
"path": "https://v3.cn.vuejs.org",
"name": "vue3",
"meta": {
"title": "VUE 3",
"type": "iframe"
}
},
{
"path": "https://element-plus.gitee.io",
"name": "elementplus",
"meta": {
"title": "Element Plus",
"type": "iframe"
}
},
{
"path": "https://lolicode.gitee.io/scui-doc",
"name": "scuidoc",
"meta": {
"title": "SCUI文档",
"type": "iframe"
}
}
]
}
]
},
{
"name": "test",
"path": "/test",
"meta": {
"title": "实验室",
"icon": "el-icon-mouse",
"type": "menu"
},
"children": [
{
"path": "/test/autocode",
"name": "autocode",
"meta": {
"title": "代码生成器",
"icon": "sc-icon-code",
"type": "menu"
},
"component": "test/autocode/index",
"children": [
{
"path": "/test/autocode/table",
"name": "autocode-table",
"meta": {
"title": "CRUD代码生成",
"hidden": true,
"active": "/test/autocode",
"type": "menu"
},
"component": "test/autocode/table"
}
]
},
{
"path": "/test/codebug",
"name": "codebug",
"meta": {
"title": "异常处理",
"icon": "sc-icon-bug-line",
"type": "menu"
},
"component": "test/codebug"
}
]
},
{
"name": "setting",
"path": "/setting",
"meta": {
"title": "配置",
"icon": "el-icon-setting",
"type": "menu"
},
"children": [
{
"path": "/setting/system",
"name": "system",
"meta": {
"title": "系统设置",
"icon": "el-icon-tools",
"type": "menu"
},
"component": "setting/system"
},
{
"path": "/setting/user",
"name": "user",
"meta": {
"title": "用户管理",
"icon": "el-icon-user-filled",
"type": "menu"
},
"component": "setting/user"
},
{
"path": "/setting/role",
"name": "role",
"meta": {
"title": "角色管理",
"icon": "el-icon-notebook",
"type": "menu"
},
"component": "setting/role"
},
{
"path": "/setting/dept",
"name": "dept",
"meta": {
"title": "部门管理",
"icon": "sc-icon-organization",
"type": "menu"
},
"component": "setting/dept"
},
{
"path": "/setting/dic",
"name": "dic",
"meta": {
"title": "字典管理",
"icon": "el-icon-document",
"type": "menu"
},
"component": "setting/dic"
},
{
"path": "/setting/table",
"name": "tableSetting",
"meta": {
"title": "表格列管理",
"icon": "el-icon-scale-to-original",
"type": "menu"
},
"component": "setting/table"
},
{
"path": "/setting/menu",
"name": "settingMenu",
"meta": {
"title": "菜单管理",
"icon": "el-icon-fold",
"type": "menu"
},
"component": "setting/menu"
},
{
"path": "/setting/task",
"name": "task",
"meta": {
"title": "计划任务",
"icon": "el-icon-alarm-clock",
"type": "menu"
},
"component": "setting/task"
},
{
"path": "/setting/client",
"name": "client",
"meta": {
"title": "应用管理",
"icon": "el-icon-help-filled",
"type": "menu"
},
"component": "setting/client"
},
{
"path": "/setting/log",
"name": "log",
"meta": {
"title": "系统日志",
"icon": "el-icon-warning",
"type": "menu"
},
"component": "setting/log"
}
]
},
{
"path": "/other/about",
"name": "about",
"meta": {
"title": "关于",
"icon": "el-icon-info-filled",
"type": "menu"
},
"component": "other/about"
}
]

243
process/Monitor.php Normal file
View File

@ -0,0 +1,243 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace process;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
use Workerman\Timer;
use Workerman\Worker;
/**
* Class FileMonitor
* @package process
*/
class Monitor
{
/**
* @var array
*/
protected $paths = [];
/**
* @var array
*/
protected $extensions = [];
/**
* @var string
*/
public static $lockFile = __DIR__ . '/../runtime/monitor.lock';
/**
* Pause monitor
* @return void
*/
public static function pause()
{
file_put_contents(static::$lockFile, time());
}
/**
* Resume monitor
* @return void
*/
public static function resume(): void
{
clearstatcache();
if (is_file(static::$lockFile)) {
unlink(static::$lockFile);
}
}
/**
* Whether monitor is paused
* @return bool
*/
public static function isPaused(): bool
{
clearstatcache();
return file_exists(static::$lockFile);
}
/**
* FileMonitor constructor.
* @param $monitorDir
* @param $monitorExtensions
* @param array $options
*/
public function __construct($monitorDir, $monitorExtensions, array $options = [])
{
static::resume();
$this->paths = (array)$monitorDir;
$this->extensions = $monitorExtensions;
if (!Worker::getAllWorkers()) {
return;
}
$disableFunctions = explode(',', ini_get('disable_functions'));
if (in_array('exec', $disableFunctions, true)) {
echo "\nMonitor file change turned off because exec() has been disabled by disable_functions setting in " . PHP_CONFIG_FILE_PATH . "/php.ini\n";
} else {
if ($options['enable_file_monitor'] ?? true) {
Timer::add(1, function () {
$this->checkAllFilesChange();
});
}
}
$memoryLimit = $this->getMemoryLimit($options['memory_limit'] ?? null);
if ($memoryLimit && ($options['enable_memory_monitor'] ?? true)) {
Timer::add(60, [$this, 'checkMemory'], [$memoryLimit]);
}
}
/**
* @param $monitorDir
* @return bool
*/
public function checkFilesChange($monitorDir): bool
{
static $lastMtime, $tooManyFilesCheck;
if (!$lastMtime) {
$lastMtime = time();
}
clearstatcache();
if (!is_dir($monitorDir)) {
if (!is_file($monitorDir)) {
return false;
}
$iterator = [new SplFileInfo($monitorDir)];
} else {
// recursive traversal directory
$dirIterator = new RecursiveDirectoryIterator($monitorDir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS);
$iterator = new RecursiveIteratorIterator($dirIterator);
}
$count = 0;
foreach ($iterator as $file) {
$count ++;
/** var SplFileInfo $file */
if (is_dir($file->getRealPath())) {
continue;
}
// check mtime
if (in_array($file->getExtension(), $this->extensions, true) && $lastMtime < $file->getMTime()) {
$var = 0;
exec('"'.PHP_BINARY . '" -l ' . $file, $out, $var);
$lastMtime = $file->getMTime();
if ($var) {
continue;
}
echo $file . " update and reload\n";
// send SIGUSR1 signal to master process for reload
if (DIRECTORY_SEPARATOR === '/') {
posix_kill(posix_getppid(), SIGUSR1);
} else {
return true;
}
break;
}
}
if (!$tooManyFilesCheck && $count > 1000) {
echo "Monitor: There are too many files ($count files) in $monitorDir which makes file monitoring very slow\n";
$tooManyFilesCheck = 1;
}
return false;
}
/**
* @return bool
*/
public function checkAllFilesChange(): bool
{
if (static::isPaused()) {
return false;
}
foreach ($this->paths as $path) {
if ($this->checkFilesChange($path)) {
return true;
}
}
return false;
}
/**
* @param $memoryLimit
* @return void
*/
public function checkMemory($memoryLimit)
{
if (static::isPaused() || $memoryLimit <= 0) {
return;
}
$ppid = posix_getppid();
$childrenFile = "/proc/$ppid/task/$ppid/children";
if (!is_file($childrenFile) || !($children = file_get_contents($childrenFile))) {
return;
}
foreach (explode(' ', $children) as $pid) {
$pid = (int)$pid;
$statusFile = "/proc/$pid/status";
if (!is_file($statusFile) || !($status = file_get_contents($statusFile))) {
continue;
}
$mem = 0;
if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) {
$mem = $match[1];
}
$mem = (int)($mem / 1024);
if ($mem >= $memoryLimit) {
posix_kill($pid, SIGINT);
}
}
}
/**
* Get memory limit
* @return float
*/
protected function getMemoryLimit($memoryLimit)
{
if ($memoryLimit === 0) {
return 0;
}
$usePhpIni = false;
if (!$memoryLimit) {
$memoryLimit = ini_get('memory_limit');
$usePhpIni = true;
}
if ($memoryLimit == -1) {
return 0;
}
$unit = strtolower($memoryLimit[strlen($memoryLimit) - 1]);
if ($unit === 'g') {
$memoryLimit = 1024 * (int)$memoryLimit;
} else if ($unit === 'm') {
$memoryLimit = (int)$memoryLimit;
} else if ($unit === 'k') {
$memoryLimit = ((int)$memoryLimit / 1024);
} else {
$memoryLimit = ((int)$memoryLimit / (1024 * 1024));
}
if ($memoryLimit < 30) {
$memoryLimit = 30;
}
if ($usePhpIni) {
$memoryLimit = (int)(0.8 * $memoryLimit);
}
return $memoryLimit;
}
}

12
public/404.html Normal file
View File

@ -0,0 +1,12 @@
<html>
<head>
<title>404 Not Found - webman</title>
</head>
<body>
<center>
<h1>404 Not Found</h1>
</center>
<hr>
<center>webman</center>
</body>
</html>

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

4
start.php Normal file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env php
<?php
require_once __DIR__ . '/vendor/autoload.php';
support\App::run();

24
support/Request.php Normal file
View File

@ -0,0 +1,24 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace support;
/**
* Class Request
* @package support
*/
class Request extends \Webman\Http\Request
{
}

24
support/Response.php Normal file
View File

@ -0,0 +1,24 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace support;
/**
* Class Response
* @package support
*/
class Response extends \Webman\Http\Response
{
}

133
support/bootstrap.php Normal file
View File

@ -0,0 +1,133 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use Dotenv\Dotenv;
use support\Log;
use Webman\Bootstrap;
use Webman\Config;
use Webman\Middleware;
use Webman\Route;
use Webman\Util;
$worker = $worker ?? null;
set_error_handler(function ($level, $message, $file = '', $line = 0) {
if (error_reporting() & $level) {
throw new ErrorException($message, 0, $level, $file, $line);
}
});
if ($worker) {
register_shutdown_function(function ($startTime) {
if (time() - $startTime <= 0.1) {
sleep(1);
}
}, time());
}
if (class_exists('Dotenv\Dotenv') && file_exists(base_path(false) . '/.env')) {
if (method_exists('Dotenv\Dotenv', 'createUnsafeMutable')) {
Dotenv::createUnsafeMutable(base_path(false))->load();
} else {
Dotenv::createMutable(base_path(false))->load();
}
}
Config::clear();
support\App::loadAllConfig(['route']);
if ($timezone = config('app.default_timezone')) {
date_default_timezone_set($timezone);
}
foreach (config('autoload.files', []) as $file) {
include_once $file;
}
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['autoload']['files'] ?? [] as $file) {
include_once $file;
}
}
foreach ($projects['autoload']['files'] ?? [] as $file) {
include_once $file;
}
}
Middleware::load(config('middleware', []));
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project) || $name === 'static') {
continue;
}
Middleware::load($project['middleware'] ?? []);
}
Middleware::load($projects['middleware'] ?? [], $firm);
if ($staticMiddlewares = config("plugin.$firm.static.middleware")) {
Middleware::load(['__static__' => $staticMiddlewares], $firm);
}
}
Middleware::load(['__static__' => config('static.middleware', [])]);
foreach (config('bootstrap', []) as $className) {
if (!class_exists($className)) {
$log = "Warning: Class $className setting in config/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $className */
$className::start($worker);
}
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['bootstrap'] ?? [] as $className) {
if (!class_exists($className)) {
$log = "Warning: Class $className setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $className */
$className::start($worker);
}
}
foreach ($projects['bootstrap'] ?? [] as $className) {
/** @var string $className */
if (!class_exists($className)) {
$log = "Warning: Class $className setting in plugin/$firm/config/bootstrap.php not found\r\n";
echo $log;
Log::error($log);
continue;
}
/** @var Bootstrap $className */
$className::start($worker);
}
}
$directory = base_path() . '/plugin';
$paths = [config_path()];
foreach (Util::scanDir($directory) as $path) {
if (is_dir($path = "$path/config")) {
$paths[] = $path;
}
}
Route::load($paths);

528
support/helpers.php Normal file
View File

@ -0,0 +1,528 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use support\Container;
use support\Request;
use support\Response;
use support\Translation;
use support\view\Blade;
use support\view\Raw;
use support\view\ThinkPHP;
use support\view\Twig;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Webman\App;
use Webman\Config;
use Webman\Route;
use Workerman\Protocols\Http\Session;
use Workerman\Worker;
// Project base path
define('BASE_PATH', dirname(__DIR__));
/**
* return the program execute directory
* @param string $path
* @return string
*/
function run_path(string $path = ''): string
{
static $runPath = '';
if (!$runPath) {
$runPath = is_phar() ? dirname(Phar::running(false)) : BASE_PATH;
}
return path_combine($runPath, $path);
}
/**
* if the param $path equal false,will return this program current execute directory
* @param string|false $path
* @return string
*/
function base_path($path = ''): string
{
if (false === $path) {
return run_path();
}
return path_combine(BASE_PATH, $path);
}
/**
* App path
* @param string $path
* @return string
*/
function app_path(string $path = ''): string
{
return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'app', $path);
}
/**
* Public path
* @param string $path
* @return string
*/
function public_path(string $path = ''): string
{
static $publicPath = '';
if (!$publicPath) {
$publicPath = \config('app.public_path') ?: run_path('public');
}
return path_combine($publicPath, $path);
}
/**
* Config path
* @param string $path
* @return string
*/
function config_path(string $path = ''): string
{
return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'config', $path);
}
/**
* Runtime path
* @param string $path
* @return string
*/
function runtime_path(string $path = ''): string
{
static $runtimePath = '';
if (!$runtimePath) {
$runtimePath = \config('app.runtime_path') ?: run_path('runtime');
}
return path_combine($runtimePath, $path);
}
/**
* Generate paths based on given information
* @param string $front
* @param string $back
* @return string
*/
function path_combine(string $front, string $back): string
{
return $front . ($back ? (DIRECTORY_SEPARATOR . ltrim($back, DIRECTORY_SEPARATOR)) : $back);
}
/**
* Response
* @param int $status
* @param array $headers
* @param string $body
* @return Response
*/
function response(string $body = '', int $status = 200, array $headers = []): Response
{
return new Response($status, $headers, $body);
}
/**
* Json response
* @param $data
* @param int $options
* @return Response
*/
function json($data, int $options = JSON_UNESCAPED_UNICODE): Response
{
return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
}
/**
* Xml response
* @param $xml
* @return Response
*/
function xml($xml): Response
{
if ($xml instanceof SimpleXMLElement) {
$xml = $xml->asXML();
}
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
}
/**
* Jsonp response
* @param $data
* @param string $callbackName
* @return Response
*/
function jsonp($data, string $callbackName = 'callback'): Response
{
if (!is_scalar($data) && null !== $data) {
$data = json_encode($data);
}
return new Response(200, [], "$callbackName($data)");
}
/**
* Redirect response
* @param string $location
* @param int $status
* @param array $headers
* @return Response
*/
function redirect(string $location, int $status = 302, array $headers = []): Response
{
$response = new Response($status, ['Location' => $location]);
if (!empty($headers)) {
$response->withHeaders($headers);
}
return $response;
}
/**
* View response
* @param string $template
* @param array $vars
* @param string|null $app
* @param string|null $plugin
* @return Response
*/
function view(string $template, array $vars = [], string $app = null, string $plugin = null): Response
{
$request = \request();
$plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
$handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
return new Response(200, [], $handler::render($template, $vars, $app, $plugin));
}
/**
* Raw view response
* @param string $template
* @param array $vars
* @param string|null $app
* @return Response
* @throws Throwable
*/
function raw_view(string $template, array $vars = [], string $app = null): Response
{
return new Response(200, [], Raw::render($template, $vars, $app));
}
/**
* Blade view response
* @param string $template
* @param array $vars
* @param string|null $app
* @return Response
*/
function blade_view(string $template, array $vars = [], string $app = null): Response
{
return new Response(200, [], Blade::render($template, $vars, $app));
}
/**
* Think view response
* @param string $template
* @param array $vars
* @param string|null $app
* @return Response
*/
function think_view(string $template, array $vars = [], string $app = null): Response
{
return new Response(200, [], ThinkPHP::render($template, $vars, $app));
}
/**
* Twig view response
* @param string $template
* @param array $vars
* @param string|null $app
* @return Response
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*/
function twig_view(string $template, array $vars = [], string $app = null): Response
{
return new Response(200, [], Twig::render($template, $vars, $app));
}
/**
* Get request
* @return \Webman\Http\Request|Request|null
*/
function request()
{
return App::request();
}
/**
* Get config
* @param string|null $key
* @param $default
* @return array|mixed|null
*/
function config(string $key = null, $default = null)
{
return Config::get($key, $default);
}
/**
* Create url
* @param string $name
* @param ...$parameters
* @return string
*/
function route(string $name, ...$parameters): string
{
$route = Route::getByName($name);
if (!$route) {
return '';
}
if (!$parameters) {
return $route->url();
}
if (is_array(current($parameters))) {
$parameters = current($parameters);
}
return $route->url($parameters);
}
/**
* Session
* @param mixed $key
* @param mixed $default
* @return mixed|bool|Session
*/
function session($key = null, $default = null)
{
$session = \request()->session();
if (null === $key) {
return $session;
}
if (is_array($key)) {
$session->put($key);
return null;
}
if (strpos($key, '.')) {
$keyArray = explode('.', $key);
$value = $session->all();
foreach ($keyArray as $index) {
if (!isset($value[$index])) {
return $default;
}
$value = $value[$index];
}
return $value;
}
return $session->get($key, $default);
}
/**
* Translation
* @param string $id
* @param array $parameters
* @param string|null $domain
* @param string|null $locale
* @return string
*/
function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string
{
$res = Translation::trans($id, $parameters, $domain, $locale);
return $res === '' ? $id : $res;
}
/**
* Locale
* @param string|null $locale
* @return string
*/
function locale(string $locale = null): string
{
if (!$locale) {
return Translation::getLocale();
}
Translation::setLocale($locale);
return $locale;
}
/**
* 404 not found
* @return Response
*/
function not_found(): Response
{
return new Response(404, [], file_get_contents(public_path() . '/404.html'));
}
/**
* Copy dir
* @param string $source
* @param string $dest
* @param bool $overwrite
* @return void
*/
function copy_dir(string $source, string $dest, bool $overwrite = false)
{
if (is_dir($source)) {
if (!is_dir($dest)) {
mkdir($dest);
}
$files = scandir($source);
foreach ($files as $file) {
if ($file !== "." && $file !== "..") {
copy_dir("$source/$file", "$dest/$file", $overwrite);
}
}
} else if (file_exists($source) && ($overwrite || !file_exists($dest))) {
copy($source, $dest);
}
}
/**
* Remove dir
* @param string $dir
* @return bool
*/
function remove_dir(string $dir): bool
{
if (is_link($dir) || is_file($dir)) {
return unlink($dir);
}
$files = array_diff(scandir($dir), array('.', '..'));
foreach ($files as $file) {
(is_dir("$dir/$file") && !is_link($dir)) ? remove_dir("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}
/**
* Bind worker
* @param $worker
* @param $class
*/
function worker_bind($worker, $class)
{
$callbackMap = [
'onConnect',
'onMessage',
'onClose',
'onError',
'onBufferFull',
'onBufferDrain',
'onWorkerStop',
'onWebSocketConnect',
'onWorkerReload'
];
foreach ($callbackMap as $name) {
if (method_exists($class, $name)) {
$worker->$name = [$class, $name];
}
}
if (method_exists($class, 'onWorkerStart')) {
call_user_func([$class, 'onWorkerStart'], $worker);
}
}
/**
* Start worker
* @param $processName
* @param $config
* @return void
*/
function worker_start($processName, $config)
{
$worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
$propertyMap = [
'count',
'user',
'group',
'reloadable',
'reusePort',
'transport',
'protocol',
];
$worker->name = $processName;
foreach ($propertyMap as $property) {
if (isset($config[$property])) {
$worker->$property = $config[$property];
}
}
$worker->onWorkerStart = function ($worker) use ($config) {
require_once base_path('/support/bootstrap.php');
if (isset($config['handler'])) {
if (!class_exists($config['handler'])) {
echo "process error: class {$config['handler']} not exists\r\n";
return;
}
$instance = Container::make($config['handler'], $config['constructor'] ?? []);
worker_bind($worker, $instance);
}
};
}
/**
* Get realpath
* @param string $filePath
* @return string
*/
function get_realpath(string $filePath): string
{
if (strpos($filePath, 'phar://') === 0) {
return $filePath;
} else {
return realpath($filePath);
}
}
/**
* Is phar
* @return bool
*/
function is_phar(): bool
{
return class_exists(Phar::class, false) && Phar::running();
}
/**
* Get cpu count
* @return int
*/
function cpu_count(): int
{
// Windows does not support the number of processes setting.
if (DIRECTORY_SEPARATOR === '\\') {
return 1;
}
$count = 4;
if (is_callable('shell_exec')) {
if (strtolower(PHP_OS) === 'darwin') {
$count = (int)shell_exec('sysctl -n machdep.cpu.core_count');
} else {
$count = (int)shell_exec('nproc');
}
}
return $count > 0 ? $count : 4;
}
/**
* Get request parameters, if no parameter name is passed, an array of all values is returned, default values is supported
* @param string|null $param param's name
* @param mixed|null $default default value
* @return mixed|null
*/
function input(string $param = null, $default = null)
{
return is_null($param) ? request()->all() : request()->input($param, $default);
}

6
test.php Normal file
View File

@ -0,0 +1,6 @@
<?php
$arr = unpack("C3", "PHP");
var_export($arr);
var_dump(implode(',', $arr));
echo pack("C3",80,72,80);

57
webman Normal file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env php
<?php
use Webman\Config;
use Webman\Console\Command;
use Webman\Console\Util;
use support\Container;
require_once __DIR__ . '/vendor/autoload.php';
if (!in_array($argv[1] ?? '', ['start', 'restart', 'stop', 'status', 'reload', 'connections'])) {
require_once __DIR__ . '/support/bootstrap.php';
} else {
if (class_exists('Support\App')) {
Support\App::loadAllConfig(['route']);
} else {
Config::reload(config_path(), ['route', 'container']);
}
}
$cli = new Command();
$cli->setName('webman cli');
$cli->installInternalCommands();
if (is_dir($command_path = Util::guessPath(app_path(), '/command', true))) {
$cli->installCommands($command_path);
}
foreach (config('plugin', []) as $firm => $projects) {
if (isset($projects['app'])) {
if ($command_str = Util::guessPath(base_path() . "/plugin/$firm", 'command')) {
$command_path = base_path() . "/plugin/$firm/$command_str";
$cli->installCommands($command_path, "plugin\\$firm\\$command_str");
}
}
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['command'] ?? [] as $class_name) {
$reflection = new \ReflectionClass($class_name);
if ($reflection->isAbstract()) {
continue;
}
$properties = $reflection->getStaticProperties();
$name = $properties['defaultName'];
if (!$name) {
throw new RuntimeException("Command {$class_name} has no defaultName");
}
$description = $properties['defaultDescription'] ?? '';
$command = Container::get($class_name);
$command->setName($name)->setDescription($description);
$cli->add($command);
}
}
}
$cli->run();

3
windows.bat Normal file
View File

@ -0,0 +1,3 @@
CHCP 65001
php windows.php
pause

118
windows.php Normal file
View File

@ -0,0 +1,118 @@
<?php
/**
* Start file for windows
*/
require_once __DIR__ . '/vendor/autoload.php';
use Dotenv\Dotenv;
use process\Monitor;
use support\App;
use Workerman\Worker;
ini_set('display_errors', 'on');
error_reporting(E_ALL);
if (class_exists('Dotenv\Dotenv') && file_exists(base_path() . '/.env')) {
if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
Dotenv::createUnsafeImmutable(base_path())->load();
} else {
Dotenv::createMutable(base_path())->load();
}
}
App::loadAllConfig(['route']);
$errorReporting = config('app.error_reporting');
if (isset($errorReporting)) {
error_reporting($errorReporting);
}
$runtimeProcessPath = runtime_path() . DIRECTORY_SEPARATOR . '/windows';
if (!is_dir($runtimeProcessPath)) {
mkdir($runtimeProcessPath);
}
$processFiles = [
__DIR__ . DIRECTORY_SEPARATOR . 'start.php'
];
foreach (config('process', []) as $processName => $config) {
$processFiles[] = write_process_file($runtimeProcessPath, $processName, '');
}
foreach (config('plugin', []) as $firm => $projects) {
foreach ($projects as $name => $project) {
if (!is_array($project)) {
continue;
}
foreach ($project['process'] ?? [] as $processName => $config) {
$processFiles[] = write_process_file($runtimeProcessPath, $processName, "$firm.$name");
}
}
foreach ($projects['process'] ?? [] as $processName => $config) {
$processFiles[] = write_process_file($runtimeProcessPath, $processName, $firm);
}
}
function write_process_file($runtimeProcessPath, $processName, $firm): string
{
$processParam = $firm ? "plugin.$firm.$processName" : $processName;
$configParam = $firm ? "config('plugin.$firm.process')['$processName']" : "config('process')['$processName']";
$fileContent = <<<EOF
<?php
require_once __DIR__ . '/../../vendor/autoload.php';
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Webman\Config;
use support\App;
ini_set('display_errors', 'on');
error_reporting(E_ALL);
if (is_callable('opcache_reset')) {
opcache_reset();
}
App::loadAllConfig(['route']);
worker_start('$processParam', $configParam);
if (DIRECTORY_SEPARATOR != "/") {
Worker::\$logFile = config('server')['log_file'] ?? Worker::\$logFile;
TcpConnection::\$defaultMaxPackageSize = config('server')['max_package_size'] ?? 10*1024*1024;
}
Worker::runAll();
EOF;
$processFile = $runtimeProcessPath . DIRECTORY_SEPARATOR . "start_$processParam.php";
file_put_contents($processFile, $fileContent);
return $processFile;
}
if ($monitorConfig = config('process.monitor.constructor')) {
$monitor = new Monitor(...array_values($monitorConfig));
}
function popen_processes($processFiles)
{
$cmd = '"' . PHP_BINARY . '" ' . implode(' ', $processFiles);
$descriptorspec = [STDIN, STDOUT, STDOUT];
$resource = proc_open($cmd, $descriptorspec, $pipes, null, null, ['bypass_shell' => true]);
if (!$resource) {
exit("Can not execute $cmd\r\n");
}
return $resource;
}
$resource = popen_processes($processFiles);
echo "\r\n";
while (1) {
sleep(1);
if (!empty($monitor) && $monitor->checkAllFilesChange()) {
$status = proc_get_status($resource);
$pid = $status['pid'];
shell_exec("taskkill /F /T /PID $pid");
proc_close($resource);
$resource = popen_processes($processFiles);
}
}