<?php/** This file is part of the overtrue/wechat.** (c) overtrue <i@overtrue.me>** This source file is subject to the MIT license that is bundled* with this source code in the file LICENSE.*/namespace EasyWeChat\Kernel;use EasyWeChat\Kernel\Contracts\MessageInterface;use EasyWeChat\Kernel\Exceptions\BadRequestException;use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;use EasyWeChat\Kernel\Messages\Message;use EasyWeChat\Kernel\Messages\News;use EasyWeChat\Kernel\Messages\NewsItem;use EasyWeChat\Kernel\Messages\Raw as RawMessage;use EasyWeChat\Kernel\Messages\Text;use EasyWeChat\Kernel\Support\XML;use EasyWeChat\Kernel\Traits\Observable;use EasyWeChat\Kernel\Traits\ResponseCastable;use Symfony\Component\HttpFoundation\Response;/*** Class ServerGuard.** 1. url 里的 signature 只是将 token+nonce+timestamp 得到的签名,只是用于验证当前请求的,在公众号环境下一直有* 2. 企业号消息发送时是没有的,因为固定为完全模式,所以 url 里不会存在 signature, 只有 msg_signature 用于解密消息的** @author overtrue <i@overtrue.me>*/class ServerGuard{use Observable;use ResponseCastable;/*** @var bool*/protected $alwaysValidate = false;/*** Empty string.*/const SUCCESS_EMPTY_RESPONSE = 'success';/*** @var array*/const MESSAGE_TYPE_MAPPING = ['text' => Message::TEXT,'image' => Message::IMAGE,'voice' => Message::VOICE,'video' => Message::VIDEO,'shortvideo' => Message::SHORT_VIDEO,'location' => Message::LOCATION,'link' => Message::LINK,'device_event' => Message::DEVICE_EVENT,'device_text' => Message::DEVICE_TEXT,'event' => Message::EVENT,'file' => Message::FILE,'miniprogrampage' => Message::MINIPROGRAM_PAGE,];/*** @var \EasyWeChat\Kernel\ServiceContainer*/protected $app;/*** Constructor.** @codeCoverageIgnore** @param \EasyWeChat\Kernel\ServiceContainer $app*/public function __construct(ServiceContainer $app){$this->app = $app;foreach ($this->app->extension->observers() as $observer) {call_user_func_array([$this, 'push'], $observer);}}/*** Handle and return response.** @return Response** @throws BadRequestException* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException*/public function serve(): Response{$this->app['logger']->debug('Request received:', ['method' => $this->app['request']->getMethod(),'uri' => $this->app['request']->getUri(),'content-type' => $this->app['request']->getContentType(),'content' => $this->app['request']->getContent(),]);$response = $this->validate()->resolve();$this->app['logger']->debug('Server response created:', ['content' => $response->getContent()]);return $response;}/*** @return $this** @throws \EasyWeChat\Kernel\Exceptions\BadRequestException*/public function validate(){if (!$this->alwaysValidate && !$this->isSafeMode()) {return $this;}if ($this->app['request']->get('signature') !== $this->signature([$this->getToken(),$this->app['request']->get('timestamp'),$this->app['request']->get('nonce'),])) {throw new BadRequestException('Invalid request signature.', 400);}return $this;}/*** Force validate request.** @return $this*/public function forceValidate(){$this->alwaysValidate = true;return $this;}/*** Get request message.** @return array|\EasyWeChat\Kernel\Support\Collection|object|string** @throws BadRequestException* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException*/public function getMessage(){$message = $this->parseMessage($this->app['request']->getContent(false));if (!is_array($message) || empty($message)) {throw new BadRequestException('No message received.');}if ($this->isSafeMode() && !empty($message['Encrypt'])) {$message = $this->decryptMessage($message);// Handle JSON format.$dataSet = json_decode($message, true);if ($dataSet && (JSON_ERROR_NONE === json_last_error())) {return $dataSet;}$message = XML::parse($message);}return $this->detectAndCastResponseToType($message, $this->app->config->get('response_type'));}/*** Resolve server request and return the response.** @return \Symfony\Component\HttpFoundation\Response** @throws \EasyWeChat\Kernel\Exceptions\BadRequestException* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException*/protected function resolve(): Response{$result = $this->handleRequest();if ($this->shouldReturnRawResponse()) {$response = new Response($result['response']);} else {$response = new Response($this->buildResponse($result['to'], $result['from'], $result['response']),200,['Content-Type' => 'application/xml']);}$this->app->events->dispatch(new Events\ServerGuardResponseCreated($response));return $response;}/*** @return string|null*/protected function getToken(){return $this->app['config']['token'];}/*** @param string $to* @param string $from* @param \EasyWeChat\Kernel\Contracts\MessageInterface|string|int $message** @return string** @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException*/public function buildResponse(string $to, string $from, $message){if (empty($message) || self::SUCCESS_EMPTY_RESPONSE === $message) {return self::SUCCESS_EMPTY_RESPONSE;}if ($message instanceof RawMessage) {return $message->get('content', self::SUCCESS_EMPTY_RESPONSE);}if (is_string($message) || is_numeric($message)) {$message = new Text((string) $message);}if (is_array($message) && reset($message) instanceof NewsItem) {$message = new News($message);}if (!($message instanceof Message)) {throw new InvalidArgumentException(sprintf('Invalid Messages type "%s".', gettype($message)));}return $this->buildReply($to, $from, $message);}/*** Handle request.** @return array** @throws \EasyWeChat\Kernel\Exceptions\BadRequestException* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException*/protected function handleRequest(): array{$castedMessage = $this->getMessage();$messageArray = $this->detectAndCastResponseToType($castedMessage, 'array');$response = $this->dispatch(self::MESSAGE_TYPE_MAPPING[$messageArray['MsgType'] ?? $messageArray['msg_type'] ?? 'text'], $castedMessage);return ['to' => $messageArray['FromUserName'] ?? '','from' => $messageArray['ToUserName'] ?? '','response' => $response,];}/*** Build reply XML.** @param string $to* @param string $from* @param \EasyWeChat\Kernel\Contracts\MessageInterface $message** @return string*/protected function buildReply(string $to, string $from, MessageInterface $message): string{$prepends = ['ToUserName' => $to,'FromUserName' => $from,'CreateTime' => time(),'MsgType' => $message->getType(),];$response = $message->transformToXml($prepends);if ($this->isSafeMode()) {$this->app['logger']->debug('Messages safe mode is enabled.');$response = $this->app['encryptor']->encrypt($response);}return $response;}/*** @param array $params** @return string*/protected function signature(array $params){sort($params, SORT_STRING);return sha1(implode($params));}/*** Parse message array from raw php input.** @param string $content** @return array** @throws \EasyWeChat\Kernel\Exceptions\BadRequestException*/protected function parseMessage($content){try {if (0 === stripos($content, '<')) {$content = XML::parse($content);} else {// Handle JSON format.$dataSet = json_decode($content, true);if ($dataSet && (JSON_ERROR_NONE === json_last_error())) {$content = $dataSet;}}return (array) $content;} catch (\Exception $e) {throw new BadRequestException(sprintf('Invalid message content:(%s) %s', $e->getCode(), $e->getMessage()), $e->getCode());}}/*** Check the request message safe mode.** @return bool*/protected function isSafeMode(): bool{return $this->app['request']->get('signature') && 'aes' === $this->app['request']->get('encrypt_type');}/*** @return bool*/protected function shouldReturnRawResponse(): bool{return false;}/*** @param array $message** @return mixed*/protected function decryptMessage(array $message){return $message = $this->app['encryptor']->decrypt($message['Encrypt'],$this->app['request']->get('msg_signature'),$this->app['request']->get('nonce'),$this->app['request']->get('timestamp'));}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。