Commit b7568470 by 王召彬

init

parents
* text=auto
/build export-ignore
/tests export-ignore
/.github export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.scrutinizer.yml export-ignore
phpstan.neon export-ignore
phpunit.php export-ignore
phpunit.xml export-ignore
phpunit.xml.dist export-ignore
.php_cs export-ignore
*.DS_Store
/vendor
sftp-config.json
/*.php
/.idea
/coverage
/.split
/composer.lock
.php_cs.cache
\ No newline at end of file
<?php
$header = <<<EOF
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.
EOF;
return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules(array(
'@Symfony' => true,
'header_comment' => array('header' => $header),
'array_syntax' => array('syntax' => 'short'),
'ordered_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'php_unit_construct' => true,
'php_unit_strict' => true,
'yoda_style' => false,
))
->setFinder(
PhpCsFixer\Finder::create()
->exclude('vendor')
->in(__DIR__)
)
;
This source diff could not be displayed because it is too large. You can view the blob instead.
# Contribute
## Introduction
First, thank you for considering contributing to wechat! It's people like you that make the open source community such a great community! 😊
We welcome any type of contribution, not only code. You can help with
- **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open)
- **Marketing**: writing blog posts, howto's, printing stickers, ...
- **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ...
- **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them.
- **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/wechat).
## Your First Contribution
Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
## Submitting code
Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests.
## Code review process
The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge.
It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you?
## Financial contributions
We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/wechat).
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
## Questions
If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!).
You can also reach us at hello@wechat.opencollective.com.
## Credits
### Contributors
Thank you to all the people who have already contributed to wechat!
<a href="graphs/contributors"><img src="https://opencollective.com/wechat/contributors.svg?width=890" /></a>
### Backers
Thank you to all our backers! [[Become a backer](https://opencollective.com/wechat#backer)]
<a href="https://opencollective.com/wechat#backers" target="_blank"><img src="https://opencollective.com/wechat/backers.svg?width=890"></a>
### Sponsors
Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/wechat#sponsor))
<a href="https://opencollective.com/wechat/sponsor/0/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/1/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/2/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/3/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/4/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/5/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/6/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/7/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/8/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/wechat/sponsor/9/website" target="_blank"><img src="https://opencollective.com/wechat/sponsor/9/avatar.svg"></a>
<!-- This `CONTRIBUTING.md` is based on @nayafia's template https://github.com/nayafia/contributing-template -->
\ No newline at end of file
The MIT License (MIT)
Copyright (c) overtrue <i@overtrue.me>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
<img align="right" width="100" src="https://user-images.githubusercontent.com/1472352/49656357-1e874080-fa78-11e8-80ea-69e2103345cf.png" alt="EasyWeChat Logo"/>
<h1 align="left"><a href="https://www.easywechat.com">EasyWeChat</a></h1>
📦 It is probably the best SDK in the world for developing Wechat App.
[![Test Status](https://github.com/overtrue/wechat/workflows/Test/badge.svg)](https://github.com/overtrue/wechat/actions)
[![Lint Status](https://github.com/overtrue/wechat/workflows/Lint/badge.svg)](https://github.com/overtrue/wechat/actions)
[![Latest Stable Version](https://poser.pugx.org/overtrue/wechat/v/stable.svg)](https://packagist.org/packages/overtrue/wechat)
[![Latest Unstable Version](https://poser.pugx.org/overtrue/wechat/v/unstable.svg)](https://packagist.org/packages/overtrue/wechat)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/overtrue/wechat/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/overtrue/wechat/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/overtrue/wechat/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/overtrue/wechat/?branch=master)
[![Total Downloads](https://poser.pugx.org/overtrue/wechat/downloads)](https://packagist.org/packages/overtrue/wechat)
[![License](https://poser.pugx.org/overtrue/wechat/license)](https://packagist.org/packages/overtrue/wechat)
## Requirement
1. PHP >= 7.1
2. **[Composer](https://getcomposer.org/)**
3. openssl 拓展
4. fileinfo 拓展(素材管理模块需要用到)
## Installation
```shell
$ composer require "overtrue/wechat:^4.2" -vvv
```
## Usage
基本使用(以服务端为例):
```php
<?php
use EasyWeChat\Factory;
$options = [
'app_id' => 'wx3cf0f39249eb0exxx',
'secret' => 'f1c242f4f28f735d4687abb469072xxx',
'token' => 'easywechat',
'log' => [
'level' => 'debug',
'file' => '/tmp/easywechat.log',
],
// ...
];
$app = Factory::officialAccount($options);
$server = $app->server;
$user = $app->user;
$server->push(function($message) use ($user) {
$fromUser = $user->get($message['FromUserName']);
return "{$fromUser->nickname} 您好!欢迎关注 overtrue!";
});
$server->serve()->send();
```
更多请参考 [https://www.easywechat.com/](https://www.easywechat.com/)
## Documentation
[官网](https://www.easywechat.com) · [教程](https://www.easywechat.com/tutorials) · [讨论](https://yike.io/) · [微信公众平台](https://mp.weixin.qq.com/wiki) · [WeChat Official](http://admin.wechat.com/wiki)
## Integration
[Laravel 5 拓展包: overtrue/laravel-wechat](https://github.com/overtrue/laravel-wechat)
## Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/overtrue/wechat/graphs/contributors"><img src="https://opencollective.com/wechat/contributors.svg?width=890" /></a>
## PHP 扩展包开发
> 想知道如何从零开始构建 PHP 扩展包?
>
> 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package)
## License
MIT
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fovertrue%2Fwechat.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fovertrue%2Fwechat?ref=badge_large)
#!/bin/bash
#
TARGET_BRANCH="develop"
mkdir .tmp
cd .tmp
delete_branch()
{
TEMPDIR=$1
mkdir -p $TEMPDIR
git clone git@github.com:EasyWeChat/$1.git
pushd $TEMPDIR
git push origin :$TARGET_BRANCH -f
pwd
popd
}
delete_branch card
delete_branch core
delete_branch device
delete_branch encryption
delete_branch js
delete_branch material
delete_branch menu
delete_branch message
delete_branch notice
delete_branch payment
delete_branch qrcode
delete_branch semantic
delete_branch server
delete_branch staff
delete_branch stats
delete_branch store
delete_branch poi
delete_branch support
delete_branch url
delete_branch user
delete_branch broadcast
delete_branch reply
delete_branch shake-around
rm -rf ./.tmp
\ No newline at end of file
#!/bin/bash
#
HEADS="master"
TAGS="3.1.8"
split()
{
SUBDIR=./.split/$1
SPLIT=$2
mkdir -p $SUBDIR;
pushd $SUBDIR;
for HEAD in $HEADS
do
mkdir -p $HEAD
pushd $HEAD
git subsplit init git@github.com:overtrue/wechat.git
git subsplit update
time git subsplit publish --heads="$3" "$SPLIT" --tags=$4
popd
done
popd
rm -rf ./.split
}
split card src/Card:git@github.com:easywechat/card.git $HEADS $TAGS
split core src/Core:git@github.com:easywechat/core.git $HEADS $TAGS
split device src/Device:git@github.com:easywechat/device.git $HEADS $TAGS
split encryption src/Encryption:git@github.com:easywechat/encryption.git $HEADS $TAGS
split js src/Js:git@github.com:easywechat/js.git $HEADS $TAGS
split material src/Material:git@github.com:easywechat/material.git $HEADS $TAGS
split menu src/Menu:git@github.com:easywechat/menu.git $HEADS $TAGS
split message src/Message:git@github.com:easywechat/message.git $HEADS $TAGS
split notice src/Notice:git@github.com:easywechat/notice.git $HEADS $TAGS
split payment src/Payment:git@github.com:easywechat/payment.git $HEADS $TAGS
split qrcode src/QRCode:git@github.com:easywechat/qrcode.git $HEADS $TAGS
split semantic src/Semantic:git@github.com:easywechat/semantic.git $HEADS $TAGS
split server src/Server:git@github.com:easywechat/server.git $HEADS $TAGS
split staff src/Staff:git@github.com:easywechat/staff.git $HEADS $TAGS
split stats src/Stats:git@github.com:easywechat/stats.git $HEADS $TAGS
split store src/Store:git@github.com:easywechat/store.git $HEADS $TAGS
split poi src/POI:git@github.com:easywechat/poi.git $HEADS $TAGS
split support src/Support:git@github.com:easywechat/support.git $HEADS $TAGS
split url src/Url:git@github.com:easywechat/url.git $HEADS $TAGS
split user src/User:git@github.com:easywechat/user.git $HEADS $TAGS
split broadcast src/Broadcast:git@github.com:easywechat/broadcast.git $HEADS $TAGS
split reply src/Reply:git@github.com:easywechat/reply.git $HEADS $TAGS
split shake-around src/ShakeAround:git@github.com:easywechat/shake-around.git $HEADS $TAGS
\ No newline at end of file
{
"name": "overtrue/wechat",
"description": "微信SDK",
"keywords": [
"wechat",
"weixin",
"weixin-sdk",
"sdk"
],
"license": "MIT",
"authors": [
{
"name": "overtrue",
"email": "anzhengchao@gmail.com"
}
],
"require": {
"php": ">=7.1",
"ext-fileinfo": "*",
"ext-openssl": "*",
"ext-simplexml": "*",
"easywechat-composer/easywechat-composer": "^1.1",
"guzzlehttp/guzzle": "^6.2",
"monolog/monolog": "^1.22 || ^2.0",
"overtrue/socialite": "~2.0",
"pimple/pimple": "^3.0",
"psr/simple-cache": "^1.0",
"symfony/cache": "^3.3 || ^4.3",
"symfony/event-dispatcher": "^4.3",
"symfony/http-foundation": "^2.7 || ^3.0 || ^4.0",
"symfony/psr-http-message-bridge": "^0.3 || ^1.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.15",
"mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2.3",
"phpstan/phpstan": "^0.11.12",
"phpunit/phpunit": "^7.5"
},
"autoload": {
"psr-4": {
"EasyWeChat\\": "src/"
},
"files": [
"src/Kernel/Support/Helpers.php",
"src/Kernel/Helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"EasyWeChat\\Tests\\": "tests/"
}
},
"scripts": {
"phpcs": "vendor/bin/php-cs-fixer fix",
"phpstan": "vendor/bin/phpstan analyse",
"test": "vendor/bin/phpunit"
}
}
parameters:
level: 3
paths:
- src
inferPrivatePropertyTypeFromConstructor: true
ignoreErrors:
- '#EasyWeChat\\MicroMerchant\\Kernel\\BaseClient::__construct\(\) does not call parent constructor from EasyWeChat\\Payment\\Kernel\\BaseClient#'
- '#Psr.*Exception is not subtype of Throwable#'
- '#Call to an undefined method Overtrue\\Socialite\\ProviderInterface#'
- '#Access to an undefined property .*\$base#'
- '#Call to an undefined method EasyWeChat\\Payment\\Application::verifySignature\(\)#'
- '#Access to an undefined property EasyWeChat\\Kernel\\Messages\\Raw::\$content#'
- '#Default value of the parameter \#2 \$depth \(INF.0\) of method EasyWeChat\\Kernel\\Support\\Arr::flatten\(\) is incompatible with type int#'
- '#Access to an undefined property EasyWeChat\\Kernel\\ServiceContainer::\$extension#'
- '#Method EasyWeChat\\Kernel\\Contracts\\MessageInterface::transformToXml\(\) invoked with 1 parameter, 0 required#'
- '#Method EasyWeChat\\Kernel\\Support\\Collection::__set_state\(\) should return object but returns array#'
- '#Property EasyWeChat\\Payment\\Kernel\\BaseClient::\$app \(EasyWeChat\\Payment\\Application\) does not accept EasyWeChat\\MicroMerchant\\Application#'
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
<exclude>
<directory suffix="ServiceProvider.php">src/</directory>
<directory suffix="Exception.php">src/</directory>
<directory suffix="Helpers.php">src/Kernel/Support</directory>
<directory>src/Encryption</directory>
<directory>src/Support</directory>
</exclude>
</whitelist>
</filter>
<php>
<const name="PHPUNIT_RUNNING" value="true" />
</php>
</phpunit>
<?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\BasicService;
use EasyWeChat\Kernel\ServiceContainer;
/**
* Class Application.
*
* @author overtrue <i@overtrue.me>
*
* @property \EasyWeChat\BasicService\Jssdk\Client $jssdk
* @property \EasyWeChat\BasicService\Media\Client $media
* @property \EasyWeChat\BasicService\QrCode\Client $qrcode
* @property \EasyWeChat\BasicService\Url\Client $url
* @property \EasyWeChat\BasicService\ContentSecurity\Client $content_security
*/
class Application extends ServiceContainer
{
/**
* @var array
*/
protected $providers = [
Jssdk\ServiceProvider::class,
QrCode\ServiceProvider::class,
Media\ServiceProvider::class,
Url\ServiceProvider::class,
ContentSecurity\ServiceProvider::class,
];
}
<?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\BasicService\ContentSecurity;
use EasyWeChat\Kernel\BaseClient;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
/**
* Class Client.
*
* @author tianyong90 <412039588@qq.com>
*/
class Client extends BaseClient
{
/**
* @var string
*/
protected $baseUri = 'https://api.weixin.qq.com/wxa/';
/**
* Text content security check.
*
* @param string $text
*
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function checkText(string $text)
{
$params = [
'content' => $text,
];
return $this->httpPostJson('msg_sec_check', $params);
}
/**
* Image security check.
*
* @param string $path
*
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function checkImage(string $path)
{
return $this->httpUpload('img_sec_check', ['media' => $path]);
}
/**
* Media security check.
*
* @param string $mediaUrl
* @param int $mediaType
*
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
*/
public function checkMediaAsync(string $mediaUrl, int $mediaType)
{
/*
* 1:音频;2:图片
*/
$mediaTypes = [1, 2];
if (!in_array($mediaType, $mediaTypes, true)) {
throw new InvalidArgumentException('media type must be 1 or 2');
}
$params = [
'media_url' => $mediaUrl,
'media_type' => $mediaType,
];
return $this->httpPostJson('media_check_async', $params);
}
/**
* Image security check async.
*
* @param string $mediaUrl
*
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function checkImageAsync(string $mediaUrl)
{
return $this->checkMediaAsync($mediaUrl, 2);
}
/**
* Audio security check async.
*
* @param string $mediaUrl
*
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function checkAudioAsync(string $mediaUrl)
{
return $this->checkMediaAsync($mediaUrl, 1);
}
}
<?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\BasicService\ContentSecurity;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class ServiceProvider.
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['content_security'] = function ($app) {
return new Client($app);
};
}
}
<?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\BasicService\Jssdk;
use EasyWeChat\Kernel\BaseClient;
use EasyWeChat\Kernel\Exceptions\RuntimeException;
use EasyWeChat\Kernel\Support;
use EasyWeChat\Kernel\Traits\InteractsWithCache;
/**
* Class Client.
*
* @author overtrue <i@overtrue.me>
*/
class Client extends BaseClient
{
use InteractsWithCache;
/**
* @var string
*/
protected $ticketEndpoint = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket';
/**
* Current URI.
*
* @var string
*/
protected $url;
/**
* Get config json for jsapi.
*
* @param array $jsApiList
* @param bool $debug
* @param bool $beta
* @param bool $json
*
* @return array|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function buildConfig(array $jsApiList, bool $debug = false, bool $beta = false, bool $json = true)
{
$config = array_merge(compact('debug', 'beta', 'jsApiList'), $this->configSignature());
return $json ? json_encode($config) : $config;
}
/**
* Return jsapi config as a PHP array.
*
* @param array $apis
* @param bool $debug
* @param bool $beta
*
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function getConfigArray(array $apis, bool $debug = false, bool $beta = false)
{
return $this->buildConfig($apis, $debug, $beta, false);
}
/**
* Get js ticket.
*
* @param bool $refresh
* @param string $type
*
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function getTicket(bool $refresh = false, string $type = 'jsapi'): array
{
$cacheKey = sprintf('easywechat.basic_service.jssdk.ticket.%s.%s', $type, $this->getAppId());
if (!$refresh && $this->getCache()->has($cacheKey)) {
return $this->getCache()->get($cacheKey);
}
/** @var array<string, mixed> $result */
$result = $this->castResponseToType(
$this->requestRaw($this->ticketEndpoint, 'GET', ['query' => ['type' => $type]]),
'array'
);
$this->getCache()->set($cacheKey, $result, $result['expires_in'] - 500);
if (!$this->getCache()->has($cacheKey)) {
throw new RuntimeException('Failed to cache jssdk ticket.');
}
return $result;
}
/**
* Build signature.
*
* @param string|null $url
* @param string|null $nonce
* @param int|null $timestamp
*
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
protected function configSignature(string $url = null, string $nonce = null, $timestamp = null): array
{
$url = $url ?: $this->getUrl();
$nonce = $nonce ?: Support\Str::quickRandom(10);
$timestamp = $timestamp ?: time();
return [
'appId' => $this->getAppId(),
'nonceStr' => $nonce,
'timestamp' => $timestamp,
'url' => $url,
'signature' => $this->getTicketSignature($this->getTicket()['ticket'], $nonce, $timestamp, $url),
];
}
/**
* Sign the params.
*
* @param string $ticket
* @param string $nonce
* @param int $timestamp
* @param string $url
*
* @return string
*/
public function getTicketSignature($ticket, $nonce, $timestamp, $url): string
{
return sha1(sprintf('jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s', $ticket, $nonce, $timestamp, $url));
}
/**
* @return string
*/
public function dictionaryOrderSignature()
{
$params = func_get_args();
sort($params, SORT_STRING);
return sha1(implode('', $params));
}
/**
* Set current url.
*
* @param string $url
*
* @return $this
*/
public function setUrl(string $url)
{
$this->url = $url;
return $this;
}
/**
* Get current url.
*
* @return string
*/
public function getUrl(): string
{
if ($this->url) {
return $this->url;
}
return Support\current_url();
}
/**
* @return string
*/
protected function getAppId()
{
return $this->app['config']->get('app_id');
}
}
<?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\BasicService\Jssdk;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class ServiceProvider.
*
* @author overtrue <i@overtrue.me>
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['jssdk'] = function ($app) {
return new Client($app);
};
}
}
<?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\BasicService\Media;
use EasyWeChat\Kernel\BaseClient;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Http\StreamResponse;
/**
* Class Client.
*
* @author overtrue <i@overtrue.me>
*/
class Client extends BaseClient
{
/**
* @var string
*/
protected $baseUri = 'https://api.weixin.qq.com/cgi-bin/';
/**
* Allow media type.
*
* @var array
*/
protected $allowTypes = ['image', 'voice', 'video', 'thumb'];
/**
* Upload image.
*
* @param string $path
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function uploadImage($path)
{
return $this->upload('image', $path);
}
/**
* Upload video.
*
* @param string $path
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function uploadVideo($path)
{
return $this->upload('video', $path);
}
/**
* @param string $path
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function uploadVoice($path)
{
return $this->upload('voice', $path);
}
/**
* @param string $path
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function uploadThumb($path)
{
return $this->upload('thumb', $path);
}
/**
* Upload temporary material.
*
* @param string $type
* @param string $path
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function upload(string $type, string $path)
{
if (!file_exists($path) || !is_readable($path)) {
throw new InvalidArgumentException(sprintf("File does not exist, or the file is unreadable: '%s'", $path));
}
if (!in_array($type, $this->allowTypes, true)) {
throw new InvalidArgumentException(sprintf("Unsupported media type: '%s'", $type));
}
return $this->httpUpload('media/upload', ['media' => $path], ['type' => $type]);
}
/**
* @param string $path
* @param string $title
* @param string $description
*
* @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function uploadVideoForBroadcasting(string $path, string $title, string $description)
{
$response = $this->uploadVideo($path);
/** @var array $arrayResponse */
$arrayResponse = $this->detectAndCastResponseToType($response, 'array');
if (!empty($arrayResponse['media_id'])) {
return $this->createVideoForBroadcasting($arrayResponse['media_id'], $title, $description);
}
return $response;
}
/**
* @param string $mediaId
* @param string $title
* @param string $description
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function createVideoForBroadcasting(string $mediaId, string $title, string $description)
{
return $this->httpPostJson('media/uploadvideo', [
'media_id' => $mediaId,
'title' => $title,
'description' => $description,
]);
}
/**
* Fetch item from WeChat server.
*
* @param string $mediaId
*
* @return \EasyWeChat\Kernel\Http\StreamResponse|\Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function get(string $mediaId)
{
$response = $this->requestRaw('media/get', 'GET', [
'query' => [
'media_id' => $mediaId,
],
]);
if (false !== stripos($response->getHeaderLine('Content-disposition'), 'attachment')) {
return StreamResponse::buildFromPsrResponse($response);
}
return $this->castResponseToType($response, $this->app['config']->get('response_type'));
}
/**
* @param string $mediaId
*
* @return array|\EasyWeChat\Kernel\Http\Response|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getJssdkMedia(string $mediaId)
{
$response = $this->requestRaw('media/get/jssdk', 'GET', [
'query' => [
'media_id' => $mediaId,
],
]);
if (false !== stripos($response->getHeaderLine('Content-disposition'), 'attachment')) {
return StreamResponse::buildFromPsrResponse($response);
}
return $this->castResponseToType($response, $this->app['config']->get('response_type'));
}
}
<?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.
*/
/**
* ServiceProvider.php.
*
* This file is part of the 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\BasicService\Media;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class ServiceProvider.
*
* @author overtrue <i@overtrue.me>
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['media'] = function ($app) {
return new Client($app);
};
}
}
<?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\BasicService\QrCode;
use EasyWeChat\Kernel\BaseClient;
/**
* Class Client.
*
* @author overtrue <i@overtrue.me>
*/
class Client extends BaseClient
{
/**
* @var string
*/
protected $baseUri = 'https://api.weixin.qq.com/cgi-bin/';
const DAY = 86400;
const SCENE_MAX_VALUE = 100000;
const SCENE_QR_CARD = 'QR_CARD';
const SCENE_QR_TEMPORARY = 'QR_SCENE';
const SCENE_QR_TEMPORARY_STR = 'QR_STR_SCENE';
const SCENE_QR_FOREVER = 'QR_LIMIT_SCENE';
const SCENE_QR_FOREVER_STR = 'QR_LIMIT_STR_SCENE';
/**
* Create forever QR code.
*
* @param string|int $sceneValue
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*/
public function forever($sceneValue)
{
if (is_int($sceneValue) && $sceneValue > 0 && $sceneValue < self::SCENE_MAX_VALUE) {
$type = self::SCENE_QR_FOREVER;
$sceneKey = 'scene_id';
} else {
$type = self::SCENE_QR_FOREVER_STR;
$sceneKey = 'scene_str';
}
$scene = [$sceneKey => $sceneValue];
return $this->create($type, $scene, false);
}
/**
* Create temporary QR code.
*
* @param string|int $sceneValue
* @param int|null $expireSeconds
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*/
public function temporary($sceneValue, $expireSeconds = null)
{
if (is_int($sceneValue) && $sceneValue > 0) {
$type = self::SCENE_QR_TEMPORARY;
$sceneKey = 'scene_id';
} else {
$type = self::SCENE_QR_TEMPORARY_STR;
$sceneKey = 'scene_str';
}
$scene = [$sceneKey => $sceneValue];
return $this->create($type, $scene, true, $expireSeconds);
}
/**
* Return url for ticket.
* Detail: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542 .
*
* @param string $ticket
*
* @return string
*/
public function url($ticket)
{
return sprintf('https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s', urlencode($ticket));
}
/**
* Create a QrCode.
*
* @param string $actionName
* @param array $actionInfo
* @param bool $temporary
* @param int $expireSeconds
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function create($actionName, $actionInfo, $temporary = true, $expireSeconds = null)
{
null !== $expireSeconds || $expireSeconds = 7 * self::DAY;
$params = [
'action_name' => $actionName,
'action_info' => ['scene' => $actionInfo],
];
if ($temporary) {
$params['expire_seconds'] = min($expireSeconds, 30 * self::DAY);
}
return $this->httpPostJson('qrcode/create', $params);
}
}
<?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\BasicService\QrCode;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class ServiceProvider.
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['qrcode'] = function ($app) {
return new Client($app);
};
}
}
<?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\BasicService\Url;
use EasyWeChat\Kernel\BaseClient;
/**
* Class Client.
*
* @author overtrue <i@overtrue.me>
*/
class Client extends BaseClient
{
/**
* @var string
*/
protected $baseUri = 'https://api.weixin.qq.com/';
/**
* Shorten the url.
*
* @param string $url
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function shorten(string $url)
{
$params = [
'action' => 'long2short',
'long_url' => $url,
];
return $this->httpPostJson('cgi-bin/shorturl', $params);
}
}
<?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\BasicService\Url;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class ServiceProvider.
*/
class ServiceProvider implements ServiceProviderInterface
{
/**
* {@inheritdoc}.
*/
public function register(Container $app)
{
$app['url'] = function ($app) {
return new Client($app);
};
}
}
<?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;
/**
* Class Factory.
*
* @method static \EasyWeChat\Payment\Application payment(array $config)
* @method static \EasyWeChat\MiniProgram\Application miniProgram(array $config)
* @method static \EasyWeChat\OpenPlatform\Application openPlatform(array $config)
* @method static \EasyWeChat\OfficialAccount\Application officialAccount(array $config)
* @method static \EasyWeChat\BasicService\Application basicService(array $config)
* @method static \EasyWeChat\Work\Application work(array $config)
* @method static \EasyWeChat\OpenWork\Application openWork(array $config)
* @method static \EasyWeChat\MicroMerchant\Application microMerchant(array $config)
*/
class Factory
{
/**
* @param string $name
* @param array $config
*
* @return \EasyWeChat\Kernel\ServiceContainer
*/
public static function make($name, array $config)
{
$namespace = Kernel\Support\Str::studly($name);
$application = "\\EasyWeChat\\{$namespace}\\Application";
return new $application($config);
}
/**
* Dynamically pass methods to the application.
*
* @param string $name
* @param array $arguments
*
* @return mixed
*/
public static function __callStatic($name, $arguments)
{
return self::make($name, ...$arguments);
}
}
<?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\AccessTokenInterface;
use EasyWeChat\Kernel\Exceptions\HttpException;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\RuntimeException;
use EasyWeChat\Kernel\Traits\HasHttpRequests;
use EasyWeChat\Kernel\Traits\InteractsWithCache;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Class AccessToken.
*
* @author overtrue <i@overtrue.me>
*/
abstract class AccessToken implements AccessTokenInterface
{
use HasHttpRequests;
use InteractsWithCache;
/**
* @var \EasyWeChat\Kernel\ServiceContainer
*/
protected $app;
/**
* @var string
*/
protected $requestMethod = 'GET';
/**
* @var string
*/
protected $endpointToGetToken;
/**
* @var string
*/
protected $queryName;
/**
* @var array
*/
protected $token;
/**
* @var int
*/
protected $safeSeconds = 500;
/**
* @var string
*/
protected $tokenKey = 'access_token';
/**
* @var string
*/
protected $cachePrefix = 'easywechat.kernel.access_token.';
/**
* AccessToken constructor.
*
* @param \EasyWeChat\Kernel\ServiceContainer $app
*/
public function __construct(ServiceContainer $app)
{
$this->app = $app;
}
/**
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function getRefreshedToken(): array
{
return $this->getToken(true);
}
/**
* @param bool $refresh
*
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function getToken(bool $refresh = false): array
{
$cacheKey = $this->getCacheKey();
$cache = $this->getCache();
if (!$refresh && $cache->has($cacheKey)) {
return $cache->get($cacheKey);
}
/** @var array $token */
$token = $this->requestToken($this->getCredentials(), true);
$this->setToken($token[$this->tokenKey], $token['expires_in'] ?? 7200);
$this->app->events->dispatch(new Events\AccessTokenRefreshed($this));
return $token;
}
/**
* @param string $token
* @param int $lifetime
*
* @return \EasyWeChat\Kernel\Contracts\AccessTokenInterface
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function setToken(string $token, int $lifetime = 7200): AccessTokenInterface
{
$this->getCache()->set($this->getCacheKey(), [
$this->tokenKey => $token,
'expires_in' => $lifetime,
], $lifetime - $this->safeSeconds);
if (!$this->getCache()->has($this->getCacheKey())) {
throw new RuntimeException('Failed to cache access token.');
}
return $this;
}
/**
* @return \EasyWeChat\Kernel\Contracts\AccessTokenInterface
*
* @throws \EasyWeChat\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function refresh(): AccessTokenInterface
{
$this->getToken(true);
return $this;
}
/**
* @param array $credentials
* @param bool $toArray
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\HttpException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
*/
public function requestToken(array $credentials, $toArray = false)
{
$response = $this->sendRequest($credentials);
$result = json_decode($response->getBody()->getContents(), true);
$formatted = $this->castResponseToType($response, $this->app['config']->get('response_type'));
if (empty($result[$this->tokenKey])) {
throw new HttpException('Request access_token fail: '.json_encode($result, JSON_UNESCAPED_UNICODE), $response, $formatted);
}
return $toArray ? $result : $formatted;
}
/**
* @param \Psr\Http\Message\RequestInterface $request
* @param array $requestOptions
*
* @return \Psr\Http\Message\RequestInterface
*
* @throws \EasyWeChat\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function applyToRequest(RequestInterface $request, array $requestOptions = []): RequestInterface
{
parse_str($request->getUri()->getQuery(), $query);
$query = http_build_query(array_merge($this->getQuery(), $query));
return $request->withUri($request->getUri()->withQuery($query));
}
/**
* Send http request.
*
* @param array $credentials
*
* @return ResponseInterface
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function sendRequest(array $credentials): ResponseInterface
{
$options = [
('GET' === $this->requestMethod) ? 'query' : 'json' => $credentials,
];
return $this->setHttpClient($this->app['http_client'])->request($this->getEndpoint(), $this->requestMethod, $options);
}
/**
* @return string
*/
protected function getCacheKey()
{
return $this->cachePrefix.md5(json_encode($this->getCredentials()));
}
/**
* The request query will be used to add to the request.
*
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\HttpException
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
protected function getQuery(): array
{
return [$this->queryName ?? $this->tokenKey => $this->getToken()[$this->tokenKey]];
}
/**
* @return string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
*/
public function getEndpoint(): string
{
if (empty($this->endpointToGetToken)) {
throw new InvalidArgumentException('No endpoint for access token request.');
}
return $this->endpointToGetToken;
}
/**
* @return string
*/
public function getTokenKey()
{
return $this->tokenKey;
}
/**
* Credential for get token.
*
* @return array
*/
abstract protected function getCredentials(): array;
}
<?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\AccessTokenInterface;
use EasyWeChat\Kernel\Http\Response;
use EasyWeChat\Kernel\Traits\HasHttpRequests;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LogLevel;
/**
* Class BaseClient.
*
* @author overtrue <i@overtrue.me>
*/
class BaseClient
{
use HasHttpRequests { request as performRequest; }
/**
* @var \EasyWeChat\Kernel\ServiceContainer
*/
protected $app;
/**
* @var \EasyWeChat\Kernel\Contracts\AccessTokenInterface
*/
protected $accessToken;
/**
* @var string
*/
protected $baseUri;
/**
* BaseClient constructor.
*
* @param \EasyWeChat\Kernel\ServiceContainer $app
* @param \EasyWeChat\Kernel\Contracts\AccessTokenInterface|null $accessToken
*/
public function __construct(ServiceContainer $app, AccessTokenInterface $accessToken = null)
{
$this->app = $app;
$this->accessToken = $accessToken ?? $this->app['access_token'];
}
/**
* GET request.
*
* @param string $url
* @param array $query
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function httpGet(string $url, array $query = [])
{
return $this->request($url, 'GET', ['query' => $query]);
}
/**
* POST request.
*
* @param string $url
* @param array $data
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function httpPost(string $url, array $data = [])
{
return $this->request($url, 'POST', ['form_params' => $data]);
}
/**
* JSON request.
*
* @param string $url
* @param array $data
* @param array $query
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function httpPostJson(string $url, array $data = [], array $query = [])
{
return $this->request($url, 'POST', ['query' => $query, 'json' => $data]);
}
/**
* Upload file.
*
* @param string $url
* @param array $files
* @param array $form
* @param array $query
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function httpUpload(string $url, array $files = [], array $form = [], array $query = [])
{
$multipart = [];
foreach ($files as $name => $path) {
$multipart[] = [
'name' => $name,
'contents' => fopen($path, 'r'),
];
}
foreach ($form as $name => $contents) {
$multipart[] = compact('name', 'contents');
}
return $this->request($url, 'POST', ['query' => $query, 'multipart' => $multipart, 'connect_timeout' => 30, 'timeout' => 30, 'read_timeout' => 30]);
}
/**
* @return AccessTokenInterface
*/
public function getAccessToken(): AccessTokenInterface
{
return $this->accessToken;
}
/**
* @param \EasyWeChat\Kernel\Contracts\AccessTokenInterface $accessToken
*
* @return $this
*/
public function setAccessToken(AccessTokenInterface $accessToken)
{
$this->accessToken = $accessToken;
return $this;
}
/**
* @param string $url
* @param string $method
* @param array $options
* @param bool $returnRaw
*
* @return \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function request(string $url, string $method = 'GET', array $options = [], $returnRaw = false)
{
if (empty($this->middlewares)) {
$this->registerHttpMiddlewares();
}
$response = $this->performRequest($url, $method, $options);
$this->app->events->dispatch(new Events\HttpResponseCreated($response));
return $returnRaw ? $response : $this->castResponseToType($response, $this->app->config->get('response_type'));
}
/**
* @param string $url
* @param string $method
* @param array $options
*
* @return \EasyWeChat\Kernel\Http\Response
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function requestRaw(string $url, string $method = 'GET', array $options = [])
{
return Response::buildFromPsrResponse($this->request($url, $method, $options, true));
}
/**
* Register Guzzle middlewares.
*/
protected function registerHttpMiddlewares()
{
// retry
$this->pushMiddleware($this->retryMiddleware(), 'retry');
// access token
$this->pushMiddleware($this->accessTokenMiddleware(), 'access_token');
// log
$this->pushMiddleware($this->logMiddleware(), 'log');
}
/**
* Attache access token to request query.
*
* @return \Closure
*/
protected function accessTokenMiddleware()
{
return function (callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
if ($this->accessToken) {
$request = $this->accessToken->applyToRequest($request, $options);
}
return $handler($request, $options);
};
};
}
/**
* Log the request.
*
* @return \Closure
*/
protected function logMiddleware()
{
$formatter = new MessageFormatter($this->app['config']['http.log_template'] ?? MessageFormatter::DEBUG);
return Middleware::log($this->app['logger'], $formatter, LogLevel::DEBUG);
}
/**
* Return retry middleware.
*
* @return \Closure
*/
protected function retryMiddleware()
{
return Middleware::retry(function (
$retries,
RequestInterface $request,
ResponseInterface $response = null
) {
// Limit the number of retries to 2
if ($retries < $this->app->config->get('http.max_retries', 1) && $response && $body = $response->getBody()) {
// Retry on server errors
$response = json_decode($body, true);
if (!empty($response['errcode']) && in_array(abs($response['errcode']), [40001, 40014, 42001], true)) {
$this->accessToken->refresh();
$this->app['logger']->debug('Retrying with refreshed access token.');
return true;
}
}
return false;
}, function () {
return abs($this->app->config->get('http.retry_delay', 500));
});
}
}
<?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\Clauses;
/**
* Class Clause.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class Clause
{
/**
* @var array
*/
protected $clauses = [
'where' => [],
];
/**
* @param mixed ...$args
*
* @return $this
*/
public function where(...$args)
{
array_push($this->clauses['where'], $args);
return $this;
}
/**
* @param mixed $payload
*
* @return bool
*/
public function intercepted($payload)
{
return (bool) $this->interceptWhereClause($payload);
}
/**
* @param mixed $payload
*
* @return bool
*/
protected function interceptWhereClause($payload)
{
foreach ($this->clauses['where'] as $item) {
list($key, $value) = $item;
if (isset($payload[$key]) && $payload[$key] !== $value) {
return true;
}
}
}
}
<?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\Support\Collection;
/**
* Class Config.
*
* @author overtrue <i@overtrue.me>
*/
class Config extends Collection
{
}
<?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\Contracts;
use Psr\Http\Message\RequestInterface;
/**
* Interface AuthorizerAccessToken.
*
* @author overtrue <i@overtrue.me>
*/
interface AccessTokenInterface
{
/**
* @return array
*/
public function getToken(): array;
/**
* @return \EasyWeChat\Kernel\Contracts\AccessTokenInterface
*/
public function refresh(): self;
/**
* @param \Psr\Http\Message\RequestInterface $request
* @param array $requestOptions
*
* @return \Psr\Http\Message\RequestInterface
*/
public function applyToRequest(RequestInterface $request, array $requestOptions = []): RequestInterface;
}
<?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\Contracts;
use ArrayAccess;
/**
* Interface Arrayable.
*
* @author overtrue <i@overtrue.me>
*/
interface Arrayable extends ArrayAccess
{
/**
* Get the instance as an array.
*
* @return array
*/
public function toArray();
}
<?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\Contracts;
/**
* Interface EventHandlerInterface.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
interface EventHandlerInterface
{
/**
* @param mixed $payload
*/
public function handle($payload = null);
}
<?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\Contracts;
/**
* Interface MediaInterface.
*
* @author overtrue <i@overtrue.me>
*/
interface MediaInterface extends MessageInterface
{
/**
* @return string
*/
public function getMediaId(): string;
}
<?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\Contracts;
/**
* Interface MessageInterface.
*
* @author overtrue <i@overtrue.me>
*/
interface MessageInterface
{
/**
* @return string
*/
public function getType(): string;
/**
* @return array
*/
public function transformForJsonRequest(): array;
/**
* @return string
*/
public function transformToXml(): string;
}
<?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\Decorators;
/**
* Class FinallyResult.
*
* @author overtrue <i@overtrue.me>
*/
class FinallyResult
{
/**
* @var mixed
*/
public $content;
/**
* FinallyResult constructor.
*
* @param mixed $content
*/
public function __construct($content)
{
$this->content = $content;
}
}
<?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\Decorators;
/**
* Class TerminateResult.
*
* @author overtrue <i@overtrue.me>
*/
class TerminateResult
{
/**
* @var mixed
*/
public $content;
/**
* FinallyResult constructor.
*
* @param mixed $content
*/
public function __construct($content)
{
$this->content = $content;
}
}
<?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\Exceptions\RuntimeException;
use EasyWeChat\Kernel\Support\AES;
use function EasyWeChat\Kernel\Support\str_random;
use EasyWeChat\Kernel\Support\XML;
use Throwable;
/**
* Class Encryptor.
*
* @author overtrue <i@overtrue.me>
*/
class Encryptor
{
const ERROR_INVALID_SIGNATURE = -40001; // Signature verification failed
const ERROR_PARSE_XML = -40002; // Parse XML failed
const ERROR_CALC_SIGNATURE = -40003; // Calculating the signature failed
const ERROR_INVALID_AES_KEY = -40004; // Invalid AESKey
const ERROR_INVALID_APP_ID = -40005; // Check AppID failed
const ERROR_ENCRYPT_AES = -40006; // AES EncryptionInterface failed
const ERROR_DECRYPT_AES = -40007; // AES decryption failed
const ERROR_INVALID_XML = -40008; // Invalid XML
const ERROR_BASE64_ENCODE = -40009; // Base64 encoding failed
const ERROR_BASE64_DECODE = -40010; // Base64 decoding failed
const ERROR_XML_BUILD = -40011; // XML build failed
const ILLEGAL_BUFFER = -41003; // Illegal buffer
/**
* App id.
*
* @var string
*/
protected $appId;
/**
* App token.
*
* @var string
*/
protected $token;
/**
* @var string
*/
protected $aesKey;
/**
* Block size.
*
* @var int
*/
protected $blockSize = 32;
/**
* Constructor.
*
* @param string $appId
* @param string|null $token
* @param string|null $aesKey
*/
public function __construct(string $appId, string $token = null, string $aesKey = null)
{
$this->appId = $appId;
$this->token = $token;
$this->aesKey = base64_decode($aesKey.'=', true);
}
/**
* Get the app token.
*
* @return string
*/
public function getToken(): string
{
return $this->token;
}
/**
* Encrypt the message and return XML.
*
* @param string $xml
* @param string $nonce
* @param int $timestamp
*
* @return string
*
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function encrypt($xml, $nonce = null, $timestamp = null): string
{
try {
$xml = $this->pkcs7Pad(str_random(16).pack('N', strlen($xml)).$xml.$this->appId, $this->blockSize);
$encrypted = base64_encode(AES::encrypt(
$xml,
$this->aesKey,
substr($this->aesKey, 0, 16),
OPENSSL_NO_PADDING
));
// @codeCoverageIgnoreStart
} catch (Throwable $e) {
throw new RuntimeException($e->getMessage(), self::ERROR_ENCRYPT_AES);
}
// @codeCoverageIgnoreEnd
!is_null($nonce) || $nonce = substr($this->appId, 0, 10);
!is_null($timestamp) || $timestamp = time();
$response = [
'Encrypt' => $encrypted,
'MsgSignature' => $this->signature($this->token, $timestamp, $nonce, $encrypted),
'TimeStamp' => $timestamp,
'Nonce' => $nonce,
];
//生成响应xml
return XML::build($response);
}
/**
* Decrypt message.
*
* @param string $content
* @param string $msgSignature
* @param string $nonce
* @param string $timestamp
*
* @return string
*
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function decrypt($content, $msgSignature, $nonce, $timestamp): string
{
$signature = $this->signature($this->token, $timestamp, $nonce, $content);
if ($signature !== $msgSignature) {
throw new RuntimeException('Invalid Signature.', self::ERROR_INVALID_SIGNATURE);
}
$decrypted = AES::decrypt(
base64_decode($content, true),
$this->aesKey,
substr($this->aesKey, 0, 16),
OPENSSL_NO_PADDING
);
$result = $this->pkcs7Unpad($decrypted);
$content = substr($result, 16, strlen($result));
$contentLen = unpack('N', substr($content, 0, 4))[1];
if (trim(substr($content, $contentLen + 4)) !== $this->appId) {
throw new RuntimeException('Invalid appId.', self::ERROR_INVALID_APP_ID);
}
return substr($content, 4, $contentLen);
}
/**
* Get SHA1.
*
* @return string
*/
public function signature(): string
{
$array = func_get_args();
sort($array, SORT_STRING);
return sha1(implode($array));
}
/**
* PKCS#7 pad.
*
* @param string $text
* @param int $blockSize
*
* @return string
*
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function pkcs7Pad(string $text, int $blockSize): string
{
if ($blockSize > 256) {
throw new RuntimeException('$blockSize may not be more than 256');
}
$padding = $blockSize - (strlen($text) % $blockSize);
$pattern = chr($padding);
return $text.str_repeat($pattern, $padding);
}
/**
* PKCS#7 unpad.
*
* @param string $text
*
* @return string
*/
public function pkcs7Unpad(string $text): string
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > $this->blockSize) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
}
<?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\Events;
use EasyWeChat\Kernel\AccessToken;
/**
* Class AccessTokenRefreshed.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class AccessTokenRefreshed
{
/**
* @var \EasyWeChat\Kernel\AccessToken
*/
public $accessToken;
/**
* @param \EasyWeChat\Kernel\AccessToken $accessToken
*/
public function __construct(AccessToken $accessToken)
{
$this->accessToken = $accessToken;
}
}
<?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\Events;
use EasyWeChat\Kernel\ServiceContainer;
/**
* Class ApplicationInitialized.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class ApplicationInitialized
{
/**
* @var \EasyWeChat\Kernel\ServiceContainer
*/
public $app;
/**
* @param \EasyWeChat\Kernel\ServiceContainer $app
*/
public function __construct(ServiceContainer $app)
{
$this->app = $app;
}
}
<?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\Events;
use Psr\Http\Message\ResponseInterface;
/**
* Class HttpResponseCreated.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class HttpResponseCreated
{
/**
* @var \Psr\Http\Message\ResponseInterface
*/
public $response;
/**
* @param \Psr\Http\Message\ResponseInterface $response
*/
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}
}
<?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\Events;
use Symfony\Component\HttpFoundation\Response;
/**
* Class ServerGuardResponseCreated.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class ServerGuardResponseCreated
{
/**
* @var \Symfony\Component\HttpFoundation\Response
*/
public $response;
/**
* @param \Symfony\Component\HttpFoundation\Response $response
*/
public function __construct(Response $response)
{
$this->response = $response;
}
}
<?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\Exceptions;
/**
* Class BadRequestException.
*
* @author overtrue <i@overtrue.me>
*/
class BadRequestException extends Exception
{
}
<?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\Exceptions;
class DecryptException extends Exception
{
}
<?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\Exceptions;
use Exception as BaseException;
/**
* Class Exception.
*
* @author overtrue <i@overtrue.me>
*/
class Exception extends BaseException
{
}
<?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\Exceptions;
use Psr\Http\Message\ResponseInterface;
/**
* Class HttpException.
*
* @author overtrue <i@overtrue.me>
*/
class HttpException extends Exception
{
/**
* @var \Psr\Http\Message\ResponseInterface|null
*/
public $response;
/**
* @var \Psr\Http\Message\ResponseInterface|\EasyWeChat\Kernel\Support\Collection|array|object|string|null
*/
public $formattedResponse;
/**
* HttpException constructor.
*
* @param string $message
* @param \Psr\Http\Message\ResponseInterface|null $response
* @param null $formattedResponse
* @param int|null $code
*/
public function __construct($message, ResponseInterface $response = null, $formattedResponse = null, $code = null)
{
parent::__construct($message, $code);
$this->response = $response;
$this->formattedResponse = $formattedResponse;
if ($response) {
$response->getBody()->rewind();
}
}
}
<?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\Exceptions;
/**
* Class InvalidArgumentException.
*
* @author overtrue <i@overtrue.me>
*/
class InvalidArgumentException extends Exception
{
}
<?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\Exceptions;
/**
* Class InvalidConfigException.
*
* @author overtrue <i@overtrue.me>
*/
class InvalidConfigException extends Exception
{
}
<?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\Exceptions;
/**
* Class RuntimeException.
*
* @author overtrue <i@overtrue.me>
*/
class RuntimeException extends Exception
{
}
<?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\Exceptions;
/**
* Class InvalidConfigException.
*
* @author overtrue <i@overtrue.me>
*/
class UnboundServiceException extends Exception
{
}
<?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\Arrayable;
use EasyWeChat\Kernel\Exceptions\RuntimeException;
use EasyWeChat\Kernel\Support\Arr;
use EasyWeChat\Kernel\Support\Collection;
function data_get($data, $key, $default = null)
{
switch (true) {
case is_array($data):
return Arr::get($data, $key, $default);
case $data instanceof Collection:
return $data->get($key, $default);
case $data instanceof Arrayable:
return Arr::get($data->toArray(), $key, $default);
case $data instanceof \ArrayIterator:
return $data->getArrayCopy()[$key] ?? $default;
case $data instanceof \ArrayAccess:
return $data[$key] ?? $default;
case $data instanceof \IteratorAggregate && $data->getIterator() instanceof \ArrayIterator:
return $data->getIterator()->getArrayCopy()[$key] ?? $default;
default:
throw new RuntimeException(sprintf('Can\'t access data with key "%s"', $key));
}
}
function data_to_array($data)
{
switch (true) {
case is_array($data):
return $data;
case $data instanceof Collection:
return $data->all();
case $data instanceof Arrayable:
return $data->toArray();
case $data instanceof \IteratorAggregate && $data->getIterator() instanceof \ArrayIterator:
return $data->getIterator()->getArrayCopy();
case $data instanceof \ArrayIterator:
return $data->getArrayCopy();
default:
throw new RuntimeException(sprintf('Can\'t transform data to array'));
}
}
<?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\Http;
use EasyWeChat\Kernel\Support\Collection;
use EasyWeChat\Kernel\Support\XML;
use GuzzleHttp\Psr7\Response as GuzzleResponse;
use Psr\Http\Message\ResponseInterface;
/**
* Class Response.
*
* @author overtrue <i@overtrue.me>
*/
class Response extends GuzzleResponse
{
/**
* @return string
*/
public function getBodyContents()
{
$this->getBody()->rewind();
$contents = $this->getBody()->getContents();
$this->getBody()->rewind();
return $contents;
}
/**
* @param \Psr\Http\Message\ResponseInterface $response
*
* @return \EasyWeChat\Kernel\Http\Response
*/
public static function buildFromPsrResponse(ResponseInterface $response)
{
return new static(
$response->getStatusCode(),
$response->getHeaders(),
$response->getBody(),
$response->getProtocolVersion(),
$response->getReasonPhrase()
);
}
/**
* Build to json.
*
* @return string
*/
public function toJson()
{
return json_encode($this->toArray());
}
/**
* Build to array.
*
* @return array
*/
public function toArray()
{
$content = $this->removeControlCharacters($this->getBodyContents());
if (false !== stripos($this->getHeaderLine('Content-Type'), 'xml') || 0 === stripos($content, '<xml')) {
return XML::parse($content);
}
$array = json_decode($content, true, 512, JSON_BIGINT_AS_STRING);
if (JSON_ERROR_NONE === json_last_error()) {
return (array) $array;
}
return [];
}
/**
* Get collection data.
*
* @return \EasyWeChat\Kernel\Support\Collection
*/
public function toCollection()
{
return new Collection($this->toArray());
}
/**
* @return object
*/
public function toObject()
{
return json_decode($this->toJson());
}
/**
* @return bool|string
*/
public function __toString()
{
return $this->getBodyContents();
}
/**
* @param string $content
*
* @return string
*/
protected function removeControlCharacters(string $content)
{
return \preg_replace('/[\x00-\x1F\x80-\x9F]/u', '', $content);
}
}
<?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\Http;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\RuntimeException;
use EasyWeChat\Kernel\Support\File;
/**
* Class StreamResponse.
*
* @author overtrue <i@overtrue.me>
*/
class StreamResponse extends Response
{
/**
* @param string $directory
* @param string $filename
* @param bool $appendSuffix
*
* @return bool|int
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function save(string $directory, string $filename = '', bool $appendSuffix = true)
{
$this->getBody()->rewind();
$directory = rtrim($directory, '/');
if (!is_dir($directory)) {
mkdir($directory, 0755, true); // @codeCoverageIgnore
}
if (!is_writable($directory)) {
throw new InvalidArgumentException(sprintf("'%s' is not writable.", $directory));
}
$contents = $this->getBody()->getContents();
if (empty($contents) || '{' === $contents[0]) {
throw new RuntimeException('Invalid media response content.');
}
if (empty($filename)) {
if (preg_match('/filename="(?<filename>.*?)"/', $this->getHeaderLine('Content-Disposition'), $match)) {
$filename = $match['filename'];
} else {
$filename = md5($contents);
}
}
if ($appendSuffix && empty(pathinfo($filename, PATHINFO_EXTENSION))) {
$filename .= File::getStreamExt($contents);
}
file_put_contents($directory.'/'.$filename, $contents);
return $filename;
}
/**
* @param string $directory
* @param string $filename
* @param bool $appendSuffix
*
* @return bool|int
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
*/
public function saveAs(string $directory, string $filename, bool $appendSuffix = true)
{
return $this->save($directory, $filename, $appendSuffix);
}
}
<?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\Messages;
/**
* Class Article.
*/
class Article extends Message
{
/**
* @var string
*/
protected $type = 'mpnews';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'thumb_media_id',
'author',
'title',
'content',
'digest',
'source_url',
'show_cover',
];
/**
* Aliases of attribute.
*
* @var array
*/
protected $jsonAliases = [
'content_source_url' => 'source_url',
'show_cover_pic' => 'show_cover',
];
/**
* @var array
*/
protected $required = [
'thumb_media_id',
'title',
'content',
'show_cover',
];
}
<?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.
*/
/**
* Card.php.
*
* @author overtrue <i@overtrue.me>
* @copyright 2015 overtrue <i@overtrue.me>
*
* @see https://github.com/overtrue
* @see http://overtrue.me
*/
namespace EasyWeChat\Kernel\Messages;
/**
* Class Card.
*/
class Card extends Message
{
/**
* Message type.
*
* @var string
*/
protected $type = 'wxcard';
/**
* Properties.
*
* @var array
*/
protected $properties = ['card_id'];
/**
* Media constructor.
*
* @param string $cardId
*/
public function __construct(string $cardId)
{
parent::__construct(['card_id' => $cardId]);
}
}
<?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\Messages;
/**
* Class DeviceEvent.
*
* @property string $media_id
*/
class DeviceEvent extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'device_event';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'device_type',
'device_id',
'content',
'session_id',
'open_id',
];
}
<?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\Messages;
/**
* Class DeviceText.
*
* @property string $content
*/
class DeviceText extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'device_text';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'device_type',
'device_id',
'content',
'session_id',
'open_id',
];
public function toXmlArray()
{
return [
'DeviceType' => $this->get('device_type'),
'DeviceID' => $this->get('device_id'),
'SessionID' => $this->get('session_id'),
'Content' => base64_encode($this->get('content')),
];
}
}
<?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\Messages;
/**
* Class Image.
*
* @property string $media_id
*/
class File extends Media
{
/**
* @var string
*/
protected $type = 'file';
}
<?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\Messages;
/**
* Class Image.
*
* @property string $media_id
*/
class Image extends Media
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'image';
}
<?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\Messages;
/**
* Class Link.
*/
class Link extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'link';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'title',
'description',
'url',
];
}
<?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\Messages;
/**
* Class Location.
*/
class Location extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'location';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'latitude',
'longitude',
'scale',
'label',
'precision',
];
}
<?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\Messages;
use EasyWeChat\Kernel\Contracts\MediaInterface;
use EasyWeChat\Kernel\Support\Str;
/**
* Class Media.
*/
class Media extends Message implements MediaInterface
{
/**
* Properties.
*
* @var array
*/
protected $properties = ['media_id'];
/**
* @var array
*/
protected $required = [
'media_id',
];
/**
* MaterialClient constructor.
*
* @param string $mediaId
* @param string $type
* @param array $attributes
*/
public function __construct(string $mediaId, $type = null, array $attributes = [])
{
parent::__construct(array_merge(['media_id' => $mediaId], $attributes));
!empty($type) && $this->setType($type);
}
/**
* @return string
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
*/
public function getMediaId(): string
{
$this->checkRequiredAttributes();
return $this->get('media_id');
}
public function toXmlArray()
{
return [
Str::studly($this->getType()) => [
'MediaId' => $this->get('media_id'),
],
];
}
}
<?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\Messages;
use EasyWeChat\Kernel\Contracts\MessageInterface;
use EasyWeChat\Kernel\Support\XML;
use EasyWeChat\Kernel\Traits\HasAttributes;
use Mockery\Exception\BadMethodCallException;
/**
* Class Messages.
*/
abstract class Message implements MessageInterface
{
use HasAttributes;
const TEXT = 2;
const IMAGE = 4;
const VOICE = 8;
const VIDEO = 16;
const SHORT_VIDEO = 32;
const LOCATION = 64;
const LINK = 128;
const DEVICE_EVENT = 256;
const DEVICE_TEXT = 512;
const FILE = 1024;
const TEXT_CARD = 2048;
const TRANSFER = 4096;
const EVENT = 1048576;
const MINIPROGRAM_PAGE = 2097152;
const ALL = self::TEXT | self::IMAGE | self::VOICE | self::VIDEO | self::SHORT_VIDEO | self::LOCATION | self::LINK
| self::DEVICE_EVENT | self::DEVICE_TEXT | self::FILE | self::TEXT_CARD | self::TRANSFER | self::EVENT | self::MINIPROGRAM_PAGE;
/**
* @var string
*/
protected $type;
/**
* @var int
*/
protected $id;
/**
* @var string
*/
protected $to;
/**
* @var string
*/
protected $from;
/**
* @var array
*/
protected $properties = [];
/**
* @var array
*/
protected $jsonAliases = [];
/**
* Message constructor.
*
* @param array $attributes
*/
public function __construct(array $attributes = [])
{
$this->setAttributes($attributes);
}
/**
* Return type name message.
*
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* @param string $type
*/
public function setType(string $type)
{
$this->type = $type;
}
/**
* Magic getter.
*
* @param string $property
*
* @return mixed
*/
public function __get($property)
{
if (property_exists($this, $property)) {
return $this->$property;
}
return $this->getAttribute($property);
}
/**
* Magic setter.
*
* @param string $property
* @param mixed $value
*
* @return Message
*/
public function __set($property, $value)
{
if (property_exists($this, $property)) {
$this->$property = $value;
} else {
$this->setAttribute($property, $value);
}
return $this;
}
/**
* @param array $appends
*
* @return array
*/
public function transformForJsonRequestWithoutType(array $appends = [])
{
return $this->transformForJsonRequest($appends, false);
}
/**
* @param array $appends
* @param bool $withType
*
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
*/
public function transformForJsonRequest(array $appends = [], $withType = true): array
{
if (!$withType) {
return $this->propertiesToArray([], $this->jsonAliases);
}
$messageType = $this->getType();
$data = array_merge(['msgtype' => $messageType], $appends);
$data[$messageType] = array_merge($data[$messageType] ?? [], $this->propertiesToArray([], $this->jsonAliases));
return $data;
}
/**
* @param array $appends
* @param bool $returnAsArray
*
* @return string
*/
public function transformToXml(array $appends = [], bool $returnAsArray = false): string
{
$data = array_merge(['MsgType' => $this->getType()], $this->toXmlArray(), $appends);
return $returnAsArray ? $data : XML::build($data);
}
/**
* @param array $data
* @param array $aliases
*
* @return array
*
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
*/
protected function propertiesToArray(array $data, array $aliases = []): array
{
$this->checkRequiredAttributes();
foreach ($this->attributes as $property => $value) {
if (is_null($value) && !$this->isRequired($property)) {
continue;
}
$alias = array_search($property, $aliases, true);
$data[$alias ?: $property] = $this->get($property);
}
return $data;
}
public function toXmlArray()
{
throw new BadMethodCallException(sprintf('Class "%s" cannot support transform to XML message.', __CLASS__));
}
}
<?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\Messages;
/**
* Class MiniProgramPage.
*/
class MiniProgramPage extends Message
{
protected $type = 'miniprogrampage';
protected $properties = [
'title',
'appid',
'pagepath',
'thumb_media_id',
];
protected $required = [
'thumb_media_id', 'appid', 'pagepath',
];
}
<?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\Messages;
/**
* Class Music.
*
* @property string $url
* @property string $hq_url
* @property string $title
* @property string $description
* @property string $thumb_media_id
* @property string $format
*/
class Music extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'music';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'title',
'description',
'url',
'hq_url',
'thumb_media_id',
'format',
];
/**
* Aliases of attribute.
*
* @var array
*/
protected $jsonAliases = [
'musicurl' => 'url',
'hqmusicurl' => 'hq_url',
];
public function toXmlArray()
{
$music = [
'Music' => [
'Title' => $this->get('title'),
'Description' => $this->get('description'),
'MusicUrl' => $this->get('url'),
'HQMusicUrl' => $this->get('hq_url'),
],
];
if ($thumbMediaId = $this->get('thumb_media_id')) {
$music['Music']['ThumbMediaId'] = $thumbMediaId;
}
return $music;
}
}
<?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\Messages;
/**
* Class News.
*
* @author overtrue <i@overtrue.me>
*/
class News extends Message
{
/**
* @var string
*/
protected $type = 'news';
/**
* @var array
*/
protected $properties = [
'items',
];
/**
* News constructor.
*
* @param array $items
*/
public function __construct(array $items = [])
{
parent::__construct(compact('items'));
}
/**
* @param array $data
* @param array $aliases
*
* @return array
*/
public function propertiesToArray(array $data, array $aliases = []): array
{
return ['articles' => array_map(function ($item) {
if ($item instanceof NewsItem) {
return $item->toJsonArray();
}
}, $this->get('items'))];
}
public function toXmlArray()
{
$items = [];
foreach ($this->get('items') as $item) {
if ($item instanceof NewsItem) {
$items[] = $item->toXmlArray();
}
}
return [
'ArticleCount' => count($items),
'Articles' => $items,
];
}
}
<?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\Messages;
/**
* Class NewsItem.
*/
class NewsItem extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'news';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'title',
'description',
'url',
'image',
];
public function toJsonArray()
{
return [
'title' => $this->get('title'),
'description' => $this->get('description'),
'url' => $this->get('url'),
'picurl' => $this->get('image'),
];
}
public function toXmlArray()
{
return [
'Title' => $this->get('title'),
'Description' => $this->get('description'),
'Url' => $this->get('url'),
'PicUrl' => $this->get('image'),
];
}
}
<?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\Messages;
/**
* Class Raw.
*/
class Raw extends Message
{
/**
* @var string
*/
protected $type = 'raw';
/**
* Properties.
*
* @var array
*/
protected $properties = ['content'];
/**
* Constructor.
*
* @param string $content
*/
public function __construct(string $content)
{
parent::__construct(['content' => strval($content)]);
}
/**
* @param array $appends
* @param bool $withType
*
* @return array
*/
public function transformForJsonRequest(array $appends = [], $withType = true): array
{
return json_decode($this->content, true) ?? [];
}
public function __toString()
{
return $this->get('content') ?? '';
}
}
<?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\Messages;
/**
* Class ShortVideo.
*
* @property string $title
* @property string $media_id
* @property string $description
* @property string $thumb_media_id
*/
class ShortVideo extends Video
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'shortvideo';
}
<?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\Messages;
/**
* Class TaskCard.
*
* @property string $title
* @property string $description
* @property string $url
* @property string $task_id
* @property array $btn
*/
class TaskCard extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'taskcard';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'title',
'description',
'url',
'task_id',
'btn',
];
}
<?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\Messages;
/**
* Class Text.
*
* @property string $content
*/
class Text extends Message
{
/**
* Message type.
*
* @var string
*/
protected $type = 'text';
/**
* Properties.
*
* @var array
*/
protected $properties = ['content'];
/**
* Text constructor.
*
* @param string $content
*/
public function __construct(string $content)
{
parent::__construct(compact('content'));
}
/**
* @return array
*/
public function toXmlArray()
{
return [
'Content' => $this->get('content'),
];
}
}
<?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\Messages;
/**
* Class Text.
*
* @property string $title
* @property string $description
* @property string $url
*/
class TextCard extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'textcard';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'title',
'description',
'url',
];
}
<?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\Messages;
/**
* Class Transfer.
*
* @property string $to
* @property string $account
*/
class Transfer extends Message
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'transfer_customer_service';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'account',
];
/**
* Transfer constructor.
*
* @param string|null $account
*/
public function __construct(string $account = null)
{
parent::__construct(compact('account'));
}
public function toXmlArray()
{
return empty($this->get('account')) ? [] : [
'TransInfo' => [
'KfAccount' => $this->get('account'),
],
];
}
}
<?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\Messages;
/**
* Class Video.
*
* @property string $video
* @property string $title
* @property string $media_id
* @property string $description
* @property string $thumb_media_id
*/
class Video extends Media
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'video';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'title',
'description',
'media_id',
'thumb_media_id',
];
/**
* Video constructor.
*
* @param string $mediaId
* @param array $attributes
*/
public function __construct(string $mediaId, array $attributes = [])
{
parent::__construct($mediaId, 'video', $attributes);
}
public function toXmlArray()
{
return [
'Video' => [
'MediaId' => $this->get('media_id'),
'Title' => $this->get('title'),
'Description' => $this->get('description'),
],
];
}
}
<?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\Messages;
/**
* Class Voice.
*
* @property string $media_id
*/
class Voice extends Media
{
/**
* Messages type.
*
* @var string
*/
protected $type = 'voice';
/**
* Properties.
*
* @var array
*/
protected $properties = [
'media_id',
'recognition',
];
}
<?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\Providers;
use EasyWeChat\Kernel\Config;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class ConfigServiceProvider.
*
* @author overtrue <i@overtrue.me>
*/
class ConfigServiceProvider implements ServiceProviderInterface
{
/**
* Registers services on the given container.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*
* @param Container $pimple A container instance
*/
public function register(Container $pimple)
{
$pimple['config'] = function ($app) {
return new Config($app->getConfig());
};
}
}
<?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\Providers;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
/**
* Class EventDispatcherServiceProvider.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class EventDispatcherServiceProvider implements ServiceProviderInterface
{
/**
* Registers services on the given container.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*
* @param Container $pimple A container instance
*/
public function register(Container $pimple)
{
$pimple['events'] = function ($app) {
$dispatcher = new EventDispatcher();
foreach ($app->config->get('events.listen', []) as $event => $listeners) {
foreach ($listeners as $listener) {
$dispatcher->addListener($event, $listener);
}
}
return $dispatcher;
};
}
}
<?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\Providers;
use EasyWeChatComposer\Extension;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class ExtensionServiceProvider.
*
* @author overtrue <i@overtrue.me>
*/
class ExtensionServiceProvider implements ServiceProviderInterface
{
/**
* Registers services on the given container.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*
* @param Container $pimple A container instance
*/
public function register(Container $pimple)
{
$pimple['extension'] = function ($app) {
return new Extension($app);
};
}
}
<?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\Providers;
use GuzzleHttp\Client;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class HttpClientServiceProvider.
*
* @author overtrue <i@overtrue.me>
*/
class HttpClientServiceProvider implements ServiceProviderInterface
{
/**
* Registers services on the given container.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*
* @param Container $pimple A container instance
*/
public function register(Container $pimple)
{
$pimple['http_client'] = function ($app) {
return new Client($app['config']->get('http', []));
};
}
}
<?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\Providers;
use EasyWeChat\Kernel\Log\LogManager;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* Class LoggingServiceProvider.
*
* @author overtrue <i@overtrue.me>
*/
class LogServiceProvider implements ServiceProviderInterface
{
/**
* Registers services on the given container.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*
* @param Container $pimple A container instance
*/
public function register(Container $pimple)
{
$pimple['logger'] = $pimple['log'] = function ($app) {
$config = $this->formatLogConfig($app);
if (!empty($config)) {
$app->rebind('config', $app['config']->merge($config));
}
return new LogManager($app);
};
}
public function formatLogConfig($app)
{
if (!empty($app['config']->get('log.channels'))) {
return $app['config']->get('log');
}
if (empty($app['config']->get('log'))) {
return [
'log' => [
'default' => 'errorlog',
'channels' => [
'errorlog' => [
'driver' => 'errorlog',
'level' => 'debug',
],
],
],
];
}
return [
'log' => [
'default' => 'single',
'channels' => [
'single' => [
'driver' => 'single',
'path' => $app['config']->get('log.file') ?: \sys_get_temp_dir().'/logs/easywechat.log',
'level' => $app['config']->get('log.level', 'debug'),
],
],
],
];
}
}
<?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\Providers;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Class RequestServiceProvider.
*
* @author overtrue <i@overtrue.me>
*/
class RequestServiceProvider implements ServiceProviderInterface
{
/**
* Registers services on the given container.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*
* @param Container $pimple A container instance
*/
public function register(Container $pimple)
{
$pimple['request'] = function () {
return Request::createFromGlobals();
};
}
}
<?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\Providers\ConfigServiceProvider;
use EasyWeChat\Kernel\Providers\EventDispatcherServiceProvider;
use EasyWeChat\Kernel\Providers\ExtensionServiceProvider;
use EasyWeChat\Kernel\Providers\HttpClientServiceProvider;
use EasyWeChat\Kernel\Providers\LogServiceProvider;
use EasyWeChat\Kernel\Providers\RequestServiceProvider;
use EasyWeChatComposer\Traits\WithAggregator;
use Pimple\Container;
/**
* Class ServiceContainer.
*
* @author overtrue <i@overtrue.me>
*
* @property \EasyWeChat\Kernel\Config $config
* @property \Symfony\Component\HttpFoundation\Request $request
* @property \GuzzleHttp\Client $http_client
* @property \Monolog\Logger $logger
* @property \Symfony\Component\EventDispatcher\EventDispatcher $events
*/
class ServiceContainer extends Container
{
use WithAggregator;
/**
* @var string
*/
protected $id;
/**
* @var array
*/
protected $providers = [];
/**
* @var array
*/
protected $defaultConfig = [];
/**
* @var array
*/
protected $userConfig = [];
/**
* Constructor.
*
* @param array $config
* @param array $prepends
* @param string|null $id
*/
public function __construct(array $config = [], array $prepends = [], string $id = null)
{
$this->registerProviders($this->getProviders());
parent::__construct($prepends);
$this->userConfig = $config;
$this->id = $id;
$this->aggregate();
$this->events->dispatch(new Events\ApplicationInitialized($this));
}
/**
* @return string
*/
public function getId()
{
return $this->id ?? $this->id = md5(json_encode($this->userConfig));
}
/**
* @return array
*/
public function getConfig()
{
$base = [
// http://docs.guzzlephp.org/en/stable/request-options.html
'http' => [
'timeout' => 30.0,
'base_uri' => 'https://api.weixin.qq.com/',
],
];
return array_replace_recursive($base, $this->defaultConfig, $this->userConfig);
}
/**
* Return all providers.
*
* @return array
*/
public function getProviders()
{
return array_merge([
ConfigServiceProvider::class,
LogServiceProvider::class,
RequestServiceProvider::class,
HttpClientServiceProvider::class,
ExtensionServiceProvider::class,
EventDispatcherServiceProvider::class,
], $this->providers);
}
/**
* @param string $id
* @param mixed $value
*/
public function rebind($id, $value)
{
$this->offsetUnset($id);
$this->offsetSet($id, $value);
}
/**
* Magic get access.
*
* @param string $id
*
* @return mixed
*/
public function __get($id)
{
if ($this->shouldDelegate($id)) {
return $this->delegateTo($id);
}
return $this->offsetGet($id);
}
/**
* Magic set access.
*
* @param string $id
* @param mixed $value
*/
public function __set($id, $value)
{
$this->offsetSet($id, $value);
}
/**
* @param array $providers
*/
public function registerProviders(array $providers)
{
foreach ($providers as $provider) {
parent::register(new $provider());
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment