作者 郭文星

123

正在显示 100 个修改的文件 包含 4389 行增加16 行删除

要显示太多修改。

为保证性能只显示 100 of 100+ 个文件。

1 -/nbproject/  
2 -/runtime/*  
3 -/addons/*  
4 -/public/assets/libs/  
5 -/public/assets/addons/*  
6 -/public/uploads/*  
7 -.idea  
8 -composer.lock  
9 -*.log  
10 -*.css.map  
11 -!.gitkeep  
12 -.env  
13 -.svn  
14 -.vscode  
15 -node_modules  
16 -.user.ini  
  1 +# Default ignored files
  2 +/shelf/
  3 +/workspace.xml
  4 +# Editor-based HTTP Client requests
  5 +/httpRequests/
  6 +# Datasource local storage ignored files
  7 +/dataSources/
  8 +/dataSources.local.xml
  1 +<component name="InspectionProjectProfileManager">
  2 + <profile version="1.0">
  3 + <option name="myName" value="Project Default" />
  4 + <inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
  5 + </profile>
  6 +</component>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="JSHintConfiguration" version="2.13.0" use-config-file="true" use-custom-config-file="true" custom-config-file-path="$PROJECT_DIR$/public/assets/libs/bootstrap/grunt/.jshintrc">
  4 + <option bitwise="true" />
  5 + <option browser="true" />
  6 + <option curly="true" />
  7 + <option eqeqeq="true" />
  8 + <option forin="true" />
  9 + <option maxerr="50" />
  10 + <option noarg="true" />
  11 + <option noempty="true" />
  12 + <option nonew="true" />
  13 + <option strict="true" />
  14 + <option undef="true" />
  15 + </component>
  16 +</project>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="ProjectModuleManager">
  4 + <modules>
  5 + <module fileurl="file://$PROJECT_DIR$/.idea/net_car.iml" filepath="$PROJECT_DIR$/.idea/net_car.iml" />
  6 + </modules>
  7 + </component>
  8 +</project>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<module type="WEB_MODULE" version="4">
  3 + <component name="NewModuleRootManager">
  4 + <content url="file://$MODULE_DIR$">
  5 + <sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
  6 + <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
  7 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher-contracts" />
  8 + <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
  9 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
  10 + <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
  11 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
  12 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
  13 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/cache" />
  14 + <excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
  15 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/psr-http-message-bridge" />
  16 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
  17 + <excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
  18 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
  19 + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/cache" />
  20 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
  21 + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
  22 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-exporter" />
  23 + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
  24 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/cache-contracts" />
  25 + <excludeFolder url="file://$MODULE_DIR$/vendor/maennchen/zipstream-php" />
  26 + <excludeFolder url="file://$MODULE_DIR$/vendor/txthinking/mailer" />
  27 + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/event-dispatcher" />
  28 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/finder" />
  29 + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
  30 + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-client" />
  31 + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" />
  32 + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-factory" />
  33 + <excludeFolder url="file://$MODULE_DIR$/vendor/nelexa/zip" />
  34 + <excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/php-enum" />
  35 + <excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" />
  36 + <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-captcha" />
  37 + <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-installer" />
  38 + <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-queue" />
  39 + <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-helper" />
  40 + <excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
  41 + <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/wechat" />
  42 + <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/pinyin" />
  43 + <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/socialite" />
  44 + <excludeFolder url="file://$MODULE_DIR$/vendor/easywechat-composer/easywechat-composer" />
  45 + <excludeFolder url="file://$MODULE_DIR$/vendor/ezyang/htmlpurifier" />
  46 + <excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/matrix" />
  47 + <excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/complex" />
  48 + <excludeFolder url="file://$MODULE_DIR$/vendor/phpoffice/phpspreadsheet" />
  49 + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
  50 + <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
  51 + <excludeFolder url="file://$MODULE_DIR$/vendor/pimple/pimple" />
  52 + <excludeFolder url="file://$MODULE_DIR$/vendor/karsonzhang/fastadmin-addons" />
  53 + </content>
  54 + <orderEntry type="inheritedJdk" />
  55 + <orderEntry type="sourceFolder" forTests="false" />
  56 + </component>
  57 +</module>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="PhpIncludePathManager">
  4 + <include_path>
  5 + <path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
  6 + <path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
  7 + <path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
  8 + <path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
  9 + <path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
  10 + <path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
  11 + <path value="$PROJECT_DIR$/vendor/symfony/cache" />
  12 + <path value="$PROJECT_DIR$/vendor/paragonie/random_compat" />
  13 + <path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" />
  14 + <path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
  15 + <path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
  16 + <path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
  17 + <path value="$PROJECT_DIR$/vendor/psr/cache" />
  18 + <path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
  19 + <path value="$PROJECT_DIR$/vendor/psr/log" />
  20 + <path value="$PROJECT_DIR$/vendor/symfony/var-exporter" />
  21 + <path value="$PROJECT_DIR$/vendor/psr/http-message" />
  22 + <path value="$PROJECT_DIR$/vendor/symfony/cache-contracts" />
  23 + <path value="$PROJECT_DIR$/vendor/maennchen/zipstream-php" />
  24 + <path value="$PROJECT_DIR$/vendor/txthinking/mailer" />
  25 + <path value="$PROJECT_DIR$/vendor/psr/event-dispatcher" />
  26 + <path value="$PROJECT_DIR$/vendor/symfony/finder" />
  27 + <path value="$PROJECT_DIR$/vendor/psr/container" />
  28 + <path value="$PROJECT_DIR$/vendor/psr/http-client" />
  29 + <path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
  30 + <path value="$PROJECT_DIR$/vendor/psr/http-factory" />
  31 + <path value="$PROJECT_DIR$/vendor/nelexa/zip" />
  32 + <path value="$PROJECT_DIR$/vendor/myclabs/php-enum" />
  33 + <path value="$PROJECT_DIR$/vendor/monolog/monolog" />
  34 + <path value="$PROJECT_DIR$/vendor/topthink/think-captcha" />
  35 + <path value="$PROJECT_DIR$/vendor/topthink/think-installer" />
  36 + <path value="$PROJECT_DIR$/vendor/topthink/think-queue" />
  37 + <path value="$PROJECT_DIR$/vendor/topthink/think-helper" />
  38 + <path value="$PROJECT_DIR$/vendor/composer" />
  39 + <path value="$PROJECT_DIR$/vendor/overtrue/wechat" />
  40 + <path value="$PROJECT_DIR$/vendor/overtrue/pinyin" />
  41 + <path value="$PROJECT_DIR$/vendor/overtrue/socialite" />
  42 + <path value="$PROJECT_DIR$/vendor/easywechat-composer/easywechat-composer" />
  43 + <path value="$PROJECT_DIR$/vendor/ezyang/htmlpurifier" />
  44 + <path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
  45 + <path value="$PROJECT_DIR$/vendor/markbaker/complex" />
  46 + <path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
  47 + <path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
  48 + <path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
  49 + <path value="$PROJECT_DIR$/vendor/pimple/pimple" />
  50 + <path value="$PROJECT_DIR$/vendor/karsonzhang/fastadmin-addons" />
  51 + </include_path>
  52 + </component>
  53 + <component name="PhpProjectSharedConfiguration" php_language_level="7.2" />
  54 +</project>
  1 +deny from all
  1 +{"files":["application\\admin\\controller\\Command.php","application\\admin\\lang\\zh-cn\\command.php","application\\admin\\model\\Command.php","application\\admin\\validate\\Command.php","application\\admin\\view\\command\\add.html","application\\admin\\view\\command\\detail.html","application\\admin\\view\\command\\index.html","public\\assets\\js\\backend\\command.js"],"license":"regular","licenseto":"15629","licensekey":"t3xqhifIVFUO9y10 \/A0tfljqlYH+SOyMs1bg+A==","domains":["netcar.com"],"licensecodes":[],"validations":["9c3b042fdd8c2e4e21e4da30dcc79ba1"],"menus":["command","command\/index","command\/add","command\/detail","command\/command","command\/execute","command\/del","command\/multi"]}
  1 +<?php
  2 +
  3 +namespace addons\command;
  4 +
  5 +use app\common\library\Menu;
  6 +use think\Addons;
  7 +
  8 +/**
  9 + * 在线命令插件
  10 + */
  11 +class Command extends Addons
  12 +{
  13 +
  14 + /**
  15 + * 插件安装方法
  16 + * @return bool
  17 + */
  18 + public function install()
  19 + {
  20 + $menu = [
  21 + [
  22 + 'name' => 'command',
  23 + 'title' => '在线命令管理',
  24 + 'icon' => 'fa fa-terminal',
  25 + 'sublist' => [
  26 + ['name' => 'command/index', 'title' => '查看'],
  27 + ['name' => 'command/add', 'title' => '添加'],
  28 + ['name' => 'command/detail', 'title' => '详情'],
  29 + ['name' => 'command/command', 'title' => '生成并执行命令'],
  30 + ['name' => 'command/execute', 'title' => '再次执行命令'],
  31 + ['name' => 'command/del', 'title' => '删除'],
  32 + ['name' => 'command/multi', 'title' => '批量更新'],
  33 + ]
  34 + ]
  35 + ];
  36 + Menu::create($menu);
  37 + return true;
  38 + }
  39 +
  40 + /**
  41 + * 插件卸载方法
  42 + * @return bool
  43 + */
  44 + public function uninstall()
  45 + {
  46 + Menu::delete('command');
  47 + return true;
  48 + }
  49 +
  50 + /**
  51 + * 插件启用方法
  52 + * @return bool
  53 + */
  54 + public function enable()
  55 + {
  56 + Menu::enable('command');
  57 + return true;
  58 + }
  59 +
  60 + /**
  61 + * 插件禁用方法
  62 + * @return bool
  63 + */
  64 + public function disable()
  65 + {
  66 + Menu::disable('command');
  67 + return true;
  68 + }
  69 +
  70 +}
  1 +<?php
  2 +
  3 +return [
  4 +];
  1 +<?php
  2 +
  3 +namespace addons\command\controller;
  4 +
  5 +use think\addons\Controller;
  6 +
  7 +class Index extends Controller
  8 +{
  9 +
  10 + public function index()
  11 + {
  12 + $this->error("当前插件暂无前台页面");
  13 + }
  14 +
  15 +}
  1 +name = command
  2 +title = 在线命令
  3 +intro = 可在线执行一键生成CRUD、一键生成菜单等相关命令
  4 +author = FastAdmin
  5 +website = https://www.fastadmin.net
  6 +version = 1.1.1
  7 +state = 1
  8 +url = /addons/command
  9 +license = regular
  10 +licenseto = 15629
  1 +CREATE TABLE IF NOT EXISTS `__PREFIX__command` (
  2 + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
  3 + `type` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '类型',
  4 + `params` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '参数',
  5 + `command` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '命令',
  6 + `content` text COMMENT '返回结果',
  7 + `executetime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '执行时间',
  8 + `createtime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '创建时间',
  9 + `updatetime` bigint(16) UNSIGNED DEFAULT NULL COMMENT '更新时间',
  10 + `status` enum('successed','failured') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'failured' COMMENT '状态',
  11 + PRIMARY KEY (`id`) USING BTREE
  12 +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '在线命令表';
  1 +<?php
  2 +
  3 +namespace addons\command\library;
  4 +
  5 +/**
  6 + * Class Output
  7 + */
  8 +class Output extends \think\console\Output
  9 +{
  10 +
  11 + protected $message = [];
  12 +
  13 + public function __construct($driver = 'console')
  14 + {
  15 + parent::__construct($driver);
  16 + }
  17 +
  18 + protected function block($style, $message)
  19 + {
  20 + $this->message[] = $message;
  21 + }
  22 +
  23 + public function getMessage()
  24 + {
  25 + return $this->message;
  26 + }
  27 +
  28 +}
  1 +{"files":["application\\admin\\controller\\Epay.php","public\\assets\\addons\\epay\\css\\common.css","public\\assets\\addons\\epay\\images\\alipay.png","public\\assets\\addons\\epay\\images\\expired.png","public\\assets\\addons\\epay\\images\\logo-alipay.png","public\\assets\\addons\\epay\\images\\logo-wechat.png","public\\assets\\addons\\epay\\images\\paid.png","public\\assets\\addons\\epay\\images\\scan.png","public\\assets\\addons\\epay\\images\\screenshot-alipay.png","public\\assets\\addons\\epay\\images\\screenshot-wechat.png","public\\assets\\addons\\epay\\images\\wechat.png","public\\assets\\addons\\epay\\js\\common.js","public\\assets\\addons\\epay\\js\\jquery.qrcode.min.js","public\\assets\\addons\\epay\\less\\common.less"],"license":"regular","licenseto":"15629","licensekey":"jDldGfubs2Z1LF0e lqiyCf\/v9miOo5GwX7kPXA==","domains":["netcar.com"],"licensecodes":[],"validations":["9c3b042fdd8c2e4e21e4da30dcc79ba1"]}
  1 +<?php
  2 +
  3 +namespace addons\epay;
  4 +
  5 +use addons\epay\library\Service;
  6 +use think\Addons;
  7 +use think\Config;
  8 +use think\Loader;
  9 +
  10 +/**
  11 + * 微信支付宝整合插件
  12 + */
  13 +class Epay extends Addons
  14 +{
  15 +
  16 + /**
  17 + * 插件安装方法
  18 + * @return bool
  19 + */
  20 + public function install()
  21 + {
  22 + return true;
  23 + }
  24 +
  25 + /**
  26 + * 插件卸载方法
  27 + * @return bool
  28 + */
  29 + public function uninstall()
  30 + {
  31 + return true;
  32 + }
  33 +
  34 + /**
  35 + * 插件启用方法
  36 + * @return bool
  37 + */
  38 + public function enable()
  39 + {
  40 + return true;
  41 + }
  42 +
  43 + /**
  44 + * 插件禁用方法
  45 + * @return bool
  46 + */
  47 + public function disable()
  48 + {
  49 + return true;
  50 + }
  51 +
  52 + // 支持自定义加载
  53 + public function epayConfigInit()
  54 + {
  55 + $this->actionBegin();
  56 + }
  57 +
  58 + // 插件方法加载开始
  59 + public function addonActionBegin()
  60 + {
  61 + $this->actionBegin();
  62 + }
  63 +
  64 + // 模块控制器方法加载开始
  65 + public function actionBegin()
  66 + {
  67 + //添加命名空间
  68 + if (!class_exists('\Yansongda\Pay\Pay')) {
  69 +
  70 + //SDK版本
  71 + $version = Service::getSdkVersion();
  72 +
  73 + $libraryDir = ADDON_PATH . 'epay' . DS . 'library' . DS;
  74 + Loader::addNamespace('Yansongda\Pay', $libraryDir . $version . DS . 'Yansongda' . DS . 'Pay' . DS);
  75 +
  76 + $checkArr = [
  77 + '\Hyperf\Context\Context' => 'context',
  78 + '\Hyperf\Contract\Castable' => 'contract',
  79 + '\Hyperf\Engine\Constant' => 'engine',
  80 + '\Hyperf\Macroable\Macroable' => 'macroable',
  81 + '\Hyperf\Pimple\Container' => 'pimple',
  82 + '\Hyperf\Utils\Arr' => 'utils',
  83 + ];
  84 + foreach ($checkArr as $index => $item) {
  85 + if (!class_exists($index)) {
  86 + Loader::addNamespace(substr($index, 1, strrpos($index, '\\') - 1), $libraryDir . 'hyperf' . DS . $item . DS . 'src' . DS);
  87 + }
  88 + }
  89 +
  90 + if (!class_exists('\Yansongda\Supports\Logger')) {
  91 + Loader::addNamespace('Yansongda\Supports', $libraryDir . $version . DS . 'Yansongda' . DS . 'Supports' . DS);
  92 + }
  93 +
  94 + // V3需载入辅助函数
  95 + if ($version == Service::SDK_VERSION_V3) {
  96 + require_once $libraryDir . $version . DS . 'Yansongda' . DS . 'Pay' . DS . 'Functions.php';
  97 + }
  98 + }
  99 + }
  100 +}
  1 +-----BEGIN CERTIFICATE-----
  2 +MIID8DCCAtigAwIBAgIUI9c+NLMTUooCydaSivNg0qlO4pcwDQYJKoZIhvcNAQEL
  3 +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
  4 +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
  5 +Q0EwHhcNMjExMjEwMDMxMDQyWhcNMjYxMjA5MDMxMDQyWjCBgTETMBEGA1UEAwwK
  6 +MTU1NzI4MzU3MTEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL
  7 +DCTmt7HlnLPmnoHpgJ/liJvmg7Pnp5HmioDmnInpmZDlhazlj7gxCzAJBgNVBAYM
  8 +AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
  9 +AQoCggEBAJX49o4WWgRLNyhTzSctwer8/GQM934b0gIERFdoEN5ciPBWcTTIvuzr
  10 +z+H4puHR4icmHcyEAnOJlWc1d/nhOLZTMYGnk+DGhO+eKs4RovwqIZRbMzN2jTuF
  11 +pecl6YvQoF3InqcKjJjPWfUQvKevSdnMM0/tatZwB4liHBtLL4Lm4yEtHnoMm8kI
  12 +/qSG9qz3anSDNyGglV0HQm+yeAVdphRnccKHSGDl+ZXS28fYeRV+nXtM8FHme0Zt
  13 +1jtOikMWetiQ5l8RUxAZFHdhHTeH0r2hDpNfEANt5oqyUQZ6b7BilyzHd8faC34J
  14 +vegofWjg7CwXHix7p3KwkCubsIlRYKsCAwEAAaOBgTB/MAkGA1UdEwQCMAAwCwYD
  15 +VR0PBAQDAgTwMGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNhLml0cnVzLmNv
  16 +bS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1
  17 +NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAuuQY/gKETCEos68v
  18 +rPq8C/v50PjZxGawXWfpuudrbvi4ZiH8x+Sd+JRWXn+8mQX9Y+qYWcaeb2UIrQm1
  19 +FYP9H2nanncT75H4NpPCmCnNtgqI9/nyxwfLgViwvnTiaPQs4PEDpHJnyJeexgCV
  20 +j5EzkPFC7izcdhLQ4ruE/1sa8c8ww3yKfdUcZz+zQzb+J37bwcZtoCm5lHm9azAP
  21 +mwAMlZiIP9C8VEk0UsMC4SfDOIufjgLmTsoOg4VkIyCQ8tZBsmY7IG5W+CmQl478
  22 +duxt2bOa4V8FEspoRJ059i8inK29Bj1hWKK69CmgMI6qUToHgwRtd6g2LTbt8Mwy
  23 +K5gWAg==
  24 +-----END CERTIFICATE-----
  1 +-----BEGIN PRIVATE KEY-----
  2 +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV+PaOFloESzco
  3 +U80nLcHq/PxkDPd+G9ICBERXaBDeXIjwVnE0yL7s68/h+Kbh0eInJh3MhAJziZVn
  4 +NXf54Ti2UzGBp5PgxoTvnirOEaL8KiGUWzMzdo07haXnJemL0KBdyJ6nCoyYz1n1
  5 +ELynr0nZzDNP7WrWcAeJYhwbSy+C5uMhLR56DJvJCP6khvas92p0gzchoJVdB0Jv
  6 +sngFXaYUZ3HCh0hg5fmV0tvH2HkVfp17TPBR5ntGbdY7TopDFnrYkOZfEVMQGRR3
  7 +YR03h9K9oQ6TXxADbeaKslEGem+wYpcsx3fH2gt+Cb3oKH1o4OwsFx4se6dysJAr
  8 +m7CJUWCrAgMBAAECggEAeJjGyuM2Z5WEOS2KHyMweKIO4vV6XzJH4c9ElXTde7G1
  9 +fG5GgkebIDFP7UpiB90ampiQBdFxly7fsSrR22I8lIC5oqT6yRlZ9MWfbi3IfjXy
  10 +4rWjqpJ+Z4rFKebWstOE4WVeDrzw6kBy+hoHWmAeZsFJ0c/8xYs4xETcjSYcgomk
  11 +oJwjegWz5YwuiwT8wm9WFFJK9CQ1T8V3xDqDKUnmtT/bak7qpfu3Gkf4ddd7iWzU
  12 +MqrLFZrsYZoFjBq5Bq/sPDFB4jox8/5v/Cztwin3+H4nJoHN4tWk1PuUXaj4UPvW
  13 +iVNeTjUjttVVA2N3hdIV7Wabp9UulES+aOoA9UB04QKBgQDFQ8AOO37EiEKzHqch
  14 +rp7gwO5zQxyEZJ8/xrrpc/vTmTrJLqWkiMq51+y0Wd1H4IUt6s4uR01fEytcnwmK
  15 +7pmYePy2rPymoxRKYTpbPiueNTQhc7Ch3Ipt4Nuuu724nxccacjnPVOUZhd3WDtQ
  16 +sh6L5TiKtG/EZgEpzFz1p06kVwKBgQDCoG3hDlc2MfzHQwP0b26qwT02tmU9+Nb2
  17 +UDJCmDxrBD7wieH3N5Pep/dbOSFAFiLQvXF/aVUmta/B9c4YXpFZnob2nFFkyyOq
  18 +OhMLqOsRrn/agYKwlR8WIwakCOFBNevJFNPRm2TSPkc1SOefbT628L/ytOFIc5f5
  19 +C/GFyEIRzQKBgQCqM7SoEwljxTXXmqEV3LbUsWKMLOGUEcWdn3hyKVKk1db08ryU
  20 +5DoWc4mWatsUQ1JltezD3V2SpNWLCuWO7CO2fN1/OG6d0KOXMaDKTTARR1J9cnZ6
  21 +kfEWsSaa8v3VJ98m8tcXPcC5kAS7QnObQrQ8fEB23Qx6ZpCmYd/rTLn77QKBgELz
  22 +DNkuJbHu4BVraCDddCQo9PWtKIZ44vk3/RILbD0j6MK1q5Vu+N7OyLgInvd8pd1I
  23 +TZKWmUQHm74M4Chh334Emkia3STe/BXChGquYK5kaHaxtlMn+NmUkuQ6acIU2lcB
  24 +YxvnaHcqFTSvL34YBkrH5j+HGTfjC5QQF7T9fjzBAoGASGGE4EbPGfvzs3wTcpkY
  25 +99iiUHI2sIUaVByhCvUnh8IUzENsf3lDnV92l4MszIL7wzVALNW5sbqOVlnF7/FM
  26 +0cxK6XDbS8xlbndin2Kmale23esilo9dVU6URTxbUE1f5MdgNgG3duQELVnN/our
  27 +b7P/crZDjuCZ5IdVJHZy3+c=
  28 +-----END PRIVATE KEY-----
  1 +<form id="config-form" class="edit-form form-horizontal" role="form" data-toggle="validator" method="POST" action="">
  2 +
  3 + <div class="panel panel-default panel-intro">
  4 + <div class="panel-heading">
  5 + <ul class="nav nav-tabs nav-group">
  6 + <li class="active"><a href="#wechat" data-toggle="tab">微信支付</a></li>
  7 + <li><a href="#alipay" data-toggle="tab">支付宝</a></li>
  8 + </ul>
  9 + </div>
  10 +
  11 + <div class="panel-body">
  12 + <div id="myTabContent" class="tab-content">
  13 + {foreach $addon.config as $item}
  14 + {if $item.name=='version'}
  15 + <input type="hidden" value="{$item.value}" name="row[version]"/>
  16 +
  17 + {elseif $item.name=='wechat'/}
  18 + <div class="tab-pane fade active in" id="wechat">
  19 + <table class="table table-striped table-config">
  20 + <tbody>
  21 + <tr>
  22 + <td width="20%">APP的app_id</td>
  23 + <td>
  24 + <div class="row">
  25 + <div class="col-sm-8 col-xs-12">
  26 + <input type="text" name="row[wechat][appid]" value="{$item.value.appid|default=''}" class="form-control" data-rule="" data-tip="APP应用中支付时使用"/>
  27 + </div>
  28 + <div class="col-sm-4"></div>
  29 + </div>
  30 + </td>
  31 + </tr>
  32 + <tr>
  33 + <td>公众号的app_id</td>
  34 + <td>
  35 + <div class="row">
  36 + <div class="col-sm-8 col-xs-12">
  37 + <input type="text" name="row[wechat][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="" data-tip="公众号中支付时使用"/>
  38 + </div>
  39 + <div class="col-sm-4"></div>
  40 + </div>
  41 + </td>
  42 + </tr>
  43 + <tr>
  44 + <td>公众号的app_secret</td>
  45 + <td>
  46 + <div class="row">
  47 + <div class="col-sm-8 col-xs-12">
  48 + <input type="text" name="row[wechat][app_secret]" value="{$item.value.app_secret|default=''}" class="form-control" data-rule="" data-tip="公众号中支付时使用"/>
  49 + </div>
  50 + <div class="col-sm-4"></div>
  51 + </div>
  52 + </td>
  53 + </tr>
  54 + <tr>
  55 + <td>小程序的app_id</td>
  56 + <td>
  57 + <div class="row">
  58 + <div class="col-sm-8 col-xs-12">
  59 + <input type="text" name="row[wechat][miniapp_id]" value="{$item.value.miniapp_id|default=''}" class="form-control" data-rule="" data-tip="仅在小程序支付时使用"/>
  60 + </div>
  61 + <div class="col-sm-4"></div>
  62 + </div>
  63 + </td>
  64 + </tr>
  65 + <tr>
  66 + <td>微信支付商户号</td>
  67 + <td>
  68 + <div class="row">
  69 + <div class="col-sm-8 col-xs-12">
  70 + <input type="text" name="row[wechat][mch_id]" value="{$item.value.mch_id|default=''}" class="form-control" data-rule="" data-tip=""/>
  71 + </div>
  72 + <div class="col-sm-4"></div>
  73 + </div>
  74 + </td>
  75 + </tr>
  76 + <tr>
  77 + <td>微信支付商户API密钥V2</td>
  78 + <td>
  79 + <div class="row">
  80 + <div class="col-sm-8 col-xs-12">
  81 + <input type="text" name="row[wechat][key]" value="{$item.value.key|default=''}" class="form-control" data-rule="" data-tip=""/>
  82 + </div>
  83 + <div class="col-sm-4"></div>
  84 + </div>
  85 + </td>
  86 + </tr>
  87 + <tr>
  88 + <td>微信支付商户API密钥V3</td>
  89 + <td>
  90 + <div class="row">
  91 + <div class="col-sm-8 col-xs-12">
  92 + <input type="text" name="row[wechat][key_v3]" value="{$item.value.key_v3|default=''}" class="form-control" data-rule="" data-tip=""/>
  93 + </div>
  94 + <div class="col-sm-4"></div>
  95 + </div>
  96 + </td>
  97 + </tr>
  98 + <tr>
  99 + <td>支付模式</td>
  100 + <td>
  101 + <div class="row">
  102 + <div class="col-sm-8 col-xs-12">
  103 + {:Form::radios('row[wechat][mode]',['normal'=>'正式环境','dev'=>'沙箱环境','service'=>'服务商模式'],$item.value.mode??'normal')}
  104 + <div style="margin-top:5px;" data-type="dev" class="text-muted {if ($item.value.mode??'')!=='dev'}hidden{/if}">
  105 + <i class="fa fa-info-circle"></i> 沙箱环境:<a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_1&index=2" target="_blank">微信支付验收指引</a>
  106 + </div>
  107 + </div>
  108 + <div class="col-sm-4"></div>
  109 + </div>
  110 + </td>
  111 + </tr>
  112 + <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
  113 + <td>子商户商户号ID</td>
  114 + <td>
  115 + <div class="row">
  116 + <div class="col-sm-8 col-xs-12">
  117 + <input type="text" name="row[wechat][sub_mch_id]" value="{$item.value.sub_mch_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
  118 + </div>
  119 + <div class="col-sm-4"></div>
  120 + </div>
  121 + </td>
  122 + </tr>
  123 + <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
  124 + <td>子商户APP的app_id</td>
  125 + <td>
  126 + <div class="row">
  127 + <div class="col-sm-8 col-xs-12">
  128 + <input type="text" name="row[wechat][sub_appid]" value="{$item.value.sub_appid|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
  129 + </div>
  130 + <div class="col-sm-4"></div>
  131 + </div>
  132 + </td>
  133 + </tr>
  134 + <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
  135 + <td>子商户公众号的app_id</td>
  136 + <td>
  137 + <div class="row">
  138 + <div class="col-sm-8 col-xs-12">
  139 + <input type="text" name="row[wechat][sub_app_id]" value="{$item.value.sub_app_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
  140 + </div>
  141 + <div class="col-sm-4"></div>
  142 + </div>
  143 + </td>
  144 + </tr>
  145 + <tr data-type="service" class="{:$item.value.mode!='service'?'hidden':''}">
  146 + <td>子商户小程序的app_id</td>
  147 + <td>
  148 + <div class="row">
  149 + <div class="col-sm-8 col-xs-12">
  150 + <input type="text" name="row[wechat][sub_miniapp_id]" value="{$item.value.sub_miniapp_id|default=''}" class="form-control" data-rule="" data-tip="如果未用到子商户,请勿填写"/>
  151 + </div>
  152 + <div class="col-sm-4"></div>
  153 + </div>
  154 + </td>
  155 + </tr>
  156 + <tr>
  157 + <td>回调通知地址</td>
  158 + <td>
  159 + <div class="row">
  160 + <div class="col-sm-8 col-xs-12">
  161 + <input type="text" name="row[wechat][notify_url]" value="{$item.value.notify_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
  162 + </div>
  163 + <div class="col-sm-4"></div>
  164 + </div>
  165 + </td>
  166 + </tr>
  167 + <tr>
  168 + <td>微信支付API证书cert</td>
  169 + <td>
  170 + <div class="row">
  171 + <div class="col-sm-8 col-xs-12">
  172 + <div class="input-group">
  173 + <input id="c-cert_client" class="form-control" size="50" name="row[wechat][cert_client]" type="text" value="{$item.value.cert_client|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
  174 + <div class="input-group-addon no-border no-padding">
  175 + <span><button type="button" id="faupload-cert_client" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_client"}' data-mimetype="pem" data-input-id="c-cert_client" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  176 + </div>
  177 + <span class="msg-box n-right" for="c-cert_client"></span>
  178 + </div>
  179 + <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付API证书?</a></div>
  180 + </div>
  181 + <div class="col-sm-4"></div>
  182 + </div>
  183 + </td>
  184 + </tr>
  185 + <tr>
  186 + <td>微信支付API证书key</td>
  187 + <td>
  188 + <div class="row">
  189 + <div class="col-sm-8 col-xs-12">
  190 + <div class="input-group">
  191 + <input id="c-cert_key" class="form-control" size="50" name="row[wechat][cert_key]" type="text" value="{$item.value.cert_key|htmlentities}" data-tip="可选, 仅在退款、红包等情况时需要用到">
  192 + <div class="input-group-addon no-border no-padding">
  193 + <span><button type="button" id="faupload-cert_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"cert_key"}' data-mimetype="pem" data-input-id="c-cert_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  194 + </div>
  195 + <span class="msg-box n-right" for="c-cert_key"></span>
  196 + </div>
  197 + <div style="margin-top:5px;"><a href="https://pay.weixin.qq.com" target="_blank"><i class="fa fa-question-circle"></i> 如何获取微信支付API证书?</a></div>
  198 + </div>
  199 + <div class="col-sm-4"></div>
  200 + </div>
  201 + </td>
  202 + </tr>
  203 +
  204 + <tr>
  205 + <td>记录日志</td>
  206 + <td>
  207 + <div class="row">
  208 + <div class="col-sm-8 col-xs-12">
  209 + {:Form::radios('row[wechat][log]',['1'=>'开启','0'=>'关闭'],$item.value.log)}
  210 + </div>
  211 + <div class="col-sm-4"></div>
  212 + </div>
  213 + </td>
  214 + </tr>
  215 + </tbody>
  216 + </table>
  217 + </div>
  218 + {elseif $item.name=='alipay'}
  219 + <div class="tab-pane fade" id="alipay">
  220 + <table class="table table-striped table-config">
  221 + <tbody>
  222 + <tr>
  223 + <td>支付模式</td>
  224 + <td>
  225 + <div class="row">
  226 + <div class="col-sm-12 col-xs-12">
  227 + {:Form::radios('row[alipay][mode]',['normal'=>'正式环境','dev'=>'沙箱环境', 'service'=>'服务商模式'],$item.value.mode??'normal')}
  228 +
  229 + <div style="margin-top:5px;" data-mode="dev" class="text-muted {if ($item.value.mode??'')!=='dev'}hidden{/if}">
  230 + <i class="fa fa-info-circle"></i> 如果使用沙箱环境,务必使用沙箱的app_id和沙箱配置,以及使用沙箱账号进行测试。<br>
  231 + 沙箱环境:<a href="https://openhome.alipay.com/develop/sandbox/app" target="_blank">https://openhome.alipay.com/develop/sandbox/app</a>
  232 + </div>
  233 + </div>
  234 + </div>
  235 + </td>
  236 + </tr>
  237 + <tr class="text-muted {if ($item.value.mode??'')!=='service'}hidden{/if}" data-mode="service">
  238 + <td width="20%">服务商ID(pid)</td>
  239 + <td>
  240 + <div class="row">
  241 + <div class="col-sm-8 col-xs-12">
  242 + <input type="text" name="row[alipay][pid]" value="{$item.value.pid|default=''}" class="form-control" data-rule="" data-tip=""/>
  243 + </div>
  244 + <div class="col-sm-4"></div>
  245 + </div>
  246 + </td>
  247 + </tr>
  248 + <tr>
  249 + <td width="20%">应用ID(app_id)</td>
  250 + <td>
  251 + <div class="row">
  252 + <div class="col-sm-8 col-xs-12">
  253 + <input type="text" name="row[alipay][app_id]" value="{$item.value.app_id|default=''}" class="form-control" data-rule="" data-tip=""/>
  254 + </div>
  255 + <div class="col-sm-4"></div>
  256 + </div>
  257 + </td>
  258 + </tr>
  259 + <tr>
  260 + <td>回调通知地址</td>
  261 + <td>
  262 + <div class="row">
  263 + <div class="col-sm-8 col-xs-12">
  264 + <input type="text" name="row[alipay][notify_url]" value="{$item.value.notify_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
  265 + </div>
  266 + <div class="col-sm-4"></div>
  267 + </div>
  268 + </td>
  269 + </tr>
  270 + <tr>
  271 + <td>支付跳转地址</td>
  272 + <td>
  273 + <div class="row">
  274 + <div class="col-sm-8 col-xs-12">
  275 + <input type="text" name="row[alipay][return_url]" value="{$item.value.return_url|default=''}" class="form-control" data-rule="" data-tip="请勿随意修改,实际以逻辑代码中请求的为准"/>
  276 + </div>
  277 + <div class="col-sm-4"></div>
  278 + </div>
  279 + </td>
  280 + </tr>
  281 + <tr>
  282 + <td>应用私钥(private_key)</td>
  283 + <td>
  284 + <div class="row">
  285 + <div class="col-sm-8 col-xs-12">
  286 + <input type="text" name="row[alipay][private_key]" value="{$item.value.private_key|default=''}" class="form-control" data-rule=""/>
  287 + <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/207/201602469554" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用私钥?</a></div>
  288 + </div>
  289 + <div class="col-sm-4"></div>
  290 + </div>
  291 + </td>
  292 + </tr>
  293 + <tr>
  294 + <td>签名方式</td>
  295 + <td>
  296 + <div>
  297 + <div class="radio">
  298 + <label for="row[alipay][signtype]-publickey"><input id="row[alipay][signtype]-publickey" name="row[alipay][signtype]" {if isset($item.value.signtype)&&$item.value.signtype=='publickey'}checked{/if} type="radio" value="publickey"> 普通公钥</label>
  299 + <label for="row[alipay][signtype]-cert"><input id="row[alipay][signtype]-cert" {if isset($item.value.signtype)&&$item.value.signtype=='cert'}checked{/if} name="row[alipay][signtype]" type="radio" value="cert"> 公钥证书</label>
  300 + </div>
  301 + </div>
  302 + <div style="margin-top:5px;" class="text-muted">
  303 + <i class="fa fa-info-circle"></i> 如果要使用转账、提现功能,则必须使用公钥证书
  304 + </div>
  305 + </td>
  306 + </tr>
  307 + <tr>
  308 + <td>
  309 + <span data-signtype="publickey" class="{if ($item.value.signtype??'')==='cert'}hidden{/if}">支付宝公钥</span>
  310 + <span data-signtype="cert" class="{if ($item.value.signtype??'')==='publickey' || ($item.value.signtype??'')==''}hidden{/if}">支付宝公钥证书路径</span>
  311 + (alipay_public_key)
  312 + </td>
  313 + <td>
  314 + <div class="row">
  315 + <div class="col-sm-8 col-xs-12">
  316 + <div class="input-group">
  317 + <input id="c-ali_public_key" class="form-control" size="50" name="row[alipay][ali_public_key]" type="text" value="{$item.value.ali_public_key|default=''|htmlentities}" placeholder="普通公钥请直接粘贴,公钥证书请点击右侧的上传">
  318 + <div class="input-group-addon no-border no-padding {if ($item.value.signtype??'')==='publickey' || ($item.value.signtype??'')==''}hidden{/if}" data-signtype="cert">
  319 + <span><button type="button" id="faupload-ali_public_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"ali_public_key"}' data-mimetype="crt" data-input-id="c-ali_public_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  320 + </div>
  321 + <span class="msg-box n-right" for="c-ali_public_key"></span>
  322 + </div>
  323 + <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/FAQ/aba5803b-ad15-4474-aed6-92e43ea253ea" target="_blank"><i class="fa fa-question-circle"></i> 如何获取支付宝公钥?</a></div>
  324 + </div>
  325 + <div class="col-sm-4"></div>
  326 + </div>
  327 + </td>
  328 + </tr>
  329 + <tr>
  330 + <td>
  331 + <span data-signtype="publickey" class="{if ($item.value.signtype??'')==='cert'}hidden{/if}">应用公钥</span>
  332 + <span data-signtype="cert" class="{if ($item.value.signtype??'')==='publickey' || ($item.value.signtype??'')==''}hidden{/if}">应用公钥证书路径</span>
  333 + (app_cert_public_key)
  334 + </td>
  335 + <td>
  336 + <div class="row">
  337 + <div class="col-sm-8 col-xs-12">
  338 + <div class="input-group">
  339 + <input id="c-app_cert_public_key" class="form-control" size="50" name="row[alipay][app_cert_public_key]" type="text" value="{$item.value.app_cert_public_key|default=''|htmlentities}">
  340 + <div class="input-group-addon no-border no-padding {if ($item.value.signtype??'')==='publickey' || ($item.value.signtype??'')==''}hidden{/if}" data-signtype="cert">
  341 + <span><button type="button" id="faupload-app_cert_public_key" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"app_cert_public_key"}' data-mimetype="crt" data-input-id="c-app_cert_public_key" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  342 + </div>
  343 + <span class="msg-box n-right" for="c-app_cert_public_key"></span>
  344 + </div>
  345 + <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/FAQ/aba5803b-ad15-4474-aed6-92e43ea253ea" target="_blank"><i class="fa fa-question-circle"></i> 如何获取应用公钥?</a></div>
  346 + </div>
  347 + <div class="col-sm-4"></div>
  348 + </div>
  349 + </td>
  350 + </tr>
  351 + <tr class="{if ($item.value.signtype??'')==='publickey' || ($item.value.signtype??'')==''}hidden{/if}" data-signtype="cert">
  352 + <td>支付宝根证书路径(alipay_root_cert)</td>
  353 + <td>
  354 + <div class="row">
  355 + <div class="col-sm-8 col-xs-12">
  356 + <div class="input-group">
  357 + <input id="c-alipay_root_cert" class="form-control" size="50" name="row[alipay][alipay_root_cert]" type="text" value="{$item.value.alipay_root_cert|default=''|htmlentities}">
  358 + <div class="input-group-addon no-border no-padding">
  359 + <span><button type="button" id="faupload-alipay_root_cert" class="btn btn-danger faupload" data-url="epay/upload" data-multipart='{"certname":"alipay_root_cert"}' data-mimetype="crt" data-input-id="c-alipay_root_cert" data-multiple="false"><i class="fa fa-upload"></i> {:__('Upload')}</button></span>
  360 + </div>
  361 + <span class="msg-box n-right" for="c-alipay_root_cert"></span>
  362 + </div>
  363 + <div style="margin-top:5px;"><a href="https://opensupport.alipay.com/support/helpcenter/271/201602474998" target="_blank"><i class="fa fa-question-circle"></i> 如何获取支付宝根证书?</a></div>
  364 + </div>
  365 + <div class="col-sm-4"></div>
  366 + </div>
  367 + </td>
  368 + </tr>
  369 +
  370 + <tr>
  371 + <td>记录日志</td>
  372 + <td>
  373 + <div class="row">
  374 + <div class="col-sm-8 col-xs-12">
  375 + {:Form::radios('row[alipay][log]',['1'=>'开启','0'=>'关闭'],$item.value.log)}
  376 + </div>
  377 + <div class="col-sm-4"></div>
  378 + </div>
  379 + </td>
  380 + </tr>
  381 +
  382 + <tr>
  383 + <td>PC端使用扫码支付</td>
  384 + <td>
  385 + <div class="row">
  386 + <div class="col-sm-8 col-xs-12">
  387 + {:Form::radios('row[alipay][scanpay]',['1'=>'开启','0'=>'关闭'],$item.value.scanpay??0)}
  388 + </div>
  389 + <div class="col-sm-4"></div>
  390 + </div>
  391 + </td>
  392 + </tr>
  393 + </tbody>
  394 + </table>
  395 + </div>
  396 + {/if}
  397 + {/foreach}
  398 + <div class="form-group layer-footer">
  399 + <label class="control-label col-xs-12 col-sm-2"></label>
  400 + <div class="col-xs-12 col-sm-8">
  401 + <button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
  402 + <button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
  403 + </div>
  404 + </div>
  405 + </div>
  406 + </div>
  407 + </div>
  408 +</form>
  409 +<script>
  410 + require.callback = function () {
  411 + define('backend/addon', ['backend', 'form'], function (Backend, Form) {
  412 + var Controller = {
  413 + config: function () {
  414 + $(document).on("click", ".nav-group li a[data-toggle='tab']", function () {
  415 + if ($(this).attr("href") == "#all") {
  416 + $(".tab-pane").addClass("active in");
  417 + }
  418 +
  419 + return;
  420 + });
  421 +
  422 + $(document).on("click", "input[name='row[wechat][mode]']", function () {
  423 + $("#wechat [data-type]").addClass("hidden");
  424 + $("#wechat [data-type='" + $(this).val() + "']").removeClass("hidden");
  425 + });
  426 + $(document).on("click", "input[name='row[alipay][mode]']", function () {
  427 + $("#alipay [data-mode]").addClass("hidden");
  428 + $("#alipay [data-mode='" + $(this).val() + "']").removeClass("hidden");
  429 + });
  430 + $(document).on("click", "input[name='row[alipay][signtype]']", function () {
  431 + $("#alipay [data-signtype]").addClass("hidden");
  432 + $("#alipay [data-signtype='" + $(this).val() + "']").removeClass("hidden");
  433 + });
  434 +
  435 + Form.api.bindevent($("form[role=form]"));
  436 + }
  437 + };
  438 + return Controller;
  439 + });
  440 + };
  441 +</script>
  1 +<?php
  2 +
  3 +return [
  4 + [
  5 + 'name' => 'version',
  6 + 'title' => 'API版本(请勿修改该值)',
  7 + 'type' => 'radio',
  8 + 'content' => [],
  9 + 'value' => 'v2',
  10 + 'rule' => '',
  11 + 'msg' => '',
  12 + 'tip' => 'V2版本只支持微信支付V2密钥,V3版本只支持微信支付V3密钥,请勿修改该值!!!',
  13 + 'ok' => '',
  14 + 'extend' => '',
  15 + ],
  16 + [
  17 + 'name' => 'wechat',
  18 + 'title' => '微信',
  19 + 'type' => 'array',
  20 + 'content' => [],
  21 + 'value' => [
  22 + 'appid' => '',
  23 + 'app_id' => '',
  24 + 'app_secret' => '',
  25 + 'miniapp_id' => '',
  26 + 'mch_id' => '',
  27 + 'key' => '',
  28 + 'key_v3' => '',
  29 + 'mode' => 'normal',
  30 + 'sub_mch_id' => '',
  31 + 'sub_appid' => '',
  32 + 'sub_app_id' => '',
  33 + 'sub_miniapp_id' => '',
  34 + 'notify_url' => '',
  35 + 'cert_client' => '/addons/epay/certs/apiclient_cert.pem',
  36 + 'cert_key' => '/addons/epay/certs/apiclient_key.pem',
  37 + 'log' => '1',
  38 + ],
  39 + 'rule' => 'required',
  40 + 'msg' => '',
  41 + 'tip' => '微信参数配置',
  42 + 'ok' => '',
  43 + 'extend' => '',
  44 + ],
  45 + [
  46 + 'name' => 'alipay',
  47 + 'title' => '支付宝',
  48 + 'type' => 'array',
  49 + 'content' => [],
  50 + 'value' => [
  51 + 'app_id' => '',
  52 + 'mode' => 'normal',
  53 + 'notify_url' => '/addons/epay/api/notifyx/type/alipay',
  54 + 'return_url' => '/addons/epay/api/returnx/type/alipay',
  55 + 'private_key' => '',
  56 + 'signtype' => 'cert',
  57 + 'pid' => '',
  58 + 'ali_public_key' => '',
  59 + 'app_cert_public_key' => '',
  60 + 'alipay_root_cert' => '',
  61 + 'log' => '1',
  62 + 'scanpay' => '0',
  63 + ],
  64 + 'rule' => 'required',
  65 + 'msg' => '',
  66 + 'tip' => '支付宝参数配置',
  67 + 'ok' => '',
  68 + 'extend' => '',
  69 + ]
  70 +];
  1 +<?php
  2 +
  3 +namespace addons\epay\controller;
  4 +
  5 +use addons\epay\library\Service;
  6 +use addons\epay\library\Wechat;
  7 +use addons\third\model\Third;
  8 +use app\common\library\Auth;
  9 +use Exception;
  10 +use think\addons\Controller;
  11 +use think\Response;
  12 +use think\Session;
  13 +use Yansongda\Pay\Exceptions\GatewayException;
  14 +use Yansongda\Pay\Pay;
  15 +
  16 +/**
  17 + * API接口控制器
  18 + *
  19 + * @package addons\epay\controller
  20 + */
  21 +class Api extends Controller
  22 +{
  23 +
  24 + protected $layout = 'default';
  25 + protected $config = [];
  26 +
  27 + /**
  28 + * 默认方法
  29 + */
  30 + public function index()
  31 + {
  32 + return;
  33 + }
  34 +
  35 + /**
  36 + * 外部提交
  37 + */
  38 + public function submit()
  39 + {
  40 + $this->request->filter('trim');
  41 + $out_trade_no = $this->request->request("out_trade_no");
  42 + $title = $this->request->request("title");
  43 + $amount = $this->request->request('amount');
  44 + $type = $this->request->request('type', $this->request->request('paytype'));
  45 + $method = $this->request->request('method', 'web');
  46 + $openid = $this->request->request('openid', '');
  47 + $auth_code = $this->request->request('auth_code', '');
  48 + $notifyurl = $this->request->request('notifyurl', '');
  49 + $returnurl = $this->request->request('returnurl', '');
  50 +
  51 + if (!$amount || $amount < 0) {
  52 + $this->error("支付金额必须大于0");
  53 + }
  54 +
  55 + if (!$type || !in_array($type, ['alipay', 'wechat'])) {
  56 + $this->error("支付类型错误");
  57 + }
  58 +
  59 + $params = [
  60 + 'type' => $type,
  61 + 'out_trade_no' => $out_trade_no,
  62 + 'title' => $title,
  63 + 'amount' => $amount,
  64 + 'method' => $method,
  65 + 'openid' => $openid,
  66 + 'auth_code' => $auth_code,
  67 + 'notifyurl' => $notifyurl,
  68 + 'returnurl' => $returnurl,
  69 + ];
  70 + return Service::submitOrder($params);
  71 + }
  72 +
  73 + /**
  74 + * 微信支付(公众号支付&PC扫码支付)
  75 + */
  76 + public function wechat()
  77 + {
  78 + $config = Service::getConfig('wechat');
  79 +
  80 + $isWechat = stripos($this->request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
  81 + $isMobile = $this->request->isMobile();
  82 + $this->view->assign("isWechat", $isWechat);
  83 + $this->view->assign("isMobile", $isMobile);
  84 +
  85 + //发起PC支付(Scan支付)(PC扫码模式)
  86 + if ($this->request->isAjax()) {
  87 + $pay = Pay::wechat($config);
  88 + $orderid = $this->request->post("orderid");
  89 + try {
  90 + $result = Service::isVersionV3() ? $pay->find(['out_trade_no' => $orderid]) : $pay->find($orderid, 'scan');
  91 + $this->success("", "", ['status' => $result['trade_state'] ?? 'NOTPAY']);
  92 + } catch (GatewayException $e) {
  93 + $this->error("查询失败(1001)");
  94 + }
  95 + }
  96 +
  97 + $orderData = Session::get("wechatorderdata");
  98 + if (!$orderData) {
  99 + $this->error("请求参数错误");
  100 + }
  101 + if ($isWechat && $isMobile) {
  102 + //发起公众号(jsapi支付),openid必须
  103 +
  104 + //如果没有openid,则自动去获取openid
  105 + if (!isset($orderData['openid']) || !$orderData['openid']) {
  106 + $orderData['openid'] = Service::getOpenid();
  107 + }
  108 +
  109 + $orderData['method'] = 'mp';
  110 + $type = 'jsapi';
  111 + $payData = Service::submitOrder($orderData);
  112 + if (!isset($payData['paySign'])) {
  113 + $this->error("创建订单失败,请返回重试", "");
  114 + }
  115 + } else {
  116 + $orderData['method'] = 'scan';
  117 + $type = 'pc';
  118 + $payData = Service::submitOrder($orderData);
  119 + if (!isset($payData['code_url'])) {
  120 + $this->error("创建订单失败,请返回重试", "");
  121 + }
  122 + }
  123 + $this->view->assign("orderData", $orderData);
  124 + $this->view->assign("payData", $payData);
  125 + $this->view->assign("type", $type);
  126 +
  127 + $this->view->assign("title", "微信支付");
  128 + return $this->view->fetch();
  129 + }
  130 +
  131 + /**
  132 + * 支付宝支付(PC扫码支付)
  133 + */
  134 + public function alipay()
  135 + {
  136 + $config = Service::getConfig('alipay');
  137 +
  138 + $isWechat = stripos($this->request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
  139 + $isMobile = $this->request->isMobile();
  140 + $this->view->assign("isWechat", $isWechat);
  141 + $this->view->assign("isMobile", $isMobile);
  142 +
  143 + if ($this->request->isAjax()) {
  144 + $orderid = $this->request->post("orderid");
  145 + $pay = Pay::alipay($config);
  146 + try {
  147 + $result = $pay->find(['out_biz_no' => $orderid]);
  148 + if ($result['code'] == '10000' && $result['trade_status'] == 'TRADE_SUCCESS') {
  149 + $this->success("", "", ['status' => $result['trade_status']]);
  150 + } else {
  151 + $this->error("查询失败");
  152 + }
  153 + } catch (GatewayException $e) {
  154 + $this->error("查询失败(1001)");
  155 + }
  156 + }
  157 +
  158 + //发起PC支付(Scan支付)(PC扫码模式)
  159 + $orderData = Session::get("alipayorderdata");
  160 + if (!$orderData) {
  161 + $this->error("请求参数错误");
  162 + }
  163 +
  164 + $orderData['method'] = 'scan';
  165 + $payData = Service::submitOrder($orderData);
  166 + if (!isset($payData['qr_code'])) {
  167 + $this->error("创建订单失败,请返回重试");
  168 + }
  169 +
  170 + $type = 'pc';
  171 + $this->view->assign("orderData", $orderData);
  172 + $this->view->assign("payData", $payData);
  173 + $this->view->assign("type", $type);
  174 + $this->view->assign("title", "支付宝支付");
  175 + return $this->view->fetch();
  176 + }
  177 +
  178 + /**
  179 + * 支付成功回调
  180 + */
  181 + public function notifyx()
  182 + {
  183 + $paytype = $this->request->param('paytype');
  184 + $pay = Service::checkNotify($paytype);
  185 + if (!$pay) {
  186 + return json(['code' => 'FAIL', 'message' => '失败'], 500, ['Content-Type' => 'application/json']);
  187 + }
  188 +
  189 + // 获取回调数据,V3和V2的回调接收不同
  190 + $data = Service::isVersionV3() ? $pay->callback() : $pay->verify();
  191 +
  192 + try {
  193 + //微信支付V3返回和V2不同
  194 + if (Service::isVersionV3() && $paytype === 'wechat') {
  195 + $data = $data['resource']['ciphertext'];
  196 + $data['total_fee'] = $data['amount']['total'];
  197 + }
  198 +
  199 + \think\Log::record($data);
  200 + //获取支付金额、订单号
  201 + $payamount = $paytype == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
  202 + $out_trade_no = $data['out_trade_no'];
  203 +
  204 + \think\Log::record("回调成功,订单号:{$out_trade_no},金额:{$payamount}");
  205 +
  206 + //你可以在此编写订单逻辑
  207 + } catch (Exception $e) {
  208 + \think\Log::record("回调逻辑处理错误:" . $e->getMessage(), "error");
  209 + }
  210 +
  211 + //下面这句必须要执行,且在此之前不能有任何输出
  212 + if (Service::isVersionV3()) {
  213 + return $pay->success()->getBody()->getContents();
  214 + } else {
  215 + return $pay->success()->send();
  216 + }
  217 + }
  218 +
  219 + /**
  220 + * 支付成功返回
  221 + */
  222 + public function returnx()
  223 + {
  224 + $paytype = $this->request->param('paytype');
  225 + if (Service::checkReturn($paytype)) {
  226 + echo '签名错误';
  227 + return;
  228 + }
  229 +
  230 + //你可以在这里定义你的提示信息,但切记不可在此编写逻辑
  231 + $this->success("恭喜你!支付成功!", addon_url("epay/index/index"));
  232 + }
  233 +
  234 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\controller;
  4 +
  5 +use addons\epay\library\Service;
  6 +use fast\Random;
  7 +use think\addons\Controller;
  8 +use Exception;
  9 +
  10 +/**
  11 + * 微信支付宝整合插件首页
  12 + *
  13 + * 此控制器仅用于开发展示说明和测试,请自行添加一个新的控制器进行处理返回和回调事件,同时删除此控制器文件
  14 + *
  15 + * Class Index
  16 + * @package addons\epay\controller
  17 + */
  18 +class Index extends Controller
  19 +{
  20 + protected $layout = 'default';
  21 +
  22 + protected $config = [];
  23 +
  24 + public function _initialize()
  25 + {
  26 + parent::_initialize();
  27 + if (!config("app_debug")) {
  28 + $this->error("仅在开发环境下查看");
  29 + }
  30 + }
  31 +
  32 + public function index()
  33 + {
  34 + $this->view->assign("title", "微信支付宝整合");
  35 + return $this->view->fetch();
  36 + }
  37 +
  38 + /**
  39 + * 体验,仅供开发测试
  40 + */
  41 + public function experience()
  42 + {
  43 + $amount = $this->request->post('amount');
  44 + $type = $this->request->post('type');
  45 + $method = $this->request->post('method');
  46 + $openid = $this->request->post('openid', "");
  47 +
  48 + if (!$amount || $amount < 0) {
  49 + $this->error("支付金额必须大于0");
  50 + }
  51 +
  52 + if (!$type || !in_array($type, ['alipay', 'wechat'])) {
  53 + $this->error("支付类型不能为空");
  54 + }
  55 +
  56 + if (in_array($method, ['miniapp', 'mp']) && !$openid) {
  57 + $this->error("openid不能为空");
  58 + }
  59 +
  60 + //订单号
  61 + $out_trade_no = date("YmdHis") . mt_rand(100000, 999999);
  62 +
  63 + //订单标题
  64 + $title = '测试订单';
  65 +
  66 + //回调链接
  67 + $notifyurl = $this->request->root(true) . '/addons/epay/index/notifyx/paytype/' . $type;
  68 + $returnurl = $this->request->root(true) . '/addons/epay/index/returnx/paytype/' . $type . '/out_trade_no/' . $out_trade_no;
  69 +
  70 + $response = Service::submitOrder($amount, $out_trade_no, $type, $title, $notifyurl, $returnurl, $method, $openid);
  71 +
  72 + return $response;
  73 + }
  74 +
  75 + /**
  76 + * 支付成功,仅供开发测试
  77 + */
  78 + public function notifyx()
  79 + {
  80 + $paytype = $this->request->param('paytype');
  81 + $pay = Service::checkNotify($paytype);
  82 + if (!$pay) {
  83 + return json(['code' => 'FAIL', 'message' => '失败'], 500, ['Content-Type' => 'application/json']);
  84 + }
  85 +
  86 + // 获取回调数据,V3和V2的回调接收不同
  87 + $data = Service::isVersionV3() ? $pay->callback() : $pay->verify();
  88 +
  89 + try {
  90 + //微信支付V3返回和V2不同
  91 + if (Service::isVersionV3() && $paytype === 'wechat') {
  92 + $data = $data['resource']['ciphertext'];
  93 + $data['total_fee'] = $data['amount']['total'];
  94 + }
  95 +
  96 + \think\Log::record($data);
  97 + //获取支付金额、订单号
  98 + $payamount = $paytype == 'alipay' ? $data['total_amount'] : $data['total_fee'] / 100;
  99 + $out_trade_no = $data['out_trade_no'];
  100 +
  101 + \think\Log::record("回调成功,订单号:{$out_trade_no},金额:{$payamount}");
  102 +
  103 + //你可以在此编写订单逻辑
  104 + } catch (Exception $e) {
  105 + \think\Log::record("回调逻辑处理错误:" . $e->getMessage(), "error");
  106 + }
  107 +
  108 + //下面这句必须要执行,且在此之前不能有任何输出
  109 + if (Service::isVersionV3()) {
  110 + return $pay->success()->getBody()->getContents();
  111 + } else {
  112 + return $pay->success()->send();
  113 + }
  114 + }
  115 +
  116 + /**
  117 + * 支付返回,仅供开发测试
  118 + */
  119 + public function returnx()
  120 + {
  121 + $paytype = $this->request->param('paytype');
  122 + $out_trade_no = $this->request->param('out_trade_no');
  123 + $pay = Service::checkReturn($paytype);
  124 + if (!$pay) {
  125 + $this->error('签名错误', '');
  126 + }
  127 +
  128 + //你可以在这里定义你的提示信息,但切记不可在此编写逻辑
  129 + $this->success("请返回网站查看支付结果", addon_url("epay/index/index"));
  130 + }
  131 +}
  1 +name = epay
  2 +title = 微信支付宝整合
  3 +intro = 可用于快速整合企业微信、支付宝支付功能
  4 +author = FastAdmin
  5 +website = https://www.fastadmin.net
  6 +version = 1.3.5
  7 +state = 1
  8 +url = /addons/epay
  9 +license = regular
  10 +licenseto = 15629
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +class Collection extends \Yansongda\Supports\Collection
  6 +{
  7 +
  8 + /**
  9 + * 创建 Collection 实例
  10 + * @access public
  11 + * @param array $items 数据
  12 + * @return static
  13 + */
  14 + public static function make($items = [])
  15 + {
  16 + return new static($items);
  17 + }
  18 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +use think\Exception;
  6 +
  7 +class OrderException extends Exception
  8 +{
  9 + public function __construct($message = "", $code = 0, $data = [])
  10 + {
  11 + $this->message = $message;
  12 + $this->code = $code;
  13 + $this->data = $data;
  14 + }
  15 +
  16 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +class RedirectResponse extends \Symfony\Component\HttpFoundation\RedirectResponse implements \JsonSerializable, \Serializable
  6 +{
  7 + public function __toString()
  8 + {
  9 + return $this->getContent();
  10 + }
  11 +
  12 + public function setTargetUrl($url)
  13 + {
  14 + if ('' === ($url ?? '')) {
  15 + throw new \InvalidArgumentException('无法跳转到空页面');
  16 + }
  17 +
  18 + $this->targetUrl = $url;
  19 +
  20 + $this->setContent(
  21 + sprintf('<!DOCTYPE html>
  22 +<html>
  23 + <head>
  24 + <meta charset="UTF-8" />
  25 + <meta http-equiv="refresh" content="0;url=\'%1$s\'" />
  26 +
  27 + <title>正在跳转支付 %1$s</title>
  28 + </head>
  29 + <body>
  30 + <div id="redirect" style="display:none;">正在跳转支付 <a href="%1$s">%1$s</a></div>
  31 + <script type="text/javascript">
  32 + setTimeout(function(){
  33 + document.getElementById("redirect").style.display = "block";
  34 + }, 1000);
  35 + </script>
  36 + </body>
  37 +</html>', htmlspecialchars($url, \ENT_QUOTES, 'UTF-8')));
  38 +
  39 + $this->headers->set('Location', $url);
  40 +
  41 + return $this;
  42 + }
  43 +
  44 + public function jsonSerialize()
  45 + {
  46 + return $this->getContent();
  47 + }
  48 +
  49 + public function serialize()
  50 + {
  51 + return serialize($this->content);
  52 + }
  53 +
  54 + public function unserialize($serialized)
  55 + {
  56 + return $this->content = unserialize($serialized);
  57 + }
  58 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +class Response extends \Symfony\Component\HttpFoundation\Response implements \JsonSerializable, \Serializable
  6 +{
  7 + public function __toString()
  8 + {
  9 + return $this->getContent();
  10 + }
  11 +
  12 + public function jsonSerialize()
  13 + {
  14 + return $this->getContent();
  15 + }
  16 +
  17 + public function serialize()
  18 + {
  19 + return serialize($this->content);
  20 + }
  21 +
  22 + public function unserialize($serialized)
  23 + {
  24 + return $this->content = unserialize($serialized);
  25 + }
  26 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +use addons\third\model\Third;
  6 +use app\common\library\Auth;
  7 +use Exception;
  8 +use think\Hook;
  9 +use think\Session;
  10 +use Yansongda\Pay\Pay;
  11 +use Yansongda\Supports\Str;
  12 +
  13 +/**
  14 + * 订单服务类
  15 + *
  16 + * @package addons\epay\library
  17 + */
  18 +class Service
  19 +{
  20 +
  21 + public const SDK_VERSION_V2 = 'v2';
  22 +
  23 + public const SDK_VERSION_V3 = 'v3';
  24 +
  25 + /**
  26 + * 提交订单
  27 + * @param array|float $amount 订单金额
  28 + * @param string $orderid 订单号
  29 + * @param string $type 支付类型,可选alipay或wechat
  30 + * @param string $title 订单标题
  31 + * @param string $notifyurl 通知回调URL
  32 + * @param string $returnurl 跳转返回URL
  33 + * @param string $method 支付方法
  34 + * @param string $openid Openid
  35 + * @param array $custom 自定义微信支付宝相关配置
  36 + * @return Response|RedirectResponse|Collection
  37 + * @throws Exception
  38 + */
  39 + public static function submitOrder($amount, $orderid = null, $type = null, $title = null, $notifyurl = null, $returnurl = null, $method = null, $openid = '', $custom = [])
  40 + {
  41 + $version = self::getSdkVersion();
  42 + $request = request();
  43 + $addonConfig = get_addon_config('epay');
  44 +
  45 + if (!is_array($amount)) {
  46 + $params = [
  47 + 'amount' => $amount,
  48 + 'orderid' => $orderid,
  49 + 'type' => $type,
  50 + 'title' => $title,
  51 + 'notifyurl' => $notifyurl,
  52 + 'returnurl' => $returnurl,
  53 + 'method' => $method,
  54 + 'openid' => $openid,
  55 + 'custom' => $custom,
  56 + ];
  57 + } else {
  58 + $params = $amount;
  59 + }
  60 + $type = isset($params['type']) && in_array($params['type'], ['alipay', 'wechat']) ? $params['type'] : 'wechat';
  61 + $method = $params['method'] ?? 'web';
  62 + $orderid = $params['orderid'] ?? date("YmdHis") . mt_rand(100000, 999999);
  63 + $amount = $params['amount'] ?? 1;
  64 + $title = $params['title'] ?? "支付";
  65 + $auth_code = $params['auth_code'] ?? '';
  66 + $openid = $params['openid'] ?? '';
  67 +
  68 + //自定义微信支付宝相关配置
  69 + $custom = $params['custom'] ?? [];
  70 +
  71 + //未定义则使用默认回调和跳转
  72 + $notifyurl = !empty($params['notifyurl']) ? $params['notifyurl'] : $request->root(true) . '/addons/epay/index/notifyx/paytype/' . $type;
  73 + $returnurl = !empty($params['returnurl']) ? $params['returnurl'] : $request->root(true) . '/addons/epay/index/returnx/paytype/' . $type . '/out_trade_no/' . $orderid;
  74 +
  75 + $html = '';
  76 + $config = Service::getConfig($type, array_merge($custom, ['notify_url' => $notifyurl, 'return_url' => $returnurl]));
  77 +
  78 + //判断是否移动端或微信内浏览器
  79 + $isMobile = $request->isMobile();
  80 + $isWechat = strpos($request->server('HTTP_USER_AGENT'), 'MicroMessenger') !== false;
  81 +
  82 + $result = null;
  83 + if ($type == 'alipay') {
  84 + //如果是PC支付,判断当前环境,进行跳转
  85 + if ($method == 'web') {
  86 + //如果是微信环境或后台配置PC使用扫码支付
  87 + if ($isWechat || $addonConfig['alipay']['scanpay']) {
  88 + Session::set("alipayorderdata", $params);
  89 + $url = addon_url('epay/api/alipay', [], true, true);
  90 + return new RedirectResponse($url);
  91 + } elseif ($isMobile) {
  92 + $method = 'wap';
  93 + }
  94 + }
  95 +
  96 + //创建支付对象
  97 + $pay = Pay::alipay($config);
  98 + $params = [
  99 + 'out_trade_no' => $orderid,//你的订单号
  100 + 'total_amount' => $amount,//单位元
  101 + 'subject' => $title,
  102 + ];
  103 +
  104 + switch ($method) {
  105 + case 'web':
  106 + //电脑支付
  107 + $result = $pay->web($params);
  108 + break;
  109 + case 'wap':
  110 + //手机网页支付
  111 + $result = $pay->wap($params);
  112 + break;
  113 + case 'app':
  114 + //APP支付
  115 + $result = $pay->app($params);
  116 + break;
  117 + case 'scan':
  118 + //扫码支付
  119 + $result = $pay->scan($params);
  120 + break;
  121 + case 'pos':
  122 + //刷卡支付必须要有auth_code
  123 + $params['auth_code'] = $auth_code;
  124 + $result = $pay->pos($params);
  125 + break;
  126 + case 'mini':
  127 + case 'miniapp':
  128 + //小程序支付
  129 + //小程序支付,直接返回字符串
  130 + //小程序支付必须要有buyer_id,这里使用openid
  131 + $params['buyer_id'] = $openid;
  132 + $result = $pay->mini($params);
  133 + break;
  134 + default:
  135 + }
  136 + } else {
  137 + //如果是PC支付,判断当前环境,进行跳转
  138 + if ($method == 'web') {
  139 + //如果是移动端,但不是微信环境
  140 + if ($isMobile && !$isWechat) {
  141 + $method = 'wap';
  142 + } else {
  143 + Session::set("wechatorderdata", $params);
  144 + $url = addon_url('epay/api/wechat', [], true, true);
  145 + return new RedirectResponse($url);
  146 + }
  147 + }
  148 +
  149 + //单位分
  150 + $total_fee = function_exists('bcmul') ? bcmul($amount, 100) : $amount * 100;
  151 + $total_fee = (int)$total_fee;
  152 + $ip = $request->ip();
  153 + //微信服务商模式时需传递sub_openid参数
  154 + $openidName = $addonConfig['wechat']['mode'] == 'service' ? 'sub_openid' : 'openid';
  155 +
  156 + //创建支付对象
  157 + $pay = Pay::wechat($config);
  158 +
  159 + if (self::isVersionV3()) {
  160 + //V3支付
  161 + $params = [
  162 + 'out_trade_no' => $orderid,
  163 + 'description' => $title,
  164 + 'amount' => [
  165 + 'total' => $total_fee,
  166 + ]
  167 + ];
  168 + switch ($method) {
  169 + case 'mp':
  170 + //公众号支付
  171 + //公众号支付必须有openid
  172 + $params['payer'] = [$openidName => $openid];
  173 + $result = $pay->mp($params);
  174 + break;
  175 + case 'wap':
  176 + //手机网页支付,跳转
  177 + $params['scene_info'] = [
  178 + 'payer_client_ip' => $ip,
  179 + 'h5_info' => [
  180 + 'type' => 'Wap',
  181 + ]
  182 + ];
  183 + $result = $pay->wap($params);
  184 + break;
  185 + case 'app':
  186 + //APP支付,直接返回字符串
  187 + $result = $pay->app($params);
  188 + break;
  189 + case 'scan':
  190 + //扫码支付,直接返回字符串
  191 + $result = $pay->scan($params);
  192 + break;
  193 + case 'pos':
  194 + //刷卡支付,直接返回字符串
  195 + //刷卡支付必须要有auth_code
  196 + $params['auth_code'] = $auth_code;
  197 + $result = $pay->pos($params);
  198 + break;
  199 + case 'mini':
  200 + case 'miniapp':
  201 + //小程序支付,直接返回字符串
  202 + //小程序支付必须要有openid
  203 + $params['payer'] = [$openidName => $openid];
  204 + $result = $pay->mini($params);
  205 + break;
  206 + default:
  207 + }
  208 + } else {
  209 + //V2支付
  210 + $params = [
  211 + 'out_trade_no' => $orderid,
  212 + 'body' => $title,
  213 + 'total_fee' => $total_fee,
  214 + ];
  215 + switch ($method) {
  216 + case 'mp':
  217 + //公众号支付
  218 + //公众号支付必须有openid
  219 + $params[$openidName] = $openid;
  220 + $result = $pay->mp($params);
  221 + break;
  222 + case 'wap':
  223 + //手机网页支付,跳转
  224 + $params['spbill_create_ip'] = $ip;
  225 + $result = $pay->wap($params);
  226 + break;
  227 + case 'app':
  228 + //APP支付,直接返回字符串
  229 + $result = $pay->app($params);
  230 + break;
  231 + case 'scan':
  232 + //扫码支付,直接返回字符串
  233 + $result = $pay->scan($params);
  234 + break;
  235 + case 'pos':
  236 + //刷卡支付,直接返回字符串
  237 + //刷卡支付必须要有auth_code
  238 + $params['auth_code'] = $auth_code;
  239 + $result = $pay->pos($params);
  240 + break;
  241 + case 'mini':
  242 + case 'miniapp':
  243 + //小程序支付,直接返回字符串
  244 + //小程序支付必须要有openid
  245 + $params[$openidName] = $openid;
  246 + $result = $pay->miniapp($params);
  247 + break;
  248 + default:
  249 + }
  250 + }
  251 + }
  252 +
  253 + //使用重写的Response类、RedirectResponse、Collection类
  254 + if ($result instanceof \Symfony\Component\HttpFoundation\RedirectResponse) {
  255 + $result = new RedirectResponse($result->getTargetUrl());
  256 + } elseif ($result instanceof \Symfony\Component\HttpFoundation\Response) {
  257 + $result = new Response($result->getContent());
  258 + } elseif ($result instanceof \Yansongda\Supports\Collection) {
  259 + $result = Collection::make($result->all());
  260 + } elseif ($result instanceof \GuzzleHttp\Psr7\Response) {
  261 + $result = new Response($result->getBody());
  262 + }
  263 +
  264 + return $result;
  265 + }
  266 +
  267 + /**
  268 + * 验证回调是否成功
  269 + * @param string $type 支付类型
  270 + * @param array $custom 自定义配置信息
  271 + * @return bool|\Yansongda\Pay\Gateways\Alipay|\Yansongda\Pay\Gateways\Wechat|\Yansongda\Pay\Provider\Wechat|\Yansongda\Pay\Provider\Alipay
  272 + */
  273 + public static function checkNotify($type, $custom = [])
  274 + {
  275 + $type = strtolower($type);
  276 + if (!in_array($type, ['wechat', 'alipay'])) {
  277 + return false;
  278 + }
  279 +
  280 + $version = self::getSdkVersion();
  281 +
  282 + try {
  283 + $config = self::getConfig($type, $custom);
  284 + $pay = $type == 'wechat' ? Pay::wechat($config) : Pay::alipay($config);
  285 +
  286 + $data = Service::isVersionV3() ? $pay->callback() : $pay->verify();
  287 + if ($type == 'alipay') {
  288 + if (in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
  289 + return $pay;
  290 + }
  291 + } else {
  292 + return $pay;
  293 + }
  294 + } catch (Exception $e) {
  295 + \think\Log::record("回调请求参数解析错误", "error");
  296 + return false;
  297 + }
  298 +
  299 + return false;
  300 + }
  301 +
  302 + /**
  303 + * 验证返回是否成功,请勿用于判断是否支付成功的逻辑验证
  304 + * 已弃用
  305 + *
  306 + * @param string $type 支付类型
  307 + * @param array $custom 自定义配置信息
  308 + * @return bool
  309 + * @deprecated 已弃用,请勿用于逻辑验证
  310 + */
  311 + public static function checkReturn($type, $custom = [])
  312 + {
  313 + //由于PC及移动端无法获取请求的参数信息,取消return验证,均返回true
  314 + return true;
  315 + }
  316 +
  317 + /**
  318 + * 获取配置
  319 + * @param string $type 支付类型
  320 + * @param array $custom 自定义配置,用于覆盖插件默认配置
  321 + * @return array
  322 + */
  323 + public static function getConfig($type = 'wechat', $custom = [])
  324 + {
  325 + $addonConfig = get_addon_config('epay');
  326 + $config = $addonConfig[$type] ?? $addonConfig['wechat'];
  327 +
  328 + // SDK版本
  329 + $version = self::getSdkVersion();
  330 +
  331 + if (isset($config['cert_client']) && substr($config['cert_client'], 0, 8) == '/addons/') {
  332 + $config['cert_client'] = ROOT_PATH . str_replace('/', DS, substr($config['cert_client'], 1));
  333 + }
  334 + if (isset($config['cert_key']) && substr($config['cert_key'], 0, 8) == '/addons/') {
  335 + $config['cert_key'] = ROOT_PATH . str_replace('/', DS, substr($config['cert_key'], 1));
  336 + }
  337 + if (isset($config['app_cert_public_key']) && substr($config['app_cert_public_key'], 0, 8) == '/addons/') {
  338 + $config['app_cert_public_key'] = ROOT_PATH . str_replace('/', DS, substr($config['app_cert_public_key'], 1));
  339 + }
  340 + if (isset($config['alipay_root_cert']) && substr($config['alipay_root_cert'], 0, 8) == '/addons/') {
  341 + $config['alipay_root_cert'] = ROOT_PATH . str_replace('/', DS, substr($config['alipay_root_cert'], 1));
  342 + }
  343 + if (isset($config['ali_public_key']) && (Str::endsWith($config['ali_public_key'], '.crt') || Str::endsWith($config['ali_public_key'], '.pem'))) {
  344 + $config['ali_public_key'] = ROOT_PATH . str_replace('/', DS, substr($config['ali_public_key'], 1));
  345 + }
  346 +
  347 + // V3支付
  348 + if (self::isVersionV3()) {
  349 + if ($type == 'wechat') {
  350 + $config['app_id'] = $config['appid'] ?? '';
  351 + $config['mp_app_id'] = $config['app_id'] ?? '';
  352 + $config['mini_app_id'] = $config['miniapp_id'] ?? '';
  353 + $config['combine_mch_id'] = $config['combine_mch_id'] ?? '';
  354 + $config['mch_secret_key'] = $config['key_v3'] ?? '';
  355 + $config['mch_secret_cert'] = $config['cert_key'];
  356 + $config['mch_public_cert_path'] = $config['cert_client'];
  357 +
  358 + $config['sub_mp_app_id'] = $config['sub_appid'] ?? '';
  359 + $config['sub_app_id'] = $config['sub_app_id'] ?? '';
  360 + $config['sub_mini_app_id'] = $config['sub_miniapp_id'] ?? '';
  361 + $config['sub_mch_id'] = $config['sub_mch_id'] ?? '';
  362 + } elseif ($type == 'alipay') {
  363 + $config['app_secret_cert'] = $config['private_key'] ?? '';
  364 + $config['app_public_cert_path'] = $config['app_cert_public_key'] ?? '';
  365 + $config['alipay_public_cert_path'] = $config['ali_public_key'] ?? '';
  366 + $config['alipay_root_cert_path'] = $config['alipay_root_cert'] ?? '';
  367 + $config['service_provider_id'] = $config['pid'] ?? '';
  368 + }
  369 + $modeArr = ['normal' => 0, 'dev' => 1, 'service' => 2];
  370 + $config['mode'] = $modeArr[$config['mode']] ?? 0;
  371 + }
  372 +
  373 + // 日志
  374 + if ($config['log']) {
  375 + $config['log'] = [
  376 + 'enable' => true,
  377 + 'file' => LOG_PATH . 'epaylogs' . DS . $type . '-' . date("Y-m-d") . '.log',
  378 + 'level' => 'debug'
  379 + ];
  380 + } else {
  381 + $config['log'] = [
  382 + 'enable' => false,
  383 + ];
  384 + }
  385 +
  386 + // GuzzleHttp配置,可选
  387 + $config['http'] = [
  388 + 'timeout' => 10,
  389 + 'connect_timeout' => 10,
  390 + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html)
  391 + ];
  392 +
  393 + $config['notify_url'] = empty($config['notify_url']) ? addon_url('epay/api/notifyx', [], false) . '/type/' . $type : $config['notify_url'];
  394 + $config['notify_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['notify_url']) ? request()->root(true) . $config['notify_url'] : $config['notify_url'];
  395 + $config['return_url'] = empty($config['return_url']) ? addon_url('epay/api/returnx', [], false) . '/type/' . $type : $config['return_url'];
  396 + $config['return_url'] = !preg_match("/^(http:\/\/|https:\/\/)/i", $config['return_url']) ? request()->root(true) . $config['return_url'] : $config['return_url'];
  397 +
  398 + //合并自定义配置
  399 + $config = array_merge($config, $custom);
  400 +
  401 + //v3版本时返回的结构不同
  402 + if (self::isVersionV3()) {
  403 + $config = [$type => ['default' => $config], 'logger' => $config['log'], 'http' => $config['http'], '_force' => true];
  404 +
  405 + }
  406 + return $config;
  407 + }
  408 +
  409 + /**
  410 + * 获取微信Openid
  411 + *
  412 + * @param array $custom 自定义配置信息
  413 + * @return mixed|string
  414 + */
  415 + public static function getOpenid($custom = [])
  416 + {
  417 + $openid = '';
  418 + $auth = Auth::instance();
  419 + if ($auth->isLogin()) {
  420 + $third = get_addon_info('third');
  421 + if ($third && $third['state']) {
  422 + $thirdInfo = Third::where('user_id', $auth->id)->where('platform', 'wechat')->where('apptype', 'mp')->find();
  423 + $openid = $thirdInfo ? $thirdInfo['openid'] : '';
  424 + }
  425 + }
  426 + if (!$openid) {
  427 + $openid = Session::get("openid");
  428 +
  429 + //如果未传openid,则去读取openid
  430 + if (!$openid) {
  431 + $addonConfig = get_addon_config('epay');
  432 + $wechat = new Wechat($custom['app_id'] ?? $addonConfig['wechat']['app_id'], $custom['app_secret'] ?? $addonConfig['wechat']['app_secret']);
  433 + $openid = $wechat->getOpenid();
  434 + }
  435 + }
  436 + return $openid;
  437 + }
  438 +
  439 + /**
  440 + * 获取SDK版本
  441 + * @return mixed|string
  442 + */
  443 + public static function getSdkVersion()
  444 + {
  445 + $addonConfig = get_addon_config('epay');
  446 + return $addonConfig['version'] ?? self::SDK_VERSION_V2;
  447 + }
  448 +
  449 + /**
  450 + * 判断是否V2支付
  451 + * @return bool
  452 + */
  453 + public static function isVersionV2()
  454 + {
  455 + return self::getSdkVersion() === self::SDK_VERSION_V2;
  456 + }
  457 +
  458 + /**
  459 + * 判断是否V3支付
  460 + * @return bool
  461 + */
  462 + public static function isVersionV3()
  463 + {
  464 + return self::getSdkVersion() === self::SDK_VERSION_V3;
  465 + }
  466 +}
  1 +<?php
  2 +
  3 +namespace addons\epay\library;
  4 +
  5 +use fast\Http;
  6 +use think\Cache;
  7 +use think\Session;
  8 +
  9 +/**
  10 + * 微信授权
  11 + *
  12 + */
  13 +class Wechat
  14 +{
  15 + private $app_id = '';
  16 + private $app_secret = '';
  17 + private $scope = 'snsapi_userinfo';
  18 +
  19 + public function __construct($app_id, $app_secret)
  20 + {
  21 + $this->app_id = $app_id;
  22 + $this->app_secret = $app_secret;
  23 + }
  24 +
  25 + /**
  26 + * 获取微信授权链接
  27 + *
  28 + * @return string
  29 + */
  30 + public function getAuthorizeUrl()
  31 + {
  32 + $redirect_uri = addon_url('epay/api/wechat', [], true, true);
  33 + $redirect_uri = urlencode($redirect_uri);
  34 + $state = \fast\Random::alnum();
  35 + Session::set('state', $state);
  36 + return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->app_id}&redirect_uri={$redirect_uri}&response_type=code&scope={$this->scope}&state={$state}#wechat_redirect";
  37 + }
  38 +
  39 + /**
  40 + * 获取微信openid
  41 + *
  42 + * @return mixed|string
  43 + */
  44 + public function getOpenid()
  45 + {
  46 + $openid = Session::get('openid');
  47 + if (!$openid) {
  48 + if (!isset($_GET['code'])) {
  49 + $url = $this->getAuthorizeUrl();
  50 +
  51 + Header("Location: $url");
  52 + exit();
  53 + } else {
  54 + $state = Session::get('state');
  55 + if ($state == $_GET['state']) {
  56 + $code = $_GET['code'];
  57 + $token = $this->getAccessToken($code);
  58 + if (!isset($token['openid']) && isset($token['errmsg'])) {
  59 + exception($token['errmsg']);
  60 + }
  61 + $openid = $token['openid'] ?? '';
  62 + if ($openid) {
  63 + Session::set("openid", $openid);
  64 + }
  65 + }
  66 + }
  67 + }
  68 + return $openid;
  69 + }
  70 +
  71 + /**
  72 + * 获取授权token网页授权
  73 + *
  74 + * @param string $code
  75 + * @return mixed|string
  76 + */
  77 + public function getAccessToken($code = '')
  78 + {
  79 + $params = [
  80 + 'appid' => $this->app_id,
  81 + 'secret' => $this->app_secret,
  82 + 'code' => $code,
  83 + 'grant_type' => 'authorization_code'
  84 + ];
  85 + $ret = Http::sendRequest('https://api.weixin.qq.com/sns/oauth2/access_token', $params, 'GET');
  86 + if ($ret['ret']) {
  87 + $ar = json_decode($ret['msg'], true);
  88 + return $ar;
  89 + }
  90 + return [];
  91 + }
  92 +
  93 + public function getJsticket($code = '')
  94 + {
  95 + $jsticket = Session::get('jsticket');
  96 + if (!$jsticket) {
  97 + $token = $this->getAccessToken($code);
  98 + $params = [
  99 + 'access_token' => 'token',
  100 + 'type' => 'jsapi',
  101 + ];
  102 + $ret = Http::sendRequest('https://api.weixin.qq.com/cgi-bin/ticket/getticket', $params, 'GET');
  103 + if ($ret['ret']) {
  104 + $ar = json_decode($ret['msg'], true);
  105 + return $ar;
  106 + }
  107 + }
  108 + return $jsticket;
  109 + }
  110 +}
  1 +/tests export-ignore
  2 +/.github export-ignore
  1 +The MIT License (MIT)
  2 +
  3 +Copyright (c) Hyperf
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
  1 +{
  2 + "name": "hyperf/context",
  3 + "description": "A coroutine context library.",
  4 + "license": "MIT",
  5 + "keywords": [
  6 + "php",
  7 + "swoole",
  8 + "hyperf",
  9 + "context"
  10 + ],
  11 + "homepage": "https://hyperf.io",
  12 + "support": {
  13 + "docs": "https://hyperf.wiki",
  14 + "issues": "https://github.com/hyperf/hyperf/issues",
  15 + "pull-request": "https://github.com/hyperf/hyperf/pulls",
  16 + "source": "https://github.com/hyperf/hyperf"
  17 + },
  18 + "require": {
  19 + "php": ">=7.2",
  20 + "hyperf/engine": "^1.1"
  21 + },
  22 + "autoload": {
  23 + "psr-4": {
  24 + "Hyperf\\Context\\": "src/"
  25 + }
  26 + },
  27 + "autoload-dev": {
  28 + "psr-4": {
  29 + "HyperfTest\\Context\\": "tests/"
  30 + }
  31 + },
  32 + "config": {
  33 + "sort-packages": true
  34 + },
  35 + "extra": {
  36 + "branch-alias": {
  37 + "dev-master": "2.2-dev"
  38 + }
  39 + }
  40 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Context;
  13 +
  14 +use Hyperf\Engine\Coroutine;
  15 +
  16 +class Context
  17 +{
  18 + protected static $nonCoContext = [];
  19 +
  20 + public static function set(string $id, $value)
  21 + {
  22 + if (Coroutine::id() > 0) {
  23 + Coroutine::getContextFor()[$id] = $value;
  24 + } else {
  25 + static::$nonCoContext[$id] = $value;
  26 + }
  27 + return $value;
  28 + }
  29 +
  30 + public static function get(string $id, $default = null, $coroutineId = null)
  31 + {
  32 + if (Coroutine::id() > 0) {
  33 + return Coroutine::getContextFor($coroutineId)[$id] ?? $default;
  34 + }
  35 +
  36 + return static::$nonCoContext[$id] ?? $default;
  37 + }
  38 +
  39 + public static function has(string $id, $coroutineId = null)
  40 + {
  41 + if (Coroutine::id() > 0) {
  42 + return isset(Coroutine::getContextFor($coroutineId)[$id]);
  43 + }
  44 +
  45 + return isset(static::$nonCoContext[$id]);
  46 + }
  47 +
  48 + /**
  49 + * Release the context when you are not in coroutine environment.
  50 + */
  51 + public static function destroy(string $id)
  52 + {
  53 + unset(static::$nonCoContext[$id]);
  54 + }
  55 +
  56 + /**
  57 + * Copy the context from a coroutine to current coroutine.
  58 + * This method will delete the origin values in current coroutine.
  59 + */
  60 + public static function copy(int $fromCoroutineId, array $keys = []): void
  61 + {
  62 + $from = Coroutine::getContextFor($fromCoroutineId);
  63 + if ($from === null) {
  64 + return;
  65 + }
  66 +
  67 + $current = Coroutine::getContextFor();
  68 +
  69 + if ($keys) {
  70 + $map = array_intersect_key($from->getArrayCopy(), array_flip($keys));
  71 + } else {
  72 + $map = $from->getArrayCopy();
  73 + }
  74 +
  75 + $current->exchangeArray($map);
  76 + }
  77 +
  78 + /**
  79 + * Retrieve the value and override it by closure.
  80 + */
  81 + public static function override(string $id, \Closure $closure)
  82 + {
  83 + $value = null;
  84 + if (self::has($id)) {
  85 + $value = self::get($id);
  86 + }
  87 + $value = $closure($value);
  88 + self::set($id, $value);
  89 + return $value;
  90 + }
  91 +
  92 + /**
  93 + * Retrieve the value and store it if not exists.
  94 + * @param mixed $value
  95 + */
  96 + public static function getOrSet(string $id, $value)
  97 + {
  98 + if (! self::has($id)) {
  99 + return self::set($id, value($value));
  100 + }
  101 + return self::get($id);
  102 + }
  103 +
  104 + public static function getContainer()
  105 + {
  106 + if (Coroutine::id() > 0) {
  107 + return Coroutine::getContextFor();
  108 + }
  109 +
  110 + return static::$nonCoContext;
  111 + }
  112 +}
  1 +/tests export-ignore
  2 +/.github export-ignore
  1 +The MIT License (MIT)
  2 +
  3 +Copyright (c) Hyperf
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
  1 +{
  2 + "name": "hyperf/contract",
  3 + "description": "The contracts of Hyperf.",
  4 + "license": "MIT",
  5 + "keywords": [
  6 + "php",
  7 + "swoole",
  8 + "hyperf"
  9 + ],
  10 + "homepage": "https://hyperf.io",
  11 + "support": {
  12 + "docs": "https://hyperf.wiki",
  13 + "issues": "https://github.com/hyperf/hyperf/issues",
  14 + "pull-request": "https://github.com/hyperf/hyperf/pulls",
  15 + "source": "https://github.com/hyperf/hyperf"
  16 + },
  17 + "require": {
  18 + "php": ">=7.2"
  19 + },
  20 + "autoload": {
  21 + "psr-4": {
  22 + "Hyperf\\Contract\\": "src/"
  23 + }
  24 + },
  25 + "config": {
  26 + "sort-packages": true
  27 + },
  28 + "extra": {
  29 + "branch-alias": {
  30 + "dev-master": "2.2-dev"
  31 + }
  32 + }
  33 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface ApplicationInterface
  15 +{
  16 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface Castable
  15 +{
  16 + /**
  17 + * Get the name of the caster class to use when casting from / to this cast target.
  18 + *
  19 + * @return CastsAttributes|CastsInboundAttributes|string
  20 + */
  21 + public static function castUsing();
  22 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface CastsAttributes
  15 +{
  16 + /**
  17 + * Transform the attribute from the underlying model values.
  18 + *
  19 + * @param object $model
  20 + * @param mixed $value
  21 + * @return mixed
  22 + */
  23 + public function get($model, string $key, $value, array $attributes);
  24 +
  25 + /**
  26 + * Transform the attribute to its underlying model values.
  27 + *
  28 + * @param object $model
  29 + * @param mixed $value
  30 + * @return array|string
  31 + */
  32 + public function set($model, string $key, $value, array $attributes);
  33 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface CastsInboundAttributes
  15 +{
  16 + /**
  17 + * Transform the attribute to its underlying model values.
  18 + *
  19 + * @param object $model
  20 + * @param mixed $value
  21 + * @return array
  22 + */
  23 + public function set($model, string $key, $value, array $attributes);
  24 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface CompressInterface
  15 +{
  16 + public function compress(): UnCompressInterface;
  17 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface ConfigInterface
  15 +{
  16 + /**
  17 + * Finds an entry of the container by its identifier and returns it.
  18 + *
  19 + * @param string $key identifier of the entry to look for
  20 + * @param mixed $default default value of the entry when does not found
  21 + * @return mixed entry
  22 + */
  23 + public function get(string $key, $default = null);
  24 +
  25 + /**
  26 + * Returns true if the container can return an entry for the given identifier.
  27 + * Returns false otherwise.
  28 + *
  29 + * @param string $keys identifier of the entry to look for
  30 + * @return bool
  31 + */
  32 + public function has(string $keys);
  33 +
  34 + /**
  35 + * Set a value to the container by its identifier.
  36 + *
  37 + * @param string $key identifier of the entry to set
  38 + * @param mixed $value the value that save to container
  39 + */
  40 + public function set(string $key, $value);
  41 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface ConnectionInterface
  15 +{
  16 + /**
  17 + * Get the real connection from pool.
  18 + */
  19 + public function getConnection();
  20 +
  21 + /**
  22 + * Reconnect the connection.
  23 + */
  24 + public function reconnect(): bool;
  25 +
  26 + /**
  27 + * Check the connection is valid.
  28 + */
  29 + public function check(): bool;
  30 +
  31 + /**
  32 + * Close the connection.
  33 + */
  34 + public function close(): bool;
  35 +
  36 + /**
  37 + * Release the connection to pool.
  38 + */
  39 + public function release(): void;
  40 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Psr\Container\ContainerInterface as PsrContainerInterface;
  15 +
  16 +interface ContainerInterface extends PsrContainerInterface
  17 +{
  18 + /**
  19 + * Build an entry of the container by its name.
  20 + * This method behave like get() except resolves the entry again every time.
  21 + * For example if the entry is a class then a new instance will be created each time.
  22 + * This method makes the container behave like a factory.
  23 + *
  24 + * @param string $name entry name or a class name
  25 + * @param array $parameters Optional parameters to use to build the entry. Use this to force specific parameters
  26 + * to specific values. Parameters not defined in this array will be resolved using
  27 + * the container.
  28 + * @throws InvalidArgumentException the name parameter must be of type string
  29 + * @throws NotFoundException no entry found for the given name
  30 + */
  31 + public function make(string $name, array $parameters = []);
  32 +
  33 + /**
  34 + * Bind an arbitrary resolved entry to an identifier.
  35 + * Useful for testing 'get'.
  36 + *
  37 + * @param mixed $entry
  38 + */
  39 + public function set(string $name, $entry);
  40 +
  41 + /**
  42 + * Unbind an arbitrary resolved entry.
  43 + */
  44 + public function unbind(string $name);
  45 +
  46 + /**
  47 + * Bind an arbitrary definition to an identifier.
  48 + * Useful for testing 'make'.
  49 + *
  50 + * @param array|callable|string $definition
  51 + */
  52 + public function define(string $name, $definition);
  53 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface DispatcherInterface
  15 +{
  16 + public function dispatch(...$params);
  17 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface FrequencyInterface
  15 +{
  16 + /**
  17 + * Number of hit per time.
  18 + */
  19 + public function hit(int $number = 1): bool;
  20 +
  21 + /**
  22 + * Hits per second.
  23 + */
  24 + public function frequency(): float;
  25 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface IdGeneratorInterface
  15 +{
  16 + public function generate();
  17 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface LengthAwarePaginatorInterface extends PaginatorInterface
  15 +{
  16 + /**
  17 + * Create a range of pagination URLs.
  18 + */
  19 + public function getUrlRange(int $start, int $end): array;
  20 +
  21 + /**
  22 + * Determine the total number of items in the data store.
  23 + */
  24 + public function total(): int;
  25 +
  26 + /**
  27 + * Get the page number of the last available page.
  28 + */
  29 + public function lastPage(): int;
  30 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface MiddlewareInitializerInterface
  15 +{
  16 + public function initCoreMiddleware(string $serverName): void;
  17 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface NormalizerInterface
  15 +{
  16 + /**
  17 + * Normalizes an object into a set of arrays/scalars.
  18 + *
  19 + * @param mixed $object
  20 + * @return null|array|\ArrayObject|bool|float|int|string
  21 + */
  22 + public function normalize($object);
  23 +
  24 + /**
  25 + * Denormalizes data back into an object of the given class.
  26 + *
  27 + * @param mixed $data Data to restore
  28 + * @param string $class The expected class to instantiate
  29 + * @return mixed|object
  30 + */
  31 + public function denormalize($data, string $class);
  32 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Swoole\Http\Response;
  15 +use Swoole\Server;
  16 +
  17 +interface OnCloseInterface
  18 +{
  19 + /**
  20 + * @param Response|Server $server
  21 + */
  22 + public function onClose($server, int $fd, int $reactorId): void;
  23 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Swoole\Http\Request;
  15 +use Swoole\Http\Response;
  16 +
  17 +interface OnHandShakeInterface
  18 +{
  19 + public function onHandShake(Request $request, Response $response): void;
  20 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Swoole\Http\Response;
  15 +use Swoole\WebSocket\Frame;
  16 +use Swoole\WebSocket\Server;
  17 +
  18 +interface OnMessageInterface
  19 +{
  20 + /**
  21 + * @param Response|Server $server
  22 + */
  23 + public function onMessage($server, Frame $frame): void;
  24 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Swoole\Http\Request;
  15 +use Swoole\Http\Response;
  16 +use Swoole\WebSocket\Server;
  17 +
  18 +interface OnOpenInterface
  19 +{
  20 + /**
  21 + * @param Response|Server $server
  22 + */
  23 + public function onOpen($server, Request $request): void;
  24 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Swoole\WebSocket\Server;
  15 +
  16 +interface OnPacketInterface
  17 +{
  18 + /**
  19 + * @param Server $server
  20 + * @param mixed $data
  21 + * @param array $clientInfo
  22 + */
  23 + public function onPacket($server, $data, $clientInfo): void;
  24 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Swoole\Coroutine\Server\Connection;
  15 +use Swoole\Server as SwooleServer;
  16 +
  17 +interface OnReceiveInterface
  18 +{
  19 + /**
  20 + * @param Connection|SwooleServer $server
  21 + */
  22 + public function onReceive($server, int $fd, int $reactorId, string $data): void;
  23 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface OnRequestInterface
  15 +{
  16 + /**
  17 + * @param mixed $request swoole request or psr server request
  18 + * @param mixed $response swoole response or swow session
  19 + */
  20 + public function onRequest($request, $response): void;
  21 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface PackerInterface
  15 +{
  16 + public function pack($data): string;
  17 +
  18 + public function unpack(string $data);
  19 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface PaginatorInterface
  15 +{
  16 + /**
  17 + * Get the URL for a given page.
  18 + */
  19 + public function url(int $page): string;
  20 +
  21 + /**
  22 + * Add a set of query string values to the paginator.
  23 + *
  24 + * @param array|string $key
  25 + * @return $this
  26 + */
  27 + public function appends($key, ?string $value = null);
  28 +
  29 + /**
  30 + * Get / set the URL fragment to be appended to URLs.
  31 + *
  32 + * @return $this|string
  33 + */
  34 + public function fragment(?string $fragment = null);
  35 +
  36 + /**
  37 + * The URL for the next page, or null.
  38 + */
  39 + public function nextPageUrl(): ?string;
  40 +
  41 + /**
  42 + * Get the URL for the previous page, or null.
  43 + */
  44 + public function previousPageUrl(): ?string;
  45 +
  46 + /**
  47 + * Get all of the items being paginated.
  48 + */
  49 + public function items(): array;
  50 +
  51 + /**
  52 + * Get the "index" of the first item being paginated.
  53 + */
  54 + public function firstItem(): ?int;
  55 +
  56 + /**
  57 + * Get the "index" of the last item being paginated.
  58 + */
  59 + public function lastItem(): ?int;
  60 +
  61 + /**
  62 + * Determine how many items are being shown per page.
  63 + */
  64 + public function perPage(): int;
  65 +
  66 + /**
  67 + * Determine the current page being paginated.
  68 + */
  69 + public function currentPage(): int;
  70 +
  71 + /**
  72 + * Determine if there are enough items to split into multiple pages.
  73 + */
  74 + public function hasPages(): bool;
  75 +
  76 + /**
  77 + * Determine if there is more items in the data store.
  78 + */
  79 + public function hasMorePages(): bool;
  80 +
  81 + /**
  82 + * Determine if the list of items is empty or not.
  83 + */
  84 + public function isEmpty(): bool;
  85 +
  86 + /**
  87 + * Determine if the list of items is not empty.
  88 + */
  89 + public function isNotEmpty(): bool;
  90 +
  91 + /**
  92 + * Render the paginator using a given view.
  93 + */
  94 + public function render(?string $view = null, array $data = []): string;
  95 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface PoolInterface
  15 +{
  16 + /**
  17 + * Get a connection from the connection pool.
  18 + */
  19 + public function get(): ConnectionInterface;
  20 +
  21 + /**
  22 + * Release a connection back to the connection pool.
  23 + */
  24 + public function release(ConnectionInterface $connection): void;
  25 +
  26 + /**
  27 + * Close and clear the connection pool.
  28 + */
  29 + public function flush(): void;
  30 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface PoolOptionInterface
  15 +{
  16 + public function getMaxConnections(): int;
  17 +
  18 + public function getMinConnections(): int;
  19 +
  20 + public function getConnectTimeout(): float;
  21 +
  22 + public function getWaitTimeout(): float;
  23 +
  24 + public function getHeartbeat(): float;
  25 +
  26 + public function getMaxIdleTime(): float;
  27 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Swoole\Coroutine\Http\Server as CoHttpServer;
  15 +use Swoole\Coroutine\Server as CoServer;
  16 +use Swoole\Server;
  17 +
  18 +interface ProcessInterface
  19 +{
  20 + /**
  21 + * Create the process object according to process number and bind to server.
  22 + * @param CoHttpServer|CoServer|Server $server
  23 + */
  24 + public function bind($server): void;
  25 +
  26 + /**
  27 + * Determine if the process should start ?
  28 + * @param CoServer|Server $server
  29 + */
  30 + public function isEnable($server): bool;
  31 +
  32 + /**
  33 + * The logical of process will place in here.
  34 + */
  35 + public function handle(): void;
  36 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Psr\Http\Message\ResponseInterface;
  15 +
  16 +interface ResponseEmitterInterface
  17 +{
  18 + /**
  19 + * @param mixed $connection swoole response or swow session
  20 + */
  21 + public function emit(ResponseInterface $response, $connection, bool $withContent = true);
  22 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface SessionInterface
  15 +{
  16 + /**
  17 + * Starts the session storage.
  18 + *
  19 + * @throws \RuntimeException if session fails to start
  20 + * @return bool True if session started
  21 + */
  22 + public function start(): bool;
  23 +
  24 + /**
  25 + * Returns the session ID.
  26 + *
  27 + * @return string The session ID
  28 + */
  29 + public function getId(): string;
  30 +
  31 + /**
  32 + * Sets the session ID.
  33 + */
  34 + public function setId(string $id);
  35 +
  36 + /**
  37 + * Returns the session name.
  38 + */
  39 + public function getName(): string;
  40 +
  41 + /**
  42 + * Sets the session name.
  43 + */
  44 + public function setName(string $name);
  45 +
  46 + /**
  47 + * Invalidates the current session.
  48 + *
  49 + * Clears all session attributes and flashes and regenerates the
  50 + * session and deletes the old session from persistence.
  51 + *
  52 + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
  53 + * will leave the system settings unchanged, 0 sets the cookie
  54 + * to expire with browser session. Time is in seconds, and is
  55 + * not a Unix timestamp.
  56 + *
  57 + * @return bool True if session invalidated, false if error
  58 + */
  59 + public function invalidate(?int $lifetime = null): bool;
  60 +
  61 + /**
  62 + * Migrates the current session to a new session id while maintaining all
  63 + * session attributes.
  64 + *
  65 + * @param bool $destroy Whether to delete the old session or leave it to garbage collection
  66 + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
  67 + * will leave the system settings unchanged, 0 sets the cookie
  68 + * to expire with browser session. Time is in seconds, and is
  69 + * not a Unix timestamp.
  70 + *
  71 + * @return bool True if session migrated, false if error
  72 + */
  73 + public function migrate(bool $destroy = false, ?int $lifetime = null): bool;
  74 +
  75 + /**
  76 + * Force the session to be saved and closed.
  77 + *
  78 + * This method is generally not required for real sessions as
  79 + * the session will be automatically saved at the end of
  80 + * code execution.
  81 + */
  82 + public function save(): void;
  83 +
  84 + /**
  85 + * Checks if an attribute is defined.
  86 + *
  87 + * @param string $name The attribute name
  88 + *
  89 + * @return bool true if the attribute is defined, false otherwise
  90 + */
  91 + public function has(string $name): bool;
  92 +
  93 + /**
  94 + * Returns an attribute.
  95 + *
  96 + * @param string $name The attribute name
  97 + * @param mixed $default The default value if not found
  98 + */
  99 + public function get(string $name, $default = null);
  100 +
  101 + /**
  102 + * Sets an attribute.
  103 + * @param mixed $value
  104 + */
  105 + public function set(string $name, $value): void;
  106 +
  107 + /**
  108 + * Put a key / value pair or array of key / value pairs in the session.
  109 + *
  110 + * @param array|string $key
  111 + * @param null|mixed $value
  112 + */
  113 + public function put($key, $value = null): void;
  114 +
  115 + /**
  116 + * Returns attributes.
  117 + */
  118 + public function all(): array;
  119 +
  120 + /**
  121 + * Sets attributes.
  122 + */
  123 + public function replace(array $attributes): void;
  124 +
  125 + /**
  126 + * Removes an attribute, returning its value.
  127 + *
  128 + * @return mixed The removed value or null when it does not exist
  129 + */
  130 + public function remove(string $name);
  131 +
  132 + /**
  133 + * Remove one or many items from the session.
  134 + *
  135 + * @param array|string $keys
  136 + */
  137 + public function forget($keys): void;
  138 +
  139 + /**
  140 + * Clears all attributes.
  141 + */
  142 + public function clear(): void;
  143 +
  144 + /**
  145 + * Checks if the session was started.
  146 + */
  147 + public function isStarted(): bool;
  148 +
  149 + /**
  150 + * Get the previous URL from the session.
  151 + */
  152 + public function previousUrl(): ?string;
  153 +
  154 + /**
  155 + * Set the "previous" URL in the session.
  156 + */
  157 + public function setPreviousUrl(string $url): void;
  158 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Psr\Log\LoggerInterface;
  15 +
  16 +interface StdoutLoggerInterface extends LoggerInterface
  17 +{
  18 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface Synchronized
  15 +{
  16 + /**
  17 + * Whether the data has been synchronized.
  18 + */
  19 + public function isSynchronized(): bool;
  20 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface TranslatorInterface
  15 +{
  16 + /**
  17 + * Get the translation for a given key.
  18 + */
  19 + public function trans(string $key, array $replace = [], ?string $locale = null);
  20 +
  21 + /**
  22 + * Get a translation according to an integer value.
  23 + *
  24 + * @param array|\Countable|int $number
  25 + */
  26 + public function transChoice(string $key, $number, array $replace = [], ?string $locale = null): string;
  27 +
  28 + /**
  29 + * Get the default locale being used.
  30 + */
  31 + public function getLocale(): string;
  32 +
  33 + /**
  34 + * Set the default locale.
  35 + */
  36 + public function setLocale(string $locale);
  37 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface TranslatorLoaderInterface
  15 +{
  16 + /**
  17 + * Load the messages for the given locale.
  18 + */
  19 + public function load(string $locale, string $group, ?string $namespace = null): array;
  20 +
  21 + /**
  22 + * Add a new namespace to the loader.
  23 + */
  24 + public function addNamespace(string $namespace, string $hint);
  25 +
  26 + /**
  27 + * Add a new JSON path to the loader.
  28 + */
  29 + public function addJsonPath(string $path);
  30 +
  31 + /**
  32 + * Get an array of all the registered namespaces.
  33 + */
  34 + public function namespaces(): array;
  35 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +interface UnCompressInterface
  15 +{
  16 + public function uncompress();
  17 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Contract;
  13 +
  14 +use Hyperf\Utils\Contracts\MessageBag;
  15 +use Hyperf\Utils\Contracts\MessageProvider;
  16 +
  17 +interface ValidatorInterface extends MessageProvider
  18 +{
  19 + /**
  20 + * Run the validator's rules against its data.
  21 + */
  22 + public function validate(): array;
  23 +
  24 + /**
  25 + * Get the attributes and values that were validated.
  26 + */
  27 + public function validated(): array;
  28 +
  29 + /**
  30 + * Determine if the data fails the validation rules.
  31 + */
  32 + public function fails(): bool;
  33 +
  34 + /**
  35 + * Get the failed validation rules.
  36 + */
  37 + public function failed(): array;
  38 +
  39 + /**
  40 + * Add conditions to a given field based on a Closure.
  41 + *
  42 + * @param array|string $attribute
  43 + * @param array|string $rules
  44 + * @return $this
  45 + */
  46 + public function sometimes($attribute, $rules, callable $callback);
  47 +
  48 + /**
  49 + * Add an after validation callback.
  50 + *
  51 + * @param callable|string $callback
  52 + * @return $this
  53 + */
  54 + public function after($callback);
  55 +
  56 + /**
  57 + * Get all of the validation error messages.
  58 + */
  59 + public function errors(): MessageBag;
  60 +}
  1 +/.github export-ignore
  2 +/examples export-ignore
  3 +/tests export-ignore
  4 +
  1 +/vendor/
  2 +composer.lock
  3 +*.cache
  4 +*.log
  1 +<?php
  2 +
  3 +$header = <<<'EOF'
  4 +This file is part of Hyperf.
  5 +
  6 +@link https://www.hyperf.io
  7 +@document https://hyperf.wiki
  8 +@contact group@hyperf.io
  9 +@license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10 +EOF;
  11 +
  12 +return (new PhpCsFixer\Config())
  13 + ->setRiskyAllowed(true)
  14 + ->setRules([
  15 + '@PSR2' => true,
  16 + '@Symfony' => true,
  17 + '@DoctrineAnnotation' => true,
  18 + '@PhpCsFixer' => true,
  19 + 'header_comment' => [
  20 + 'comment_type' => 'PHPDoc',
  21 + 'header' => $header,
  22 + 'separate' => 'none',
  23 + 'location' => 'after_declare_strict',
  24 + ],
  25 + 'array_syntax' => [
  26 + 'syntax' => 'short'
  27 + ],
  28 + 'list_syntax' => [
  29 + 'syntax' => 'short'
  30 + ],
  31 + 'concat_space' => [
  32 + 'spacing' => 'one'
  33 + ],
  34 + 'blank_line_before_statement' => [
  35 + 'statements' => [
  36 + 'declare',
  37 + ],
  38 + ],
  39 + 'general_phpdoc_annotation_remove' => [
  40 + 'annotations' => [
  41 + 'author'
  42 + ],
  43 + ],
  44 + 'ordered_imports' => [
  45 + 'imports_order' => [
  46 + 'class', 'function', 'const',
  47 + ],
  48 + 'sort_algorithm' => 'alpha',
  49 + ],
  50 + 'single_line_comment_style' => [
  51 + 'comment_types' => [
  52 + ],
  53 + ],
  54 + 'yoda_style' => [
  55 + 'always_move_variable' => false,
  56 + 'equal' => false,
  57 + 'identical' => false,
  58 + ],
  59 + 'phpdoc_align' => [
  60 + 'align' => 'left',
  61 + ],
  62 + 'multiline_whitespace_before_semicolons' => [
  63 + 'strategy' => 'no_multi_line',
  64 + ],
  65 + 'constant_case' => [
  66 + 'case' => 'lower',
  67 + ],
  68 + 'class_attributes_separation' => true,
  69 + 'combine_consecutive_unsets' => true,
  70 + 'declare_strict_types' => true,
  71 + 'linebreak_after_opening_tag' => true,
  72 + 'lowercase_static_reference' => true,
  73 + 'no_useless_else' => true,
  74 + 'no_unused_imports' => true,
  75 + 'not_operator_with_successor_space' => true,
  76 + 'not_operator_with_space' => false,
  77 + 'ordered_class_elements' => true,
  78 + 'php_unit_strict' => false,
  79 + 'phpdoc_separation' => false,
  80 + 'single_quote' => true,
  81 + 'standardize_not_equals' => true,
  82 + 'multiline_comment_opening_closing' => true,
  83 + ])
  84 + ->setFinder(
  85 + PhpCsFixer\Finder::create()
  86 + ->exclude('vendor')
  87 + ->in(__DIR__)
  88 + )
  89 + ->setUsingCache(false);
  1 +<?php
  2 +
  3 +namespace PHPSTORM_META {
  4 + // Reflect
  5 + override(\Psr\Container\ContainerInterface::get(0), map('@'));
  6 +}
  1 +# Default Dockerfile
  2 +#
  3 +# @link https://www.hyperf.io
  4 +# @document https://hyperf.wiki
  5 +# @contact group@hyperf.io
  6 +# @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
  7 +
  8 +ARG PHP_VERSION
  9 +ARG ALPINE_VERSION
  10 +
  11 +FROM hyperf/hyperf:${PHP_VERSION}-alpine-${ALPINE_VERSION}-swoole
  12 +LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
  13 +
  14 +ARG timezone
  15 +ARG PHP_VERSION
  16 +
  17 +ENV TIMEZONE=${timezone:-"Asia/Shanghai"}
  18 +ENV COMPOSER_ROOT_VERSION="v1.2.0"
  19 +
  20 +# update
  21 +RUN set -ex \
  22 + # show php version and extensions
  23 + && php -v \
  24 + && php -m \
  25 + && php --ri swoole \
  26 + # ---------- some config ----------
  27 + && cd "/etc/php${PHP_VERSION%\.*}" \
  28 + # - config PHP
  29 + && { \
  30 + echo "upload_max_filesize=128M"; \
  31 + echo "post_max_size=128M"; \
  32 + echo "memory_limit=1G"; \
  33 + echo "date.timezone=${TIMEZONE}"; \
  34 + } | tee conf.d/99_overrides.ini \
  35 + # - config timezone
  36 + && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
  37 + && echo "${TIMEZONE}" > /etc/timezone \
  38 + # ---------- clear works ----------
  39 + && rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
  40 + && echo -e "\033[42;37m Build Completed :).\033[0m\n"
  41 +
  42 +WORKDIR /opt/www
  43 +
  44 +COPY . /opt/www
  45 +
  46 +RUN composer install -o
  1 +# Swoole Engine
  2 +
  3 +![Swoole Engine Test](https://github.com/hyperf/engine/workflows/Swoole%20Engine%20Test/badge.svg)
  4 +
  5 +```
  6 +composer require hyperf/engine
  7 +```
  1 +{
  2 + "name": "hyperf/engine",
  3 + "type": "library",
  4 + "license": "MIT",
  5 + "keywords": [
  6 + "php",
  7 + "hyperf"
  8 + ],
  9 + "description": "",
  10 + "autoload": {
  11 + "psr-4": {
  12 + "Hyperf\\Engine\\": "src/"
  13 + }
  14 + },
  15 + "autoload-dev": {
  16 + "psr-4": {
  17 + "HyperfTest\\": "tests"
  18 + }
  19 + },
  20 + "require": {
  21 + "php": ">=7.4"
  22 + },
  23 + "require-dev": {
  24 + "friendsofphp/php-cs-fixer": "^3.0",
  25 + "hyperf/guzzle": "^2.2",
  26 + "phpstan/phpstan": "^1.0",
  27 + "phpunit/phpunit": "^9.4",
  28 + "swoole/ide-helper": "dev-master"
  29 + },
  30 + "suggest": {
  31 + "ext-swoole": ">=4.5"
  32 + },
  33 + "minimum-stability": "dev",
  34 + "prefer-stable": true,
  35 + "config": {
  36 + "optimize-autoloader": true,
  37 + "sort-packages": true
  38 + },
  39 + "extra": {
  40 + "branch-alias": {
  41 + "dev-master": "1.2-dev"
  42 + }
  43 + },
  44 + "scripts": {
  45 + "test": "phpunit -c phpunit.xml --colors=always",
  46 + "analyse": "phpstan analyse --memory-limit 1024M -l 0 ./src",
  47 + "cs-fix": "php-cs-fixer fix $1"
  48 + }
  49 +}
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<phpunit bootstrap="tests/bootstrap.php"
  3 + backupGlobals="false"
  4 + backupStaticAttributes="false"
  5 + verbose="true"
  6 + colors="true"
  7 + convertErrorsToExceptions="true"
  8 + convertNoticesToExceptions="true"
  9 + convertWarningsToExceptions="true"
  10 + processIsolation="false"
  11 + stopOnFailure="false">
  12 + <testsuite name="Testsuite">
  13 + <directory>./tests/</directory>
  14 + </testsuite>
  15 +</phpunit>
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine;
  13 +
  14 +use Hyperf\Engine\Contract\ChannelInterface;
  15 +use Hyperf\Engine\Exception\RuntimeException;
  16 +
  17 +if (PHP_VERSION_ID > 80000 && SWOOLE_VERSION_ID >= 50000) {
  18 + class Channel extends \Swoole\Coroutine\Channel implements ChannelInterface
  19 + {
  20 + protected bool $closed = false;
  21 +
  22 + public function push(mixed $data, float $timeout = -1): bool
  23 + {
  24 + return parent::push($data, $timeout);
  25 + }
  26 +
  27 + public function pop(float $timeout = -1): mixed
  28 + {
  29 + return parent::pop($timeout);
  30 + }
  31 +
  32 + public function getCapacity(): int
  33 + {
  34 + return $this->capacity;
  35 + }
  36 +
  37 + public function getLength(): int
  38 + {
  39 + return $this->length();
  40 + }
  41 +
  42 + public function isAvailable(): bool
  43 + {
  44 + return ! $this->isClosing();
  45 + }
  46 +
  47 + public function close(): bool
  48 + {
  49 + $this->closed = true;
  50 + return parent::close();
  51 + }
  52 +
  53 + public function hasProducers(): bool
  54 + {
  55 + throw new RuntimeException('Not supported.');
  56 + }
  57 +
  58 + public function hasConsumers(): bool
  59 + {
  60 + throw new RuntimeException('Not supported.');
  61 + }
  62 +
  63 + public function isReadable(): bool
  64 + {
  65 + throw new RuntimeException('Not supported.');
  66 + }
  67 +
  68 + public function isWritable(): bool
  69 + {
  70 + throw new RuntimeException('Not supported.');
  71 + }
  72 +
  73 + public function isClosing(): bool
  74 + {
  75 + return $this->closed || $this->errCode === SWOOLE_CHANNEL_CLOSED;
  76 + }
  77 +
  78 + public function isTimeout(): bool
  79 + {
  80 + return ! $this->closed && $this->errCode === SWOOLE_CHANNEL_TIMEOUT;
  81 + }
  82 + }
  83 +} else {
  84 + class Channel extends \Swoole\Coroutine\Channel implements ChannelInterface
  85 + {
  86 + /**
  87 + * @var bool
  88 + */
  89 + protected $closed = false;
  90 +
  91 + public function getCapacity(): int
  92 + {
  93 + return $this->capacity;
  94 + }
  95 +
  96 + public function getLength(): int
  97 + {
  98 + return $this->length();
  99 + }
  100 +
  101 + public function isAvailable(): bool
  102 + {
  103 + return ! $this->isClosing();
  104 + }
  105 +
  106 + public function close(): bool
  107 + {
  108 + $this->closed = true;
  109 + return parent::close();
  110 + }
  111 +
  112 + public function hasProducers(): bool
  113 + {
  114 + throw new RuntimeException('Not supported.');
  115 + }
  116 +
  117 + public function hasConsumers(): bool
  118 + {
  119 + throw new RuntimeException('Not supported.');
  120 + }
  121 +
  122 + public function isReadable(): bool
  123 + {
  124 + throw new RuntimeException('Not supported.');
  125 + }
  126 +
  127 + public function isWritable(): bool
  128 + {
  129 + throw new RuntimeException('Not supported.');
  130 + }
  131 +
  132 + public function isClosing(): bool
  133 + {
  134 + return $this->closed || $this->errCode === SWOOLE_CHANNEL_CLOSED;
  135 + }
  136 +
  137 + public function isTimeout(): bool
  138 + {
  139 + return ! $this->closed && $this->errCode === SWOOLE_CHANNEL_TIMEOUT;
  140 + }
  141 + }
  142 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine;
  13 +
  14 +use Swoole\Coroutine\Http\Server as HttpServer;
  15 +use Swoole\Coroutine\Server;
  16 +
  17 +class Constant
  18 +{
  19 + public const ENGINE = 'Swoole';
  20 +
  21 + public static function isCoroutineServer($server): bool
  22 + {
  23 + return $server instanceof Server || $server instanceof HttpServer;
  24 + }
  25 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Contract;
  13 +
  14 +if (PHP_VERSION_ID > 80000 && SWOOLE_VERSION_ID >= 50000) {
  15 + interface ChannelInterface
  16 + {
  17 + /**
  18 + * @param float|int $timeout [optional] = -1
  19 + */
  20 + public function push(mixed $data, float $timeout = -1): bool;
  21 +
  22 + /**
  23 + * @param float $timeout seconds [optional] = -1
  24 + * @return mixed when pop failed, return false
  25 + */
  26 + public function pop(float $timeout = -1): mixed;
  27 +
  28 + /**
  29 + * Swow: When the channel is closed, all the data in it will be destroyed.
  30 + * Swoole: When the channel is closed, the data in it can still be popped out, but push behavior will no longer succeed.
  31 + */
  32 + public function close(): bool;
  33 +
  34 + public function getCapacity(): int;
  35 +
  36 + public function getLength(): int;
  37 +
  38 + public function isAvailable(): bool;
  39 +
  40 + public function hasProducers(): bool;
  41 +
  42 + public function hasConsumers(): bool;
  43 +
  44 + public function isEmpty(): bool;
  45 +
  46 + public function isFull(): bool;
  47 +
  48 + public function isReadable(): bool;
  49 +
  50 + public function isWritable(): bool;
  51 +
  52 + public function isClosing(): bool;
  53 +
  54 + public function isTimeout(): bool;
  55 + }
  56 +} else {
  57 + interface ChannelInterface
  58 + {
  59 + /**
  60 + * @param mixed $data [required]
  61 + * @param float|int $timeout [optional] = -1
  62 + * @return bool
  63 + */
  64 + public function push($data, $timeout = -1);
  65 +
  66 + /**
  67 + * @param float $timeout seconds [optional] = -1
  68 + * @return mixed when pop failed, return false
  69 + */
  70 + public function pop($timeout = -1);
  71 +
  72 + /**
  73 + * Swow: When the channel is closed, all the data in it will be destroyed.
  74 + * Swoole: When the channel is closed, the data in it can still be popped out, but push behavior will no longer succeed.
  75 + * @return mixed
  76 + */
  77 + public function close(): bool;
  78 +
  79 + /**
  80 + * @return int
  81 + */
  82 + public function getCapacity();
  83 +
  84 + /**
  85 + * @return int
  86 + */
  87 + public function getLength();
  88 +
  89 + /**
  90 + * @return bool
  91 + */
  92 + public function isAvailable();
  93 +
  94 + /**
  95 + * @return bool
  96 + */
  97 + public function hasProducers();
  98 +
  99 + /**
  100 + * @return bool
  101 + */
  102 + public function hasConsumers();
  103 +
  104 + /**
  105 + * @return bool
  106 + */
  107 + public function isEmpty();
  108 +
  109 + /**
  110 + * @return bool
  111 + */
  112 + public function isFull();
  113 +
  114 + /**
  115 + * @return bool
  116 + */
  117 + public function isReadable();
  118 +
  119 + /**
  120 + * @return bool
  121 + */
  122 + public function isWritable();
  123 +
  124 + /**
  125 + * @return bool
  126 + */
  127 + public function isClosing();
  128 +
  129 + /**
  130 + * @return bool
  131 + */
  132 + public function isTimeout();
  133 + }
  134 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Contract;
  13 +
  14 +use Hyperf\Engine\Exception\CoroutineDestroyedException;
  15 +use Hyperf\Engine\Exception\RunningInNonCoroutineException;
  16 +
  17 +interface CoroutineInterface
  18 +{
  19 + /**
  20 + * @param callable $callable [required]
  21 + */
  22 + public function __construct(callable $callable);
  23 +
  24 + /**
  25 + * @param mixed ...$data
  26 + * @return $this
  27 + */
  28 + public function execute(...$data);
  29 +
  30 + /**
  31 + * @return int
  32 + */
  33 + public function getId();
  34 +
  35 + /**
  36 + * @param callable $callable [required]
  37 + * @param mixed ...$data
  38 + * @return $this
  39 + */
  40 + public static function create(callable $callable, ...$data);
  41 +
  42 + /**
  43 + * @return int returns coroutine id from current coroutine, -1 in non coroutine environment
  44 + */
  45 + public static function id();
  46 +
  47 + /**
  48 + * Returns the parent coroutine ID.
  49 + * Returns 0 when running in the top level coroutine.
  50 + * @throws RunningInNonCoroutineException when running in non-coroutine context
  51 + * @throws CoroutineDestroyedException when the coroutine has been destroyed
  52 + */
  53 + public static function pid(?int $id = null);
  54 +
  55 + /**
  56 + * Set config to coroutine.
  57 + */
  58 + public static function set(array $config);
  59 +
  60 + /**
  61 + * @param null|int $id coroutine id
  62 + * @return null|\ArrayObject
  63 + */
  64 + public static function getContextFor(?int $id = null);
  65 +
  66 + /**
  67 + * Execute callback when coroutine destruct.
  68 + */
  69 + public static function defer(callable $callable);
  70 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Contract\Http;
  13 +
  14 +use Hyperf\Engine\Http\RawResponse;
  15 +
  16 +interface ClientInterface
  17 +{
  18 + public function set(array $settings): bool;
  19 +
  20 + /**
  21 + * @param string[][] $headers
  22 + */
  23 + public function request(string $method = 'GET', string $path = '/', array $headers = [], string $contents = '', string $version = '1.1'): RawResponse;
  24 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Contract\WebSocket;
  13 +
  14 +interface WebSocketInterface
  15 +{
  16 + public const ON_MESSAGE = 'message';
  17 +
  18 + public const ON_CLOSE = 'close';
  19 +
  20 + public function on(string $event, callable $callback): void;
  21 +
  22 + public function start(): void;
  23 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine;
  13 +
  14 +use Hyperf\Engine\Contract\CoroutineInterface;
  15 +use Hyperf\Engine\Exception\CoroutineDestroyedException;
  16 +use Hyperf\Engine\Exception\RunningInNonCoroutineException;
  17 +use Hyperf\Engine\Exception\RuntimeException;
  18 +use Swoole\Coroutine as SwooleCo;
  19 +
  20 +class Coroutine implements CoroutineInterface
  21 +{
  22 + /**
  23 + * @var callable
  24 + */
  25 + private $callable;
  26 +
  27 + /**
  28 + * @var int
  29 + */
  30 + private $id;
  31 +
  32 + public function __construct(callable $callable)
  33 + {
  34 + $this->callable = $callable;
  35 + }
  36 +
  37 + public static function create(callable $callable, ...$data)
  38 + {
  39 + $coroutine = new static($callable);
  40 + $coroutine->execute(...$data);
  41 + return $coroutine;
  42 + }
  43 +
  44 + public function execute(...$data)
  45 + {
  46 + $this->id = SwooleCo::create($this->callable, ...$data);
  47 + return $this;
  48 + }
  49 +
  50 + public function getId()
  51 + {
  52 + if (is_null($this->id)) {
  53 + throw new RuntimeException('Coroutine was not be executed.');
  54 + }
  55 + return $this->id;
  56 + }
  57 +
  58 + public static function id()
  59 + {
  60 + return SwooleCo::getCid();
  61 + }
  62 +
  63 + public static function pid(?int $id = null)
  64 + {
  65 + if ($id) {
  66 + $cid = SwooleCo::getPcid($id);
  67 + if ($cid === false) {
  68 + throw new CoroutineDestroyedException(sprintf('Coroutine #%d has been destroyed.', $id));
  69 + }
  70 + } else {
  71 + $cid = SwooleCo::getPcid();
  72 + }
  73 + if ($cid === false) {
  74 + throw new RunningInNonCoroutineException('Non-Coroutine environment don\'t has parent coroutine id.');
  75 + }
  76 + return max(0, $cid);
  77 + }
  78 +
  79 + public static function set(array $config)
  80 + {
  81 + SwooleCo::set($config);
  82 + }
  83 +
  84 + /**
  85 + * @return null|\ArrayObject
  86 + */
  87 + public static function getContextFor(?int $id = null)
  88 + {
  89 + if ($id === null) {
  90 + return SwooleCo::getContext();
  91 + }
  92 +
  93 + return SwooleCo::getContext($id);
  94 + }
  95 +
  96 + public static function defer(callable $callable)
  97 + {
  98 + SwooleCo::defer($callable);
  99 + }
  100 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Exception;
  13 +
  14 +class CoroutineDestroyedException extends RuntimeException
  15 +{
  16 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Exception;
  13 +
  14 +class HttpClientException extends RuntimeException
  15 +{
  16 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Exception;
  13 +
  14 +class RunningInNonCoroutineException extends RuntimeException
  15 +{
  16 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Exception;
  13 +
  14 +class RuntimeException extends \RuntimeException
  15 +{
  16 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine;
  13 +
  14 +class Extension
  15 +{
  16 + public static function isLoaded(): bool
  17 + {
  18 + return extension_loaded('Swoole');
  19 + }
  20 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Http;
  13 +
  14 +use Hyperf\Engine\Contract\Http\ClientInterface;
  15 +use Hyperf\Engine\Exception\HttpClientException;
  16 +use Swoole\Coroutine\Http\Client as HttpClient;
  17 +
  18 +class Client extends HttpClient implements ClientInterface
  19 +{
  20 + public function set(array $settings): bool
  21 + {
  22 + return parent::set($settings);
  23 + }
  24 +
  25 + /**
  26 + * @param string[][] $headers
  27 + */
  28 + public function request(string $method = 'GET', string $path = '/', array $headers = [], string $contents = '', string $version = '1.1'): RawResponse
  29 + {
  30 + $this->setMethod($method);
  31 + $this->setData($contents);
  32 + $this->setHeaders($this->encodeHeaders($headers));
  33 + $this->execute($path);
  34 + if ($this->errCode !== 0) {
  35 + throw new HttpClientException($this->errMsg, $this->errCode);
  36 + }
  37 + return new RawResponse(
  38 + $this->statusCode,
  39 + $this->decodeHeaders($this->headers ?? []),
  40 + $this->body,
  41 + $version
  42 + );
  43 + }
  44 +
  45 + /**
  46 + * @param string[] $headers
  47 + * @return string[][]
  48 + */
  49 + private function decodeHeaders(array $headers): array
  50 + {
  51 + $result = [];
  52 + foreach ($headers as $name => $header) {
  53 + // The key of header is lower case.
  54 + $result[$name][] = $header;
  55 + }
  56 + if ($this->set_cookie_headers) {
  57 + $result['set-cookie'] = $this->set_cookie_headers;
  58 + }
  59 + return $result;
  60 + }
  61 +
  62 + /**
  63 + * Swoole engine not support two dimensional array.
  64 + * @param string[][] $headers
  65 + * @return string[]
  66 + */
  67 + private function encodeHeaders(array $headers): array
  68 + {
  69 + $result = [];
  70 + foreach ($headers as $name => $value) {
  71 + $result[$name] = is_array($value) ? implode(',', $value) : $value;
  72 + }
  73 +
  74 + return $result;
  75 + }
  76 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Http;
  13 +
  14 +use Swoole\Http\Response;
  15 +
  16 +class FdGetter
  17 +{
  18 + public function get(Response $response): int
  19 + {
  20 + return $response->fd;
  21 + }
  22 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\Http;
  13 +
  14 +final class RawResponse
  15 +{
  16 + /**
  17 + * @var int
  18 + */
  19 + public $statusCode = 0;
  20 +
  21 + /**
  22 + * @var string[][]
  23 + */
  24 + public $headers = [];
  25 +
  26 + /**
  27 + * @var string
  28 + */
  29 + public $body = '';
  30 +
  31 + /**
  32 + * Protocol version.
  33 + * @var string
  34 + */
  35 + public $version = '';
  36 +
  37 + /**
  38 + * @param string[][] $headers
  39 + */
  40 + public function __construct(int $statusCode, array $headers, string $body, string $version)
  41 + {
  42 + $this->statusCode = $statusCode;
  43 + $this->headers = $headers;
  44 + $this->body = $body;
  45 + $this->version = $version;
  46 + }
  47 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine;
  13 +
  14 +class Socket extends \Swoole\Coroutine\Socket
  15 +{
  16 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine;
  13 +
  14 +class WaitGroup extends \Swoole\Coroutine\WaitGroup
  15 +{
  16 +}
  1 +<?php
  2 +
  3 +declare(strict_types=1);
  4 +/**
  5 + * This file is part of Hyperf.
  6 + *
  7 + * @link https://www.hyperf.io
  8 + * @document https://hyperf.wiki
  9 + * @contact group@hyperf.io
  10 + * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  11 + */
  12 +namespace Hyperf\Engine\WebSocket;
  13 +
  14 +class Frame
  15 +{
  16 + public const PING = '27890027';
  17 +
  18 + public const PONG = '278a0027';
  19 +}