init
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
build
|
||||
vendor
|
||||
.idea
|
||||
.vscode
|
||||
.phpunit*
|
||||
composer.lock
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
# console
|
||||
webman console
|
||||
https://www.workerman.net/plugin/1
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "webman/console",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"webman console"
|
||||
],
|
||||
"homepage": "http://www.workerman.net",
|
||||
"license": "MIT",
|
||||
"description": "Webman console",
|
||||
"authors": [
|
||||
{
|
||||
"name": "walkor",
|
||||
"email": "walkor@workerman.net",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"email": "walkor@workerman.net",
|
||||
"issues": "https://github.com/webman-php/console/issues",
|
||||
"forum": "http://www.workerman.net/questions",
|
||||
"wiki": "http://www.workerman.net/doc/webman",
|
||||
"source": "https://github.com/webman-php/console"
|
||||
},
|
||||
"require": {
|
||||
"symfony/console": ">=5.0",
|
||||
"doctrine/inflector": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Webman\\Console\\" : "src"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"workerman/webman": "^1.0"
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
namespace Webman\Console;
|
||||
|
||||
use support\Container;
|
||||
use support\Log;
|
||||
use support\Request;
|
||||
use Webman\App;
|
||||
use Webman\Config;
|
||||
use Workerman\Connection\TcpConnection;
|
||||
use Workerman\Protocols\Http;
|
||||
use Workerman\Worker;
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
ini_set('display_errors', 'on');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// 为了兼容老版保留的类,后续可以删除
|
||||
class Application
|
||||
{
|
||||
public static function run()
|
||||
{
|
||||
$runtime_logs_path = runtime_path() . DIRECTORY_SEPARATOR . 'logs';
|
||||
if ( !file_exists($runtime_logs_path) || !is_dir($runtime_logs_path) ) {
|
||||
if (!mkdir($runtime_logs_path,0777,true)) {
|
||||
throw new \RuntimeException("Failed to create runtime logs directory. Please check the permission.");
|
||||
}
|
||||
}
|
||||
|
||||
$runtime_views_path = runtime_path() . DIRECTORY_SEPARATOR . 'views';
|
||||
if ( !file_exists($runtime_views_path) || !is_dir($runtime_views_path) ) {
|
||||
if (!mkdir($runtime_views_path,0777,true)) {
|
||||
throw new \RuntimeException("Failed to create runtime views directory. Please check the permission.");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Config::reload(config_path(), ['route', 'container']);
|
||||
|
||||
Worker::$onMasterReload = function (){
|
||||
if (function_exists('opcache_get_status')) {
|
||||
if ($status = opcache_get_status()) {
|
||||
if (isset($status['scripts']) && $scripts = $status['scripts']) {
|
||||
foreach (array_keys($scripts) as $file) {
|
||||
opcache_invalidate($file, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$config = config('server');
|
||||
Worker::$pidFile = $config['pid_file'];
|
||||
Worker::$stdoutFile = $config['stdout_file'];
|
||||
Worker::$logFile = $config['log_file'];
|
||||
Worker::$eventLoopClass = $config['event_loop'] ?? '';
|
||||
TcpConnection::$defaultMaxPackageSize = $config['max_package_size'] ?? 10*1024*1024;
|
||||
if (property_exists(Worker::class, 'statusFile')) {
|
||||
Worker::$statusFile = $config['status_file'] ?? '';
|
||||
}
|
||||
if (property_exists(Worker::class, 'stopTimeout')) {
|
||||
Worker::$stopTimeout = $config['stop_timeout'] ?? 2;
|
||||
}
|
||||
|
||||
if ($config['listen']) {
|
||||
$worker = new Worker($config['listen'], $config['context']);
|
||||
$property_map = [
|
||||
'name',
|
||||
'count',
|
||||
'user',
|
||||
'group',
|
||||
'reusePort',
|
||||
'transport',
|
||||
'protocol'
|
||||
];
|
||||
foreach ($property_map as $property) {
|
||||
if (isset($config[$property])) {
|
||||
$worker->$property = $config[$property];
|
||||
}
|
||||
}
|
||||
|
||||
$worker->onWorkerStart = function ($worker) {
|
||||
require_once base_path() . '/support/bootstrap.php';
|
||||
$app = new App($worker, Container::instance(), Log::channel('default'), app_path(), public_path());
|
||||
Http::requestClass(config('app.request_class', config('server.request_class', Request::class)));
|
||||
$worker->onMessage = [$app, 'onMessage'];
|
||||
};
|
||||
}
|
||||
|
||||
// Windows does not support custom processes.
|
||||
if (\DIRECTORY_SEPARATOR === '/') {
|
||||
foreach (config('process', []) as $process_name => $config) {
|
||||
// Remove monitor process.
|
||||
if (class_exists(\Phar::class, false) && \Phar::running() && 'monitor' === $process_name) {
|
||||
continue;
|
||||
}
|
||||
worker_start($process_name, $config);
|
||||
}
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
foreach ($project['process'] ?? [] as $process_name => $config) {
|
||||
worker_start("plugin.$firm.$name.$process_name", $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Worker::runAll();
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console;
|
||||
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command as Commands;
|
||||
use support\Container;
|
||||
|
||||
class Command extends Application
|
||||
{
|
||||
public function installInternalCommands()
|
||||
{
|
||||
$this->installCommands(__DIR__ . '/Commands', 'Webman\Console\Commands');
|
||||
}
|
||||
|
||||
public function installCommands($path, $namspace = 'app\command')
|
||||
{
|
||||
$dir_iterator = new \RecursiveDirectoryIterator($path);
|
||||
$iterator = new \RecursiveIteratorIterator($dir_iterator);
|
||||
foreach ($iterator as $file) {
|
||||
/** @var \SplFileInfo $file */
|
||||
if (strpos($file->getFilename(), '.') === 0) {
|
||||
continue;
|
||||
}
|
||||
if ($file->getExtension() !== 'php') {
|
||||
continue;
|
||||
}
|
||||
// abc\def.php
|
||||
$relativePath = str_replace(str_replace('/', '\\', $path . '\\'), '', str_replace('/', '\\', $file->getRealPath()));
|
||||
// app\command\abc
|
||||
$realNamespace = trim($namspace . '\\' . trim(dirname(str_replace('\\', DIRECTORY_SEPARATOR, $relativePath)), '.'), '\\');
|
||||
$realNamespace = str_replace('/', '\\', $realNamespace);
|
||||
// app\command\doc\def
|
||||
$class_name = trim($realNamespace . '\\' . $file->getBasename('.php'), '\\');
|
||||
if (!class_exists($class_name) || !is_a($class_name, Commands::class, true)) {
|
||||
continue;
|
||||
}
|
||||
$reflection = new \ReflectionClass($class_name);
|
||||
if ($reflection->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
$properties = $reflection->getStaticProperties();
|
||||
$name = $properties['defaultName'] ?? null;
|
||||
if (!$name) {
|
||||
throw new RuntimeException("Command {$class_name} has no defaultName");
|
||||
}
|
||||
$description = $properties['defaultDescription'] ?? '';
|
||||
$command = Container::get($class_name);
|
||||
$command->setName($name)->setDescription($description);
|
||||
$this->add($command);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,557 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
|
||||
class AppPluginCreateCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'app-plugin:create';
|
||||
protected static $defaultDescription = 'App Plugin Create';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'App plugin name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Create App Plugin $name");
|
||||
|
||||
if (strpos($name, '/') !== false) {
|
||||
$output->writeln('<error>Bad name, name must not contain character \'/\'</error>');
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
// Create dir config/plugin/$name
|
||||
if (is_dir($plugin_config_path = base_path()."/plugin/$name")) {
|
||||
$output->writeln("<error>Dir $plugin_config_path already exists</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->createAll($name);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return void
|
||||
*/
|
||||
protected function createAll($name)
|
||||
{
|
||||
$base_path = base_path();
|
||||
$this->mkdir("$base_path/plugin/$name/app/controller", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/model", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/middleware", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/app/view/index", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/config", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/public", 0777, true);
|
||||
$this->mkdir("$base_path/plugin/$name/api", 0777, true);
|
||||
$this->createFunctionsFile("$base_path/plugin/$name/app/functions.php");
|
||||
$this->createControllerFile("$base_path/plugin/$name/app/controller/IndexController.php", $name);
|
||||
$this->createViewFile("$base_path/plugin/$name/app/view/index/index.html");
|
||||
$this->createConfigFiles("$base_path/plugin/$name/config", $name);
|
||||
$this->createApiFiles("$base_path/plugin/$name/api", $name);
|
||||
$this->createInstallSqlFile("$base_path/plugin/$name/install.sql");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return void
|
||||
*/
|
||||
protected function mkdir($path)
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
return;
|
||||
}
|
||||
echo "Create $path\r\n";
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $name
|
||||
* @return void
|
||||
*/
|
||||
protected function createControllerFile($path, $name)
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace plugin\\$name\\app\\controller;
|
||||
|
||||
use support\\Request;
|
||||
|
||||
class IndexController
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
return view('index/index', ['name' => '$name']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EOF;
|
||||
file_put_contents($path, $content);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return void
|
||||
*/
|
||||
protected function createViewFile($path)
|
||||
{
|
||||
$content = <<<EOF
|
||||
<!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 app plugin</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
hello <?=htmlspecialchars(\$name)?>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
EOF;
|
||||
file_put_contents($path, $content);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $file
|
||||
* @return void
|
||||
*/
|
||||
protected function createFunctionsFile($file)
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
/**
|
||||
* Here is your custom functions.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
EOF;
|
||||
file_put_contents($file, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $base
|
||||
* @param $name
|
||||
* @return void
|
||||
*/
|
||||
protected function createApiFiles($base, $name)
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace plugin\\$name\api;
|
||||
|
||||
use plugin\admin\api\Menu;
|
||||
use support\Db;
|
||||
use Throwable;
|
||||
|
||||
class Install
|
||||
{
|
||||
|
||||
/**
|
||||
* 数据库连接
|
||||
*/
|
||||
protected static \$connection = 'plugin.admin.mysql';
|
||||
|
||||
/**
|
||||
* 安装
|
||||
*
|
||||
* @param \$version
|
||||
* @return void
|
||||
*/
|
||||
public static function install(\$version)
|
||||
{
|
||||
// 安装数据库
|
||||
static::installSql();
|
||||
// 导入菜单
|
||||
if(\$menus = static::getMenus()) {
|
||||
Menu::import(\$menus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载
|
||||
*
|
||||
* @param \$version
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstall(\$version)
|
||||
{
|
||||
// 删除菜单
|
||||
foreach (static::getMenus() as \$menu) {
|
||||
Menu::delete(\$menu['key']);
|
||||
}
|
||||
// 卸载数据库
|
||||
static::uninstallSql();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
*
|
||||
* @param \$from_version
|
||||
* @param \$to_version
|
||||
* @param \$context
|
||||
* @return void
|
||||
*/
|
||||
public static function update(\$from_version, \$to_version, \$context = null)
|
||||
{
|
||||
// 删除不用的菜单
|
||||
if (isset(\$context['previous_menus'])) {
|
||||
static::removeUnnecessaryMenus(\$context['previous_menus']);
|
||||
}
|
||||
// 安装数据库
|
||||
static::installSql();
|
||||
// 导入新菜单
|
||||
if (\$menus = static::getMenus()) {
|
||||
Menu::import(\$menus);
|
||||
}
|
||||
// 执行更新操作
|
||||
\$update_file = __DIR__ . '/../update.php';
|
||||
if (is_file(\$update_file)) {
|
||||
include \$update_file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新前数据收集等
|
||||
*
|
||||
* @param \$from_version
|
||||
* @param \$to_version
|
||||
* @return array|array[]
|
||||
*/
|
||||
public static function beforeUpdate(\$from_version, \$to_version)
|
||||
{
|
||||
// 在更新之前获得老菜单,通过context传递给 update
|
||||
return ['previous_menus' => static::getMenus()];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getMenus()
|
||||
{
|
||||
clearstatcache();
|
||||
if (is_file(\$menu_file = __DIR__ . '/../config/menu.php')) {
|
||||
\$menus = include \$menu_file;
|
||||
return \$menus ?: [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除不需要的菜单
|
||||
*
|
||||
* @param \$previous_menus
|
||||
* @return void
|
||||
*/
|
||||
public static function removeUnnecessaryMenus(\$previous_menus)
|
||||
{
|
||||
\$menus_to_remove = array_diff(Menu::column(\$previous_menus, 'name'), Menu::column(static::getMenus(), 'name'));
|
||||
foreach (\$menus_to_remove as \$name) {
|
||||
Menu::delete(\$name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装SQL
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function installSql()
|
||||
{
|
||||
static::importSql(__DIR__ . '/../install.sql');
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载SQL
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function uninstallSql() {
|
||||
// 如果卸载数据库文件存在责直接使用
|
||||
\$uninstallSqlFile = __DIR__ . '/../uninstall.sql';
|
||||
if (is_file(\$uninstallSqlFile)) {
|
||||
static::importSql(\$uninstallSqlFile);
|
||||
return;
|
||||
}
|
||||
// 否则根据install.sql生成卸载数据库文件uninstall.sql
|
||||
\$installSqlFile = __DIR__ . '/../install.sql';
|
||||
if (!is_file(\$installSqlFile)) {
|
||||
return;
|
||||
}
|
||||
\$installSql = file_get_contents(\$installSqlFile);
|
||||
preg_match_all('/CREATE TABLE `(.+?)`/si', \$installSql, \$matches);
|
||||
\$dropSql = '';
|
||||
foreach (\$matches[1] as \$table) {
|
||||
\$dropSql .= "DROP TABLE IF EXISTS `\$table`;\\n";
|
||||
}
|
||||
file_put_contents(\$uninstallSqlFile, \$dropSql);
|
||||
static::importSql(\$uninstallSqlFile);
|
||||
unlink(\$uninstallSqlFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入数据库
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function importSql(\$mysqlDumpFile)
|
||||
{
|
||||
if (!\$mysqlDumpFile || !is_file(\$mysqlDumpFile)) {
|
||||
return;
|
||||
}
|
||||
foreach (explode(';', file_get_contents(\$mysqlDumpFile)) as \$sql) {
|
||||
if (\$sql = trim(\$sql)) {
|
||||
try {
|
||||
Db::connection(static::\$connection)->statement(\$sql);
|
||||
} catch (Throwable \$e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
EOF;
|
||||
|
||||
file_put_contents("$base/Install.php", $content);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function createInstallSqlFile($file)
|
||||
{
|
||||
file_put_contents($file, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $base
|
||||
* @param $name
|
||||
* @return void
|
||||
*/
|
||||
protected function createConfigFiles($base, $name)
|
||||
{
|
||||
// app.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
use support\\Request;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
'controller_suffix' => 'Controller',
|
||||
'controller_reuse' => false,
|
||||
'version' => '1.0.0'
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/app.php", $content);
|
||||
|
||||
// menu.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/menu.php", $content);
|
||||
|
||||
// autoload.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
return [
|
||||
'files' => [
|
||||
base_path() . '/plugin/$name/app/functions.php',
|
||||
]
|
||||
];
|
||||
EOF;
|
||||
file_put_contents("$base/autoload.php", $content);
|
||||
|
||||
// container.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
return new Webman\\Container;
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/container.php", $content);
|
||||
|
||||
|
||||
// database.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
return [];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/database.php", $content);
|
||||
|
||||
// exception.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'' => support\\exception\\Handler::class,
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/exception.php", $content);
|
||||
|
||||
// log.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'handlers' => [
|
||||
[
|
||||
'class' => Monolog\\Handler\\RotatingFileHandler::class,
|
||||
'constructor' => [
|
||||
runtime_path() . '/logs/$name.log',
|
||||
7,
|
||||
Monolog\\Logger::DEBUG,
|
||||
],
|
||||
'formatter' => [
|
||||
'class' => Monolog\\Formatter\\LineFormatter::class,
|
||||
'constructor' => [null, 'Y-m-d H:i:s', true],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/log.php", $content);
|
||||
|
||||
// middleware.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'' => [
|
||||
|
||||
]
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/middleware.php", $content);
|
||||
|
||||
// process.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
return [];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/process.php", $content);
|
||||
|
||||
// redis.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
return [
|
||||
'default' => [
|
||||
'host' => '127.0.0.1',
|
||||
'password' => null,
|
||||
'port' => 6379,
|
||||
'database' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/redis.php", $content);
|
||||
|
||||
// route.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
use Webman\\Route;
|
||||
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/route.php", $content);
|
||||
|
||||
// static.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enable' => true,
|
||||
'middleware' => [], // Static file Middleware
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/static.php", $content);
|
||||
|
||||
// translation.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
// Default language
|
||||
'locale' => 'zh_CN',
|
||||
// Fallback language
|
||||
'fallback_locale' => ['zh_CN', 'en'],
|
||||
// Folder where language files are stored
|
||||
'path' => base_path() . "/plugin/$name/resource/translations",
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/translation.php", $content);
|
||||
|
||||
// view.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
use support\\view\\Raw;
|
||||
use support\\view\\Twig;
|
||||
use support\\view\\Blade;
|
||||
use support\\view\\ThinkPHP;
|
||||
|
||||
return [
|
||||
'handler' => Raw::class
|
||||
];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/view.php", $content);
|
||||
|
||||
// thinkorm.php
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
|
||||
return [];
|
||||
|
||||
EOF;
|
||||
file_put_contents("$base/thinkorm.php", $content);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
|
||||
class AppPluginInstallCommand extends Command
|
||||
{
|
||||
|
||||
protected static $defaultName = 'app-plugin:install';
|
||||
protected static $defaultDescription = 'App Plugin Install';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName(static::$defaultName)->setDescription(static::$defaultDescription);
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'App plugin name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Install App Plugin $name");
|
||||
$class = "\\plugin\\$name\\api\\Install";
|
||||
if (!method_exists($class, 'install')) {
|
||||
throw new \RuntimeException("Method $class::install not exists");
|
||||
}
|
||||
call_user_func([$class, 'install'], config("plugin.$name.app.version"));
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
|
||||
class AppPluginUninstallCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'app-plugin:uninstall';
|
||||
protected static $defaultDescription = 'App Plugin Uninstall';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'App plugin name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Uninstall App Plugin $name");
|
||||
$class = "\\plugin\\$name\\api\\Install";
|
||||
if (!method_exists($class, 'uninstall')) {
|
||||
throw new \RuntimeException("Method $class::uninstall not exists");
|
||||
}
|
||||
call_user_func([$class, 'uninstall'], config("plugin.$name.app.version"));
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
|
||||
class AppPluginUpdateCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'app-plugin:update';
|
||||
protected static $defaultDescription = 'App Plugin Update';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'App plugin name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Update App Plugin $name");
|
||||
$class = "\\plugin\\$name\\api\\Install";
|
||||
if (!method_exists($class, 'update')) {
|
||||
throw new \RuntimeException("Method $class::update not exists");
|
||||
}
|
||||
call_user_func([$class, 'update'], config("plugin.$name.app.version"), config("plugin.$name.app.version"));
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
use ZipArchive;
|
||||
use Exception;
|
||||
use RecursiveIteratorIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
|
||||
class AppPluginZipCommand extends Command
|
||||
{
|
||||
|
||||
protected static $defaultName = 'app-plugin:zip';
|
||||
protected static $defaultDescription = 'App Plugin Zip';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName(static::$defaultName)->setDescription(static::$defaultDescription);
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'App plugin name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Zip App Plugin $name");
|
||||
$sourceDir = base_path('plugin' . DIRECTORY_SEPARATOR . $name);
|
||||
$zipFilePath = base_path('plugin' . DIRECTORY_SEPARATOR . $name . '.zip');
|
||||
if (!is_dir($sourceDir)) {
|
||||
$output->writeln("Plugin $name not exists");
|
||||
return self::FAILURE;
|
||||
}
|
||||
if (is_file($zipFilePath)) {
|
||||
unlink($zipFilePath);
|
||||
}
|
||||
$this->zipDirectory($name, $sourceDir, $zipFilePath);
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $sourceDir
|
||||
* @param $zipFilePath
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function zipDirectory($name, $sourceDir, $zipFilePath) {
|
||||
$zip = new ZipArchive();
|
||||
|
||||
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
|
||||
throw new Exception("cannot open <$zipFilePath>\n");
|
||||
}
|
||||
|
||||
$sourceDir = realpath($sourceDir);
|
||||
|
||||
$files = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($sourceDir),
|
||||
RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (!$file->isDir()) {
|
||||
$filePath = $file->getRealPath();
|
||||
$relativePath = $name . DIRECTORY_SEPARATOR . substr($filePath, strlen($sourceDir) + 1);
|
||||
$zip->addFile($filePath, $relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
return $zip->close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Phar;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZipArchive;
|
||||
|
||||
|
||||
class BuildBinCommand extends BuildPharCommand
|
||||
{
|
||||
protected static $defaultName = 'build:bin';
|
||||
protected static $defaultDescription = 'build bin';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('version', InputArgument::OPTIONAL, 'PHP version');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->checkEnv();
|
||||
|
||||
$output->writeln('Phar packing...');
|
||||
|
||||
$version = $input->getArgument('version');
|
||||
if (!$version) {
|
||||
$version = (float)PHP_VERSION;
|
||||
}
|
||||
$version = $version >= 8.0 ? $version : 8.1;
|
||||
$supportZip = class_exists(ZipArchive::class);
|
||||
$microZipFileName = $supportZip ? "php$version.micro.sfx.zip" : "php$version.micro.sfx";
|
||||
$pharFileName = config('plugin.webman.console.app.phar_filename', 'webman.phar');
|
||||
$binFileName = config('plugin.webman.console.app.bin_filename', 'webman.bin');
|
||||
$this->buildDir = config('plugin.webman.console.app.build_dir', base_path() . '/build');
|
||||
$customIni = config('plugin.webman.console.app.custom_ini', '');
|
||||
|
||||
$binFile = "$this->buildDir/$binFileName";
|
||||
$pharFile = "$this->buildDir/$pharFileName";
|
||||
$zipFile = "$this->buildDir/$microZipFileName";
|
||||
$sfxFile = "$this->buildDir/php$version.micro.sfx";
|
||||
$customIniHeaderFile = "$this->buildDir/custominiheader.bin";
|
||||
|
||||
// 打包
|
||||
$command = new BuildPharCommand();
|
||||
$command->execute($input, $output);
|
||||
|
||||
// 下载 micro.sfx.zip
|
||||
if (!is_file($sfxFile) && !is_file($zipFile)) {
|
||||
$domain = 'download.workerman.net';
|
||||
$output->writeln("\r\nDownloading PHP$version ...");
|
||||
if (extension_loaded('openssl')) {
|
||||
$context = stream_context_create([
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
]
|
||||
]);
|
||||
$client = stream_socket_client("ssl://$domain:443", $context);
|
||||
} else {
|
||||
$client = stream_socket_client("tcp://$domain:80");
|
||||
}
|
||||
|
||||
fwrite($client, "GET /php/$microZipFileName HTTP/1.0\r\nAccept: text/html\r\nHost: $domain\r\nUser-Agent: webman/console\r\n\r\n");
|
||||
$bodyLength = 0;
|
||||
$bodyBuffer = '';
|
||||
$lastPercent = 0;
|
||||
while (true) {
|
||||
$buffer = fread($client, 65535);
|
||||
if ($buffer !== false) {
|
||||
$bodyBuffer .= $buffer;
|
||||
if (!$bodyLength && $pos = strpos($bodyBuffer, "\r\n\r\n")) {
|
||||
if (!preg_match('/Content-Length: (\d+)\r\n/', $bodyBuffer, $match)) {
|
||||
$output->writeln("Download php$version.micro.sfx.zip failed");
|
||||
return self::FAILURE;
|
||||
}
|
||||
$firstLine = substr($bodyBuffer, 9, strpos($bodyBuffer, "\r\n") - 9);
|
||||
if (!preg_match('/200 /', $bodyBuffer)) {
|
||||
$output->writeln("Download php$version.micro.sfx.zip failed, $firstLine");
|
||||
return self::FAILURE;
|
||||
}
|
||||
$bodyLength = (int)$match[1];
|
||||
$bodyBuffer = substr($bodyBuffer, $pos + 4);
|
||||
}
|
||||
}
|
||||
$receiveLength = strlen($bodyBuffer);
|
||||
$percent = ceil($receiveLength * 100 / $bodyLength);
|
||||
if ($percent != $lastPercent) {
|
||||
echo '[' . str_pad('', $percent, '=') . '>' . str_pad('', 100 - $percent) . "$percent%]";
|
||||
echo $percent < 100 ? "\r" : "\n";
|
||||
}
|
||||
$lastPercent = $percent;
|
||||
if ($bodyLength && $receiveLength >= $bodyLength) {
|
||||
file_put_contents($zipFile, $bodyBuffer);
|
||||
break;
|
||||
}
|
||||
if ($buffer === false || !is_resource($client) || feof($client)) {
|
||||
$output->writeln("Fail donwload PHP$version ...");
|
||||
return self::FAILURE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$output->writeln("\r\nUse PHP$version ...");
|
||||
}
|
||||
|
||||
// 解压
|
||||
if (!is_file($sfxFile) && $supportZip) {
|
||||
$zip = new ZipArchive;
|
||||
$zip->open($zipFile, ZipArchive::CHECKCONS);
|
||||
$zip->extractTo($this->buildDir);
|
||||
}
|
||||
|
||||
// 生成二进制文件
|
||||
file_put_contents($binFile, file_get_contents($sfxFile));
|
||||
// 自定义INI
|
||||
if (!empty($customIni)) {
|
||||
if (file_exists($customIniHeaderFile)) {
|
||||
unlink($customIniHeaderFile);
|
||||
}
|
||||
$f = fopen($customIniHeaderFile, 'wb');
|
||||
fwrite($f, "\xfd\xf6\x69\xe6");
|
||||
fwrite($f, pack('N', strlen($customIni)));
|
||||
fwrite($f, $customIni);
|
||||
fclose($f);
|
||||
file_put_contents($binFile, file_get_contents($customIniHeaderFile),FILE_APPEND);
|
||||
unlink($customIniHeaderFile);
|
||||
}
|
||||
file_put_contents($binFile, file_get_contents($pharFile), FILE_APPEND);
|
||||
|
||||
// 添加执行权限
|
||||
chmod($binFile, 0755);
|
||||
|
||||
$output->writeln("\r\nSaved $binFileName to $binFile\r\nBuild Success!\r\n");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Phar;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class BuildPharCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'build:phar';
|
||||
protected static $defaultDescription = 'Can be easily packaged a project into phar files. Easy to distribute and use.';
|
||||
|
||||
protected $buildDir = '';
|
||||
|
||||
public function __construct(?string $name = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->buildDir = config('plugin.webman.console.app.build_dir', base_path() . '/build');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @deprecated 暂时保留 phar:pack 命令,下一个版本再取消
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setAliases([
|
||||
'phar:pack',
|
||||
]);
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->checkEnv();
|
||||
if (!file_exists($this->buildDir) && !is_dir($this->buildDir)) {
|
||||
if (!mkdir($this->buildDir,0777,true)) {
|
||||
throw new RuntimeException("Failed to create phar file output directory. Please check the permission.");
|
||||
}
|
||||
}
|
||||
|
||||
$phar_filename = config('plugin.webman.console.app.phar_filename', 'webman.phar');
|
||||
if (empty($phar_filename)) {
|
||||
throw new RuntimeException('Please set the phar filename.');
|
||||
}
|
||||
|
||||
$phar_file = rtrim($this->buildDir,DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $phar_filename;
|
||||
if (file_exists($phar_file)) {
|
||||
unlink($phar_file);
|
||||
}
|
||||
|
||||
$exclude_pattern = config('plugin.webman.console.app.exclude_pattern','');
|
||||
|
||||
$phar = new Phar($phar_file,0,'webman');
|
||||
|
||||
$phar->startBuffering();
|
||||
|
||||
$signature_algorithm = config('plugin.webman.console.app.signature_algorithm');
|
||||
if (!in_array($signature_algorithm,[Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512,Phar::OPENSSL])) {
|
||||
throw new RuntimeException('The signature algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512, or Phar::OPENSSL.');
|
||||
}
|
||||
if ($signature_algorithm === Phar::OPENSSL) {
|
||||
$private_key_file = config('plugin.webman.console.app.private_key_file');
|
||||
if (!file_exists($private_key_file)) {
|
||||
throw new RuntimeException("If the value of the signature algorithm is 'Phar::OPENSSL', you must set the private key file.");
|
||||
}
|
||||
$private = openssl_get_privatekey(file_get_contents($private_key_file));
|
||||
$pkey = '';
|
||||
openssl_pkey_export($private, $pkey);
|
||||
$phar->setSignatureAlgorithm($signature_algorithm, $pkey);
|
||||
} else {
|
||||
$phar->setSignatureAlgorithm($signature_algorithm);
|
||||
}
|
||||
|
||||
$phar->buildFromDirectory(BASE_PATH,$exclude_pattern);
|
||||
|
||||
|
||||
$exclude_files = config('plugin.webman.console.app.exclude_files',[]);
|
||||
// 打包生成的phar和bin文件是面向生产环境的,所以以下这些命令没有任何意义,执行的话甚至会出错,需要排除在外。
|
||||
$exclude_command_files = [
|
||||
'AppPluginCreateCommand.php',
|
||||
'BuildBinCommand.php',
|
||||
'BuildPharCommand.php',
|
||||
'MakeBootstrapCommand.php',
|
||||
'MakeCommandCommand.php',
|
||||
'MakeControllerCommand.php',
|
||||
'MakeMiddlewareCommand.php',
|
||||
'MakeModelCommand.php',
|
||||
'PluginCreateCommand.php',
|
||||
'PluginDisableCommand.php',
|
||||
'PluginEnableCommand.php',
|
||||
'PluginExportCommand.php',
|
||||
'PluginInstallCommand.php',
|
||||
'PluginUninstallCommand.php'
|
||||
];
|
||||
$exclude_command_files = array_map(function ($cmd_file) {
|
||||
return 'vendor/webman/console/src/Commands/'.$cmd_file;
|
||||
},$exclude_command_files);
|
||||
$exclude_files = array_unique(array_merge($exclude_command_files,$exclude_files));
|
||||
foreach ($exclude_files as $file) {
|
||||
if($phar->offsetExists($file)){
|
||||
$phar->delete($file);
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln('Files collect complete, begin add file to Phar.');
|
||||
|
||||
$phar->setStub("#!/usr/bin/env php
|
||||
<?php
|
||||
define('IN_PHAR', true);
|
||||
Phar::mapPhar('webman');
|
||||
require 'phar://webman/webman';
|
||||
__HALT_COMPILER();
|
||||
");
|
||||
|
||||
$output->writeln('Write requests to the Phar archive, save changes to disk.');
|
||||
|
||||
$phar->stopBuffering();
|
||||
unset($phar);
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function checkEnv(): void
|
||||
{
|
||||
if (!class_exists(Phar::class, false)) {
|
||||
throw new RuntimeException("The 'phar' extension is required for build phar package");
|
||||
}
|
||||
|
||||
if (ini_get('phar.readonly')) {
|
||||
$command = static::$defaultName;
|
||||
throw new RuntimeException(
|
||||
"The 'phar.readonly' is 'On', build phar must setting it 'Off' or exec with 'php -d phar.readonly=0 ./webman $command'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Webman\Console\Application;
|
||||
|
||||
class ConnectionsCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'connections';
|
||||
protected static $defaultDescription = 'Get worker connections.';
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (\class_exists(\Support\App::class)) {
|
||||
\Support\App::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
Application::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
|
||||
class FixDisbaleFunctionsCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'fix-disable-functions';
|
||||
protected static $defaultDescription = 'Fix disbale_functions in php.ini';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$php_ini_file = php_ini_loaded_file();
|
||||
if (!$php_ini_file) {
|
||||
$output->writeln('<error>Can not find php.ini</error>');
|
||||
return self::FAILURE;
|
||||
}
|
||||
$output->writeln("Location $php_ini_file");
|
||||
$disable_functions_str = ini_get("disable_functions");
|
||||
if (!$disable_functions_str) {
|
||||
$output->writeln('<success>Ok</success>');
|
||||
}
|
||||
|
||||
$functions_required = [
|
||||
"stream_socket_server",
|
||||
"stream_socket_accept",
|
||||
"stream_socket_client",
|
||||
"pcntl_signal_dispatch",
|
||||
"pcntl_signal",
|
||||
"pcntl_alarm",
|
||||
"pcntl_fork",
|
||||
"posix_getuid",
|
||||
"posix_getpwuid",
|
||||
"posix_kill",
|
||||
"posix_setsid",
|
||||
"posix_getpid",
|
||||
"posix_getpwnam",
|
||||
"posix_getgrnam",
|
||||
"posix_getgid",
|
||||
"posix_setgid",
|
||||
"posix_initgroups",
|
||||
"posix_setuid",
|
||||
"posix_isatty",
|
||||
"proc_open",
|
||||
"proc_get_status",
|
||||
"proc_close",
|
||||
"shell_exec",
|
||||
"exec",
|
||||
];
|
||||
|
||||
$has_disbaled_functions = false;
|
||||
foreach ($functions_required as $func) {
|
||||
if (strpos($disable_functions_str, $func) !== false) {
|
||||
$has_disbaled_functions = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$disable_functions = explode(",", $disable_functions_str);
|
||||
$disable_functions_removed = [];
|
||||
foreach ($disable_functions as $index => $func) {
|
||||
$func = trim($func);
|
||||
foreach ($functions_required as $func_prefix) {
|
||||
if (strpos($func, $func_prefix) === 0) {
|
||||
$disable_functions_removed[$func] = $func;
|
||||
unset($disable_functions[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$php_ini_content = file_get_contents($php_ini_file);
|
||||
if (!$php_ini_content) {
|
||||
$output->writeln("<error>$php_ini_file content empty</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$new_disable_functions_str = implode(",", $disable_functions);
|
||||
$php_ini_content = preg_replace("/\ndisable_functions *?=[^\n]+/", "\ndisable_functions = $new_disable_functions_str", $php_ini_content);
|
||||
|
||||
file_put_contents($php_ini_file, $php_ini_content);
|
||||
|
||||
foreach ($disable_functions_removed as $func) {
|
||||
$output->write(str_pad($func, 30));
|
||||
$output->writeln('<info>enabled</info>');
|
||||
}
|
||||
|
||||
$output->writeln('<info>Succes</info>');
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
|
||||
class InstallCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'install';
|
||||
protected static $defaultDescription = 'Execute webman installation script';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln("Execute installation for webman");
|
||||
$install_function = "\\Webman\\Install::install";
|
||||
if (is_callable($install_function)) {
|
||||
$install_function();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
$output->writeln('<error>This command requires webman-framework version >= 1.3.0</error>');
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Webman\Console\Util;
|
||||
|
||||
|
||||
class MakeBootstrapCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'make:bootstrap';
|
||||
protected static $defaultDescription = 'Make bootstrap';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Bootstrap name');
|
||||
$this->addArgument('enable', InputArgument::OPTIONAL, 'Enable or not');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$enable = in_array($input->getArgument('enable'), ['no', '0', 'false', 'n']) ? false : true;
|
||||
$output->writeln("Make bootstrap $name");
|
||||
|
||||
$name = str_replace('\\', '/', $name);
|
||||
if (!$bootstrap_str = Util::guessPath(app_path(), 'bootstrap')) {
|
||||
$bootstrap_str = Util::guessPath(app_path(), 'controller') === 'Controller' ? 'Bootstrap' : 'bootstrap';
|
||||
}
|
||||
$upper = $bootstrap_str === 'Bootstrap';
|
||||
if (!($pos = strrpos($name, '/'))) {
|
||||
$name = ucfirst($name);
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $bootstrap_str . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$namespace = $upper ? 'App\Bootstrap' : 'app\bootstrap';
|
||||
} else {
|
||||
if($real_name = Util::guessPath(app_path(), $name)) {
|
||||
$name = $real_name;
|
||||
}
|
||||
if ($upper && !$real_name) {
|
||||
$name = preg_replace_callback('/\/([a-z])/', function ($matches) {
|
||||
return '/' . strtoupper($matches[1]);
|
||||
}, ucfirst($name));
|
||||
}
|
||||
$path = "$bootstrap_str/" . substr($upper ? ucfirst($name) : $name, 0, $pos);
|
||||
$name = ucfirst(substr($name, $pos + 1));
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$namespace = str_replace('/', '\\', ($upper ? 'App/' : 'app/') . $path);
|
||||
}
|
||||
|
||||
if (is_file($file)) {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("$file already exists. Do you want to override it? (yes/no)", false);
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
$this->createBootstrap($name, $namespace, $file);
|
||||
if ($enable) {
|
||||
$this->addConfig("$namespace\\$name", config_path() . '/bootstrap.php');
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $namespace
|
||||
* @param $file
|
||||
* @return void
|
||||
*/
|
||||
protected function createBootstrap($name, $namespace, $file)
|
||||
{
|
||||
$path = pathinfo($file, PATHINFO_DIRNAME);
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
$bootstrap_content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace $namespace;
|
||||
|
||||
use Webman\Bootstrap;
|
||||
|
||||
class $name implements Bootstrap
|
||||
{
|
||||
public static function start(\$worker)
|
||||
{
|
||||
// Is it console environment ?
|
||||
\$is_console = !\$worker;
|
||||
if (\$is_console) {
|
||||
// If you do not want to execute this in console, just return.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EOF;
|
||||
file_put_contents($file, $bootstrap_content);
|
||||
}
|
||||
|
||||
public function addConfig($class, $config_file)
|
||||
{
|
||||
$config = include $config_file;
|
||||
if(!in_array($class, $config ?? [])) {
|
||||
$config_file_content = file_get_contents($config_file);
|
||||
$config_file_content = preg_replace('/\];/', " $class::class,\n];", $config_file_content);
|
||||
file_put_contents($config_file, $config_file_content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Webman\Console\Util;
|
||||
|
||||
|
||||
class MakeCommandCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'make:command';
|
||||
protected static $defaultDescription = 'Make command';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Command name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$command = $name = trim($input->getArgument('name'));
|
||||
$output->writeln("Make command $name");
|
||||
|
||||
// make:command 不支持子目录
|
||||
$name = str_replace(['\\', '/'], '', $name);
|
||||
if (!$command_str = Util::guessPath(app_path(), 'command')) {
|
||||
$command_str = Util::guessPath(app_path(), 'controller') === 'Controller' ? 'Command' : 'command';
|
||||
}
|
||||
$items= explode(':', $name);
|
||||
$name='';
|
||||
foreach ($items as $item) {
|
||||
$name.=ucfirst($item);
|
||||
}
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $command_str . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$upper = $command_str === 'Command';
|
||||
$namespace = $upper ? 'App\Command' : 'app\command';
|
||||
|
||||
if (is_file($file)) {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("$file already exists. Do you want to override it? (yes/no)", false);
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
$this->createCommand($name, $namespace, $file, $command);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
protected function getClassName($name)
|
||||
{
|
||||
return preg_replace_callback('/:([a-zA-Z])/', function ($matches) {
|
||||
return strtoupper($matches[1]);
|
||||
}, ucfirst($name)) . 'Command';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $namespace
|
||||
* @param $path
|
||||
* @return void
|
||||
*/
|
||||
protected function createCommand($name, $namespace, $file, $command)
|
||||
{
|
||||
$path = pathinfo($file, PATHINFO_DIRNAME);
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
$desc = str_replace(':', ' ', $command);
|
||||
$command_content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace $namespace;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
|
||||
class $name extends Command
|
||||
{
|
||||
protected static \$defaultName = '$command';
|
||||
protected static \$defaultDescription = '$desc';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
\$this->addArgument('name', InputArgument::OPTIONAL, 'Name description');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface \$input
|
||||
* @param OutputInterface \$output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface \$input, OutputInterface \$output): int
|
||||
{
|
||||
\$name = \$input->getArgument('name');
|
||||
\$output->writeln('Hello $command');
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EOF;
|
||||
file_put_contents($file, $command_content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Webman\Console\Util;
|
||||
|
||||
|
||||
class MakeControllerCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'make:controller';
|
||||
protected static $defaultDescription = 'Make controller';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Controller name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Make controller $name");
|
||||
$suffix = config('app.controller_suffix', '');
|
||||
|
||||
if ($suffix && !strpos($name, $suffix)) {
|
||||
$name .= $suffix;
|
||||
}
|
||||
|
||||
$name = str_replace('\\', '/', $name);
|
||||
if (!($pos = strrpos($name, '/'))) {
|
||||
$name = ucfirst($name);
|
||||
$controller_str = Util::guessPath(app_path(), 'controller') ?: 'controller';
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $controller_str . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$namespace = $controller_str === 'Controller' ? 'App\Controller' : 'app\controller';
|
||||
} else {
|
||||
$name_str = substr($name, 0, $pos);
|
||||
if($real_name_str = Util::guessPath(app_path(), $name_str)) {
|
||||
$name_str = $real_name_str;
|
||||
} else if ($real_section_name = Util::guessPath(app_path(), strstr($name_str, '/', true))) {
|
||||
$upper = strtolower($real_section_name[0]) !== $real_section_name[0];
|
||||
} else if ($real_base_controller = Util::guessPath(app_path(), 'controller')) {
|
||||
$upper = strtolower($real_base_controller[0]) !== $real_base_controller[0];
|
||||
}
|
||||
$upper = $upper ?? strtolower($name_str[0]) !== $name_str[0];
|
||||
if ($upper && !$real_name_str) {
|
||||
$name_str = preg_replace_callback('/\/([a-z])/', function ($matches) {
|
||||
return '/' . strtoupper($matches[1]);
|
||||
}, ucfirst($name_str));
|
||||
}
|
||||
$path = "$name_str/" . ($upper ? 'Controller' : 'controller');
|
||||
$name = ucfirst(substr($name, $pos + 1));
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$namespace = str_replace('/', '\\', ($upper ? 'App/' : 'app/') . $path);
|
||||
}
|
||||
|
||||
if (is_file($file)) {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("$file already exists. Do you want to override it? (yes/no)", false);
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
$this->createController($name, $namespace, $file);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $namespace
|
||||
* @param $file
|
||||
* @return void
|
||||
*/
|
||||
protected function createController($name, $namespace, $file)
|
||||
{
|
||||
$path = pathinfo($file, PATHINFO_DIRNAME);
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
$controller_content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace $namespace;
|
||||
|
||||
use support\Request;
|
||||
|
||||
class $name
|
||||
{
|
||||
public function index(Request \$request)
|
||||
{
|
||||
return response(__CLASS__);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EOF;
|
||||
file_put_contents($file, $controller_content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Webman\Console\Util;
|
||||
|
||||
|
||||
class MakeMiddlewareCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'make:middleware';
|
||||
protected static $defaultDescription = 'Make middleware';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Middleware name');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Make middleware $name");
|
||||
|
||||
$name = str_replace('\\', '/', $name);
|
||||
if (!$middleware_str = Util::guessPath(app_path(), 'middleware')) {
|
||||
$middleware_str = Util::guessPath(app_path(), 'controller') === 'Controller' ? 'Middleware' : 'middleware';
|
||||
}
|
||||
$upper = $middleware_str === 'Middleware';
|
||||
if (!($pos = strrpos($name, '/'))) {
|
||||
$name = ucfirst($name);
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $middleware_str . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$namespace = $upper ? 'App\Middleware' : 'app\middleware';
|
||||
} else {
|
||||
if($real_name = Util::guessPath(app_path(), $name)) {
|
||||
$name = $real_name;
|
||||
}
|
||||
if ($upper && !$real_name) {
|
||||
$name = preg_replace_callback('/\/([a-z])/', function ($matches) {
|
||||
return '/' . strtoupper($matches[1]);
|
||||
}, ucfirst($name));
|
||||
}
|
||||
$path = "$middleware_str/" . substr($upper ? ucfirst($name) : $name, 0, $pos);
|
||||
$name = ucfirst(substr($name, $pos + 1));
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$namespace = str_replace('/', '\\', ($upper ? 'App/' : 'app/') . $path);
|
||||
}
|
||||
|
||||
if (is_file($file)) {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("$file already exists. Do you want to override it? (yes/no)", false);
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
$this->createMiddleware($name, $namespace, $file);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $namespace
|
||||
* @param $path
|
||||
* @return void
|
||||
*/
|
||||
protected function createMiddleware($name, $namespace, $file)
|
||||
{
|
||||
$path = pathinfo($file, PATHINFO_DIRNAME);
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
$middleware_content = <<<EOF
|
||||
<?php
|
||||
namespace $namespace;
|
||||
|
||||
use Webman\MiddlewareInterface;
|
||||
use Webman\Http\Response;
|
||||
use Webman\Http\Request;
|
||||
|
||||
class $name implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request \$request, callable \$handler) : Response
|
||||
{
|
||||
return \$handler(\$request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EOF;
|
||||
file_put_contents($file, $middleware_content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
use support\Db;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Webman\Console\Util;
|
||||
|
||||
|
||||
class MakeModelCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'make:model';
|
||||
protected static $defaultDescription = 'Make model';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Model name');
|
||||
$this->addArgument('type', InputArgument::OPTIONAL, 'Type');
|
||||
$this->addOption('connection', 'c', InputOption::VALUE_OPTIONAL, 'Select database connection. ');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$name = Util::nameToClass($name);
|
||||
$type = $input->getArgument('type');
|
||||
$connection = $input->getOption('connection');
|
||||
$output->writeln("Make model $name");
|
||||
if (!($pos = strrpos($name, '/'))) {
|
||||
$name = ucfirst($name);
|
||||
$model_str = Util::guessPath(app_path(), 'model') ?: 'model';
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $model_str . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$namespace = $model_str === 'Model' ? 'App\Model' : 'app\model';
|
||||
} else {
|
||||
$name_str = substr($name, 0, $pos);
|
||||
if($real_name_str = Util::guessPath(app_path(), $name_str)) {
|
||||
$name_str = $real_name_str;
|
||||
} else if ($real_section_name = Util::guessPath(app_path(), strstr($name_str, '/', true))) {
|
||||
$upper = strtolower($real_section_name[0]) !== $real_section_name[0];
|
||||
} else if ($real_base_controller = Util::guessPath(app_path(), 'controller')) {
|
||||
$upper = strtolower($real_base_controller[0]) !== $real_base_controller[0];
|
||||
}
|
||||
$upper = $upper ?? strtolower($name_str[0]) !== $name_str[0];
|
||||
if ($upper && !$real_name_str) {
|
||||
$name_str = preg_replace_callback('/\/([a-z])/', function ($matches) {
|
||||
return '/' . strtoupper($matches[1]);
|
||||
}, ucfirst($name_str));
|
||||
}
|
||||
$path = "$name_str/" . ($upper ? 'Model' : 'model');
|
||||
$name = ucfirst(substr($name, $pos + 1));
|
||||
$file = app_path() . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . "$name.php";
|
||||
$namespace = str_replace('/', '\\', ($upper ? 'App/' : 'app/') . $path);
|
||||
}
|
||||
if (!$type) {
|
||||
$database = config('database');
|
||||
if (isset($database['default']) && strpos($database['default'], 'plugin.') === 0) {
|
||||
$database = false;
|
||||
}
|
||||
$thinkorm = config('thinkorm');
|
||||
if (isset($thinkorm['default']) && strpos($thinkorm['default'], 'plugin.') === 0) {
|
||||
$thinkorm = false;
|
||||
}
|
||||
$type = !$database && $thinkorm ? 'tp' : 'laravel';
|
||||
}
|
||||
|
||||
if (is_file($file)) {
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion("$file already exists. Do you want to override it? (yes/no)", false);
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if ($type == 'tp') {
|
||||
$this->createTpModel($name, $namespace, $file, $connection);
|
||||
} else {
|
||||
$this->createModel($name, $namespace, $file, $connection);
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
* @param $namespace
|
||||
* @param $file
|
||||
* @param string|null $connection
|
||||
* @return void
|
||||
*/
|
||||
protected function createModel($class, $namespace, $file, $connection = null)
|
||||
{
|
||||
$path = pathinfo($file, PATHINFO_DIRNAME);
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
$table = Util::classToName($class);
|
||||
$table_val = 'null';
|
||||
$pk = 'id';
|
||||
$properties = '';
|
||||
$connection = $connection ?: 'mysql';
|
||||
try {
|
||||
$prefix = config("database.connections.$connection.prefix") ?? '';
|
||||
$database = config("database.connections.$connection.database");
|
||||
$inflector = InflectorFactory::create()->build();
|
||||
$table_plura = $inflector->pluralize($inflector->tableize($class));
|
||||
$con = Db::connection($connection);
|
||||
if ($con->select("show tables like '{$prefix}{$table_plura}'")) {
|
||||
$table_val = "'$table'";
|
||||
$table = "{$prefix}{$table_plura}";
|
||||
} else if ($con->select("show tables like '{$prefix}{$table}'")) {
|
||||
$table_val = "'$table'";
|
||||
$table = "{$prefix}{$table}";
|
||||
}
|
||||
$tableComment = $con->select('SELECT table_comment FROM information_schema.`TABLES` WHERE table_schema = ? AND table_name = ?', [$database, $table]);
|
||||
if (!empty($tableComment)) {
|
||||
$comments = $tableComment[0]->table_comment ?? $tableComment[0]->TABLE_COMMENT;
|
||||
$properties .= " * {$table} {$comments}" . PHP_EOL;
|
||||
}
|
||||
foreach ($con->select("select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,COLUMN_COMMENT from INFORMATION_SCHEMA.COLUMNS where table_name = '$table' and table_schema = '$database' ORDER BY ordinal_position") as $item) {
|
||||
if ($item->COLUMN_KEY === 'PRI') {
|
||||
$pk = $item->COLUMN_NAME;
|
||||
$item->COLUMN_COMMENT .= "(主键)";
|
||||
}
|
||||
$type = $this->getType($item->DATA_TYPE);
|
||||
$properties .= " * @property $type \${$item->COLUMN_NAME} {$item->COLUMN_COMMENT}\n";
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
$properties = rtrim($properties) ?: ' *';
|
||||
$model_content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace $namespace;
|
||||
|
||||
use support\Model;
|
||||
|
||||
/**
|
||||
$properties
|
||||
*/
|
||||
class $class extends Model
|
||||
{
|
||||
/**
|
||||
* The connection name for the model.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected \$connection = '$connection';
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected \$table = $table_val;
|
||||
|
||||
/**
|
||||
* The primary key associated with the table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected \$primaryKey = '$pk';
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public \$timestamps = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
EOF;
|
||||
file_put_contents($file, $model_content);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
* @param $namespace
|
||||
* @param $file
|
||||
* @param string|null $connection
|
||||
* @return void
|
||||
*/
|
||||
protected function createTpModel($class, $namespace, $file, $connection = null)
|
||||
{
|
||||
$path = pathinfo($file, PATHINFO_DIRNAME);
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
$table = Util::classToName($class);
|
||||
$table_val = 'null';
|
||||
$pk = 'id';
|
||||
$properties = '';
|
||||
$connection = $connection ?: 'mysql';
|
||||
try {
|
||||
$prefix = config("thinkorm.connections.$connection.prefix") ?? '';
|
||||
$database = config("thinkorm.connections.$connection.database");
|
||||
$con = \think\facade\Db::connect($connection);
|
||||
if ($con->query("show tables like '{$prefix}{$table}'")) {
|
||||
$table = "{$prefix}{$table}";
|
||||
$table_val = "'$table'";
|
||||
} else if ($con->query("show tables like '{$prefix}{$table}s'")) {
|
||||
$table = "{$prefix}{$table}s";
|
||||
$table_val = "'$table'";
|
||||
}
|
||||
$tableComment = $con->query('SELECT table_comment FROM information_schema.`TABLES` WHERE table_schema = ? AND table_name = ?', [$database, $table]);
|
||||
if (!empty($tableComment)) {
|
||||
$comments = $tableComment[0]['table_comment'] ?? $tableComment[0]['TABLE_COMMENT'];
|
||||
$properties .= " * {$table} {$comments}" . PHP_EOL;
|
||||
}
|
||||
foreach ($con->query("select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,COLUMN_COMMENT from INFORMATION_SCHEMA.COLUMNS where table_name = '$table' and table_schema = '$database' ORDER BY ordinal_position") as $item) {
|
||||
if ($item['COLUMN_KEY'] === 'PRI') {
|
||||
$pk = $item['COLUMN_NAME'];
|
||||
$item['COLUMN_COMMENT'] .= "(主键)";
|
||||
}
|
||||
$type = $this->getType($item['DATA_TYPE']);
|
||||
$properties .= " * @property $type \${$item['COLUMN_NAME']} {$item['COLUMN_COMMENT']}\n";
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
$properties = rtrim($properties) ?: ' *';
|
||||
$model_content = <<<EOF
|
||||
<?php
|
||||
|
||||
namespace $namespace;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
$properties
|
||||
*/
|
||||
class $class extends Model
|
||||
{
|
||||
/**
|
||||
* The connection name for the model.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected \$connection = '$connection';
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected \$table = $table_val;
|
||||
|
||||
/**
|
||||
* The primary key associated with the table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected \$pk = '$pk';
|
||||
|
||||
|
||||
}
|
||||
|
||||
EOF;
|
||||
file_put_contents($file, $model_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
protected function getType(string $type)
|
||||
{
|
||||
if (strpos($type, 'int') !== false) {
|
||||
return 'integer';
|
||||
}
|
||||
switch ($type) {
|
||||
case 'varchar':
|
||||
case 'string':
|
||||
case 'text':
|
||||
case 'date':
|
||||
case 'time':
|
||||
case 'guid':
|
||||
case 'datetimetz':
|
||||
case 'datetime':
|
||||
case 'decimal':
|
||||
case 'enum':
|
||||
return 'string';
|
||||
case 'boolean':
|
||||
return 'integer';
|
||||
case 'float':
|
||||
return 'float';
|
||||
default:
|
||||
return 'mixed';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
|
||||
class PluginCreateCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'plugin:create';
|
||||
protected static $defaultDescription = 'Plugin create';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addOption('name', 'name', InputOption::VALUE_REQUIRED, 'Plugin name, for example foo/my-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = strtolower($input->getOption('name'));
|
||||
$output->writeln("Create Plugin $name");
|
||||
if (!strpos($name, '/')) {
|
||||
$output->writeln('<error>Bad name, name must contain character \'/\' , for example foo/MyAdmin</error>');
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$namespace = Util::nameToNamespace($name);
|
||||
|
||||
// Create dir config/plugin/$name
|
||||
if (is_dir($plugin_config_path = config_path()."/plugin/$name")) {
|
||||
$output->writeln("<error>Dir $plugin_config_path already exists</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
if (is_dir($plugin_path = base_path()."/vendor/$name")) {
|
||||
$output->writeln("<error>Dir $plugin_path already exists</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
// Add psr-4
|
||||
if ($err = $this->addAutoloadToComposerJson($name, $namespace)) {
|
||||
$output->writeln("<error>$err</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$this->createConfigFiles($plugin_config_path);
|
||||
|
||||
$this->createVendorFiles($name, $namespace, $plugin_path, $output);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
protected function addAutoloadToComposerJson($name, $namespace)
|
||||
{
|
||||
if (!is_file($composer_json_file = base_path()."/composer.json")) {
|
||||
return "$composer_json_file not exists";
|
||||
}
|
||||
$composer_json = json_decode($composer_json_str = file_get_contents($composer_json_file), true);
|
||||
if (!$composer_json) {
|
||||
return "Bad $composer_json_file";
|
||||
}
|
||||
if(isset($composer_json['autoload']['psr-4'][$namespace."\\"])) {
|
||||
return;
|
||||
}
|
||||
$namespace = str_replace("\\", "\\\\", $namespace);
|
||||
$composer_json_str = str_replace('"psr-4": {', '"psr-4": {'."\n \"$namespace\\\\\" : \"vendor/$name/src\",", $composer_json_str);
|
||||
file_put_contents($composer_json_file, $composer_json_str);
|
||||
}
|
||||
|
||||
protected function createConfigFiles($plugin_config_path)
|
||||
{
|
||||
mkdir($plugin_config_path, 0777, true);
|
||||
$app_str = <<<EOF
|
||||
<?php
|
||||
return [
|
||||
'enable' => true,
|
||||
];
|
||||
EOF;
|
||||
file_put_contents("$plugin_config_path/app.php", $app_str);
|
||||
}
|
||||
|
||||
protected function createVendorFiles($name, $namespace, $plugin_path, $output)
|
||||
{
|
||||
mkdir("$plugin_path/src", 0777, true);
|
||||
$this->createComposerJson($name, $namespace, $plugin_path);
|
||||
if (is_callable('exec')) {
|
||||
exec("composer dumpautoload");
|
||||
} else {
|
||||
$output->writeln("<info>Please run command 'composer dumpautoload'</info>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $namespace
|
||||
* @param $dest
|
||||
* @return void
|
||||
*/
|
||||
protected function createComposerJson($name, $namespace, $dest)
|
||||
{
|
||||
$namespace = str_replace('\\', '\\\\', $namespace);
|
||||
$composer_json_content = <<<EOT
|
||||
{
|
||||
"name": "$name",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"description": "Webman plugin $name",
|
||||
"require": {
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"$namespace\\\\": "src"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOT;
|
||||
file_put_contents("$dest/composer.json", $composer_json_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @param $path_relations
|
||||
* @param $dest_dir
|
||||
* @return void
|
||||
*/
|
||||
protected function writeInstallFile($namespace, $path_relations, $dest_dir)
|
||||
{
|
||||
if (!is_dir($dest_dir)) {
|
||||
mkdir($dest_dir, 0777, true);
|
||||
}
|
||||
$relations = [];
|
||||
foreach($path_relations as $relation) {
|
||||
$relations[$relation] = $relation;
|
||||
}
|
||||
$relations = var_export($relations, true);
|
||||
$install_php_content = <<<EOT
|
||||
<?php
|
||||
namespace $namespace;
|
||||
|
||||
class Install
|
||||
{
|
||||
const WEBMAN_PLUGIN = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static \$pathRelation = $relations;
|
||||
|
||||
/**
|
||||
* Install
|
||||
* @return void
|
||||
*/
|
||||
public static function install()
|
||||
{
|
||||
static::installByRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstall()
|
||||
{
|
||||
self::uninstallByRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* installByRelation
|
||||
* @return void
|
||||
*/
|
||||
public static function installByRelation()
|
||||
{
|
||||
foreach (static::\$pathRelation as \$source => \$dest) {
|
||||
if (\$pos = strrpos(\$dest, '/')) {
|
||||
\$parent_dir = base_path().'/'.substr(\$dest, 0, \$pos);
|
||||
if (!is_dir(\$parent_dir)) {
|
||||
mkdir(\$parent_dir, 0777, true);
|
||||
}
|
||||
}
|
||||
//symlink(__DIR__ . "/\$source", base_path()."/\$dest");
|
||||
copy_dir(__DIR__ . "/\$source", base_path()."/\$dest");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* uninstallByRelation
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstallByRelation()
|
||||
{
|
||||
foreach (static::\$pathRelation as \$source => \$dest) {
|
||||
/*if (is_link(base_path()."/\$dest")) {
|
||||
unlink(base_path()."/\$dest");
|
||||
}*/
|
||||
remove_dir(base_path()."/\$dest");
|
||||
}
|
||||
}
|
||||
}
|
||||
EOT;
|
||||
file_put_contents("$dest_dir/Install.php", $install_php_content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
|
||||
class PluginDisableCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'plugin:disable';
|
||||
protected static $defaultDescription = 'Disable plugin by name';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Plugin name, for example foo/my-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Disable plugin $name");
|
||||
if (!strpos($name, '/')) {
|
||||
$output->writeln('<error>Bad name, name must contain character \'/\' , for example foo/MyAdmin</error>');
|
||||
return self::FAILURE;
|
||||
}
|
||||
$config_file = config_path() . "/plugin/$name/app.php";
|
||||
if (!is_file($config_file)) {
|
||||
return self::SUCCESS;
|
||||
}
|
||||
$config = include $config_file;
|
||||
if (empty($config['enable'])) {
|
||||
return self::SUCCESS;
|
||||
}
|
||||
$config_content = file_get_contents($config_file);
|
||||
$config_content = preg_replace('/(\'enable\' *?=> *?)(true)/', '$1false', $config_content);
|
||||
file_put_contents($config_file, $config_content);
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
|
||||
class PluginEnableCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'plugin:enable';
|
||||
protected static $defaultDescription = 'Enable plugin by name';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Plugin name, for example foo/my-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Enable plugin $name");
|
||||
if (!strpos($name, '/')) {
|
||||
$output->writeln('<error>Bad name, name must contain character \'/\' , for example foo/MyAdmin</error>');
|
||||
return self::FAILURE;
|
||||
}
|
||||
$config_file = config_path() . "/plugin/$name/app.php";
|
||||
if (!is_file($config_file)) {
|
||||
$output->writeln("<error>$config_file not found</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
$config = include $config_file;
|
||||
if (!isset($config['enable'])) {
|
||||
$output->writeln("<error>Config key 'enable' not found</error>");
|
||||
return self::FAILURE;
|
||||
}
|
||||
if ($config['enable']) {
|
||||
return self::SUCCESS;
|
||||
}
|
||||
$config_content = file_get_contents($config_file);
|
||||
$config_content = preg_replace('/(\'enable\' *?=> *?)(false)/', '$1true', $config_content);
|
||||
file_put_contents($config_file, $config_content);
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Util;
|
||||
|
||||
class PluginExportCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'plugin:export';
|
||||
protected static $defaultDescription = 'Plugin export';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addOption('name', 'name', InputOption::VALUE_REQUIRED, 'Plugin name, for example foo/my-admin');
|
||||
$this->addOption('source', 'source', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Directories to export');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('Export Plugin');
|
||||
$name = strtolower($input->getOption('name'));
|
||||
if (!strpos($name, '/')) {
|
||||
$output->writeln('<error>Bad name, name must contain character \'/\' , for example foo/MyAdmin</error>');
|
||||
return self::INVALID;
|
||||
}
|
||||
$namespace = Util::nameToNamespace($name);
|
||||
$path_relations = $input->getOption('source');
|
||||
if (!in_array("config/plugin/$name", $path_relations)) {
|
||||
if (is_dir("config/plugin/$name")) {
|
||||
$path_relations[] = "config/plugin/$name";
|
||||
}
|
||||
}
|
||||
$original_dest = $dest = base_path()."/vendor/$name";
|
||||
$dest .= '/src';
|
||||
$this->writeInstallFile($namespace, $path_relations, $dest);
|
||||
$output->writeln("<info>Create $dest/Install.php</info>");
|
||||
foreach ($path_relations as $source) {
|
||||
$base_path = pathinfo("$dest/$source", PATHINFO_DIRNAME);
|
||||
if (!is_dir($base_path)) {
|
||||
mkdir($base_path, 0777, true);
|
||||
}
|
||||
$output->writeln("<info>Copy $source to $dest/$source </info>");
|
||||
copy_dir($source, "$dest/$source");
|
||||
}
|
||||
$output->writeln("<info>Saved $name to $original_dest</info>");
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $namespace
|
||||
* @param $path_relations
|
||||
* @param $dest_dir
|
||||
* @return void
|
||||
*/
|
||||
protected function writeInstallFile($namespace, $path_relations, $dest_dir)
|
||||
{
|
||||
if (!is_dir($dest_dir)) {
|
||||
mkdir($dest_dir, 0777, true);
|
||||
}
|
||||
$relations = [];
|
||||
foreach($path_relations as $relation) {
|
||||
$relations[$relation] = $relation;
|
||||
}
|
||||
$relations = var_export($relations, true);
|
||||
$install_php_content = <<<EOT
|
||||
<?php
|
||||
namespace $namespace;
|
||||
|
||||
class Install
|
||||
{
|
||||
const WEBMAN_PLUGIN = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static \$pathRelation = $relations;
|
||||
|
||||
/**
|
||||
* Install
|
||||
* @return void
|
||||
*/
|
||||
public static function install()
|
||||
{
|
||||
static::installByRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstall()
|
||||
{
|
||||
self::uninstallByRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* installByRelation
|
||||
* @return void
|
||||
*/
|
||||
public static function installByRelation()
|
||||
{
|
||||
foreach (static::\$pathRelation as \$source => \$dest) {
|
||||
if (\$pos = strrpos(\$dest, '/')) {
|
||||
\$parent_dir = base_path().'/'.substr(\$dest, 0, \$pos);
|
||||
if (!is_dir(\$parent_dir)) {
|
||||
mkdir(\$parent_dir, 0777, true);
|
||||
}
|
||||
}
|
||||
//symlink(__DIR__ . "/\$source", base_path()."/\$dest");
|
||||
copy_dir(__DIR__ . "/\$source", base_path()."/\$dest");
|
||||
echo "Create \$dest\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* uninstallByRelation
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstallByRelation()
|
||||
{
|
||||
foreach (static::\$pathRelation as \$source => \$dest) {
|
||||
\$path = base_path()."/\$dest";
|
||||
if (!is_dir(\$path) && !is_file(\$path)) {
|
||||
continue;
|
||||
}
|
||||
echo "Remove \$dest\r\n";
|
||||
if (is_file(\$path) || is_link(\$path)) {
|
||||
unlink(\$path);
|
||||
continue;
|
||||
}
|
||||
remove_dir(\$path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
EOT;
|
||||
file_put_contents("$dest_dir/Install.php", $install_php_content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Webman\Console\Util;
|
||||
|
||||
|
||||
class PluginInstallCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'plugin:install';
|
||||
protected static $defaultDescription = 'Execute plugin installation script';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Plugin name, for example foo/my-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Execute installation for plugin $name");
|
||||
$namespace = Util::nameToNamespace($name);
|
||||
$install_function = "\\{$namespace}\\Install::install";
|
||||
$plugin_const = "\\{$namespace}\\Install::WEBMAN_PLUGIN";
|
||||
if (defined($plugin_const) && is_callable($install_function)) {
|
||||
$install_function();
|
||||
}
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Webman\Console\Util;
|
||||
|
||||
|
||||
class PluginUninstallCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'plugin:uninstall';
|
||||
protected static $defaultDescription = 'Execute plugin uninstall script';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addArgument('name', InputArgument::REQUIRED, 'Plugin name, for example foo/my-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$name = $input->getArgument('name');
|
||||
$output->writeln("Execute uninstall for plugin $name");
|
||||
if (!strpos($name, '/')) {
|
||||
$output->writeln('<error>Bad name, name must contain character \'/\' , for example foo/MyAdmin</error>');
|
||||
return self::FAILURE;
|
||||
}
|
||||
$namespace = Util::nameToNamespace($name);
|
||||
$uninstall_function = "\\{$namespace}\\Install::uninstall";
|
||||
$plugin_const = "\\{$namespace}\\Install::WEBMAN_PLUGIN";
|
||||
if (defined($plugin_const) && is_callable($uninstall_function)) {
|
||||
$uninstall_function();
|
||||
}
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Application;
|
||||
|
||||
class ReStartCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'restart';
|
||||
protected static $defaultDescription = 'Restart workers. Use mode -d to start in DAEMON mode. Use mode -g to stop gracefully.';
|
||||
|
||||
protected function configure() : void
|
||||
{
|
||||
$this
|
||||
->addOption('daemon', 'd', InputOption::VALUE_NONE, 'DAEMON mode')
|
||||
->addOption('graceful', 'g', InputOption::VALUE_NONE, 'graceful stop');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (\class_exists(\Support\App::class)) {
|
||||
\Support\App::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
Application::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Application;
|
||||
|
||||
class ReloadCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'reload';
|
||||
protected static $defaultDescription = 'Reload codes. Use mode -g to reload gracefully.';
|
||||
|
||||
protected function configure() : void
|
||||
{
|
||||
$this
|
||||
->addOption('graceful', 'd', InputOption::VALUE_NONE, 'graceful reload');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (\class_exists(\Support\App::class)) {
|
||||
\Support\App::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
Application::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Webman\Route;
|
||||
|
||||
class RouteListCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'route:list';
|
||||
protected static $defaultDescription = 'Route list';
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$headers = ['uri', 'method', 'callback', 'middleware', 'name'];
|
||||
$rows = [];
|
||||
foreach (Route::getRoutes() as $route) {
|
||||
foreach ($route->getMethods() as $method) {
|
||||
$cb = $route->getCallback();
|
||||
$cb = $cb instanceof \Closure ? 'Closure' : (is_array($cb) ? json_encode($cb) : var_export($cb, 1));
|
||||
$rows[] = [$route->getPath(), $method, $cb, json_encode($route->getMiddleware() ?: null), $route->getName()];
|
||||
}
|
||||
}
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setHeaders($headers);
|
||||
$table->setRows($rows);
|
||||
$table->render();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Application;
|
||||
|
||||
class StartCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'start';
|
||||
protected static $defaultDescription = 'Start worker in DEBUG mode. Use mode -d to start in DAEMON mode.';
|
||||
|
||||
protected function configure() : void
|
||||
{
|
||||
$this->addOption('daemon', 'd', InputOption::VALUE_NONE, 'DAEMON mode');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (\class_exists(\Support\App::class)) {
|
||||
\Support\App::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
Application::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Application;
|
||||
|
||||
class StatusCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'status';
|
||||
protected static $defaultDescription = 'Get worker status. Use mode -d to show live status.';
|
||||
|
||||
protected function configure() : void
|
||||
{
|
||||
$this->addOption('live', 'd', InputOption::VALUE_NONE, 'show live status');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (\class_exists(\Support\App::class)) {
|
||||
\Support\App::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
Application::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Webman\Console\Application;
|
||||
|
||||
class StopCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'stop';
|
||||
protected static $defaultDescription = 'Stop worker. Use mode -g to stop gracefully.';
|
||||
protected function configure() : void
|
||||
{
|
||||
$this
|
||||
->addOption('graceful', 'g',InputOption::VALUE_NONE, 'graceful stop');
|
||||
}
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (\class_exists(\Support\App::class)) {
|
||||
\Support\App::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
Application::run();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class VersionCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'version';
|
||||
protected static $defaultDescription = 'Show webman version';
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$installed_file = base_path() . '/vendor/composer/installed.php';
|
||||
if (is_file($installed_file)) {
|
||||
$version_info = include $installed_file;
|
||||
}
|
||||
$webman_framework_version = $version_info['versions']['workerman/webman-framework']['pretty_version'] ?? '';
|
||||
$output->writeln("Webman-framework $webman_framework_version");
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
namespace Webman\Console;
|
||||
|
||||
class Install
|
||||
{
|
||||
const WEBMAN_PLUGIN = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $pathRelation = [
|
||||
'config/plugin/webman/console' => 'config/plugin/webman/console',
|
||||
];
|
||||
|
||||
/**
|
||||
* Install
|
||||
* @return void
|
||||
*/
|
||||
public static function install()
|
||||
{
|
||||
$dest = base_path() . "/webman";
|
||||
if (is_dir($dest)) {
|
||||
echo "Installation failed, please remove directory $dest\n";
|
||||
return;
|
||||
}
|
||||
copy(__DIR__ . "/webman", $dest);
|
||||
chmod(base_path() . "/webman", 0755);
|
||||
|
||||
static::installByRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstall()
|
||||
{
|
||||
if (is_file(base_path()."/webman")) {
|
||||
unlink(base_path() . "/webman");
|
||||
}
|
||||
self::uninstallByRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* installByRelation
|
||||
* @return void
|
||||
*/
|
||||
public static function installByRelation()
|
||||
{
|
||||
foreach (static::$pathRelation as $source => $dest) {
|
||||
if ($pos = strrpos($dest, '/')) {
|
||||
$parent_dir = base_path().'/'.substr($dest, 0, $pos);
|
||||
if (!is_dir($parent_dir)) {
|
||||
mkdir($parent_dir, 0777, true);
|
||||
}
|
||||
}
|
||||
copy_dir(__DIR__ . "/$source", base_path()."/$dest");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* uninstallByRelation
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstallByRelation()
|
||||
{
|
||||
foreach (static::$pathRelation as $source => $dest) {
|
||||
$path = base_path()."/$dest";
|
||||
if (!is_dir($path) && !is_file($path)) {
|
||||
continue;
|
||||
}
|
||||
remove_dir($path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace Webman\Console;
|
||||
|
||||
use Doctrine\Inflector\InflectorFactory;
|
||||
|
||||
class Util
|
||||
{
|
||||
public static function nameToNamespace($name)
|
||||
{
|
||||
$namespace = ucfirst($name);
|
||||
$namespace = preg_replace_callback(['/-([a-zA-Z])/', '/(\/[a-zA-Z])/'], function ($matches) {
|
||||
return strtoupper($matches[1]);
|
||||
}, $namespace);
|
||||
return str_replace('/', '\\' ,ucfirst($namespace));
|
||||
}
|
||||
|
||||
public static function classToName($class)
|
||||
{
|
||||
$class = lcfirst($class);
|
||||
return preg_replace_callback(['/([A-Z])/'], function ($matches) {
|
||||
return '_' . strtolower($matches[1]);
|
||||
}, $class);
|
||||
}
|
||||
|
||||
public static function nameToClass($class)
|
||||
{
|
||||
$class = preg_replace_callback(['/-([a-zA-Z])/', '/_([a-zA-Z])/'], function ($matches) {
|
||||
return strtoupper($matches[1]);
|
||||
}, $class);
|
||||
|
||||
if (!($pos = strrpos($class, '/'))) {
|
||||
$class = ucfirst($class);
|
||||
} else {
|
||||
$path = substr($class, 0, $pos);
|
||||
$class = ucfirst(substr($class, $pos + 1));
|
||||
$class = "$path/$class";
|
||||
}
|
||||
return $class;
|
||||
}
|
||||
|
||||
public static function guessPath($base_path, $name, $return_full_path = false)
|
||||
{
|
||||
if (!is_dir($base_path)) {
|
||||
return false;
|
||||
}
|
||||
$names = explode('/', trim(strtolower($name), '/'));
|
||||
$realname = [];
|
||||
$path = $base_path;
|
||||
foreach ($names as $name) {
|
||||
$finded = false;
|
||||
foreach (scandir($path) ?: [] as $tmp_name) {
|
||||
if (strtolower($tmp_name) === $name && is_dir("$path/$tmp_name")) {
|
||||
$path = "$path/$tmp_name";
|
||||
$realname[] = $tmp_name;
|
||||
$finded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$finded) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$realname = implode(DIRECTORY_SEPARATOR, $realname);
|
||||
return $return_full_path ? get_realpath($base_path . DIRECTORY_SEPARATOR . $realname) : $realname;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
',
|
||||
];
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Webman\Config;
|
||||
use Webman\Console\Command;
|
||||
use Webman\Console\Util;
|
||||
use support\Container;
|
||||
if (!Phar::running()) {
|
||||
chdir(__DIR__);
|
||||
}
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
if (!$appConfigFile = config_path('app.php')) {
|
||||
throw new RuntimeException('Config file not found: app.php');
|
||||
}
|
||||
$appConfig = require $appConfigFile;
|
||||
if ($timezone = $appConfig['default_timezone'] ?? '') {
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
if ($errorReporting = $appConfig['error_reporting'] ?? '') {
|
||||
error_reporting($errorReporting);
|
||||
}
|
||||
|
||||
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'])) {
|
||||
foreach (['', '/app'] as $app) {
|
||||
if ($command_str = Util::guessPath(base_path() . "/plugin/$firm{$app}", 'command')) {
|
||||
$command_path = base_path() . "/plugin/$firm{$app}/$command_str";
|
||||
$cli->installCommands($command_path, "plugin\\$firm" . str_replace('/', '\\', $app) . "\\$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();
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 webman
|
||||
|
||||
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.
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
# event
|
||||
webman event plugin
|
||||
|
||||
https://www.workerman.net/plugin/64
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "webman/event",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"description": "Webman event plugin",
|
||||
"require": {
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Webman\\Event\\": "src"
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Event;
|
||||
|
||||
use support\Container;
|
||||
use support\Log;
|
||||
|
||||
class BootStrap implements \Webman\Bootstrap
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $events = [];
|
||||
|
||||
/**
|
||||
* @param $worker
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function start($worker)
|
||||
{
|
||||
static::getEvents([config()]);
|
||||
foreach (static::$events as $name => $events) {
|
||||
// 支持排序,1 2 3 ... 9 a b c...z
|
||||
ksort($events, SORT_NATURAL);
|
||||
foreach ($events as $callbacks) {
|
||||
foreach ($callbacks as $callback) {
|
||||
Event::on($name, $callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $callbacks
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected static function convertCallable($callbacks)
|
||||
{
|
||||
if (\is_array($callbacks)) {
|
||||
$callback = \array_values($callbacks);
|
||||
if (isset($callback[1]) && \is_string($callback[0]) && \class_exists($callback[0])) {
|
||||
return [Container::get($callback[0]), $callback[1]];
|
||||
}
|
||||
}
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $configs
|
||||
* @return void
|
||||
*/
|
||||
protected static function getEvents($configs)
|
||||
{
|
||||
foreach ($configs as $config) {
|
||||
if (!is_array($config)) {
|
||||
continue;
|
||||
}
|
||||
if (isset($config['event']) && is_array($config['event']) && !isset($config['event']['app']['enable'])) {
|
||||
foreach ($config['event'] as $event_name => $callbacks) {
|
||||
$callbacks = static::convertCallable($callbacks);
|
||||
if (is_callable($callbacks)) {
|
||||
static::$events[$event_name][] = [$callbacks];
|
||||
continue;
|
||||
}
|
||||
if (!is_array($callbacks)) {
|
||||
$msg = "Events: $event_name => " .var_export($callbacks, true) . " is not callable\n";
|
||||
echo $msg;
|
||||
Log::error($msg);
|
||||
continue;
|
||||
}
|
||||
ksort($callbacks, SORT_NATURAL);
|
||||
foreach ($callbacks as $id => $callback) {
|
||||
$callback = static::convertCallable($callback);
|
||||
if (is_callable($callback)) {
|
||||
static::$events[$event_name][$id][] = $callback;
|
||||
continue;
|
||||
}
|
||||
$msg = "Events: $event_name => " . var_export($callback, true) . " is not callable\n";
|
||||
echo $msg;
|
||||
Log::error($msg);
|
||||
}
|
||||
}
|
||||
unset($config['event']);
|
||||
}
|
||||
static::getEvents($config);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+161
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Event;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use support\Log;
|
||||
|
||||
class Event
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $eventMap = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $prefixEventMap = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected static $id = 0;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected static $logger;
|
||||
|
||||
/**
|
||||
* @param mixed $event_name
|
||||
* @param callable $listener
|
||||
* @return int
|
||||
*/
|
||||
public static function on($event_name, callable $listener): int
|
||||
{
|
||||
$is_prefix_name = $event_name[strlen($event_name) - 1] === '*';
|
||||
if ($is_prefix_name) {
|
||||
static::$prefixEventMap[substr($event_name, 0, -1)][++static::$id] = $listener;
|
||||
} else {
|
||||
static::$eventMap[$event_name][++static::$id] = $listener;
|
||||
}
|
||||
return static::$id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $event_name
|
||||
* @param integer $id
|
||||
* @return int
|
||||
*/
|
||||
public static function off($event_name, int $id): int
|
||||
{
|
||||
if (isset(static::$eventMap[$event_name][$id])) {
|
||||
unset(static::$eventMap[$event_name][$id]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $event_name
|
||||
* @param mixed $data
|
||||
* @param bool $halt
|
||||
* @return array|null|mixed
|
||||
*/
|
||||
public static function emit($event_name, $data, bool $halt = false)
|
||||
{
|
||||
$listeners = static::getListeners($event_name);
|
||||
$responses = [];
|
||||
foreach ($listeners as $listener) {
|
||||
try {
|
||||
$response = $listener($data, $event_name);
|
||||
} catch (\Throwable $e) {
|
||||
$responses[] = $e;
|
||||
if (!static::$logger && is_callable('\support\Log::error')) {
|
||||
static::$logger = Log::channel();
|
||||
}
|
||||
if (static::$logger) {
|
||||
static::$logger->error($e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$responses[] = $response;
|
||||
if ($halt && !is_null($response)) {
|
||||
return $response;
|
||||
}
|
||||
if ($response === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $halt ? null : $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $event_name
|
||||
* @param mixed $data
|
||||
* @param bool $halt
|
||||
* @return array|null|mixed
|
||||
*/
|
||||
public static function dispatch($event_name, $data, bool $halt = false)
|
||||
{
|
||||
$listeners = static::getListeners($event_name);
|
||||
$responses = [];
|
||||
foreach ($listeners as $listener) {
|
||||
$response = $listener($data, $event_name);
|
||||
$responses[] = $response;
|
||||
if ($halt && !is_null($response)) {
|
||||
return $response;
|
||||
}
|
||||
if ($response === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $halt ? null : $responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function list(): array
|
||||
{
|
||||
$listeners = [];
|
||||
foreach (static::$eventMap as $event_name => $callback_items) {
|
||||
foreach ($callback_items as $id => $callback_item) {
|
||||
$listeners[$id] = [$event_name, $callback_item];
|
||||
}
|
||||
}
|
||||
foreach (static::$prefixEventMap as $event_name => $callback_items) {
|
||||
foreach ($callback_items as $id => $callback_item) {
|
||||
$listeners[$id] = [$event_name . '*', $callback_item];
|
||||
}
|
||||
}
|
||||
ksort($listeners);
|
||||
return $listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $event_name
|
||||
* @return callable[]
|
||||
*/
|
||||
public static function getListeners($event_name): array
|
||||
{
|
||||
$listeners = static::$eventMap[$event_name] ?? [];
|
||||
foreach (static::$prefixEventMap as $name => $callback_items) {
|
||||
if (strpos($event_name, $name) === 0) {
|
||||
$listeners = array_merge($listeners, $callback_items);
|
||||
}
|
||||
}
|
||||
ksort($listeners);
|
||||
return $listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $event_name
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasListener($event_name): bool
|
||||
{
|
||||
return !empty(static::getListeners($event_name));
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Webman\Event;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
|
||||
class EventListCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'event:list';
|
||||
protected static $defaultDescription = 'Show event list';
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$headers = ['id', 'event_name', 'callback'];
|
||||
$rows = [];
|
||||
foreach (Event::list() as $id => $item) {
|
||||
$event_name = $item[0];
|
||||
$callback = $item[1];
|
||||
if (is_array($callback) && is_object($callback[0])) {
|
||||
$callback[0] = get_class($callback[0]);
|
||||
}
|
||||
$cb = $callback instanceof \Closure ? 'Closure' : (is_array($callback) ? json_encode($callback) : var_export($callback, 1));
|
||||
$rows[] = [$id, $event_name, $cb];
|
||||
}
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setHeaders($headers);
|
||||
$table->setRows($rows);
|
||||
$table->render();
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
namespace Webman\Event;
|
||||
|
||||
class Install
|
||||
{
|
||||
const WEBMAN_PLUGIN = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $pathRelation = array (
|
||||
'config/plugin/webman/event' => 'config/plugin/webman/event',
|
||||
);
|
||||
|
||||
/**
|
||||
* Install
|
||||
* @return void
|
||||
*/
|
||||
public static function install()
|
||||
{
|
||||
static::installByRelation();
|
||||
$event_config_path = config_path() . '/event.php';
|
||||
if (!is_file($event_config_path)) {
|
||||
file_put_contents($event_config_path, "<?php\n\nreturn [\n \n];\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstall()
|
||||
{
|
||||
$event_config_path = config_path() . '/event.php';
|
||||
if (is_file($event_config_path)) {
|
||||
unlink($event_config_path);
|
||||
}
|
||||
self::uninstallByRelation();
|
||||
}
|
||||
|
||||
/**
|
||||
* installByRelation
|
||||
* @return void
|
||||
*/
|
||||
public static function installByRelation()
|
||||
{
|
||||
foreach (static::$pathRelation as $source => $dest) {
|
||||
if ($pos = strrpos($dest, '/')) {
|
||||
$parent_dir = base_path().'/'.substr($dest, 0, $pos);
|
||||
if (!is_dir($parent_dir)) {
|
||||
mkdir($parent_dir, 0777, true);
|
||||
}
|
||||
}
|
||||
//symlink(__DIR__ . "/$source", base_path()."/$dest");
|
||||
copy_dir(__DIR__ . "/$source", base_path()."/$dest");
|
||||
echo "Create $dest
|
||||
";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* uninstallByRelation
|
||||
* @return void
|
||||
*/
|
||||
public static function uninstallByRelation()
|
||||
{
|
||||
foreach (static::$pathRelation as $source => $dest) {
|
||||
$path = base_path()."/$dest";
|
||||
if (!is_dir($path) && !is_file($path)) {
|
||||
continue;
|
||||
}
|
||||
echo "Remove $dest
|
||||
";
|
||||
if (is_file($path) || is_link($path)) {
|
||||
unlink($path);
|
||||
continue;
|
||||
}
|
||||
remove_dir($path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
return [
|
||||
'enable' => true,
|
||||
];
|
||||
@@ -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 [
|
||||
Webman\Event\BootStrap::class,
|
||||
];
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Webman\Event\EventListCommand;
|
||||
|
||||
return [
|
||||
EventListCommand::class
|
||||
];
|
||||
Reference in New Issue
Block a user