diff --git a/application/home/config/config.php b/application/home/config/config.php index 60da52b..0b3a9a8 100644 --- a/application/home/config/config.php +++ b/application/home/config/config.php @@ -5,7 +5,7 @@ return [ // 'Smarty' => \top\library\template\driver\Smarty::class, 'Top' => \top\library\template\driver\Top::class, ], - 'decorator' => [], + 'middleware' => [], 'session' => [ 'open' => true, 'prefix' => 'home', diff --git a/application/home/decorator/index.html b/application/home/middleware/index.html similarity index 100% rename from application/home/decorator/index.html rename to application/home/middleware/index.html diff --git a/framework/extend/wechat/WeChatAPI.php b/framework/extend/wechat/WeChatAPI.php new file mode 100644 index 0000000..e92e545 --- /dev/null +++ b/framework/extend/wechat/WeChatAPI.php @@ -0,0 +1,360 @@ +url = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; + $this->config = $config; + $this->lang = $lang ? $lang : 'zh_CN'; + } + + /** + * 获取access_token + * @return mixed + * @throws WeChatAPIException + */ + private function getAccessToken() + { + $file = './access_token.json'; + if (file_exists($file)) { + $content = file_get_contents($file); + $result = json_decode($content, true); + $expires = $result['expires_in'] - (time() - filemtime($file)); + if ($expires <= 5) { + @unlink($file); + return $this->getAccessToken(); + } + return $result; + } else { + $api = $this->accessTokenAPI . "grant_type=client_credential&appid={$this->config['appid']}"; + $api .= "&secret={$this->config['appsecret']}"; + $result = $this->createHttpRequest($api, null, true); + file_put_contents($file, $result[0]); + return $result[1]; + } + } + + /** + * 获取CODE + * @param $scope + */ + private function getCode($scope) + { + $redirect = $this->url; + $api = $this->codeAPI . "appid={$this->config['appid']}&redirect_uri={$redirect}"; + $api .= "&response_type=code&scope={$scope}&state=0#wechat_redirect"; + exit(header('location:' . $api)); + } + + /** + * 获取OAuth的access_token + * @param string $scope + * @return mixed + * @throws WeChatAPIException + */ + private function getOAuthAccessToken($scope = 'snsapi_base') + { + if (isset($_SESSION['oauth_access_token']) && !empty($_SESSION['oauth_access_token'])) { + $session = $_SESSION['oauth_access_token']; + $content = $session['content']; + $result = json_decode($content, true); + $expires = $result['expires_in'] - (time() - $session['time']); + if ($expires <= 5) { + unset($_SESSION['oauth_access_token']); + return $this->getOAuthAccessToken(); + } + return $result; + } else { + $code = isset($_GET['code']) ? $_GET['code'] : null; + if (!$code) { + $this->getCode($scope); + } + $api = $this->oauthAccessTokenAPI . "appid={$this->config['appid']}"; + $api .= "&secret={$this->config['appsecret']}&code={$code}&grant_type=authorization_code"; + $result = $this->createHttpRequest($api, null, true); + $_SESSION['oauth_access_token'] = [ + 'time' => time(), + 'content' => $result[0] + ]; + return $result[1]; + } + } + + /** + * 拉取用户信息 + * @param null $openid + * @return bool|mixed + * @throws WeChatAPIException + */ + public function getUserInfo($openid = null) + { + $postData = []; + if ($openid) { + $accessToken = $this->getAccessToken(); + $api = $this->userinfoUnionIdAPI . "access_token={$accessToken['access_token']}"; + if (is_array($openid)) { + $postData = json_encode([ + 'user_list' => $openid + ]); + } else { + $api .= "&openid={$openid}&lang={$this->lang}"; + } + } else { + $accessToken = $this->getOAuthAccessToken('snsapi_userinfo'); + $api = $this->userinfoAPI . "access_token={$accessToken['access_token']}"; + $api .= "&openid={$accessToken['openid']}&lang={$this->lang}"; + } + $result = $this->createHttpRequest($api, $postData); + return $result; + } + + /** + * 创建公众号菜单 + * $menu数据示例 + * [ + * [ + * 'type' => 'view', + * 'name' => 'TOP糯米', + * 'url' => 'https://www.topnuomi.com/' + * ], + * [ + * 'name' => '测试多级', + * 'sub_button' => [ + * [ + * 'type' => 'view', + * 'name' => '我的主页', + * 'url' => 'https://topnuomi.com/' + * ], + * [ + * 'type' => 'click', + * 'name' => '点击', + * 'key' => 'V1001_TODAY_MUSIC' + * ] + * ] + * ] + * ] + * @param array $menu + * @param array $matchrule + * @return bool + * @throws WeChatAPIException + */ + public function createMenu($menu = [], $matchrule = []) + { + // 公众号菜单 + $menu = [ + 'button' => $menu + ]; + // 个性化菜单 + $matchrule = (!empty($matchrule)) ? [ + 'matchrule' => $matchrule + ] : []; + $menu = array_merge($menu, $matchrule); + $menuJson = json_encode($menu, JSON_UNESCAPED_UNICODE); + $type = (!empty($matchrule)) ? 'addconditional' : 'create'; + return $this->menuAction($type, $menuJson); + } + + /** + * 获取公众号菜单 + * @return mixed + * @throws WeChatAPIException + */ + public function getMenu() + { + return $this->menuAction('get'); + } + + /** + * 删除公众号自定义菜单 + * @return mixed + * @throws WeChatAPIException + */ + public function deleteMenu($menuid = null) + { + $json = null; + if ($menuid) { + $json = json_encode([ + 'menuid' => $menuid + ]); + } + $type = ($menuid) ? 'delconditional' : 'delete'; + return $this->menuAction($type, $json, $menuid); + } + + /** + * 公众号菜单的基础操作 + * @param null $type + * @param null $postData + * @return bool|mixed + * @throws WeChatAPIException + */ + private function menuAction($type = null, $postData = null, $menuid = null) + { + $typePool = ['create', 'get', 'delete', 'addconditional', 'delconditional']; + if (in_array($type, $typePool)) { + $accessToken = $this->getAccessToken(); + $api = $this->menuAPI . "/{$type}?access_token={$accessToken['access_token']}"; + $result = $this->createHttpRequest($api, $postData); + return ($type == 'get' || $type == 'addconditional') ? $result : true; + } else { + throw new WeChatAPIException('对公众号菜单的操作不在允许列表'); + } + } + + /** + * 获取自定义菜单配置 + * @return mixed + * @throws WeChatAPIException + */ + public function getCurrentSelfmenuInfo() + { + $accessToken = $this->getAccessToken(); + $api = $this->currentSelfmenuAPI . "access_token={$accessToken['access_token']}"; + $result = $this->createHttpRequest($api); + return $result; + } + + /** + * 设置所属行业 + * post数据示例 + * { + * "industry_id1":"1", + * "industry_id2":"4" + * } + * @param $id1 + * @param $id2 + * @return bool + * @throws WeChatAPIException + */ + public function setIndustry($id1, $id2) + { + $accessToken = $this->getAccessToken(); + $api = $this->templateAPI . "api_set_industry?access_token={$accessToken['access_token']}"; + $postData = json_encode([ + 'industry_id1' => $id1, + 'industry_id2' => $id2 + ]); + $this->createHttpRequest($api, $postData); + return true; + } + + /** + * 获取设置的行业信息 + * @return mixed + */ + public function getIndustry() + { + $accessToken = $this->getAccessToken(); + $api = $this->templateAPI . "get_industry?access_token={$accessToken['access_token']}"; + $result = $this->createHttpRequest($api); + return $result; + } + + /** + * 获取模板列表 + * @return mixed + */ + public function getTemplateList() + { + $accessToken = $this->getAccessToken(); + $api = $this->templateAPI . "get_all_private_template?access_token={$accessToken['access_token']}"; + $result = $this->createHttpRequest($api); + return $result; + } + + /** + * 创建HTTP请求 + * @param $api + * @param array $data + * @param bool $includeJson + * @return mixed + */ + private function createHttpRequest($api, $data = [], $includeJson = false) + { + $json = create_http_request($api, $data); + $result = json_decode($json, true); + if (isset($result['errcode']) && $result['errcode'] != 0) { + throw new WeChatAPIException("code:{$result['errcode']}, {$result['errmsg']}"); + } + return ($includeJson) ? [$json, $result] : $result; + } + + /** + * 获取错误信息 + * @return null + */ + public function getError() + { + return $this->error; + } + +} diff --git a/framework/extend/wechat/WeChatAPIException.php b/framework/extend/wechat/WeChatAPIException.php new file mode 100644 index 0000000..6471e30 --- /dev/null +++ b/framework/extend/wechat/WeChatAPIException.php @@ -0,0 +1,8 @@ +checkSignature()) { + echo $echoStr; + exit; + } + } + + public function setToken($token) + { + $this->token = $token; + return $this; + } + + public function responseMsg() + { + //get post data, May be due to the different environments + $postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; + //extract post data + if (!empty($postStr)) { + $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); + $fromUsername = $postObj->FromUserName; + $toUsername = $postObj->ToUserName; + $keyword = trim($postObj->Content); + $time = time(); + $textTpl = " + + + %s + + + 0 + "; + if (!empty($keyword)) { + $msgType = "text"; + $contentStr = "Welcome to wechat world!"; + $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr); + echo $resultStr; + } else { + echo "Input something..."; + } + } else { + echo ""; + exit; + } + } + + private function checkSignature() + { + $signature = $_GET["signature"]; + $timestamp = $_GET["timestamp"]; + $nonce = $_GET["nonce"]; + $token = $this->token; + $tmpArr = array($token, $timestamp, $nonce); + sort($tmpArr); + $tmpStr = implode($tmpArr); + $tmpStr = sha1($tmpStr); + if ($tmpStr == $signature) { + return true; + } else { + return false; + } + } +} \ No newline at end of file diff --git a/framework/library/exception/RequestException.php b/framework/library/exception/RequestException.php new file mode 100644 index 0000000..88ce980 --- /dev/null +++ b/framework/library/exception/RequestException.php @@ -0,0 +1,8 @@ +router->params; } - /** - * 指定装饰器 - * @param DecoratorIfs $decorator - */ - private function decorator(DecoratorIfs $decorator) - { - $this->decorator[] = $decorator; - } - - /** - * 装饰器前置方法 - */ - private function beforeRoute() - { - foreach ($this->decorator as $decorator) { - $decorator->before(); - } - } - - /** - * 装饰器后置方法 - * @param $data - */ - private function afterRoute($data) - { - $this->decorator = array_reverse($this->decorator); - foreach ($this->decorator as $decorator) { - $decorator->after($data); - } - } - - /** - * 指定路由驱动 - * @param $type - * @return string|Command|Pathinfo - */ - private function routeDriver($type) - { - $routeDriver = ''; - if (php_sapi_name() == 'cli') { - // 命令行运行程序 - $routeDriver = new Command(); - } else { - // 其他方式 - switch ($type) { - case 1: - $routeDriver = new Pathinfo(); - break; - default: - // 其他 - } - } - return $routeDriver; - } - - /** - * 设置路由并执行程序 - * @param $type - * @param $defaultModule - * @return mixed - * @throws \top\library\exception\RouteException - */ - public function execute($type, $defaultModule) - { - // 实例化路由,并执行对应方法 - $routeDriver = $this->routeDriver($type); - $this->router = (new Router($routeDriver, $defaultModule))->handler(); - $data = $this->runAction(); - return $data; - } - - /** - * 调用对应方法 - * @return mixed - * @throws \ReflectionException - */ - private function runAction() - { - $userDecorators = Register::get('Config')->get('decorator'); - $systemDecorators = [InitDecorator::class]; - - $decorators = array_merge($systemDecorators, $userDecorators); - foreach ($decorators as $key => $value) { - $this->decorator(new $value()); - } - - $this->beforeRoute(); - - $ctrl = $this->router->class; - $method = $this->router->method; - $params = $this->router->params; - - $object = new $ctrl(); - $reflectionClass = new \ReflectionClass($ctrl); - if ($reflectionClass->hasMethod('_init')) { - $data = $object->_init(); - } - if (!isset($data)) { - $reflectionMethod = new \ReflectionMethod($ctrl, $method); - $data = $reflectionMethod->invokeArgs($object, $params); - } - - $this->afterRoute($data); - - return $data; - } - /** * 移除值 * @param $field @@ -426,6 +318,113 @@ class Request } } + /** + * 设置中间件 + * @param MiddlewareIfs $middleware + */ + private function middleware(MiddlewareIfs $middleware) + { + $this->middleware[] = $middleware; + } + + /** + * 中间件前置方法 + */ + private function beforeRoute() + { + foreach ($this->middleware as $middleware) { + $middleware->before(); + } + } + + /** + * 中间件后置方法 + * @param $data + */ + private function afterRoute($data) + { + $this->middleware = array_reverse($this->middleware); + foreach ($this->middleware as $middleware) { + $middleware->after($data); + } + } + + /** + * 指定路由驱动 + * @param $type + * @return string|Command|Pathinfo + */ + private function routeDriver($type) + { + $routeDriver = ''; + if (php_sapi_name() == 'cli') { + // 命令行运行程序 + $routeDriver = new Command(); + } else { + // 其他方式 + switch ($type) { + case 1: + $routeDriver = new Pathinfo(); + break; + default: + // 其他 + } + } + return $routeDriver; + } + + /** + * 设置路由并执行程序 + * @param $type + * @param $defaultModule + * @return mixed + * @throws \top\library\exception\RouteException + */ + public function execute($type, $defaultModule) + { + // 实例化路由,并执行对应方法 + $routeDriver = $this->routeDriver($type); + $this->router = (new Router($routeDriver, $defaultModule))->handler(); + $data = $this->runAction(); + return $data; + } + + /** + * 调用对应方法 + * @return mixed + * @throws \ReflectionException + */ + private function runAction() + { + $userMiddleware = Register::get('Config')->get('middleware'); + $systemMiddleware = [Init::class]; + + $middleware = array_merge($systemMiddleware, $userMiddleware); + foreach ($middleware as $key => $value) { + $this->middleware(new $value()); + } + + $this->beforeRoute(); + + $ctrl = $this->router->class; + $method = $this->router->method; + $params = $this->router->params; + + $object = new $ctrl(); + $reflectionClass = new \ReflectionClass($ctrl); + if ($reflectionClass->hasMethod('_init')) { + $data = $object->_init(); + } + if (!isset($data)) { + $reflectionMethod = new \ReflectionMethod($ctrl, $method); + $data = $reflectionMethod->invokeArgs($object, $params); + } + + $this->afterRoute($data); + + return $data; + } + public function __destruct() { } diff --git a/framework/library/route/driver/Pathinfo.php b/framework/library/route/driver/Pathinfo.php index 0b753ec..6118ab5 100644 --- a/framework/library/route/driver/Pathinfo.php +++ b/framework/library/route/driver/Pathinfo.php @@ -119,7 +119,7 @@ class Pathinfo implements RouteIfs unset($this->uriArray[0], $this->uriArray[1], $this->uriArray[2]); $this->uriArray = array_merge($this->uriArray, []); if (!empty($this->uriArray) && class_exists($this->class)) { - $paramName = (new \ReflectionMethod($this->class, $this->action))->getParameters(); + $paramName = (new \ReflectionMethod($this->class, $this->method))->getParameters(); $paramNameArray = []; for ($i = 0; $i < count($paramName); $i++) { $paramNameArray[$paramName[$i]->name] = ''; @@ -127,7 +127,7 @@ class Pathinfo implements RouteIfs $params = []; for ($i = 0; $i < count($this->uriArray); $i = $i + 2) { if (isset($this->uriArray[$i + 1]) && $this->uriArray[$i + 1] != '') { - $_GET[$this->uriArray[$i]] = $this->uriArray[$i + 1]; + // $_GET[$this->uriArray[$i]] = $this->uriArray[$i + 1]; if (isset($paramNameArray[$this->uriArray[$i]])) { $params[$this->uriArray[$i]] = $this->uriArray[$i + 1]; } @@ -150,6 +150,7 @@ class Pathinfo implements RouteIfs $uri = ($pathinfo != '') ? $pathinfo : $this->default; } else { $uri = isset($_GET['s']) ? ltrim($_GET['s'], '/') : $this->default; + unset($_GET['s']); } $uri = str_replace('.html', '', $uri); $this->rawUri = $uri; diff --git a/framework/decorator/InitDecorator.php b/framework/middleware/Init.php similarity index 90% rename from framework/decorator/InitDecorator.php rename to framework/middleware/Init.php index d2c2b1c..f30c84c 100644 --- a/framework/decorator/InitDecorator.php +++ b/framework/middleware/Init.php @@ -1,18 +1,18 @@