update
This commit is contained in:
21
vendor/overtrue/pinyin/LICENSE
vendored
Normal file
21
vendor/overtrue/pinyin/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 安正超
|
||||
|
||||
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.
|
||||
120
vendor/overtrue/pinyin/README.md
vendored
Normal file
120
vendor/overtrue/pinyin/README.md
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
Pinyin
|
||||
======
|
||||
|
||||
[](https://travis-ci.org/overtrue/pinyin)
|
||||
[](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin) [](https://packagist.org/packages/overtrue/pinyin)
|
||||
[](https://scrutinizer-ci.com/g/overtrue/pinyin/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/overtrue/pinyin/?branch=master)
|
||||
|
||||
<p align="center">
|
||||
<br>
|
||||
<b>创造不息,交付不止</b>
|
||||
<br>
|
||||
<a href="https://www.yousails.com">
|
||||
<img src="https://yousails.com/banners/brand.png" width=350>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
:cn: 基于 [CC-CEDICT](http://cc-cedict.org/wiki/) 词典的中文转拼音工具,更准确的支持多音字的汉字转拼音解决方案。
|
||||
|
||||
|
||||
## 安装
|
||||
|
||||
使用 Composer 安装:
|
||||
|
||||
```
|
||||
composer require "overtrue/pinyin:~3.0"
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
可选转换方案:
|
||||
|
||||
- 内存型,适用于服务器内存空间较富余,优点:转换快
|
||||
- 小内存型(默认),适用于内存比较紧张的环境,优点:占用内存小,转换不如内存型快
|
||||
- I/O型,适用于虚拟机,内存限制比较严格环境。优点:非常微小内存消耗。缺点:转换慢,不如内存型转换快,php >= 5.5
|
||||
|
||||
### 拼音数组
|
||||
|
||||
```php
|
||||
use Overtrue\Pinyin\Pinyin;
|
||||
|
||||
// 小内存型
|
||||
$pinyin = new Pinyin(); // 默认
|
||||
// 内存型
|
||||
// $pinyin = new Pinyin('Overtrue\Pinyin\MemoryFileDictLoader');
|
||||
// I/O型
|
||||
// $pinyin = new Pinyin('Overtrue\Pinyin\GeneratorFileDictLoader');
|
||||
|
||||
$pinyin->convert('带着希望去旅行,比到达终点更美好');
|
||||
// ["dai", "zhe", "xi", "wang", "qu", "lv", "xing", "bi", "dao", "da", "zhong", "dian", "geng", "mei", "hao"]
|
||||
|
||||
$pinyin->convert('带着希望去旅行,比到达终点更美好', PINYIN_UNICODE);
|
||||
// ["dài","zhe","xī","wàng","qù","lǚ","xíng","bǐ","dào","dá","zhōng","diǎn","gèng","měi","hǎo"]
|
||||
|
||||
$pinyin->convert('带着希望去旅行,比到达终点更美好', PINYIN_ASCII);
|
||||
//["dai4","zhe","xi1","wang4","qu4","lv3","xing2","bi3","dao4","da2","zhong1","dian3","geng4","mei3","hao3"]
|
||||
```
|
||||
|
||||
- 小内存型: 将字典分片载入内存
|
||||
- 内存型: 将所有字典预先载入内存
|
||||
- I/O型: 不载入内存,将字典使用文件流打开逐行遍历并运用php5.5生成器(yield)特性分配单行内存
|
||||
|
||||
|
||||
选项:
|
||||
|
||||
| 选项 | 描述 |
|
||||
| ------------- | ---------------------------------------------------|
|
||||
| `PINYIN_NONE` | 不带音调输出: `mei hao` |
|
||||
| `PINYIN_ASCII` | 带数字式音调: `mei3 hao3` |
|
||||
| `PINYIN_UNICODE` | UNICODE 式音调:`měi hǎo` |
|
||||
|
||||
### 生成用于链接的拼音字符串
|
||||
|
||||
```php
|
||||
$pinyin->permalink('带着希望去旅行'); // dai-zhe-xi-wang-qu-lv-xing
|
||||
$pinyin->permalink('带着希望去旅行', '.'); // dai.zhe.xi.wang.qu.lv.xing
|
||||
```
|
||||
|
||||
### 获取首字符字符串
|
||||
|
||||
```php
|
||||
$pinyin->abbr('带着希望去旅行'); // dzxwqlx
|
||||
$pinyin->abbr('带着希望去旅行', '-'); // d-z-x-w-q-l-x
|
||||
```
|
||||
|
||||
### 翻译整段文字为拼音
|
||||
|
||||
将会保留中文字符:`,。 ! ? : “ ” ‘ ’` 并替换为对应的英文符号。
|
||||
|
||||
```php
|
||||
$pinyin->sentence('带着希望去旅行,比到达终点更美好!');
|
||||
// dai zhe xi wang qu lv xing, bi dao da zhong dian geng mei hao!
|
||||
|
||||
$pinyin->sentence('带着希望去旅行,比到达终点更美好!', true);
|
||||
// dài zhe xī wàng qù lǚ xíng, bǐ dào dá zhōng diǎn gèng měi hǎo!
|
||||
```
|
||||
|
||||
### 翻译姓名
|
||||
|
||||
姓名的姓的读音有些与普通字不一样,比如 ‘单’ 常见的音为 `dan`,而作为姓的时候读 `shan`。
|
||||
|
||||
```php
|
||||
$pinyin->name('单某某'); // ['shan', 'mou', 'mou']
|
||||
$pinyin->name('单某某', PINYIN_UNICODE); // ["shàn","mǒu","mǒu"]
|
||||
```
|
||||
|
||||
## 在 Laravel 中使用
|
||||
|
||||
独立的包在这里:[overtrue/laravel-pinyin](https://github.com/overtrue/laravel-pinyin)
|
||||
|
||||
## Contribution
|
||||
欢迎提意见及完善补充词库 [`overtrue/pinyin-dictionary-maker`](https://github.com/overtrue/pinyin-dictionary-maker/tree/master/patches) :kiss:
|
||||
|
||||
## 参考
|
||||
|
||||
- [详细参考资料](https://github.com/overtrue/pinyin-resources)
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
33
vendor/overtrue/pinyin/composer.json
vendored
Normal file
33
vendor/overtrue/pinyin/composer.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "overtrue/pinyin",
|
||||
"description": "Chinese to pinyin translator.",
|
||||
"keywords": [
|
||||
"chinese",
|
||||
"pinyin",
|
||||
"cn2pinyin"
|
||||
],
|
||||
"homepage": "https://github.com/overtrue/pinyin",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Carlos",
|
||||
"homepage": "http://github.com/overtrue"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Overtrue\\Pinyin\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Overtrue\\Pinyin\\Test\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php":">=5.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8"
|
||||
}
|
||||
}
|
||||
84
vendor/overtrue/pinyin/data/surnames
vendored
Normal file
84
vendor/overtrue/pinyin/data/surnames
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
return array (
|
||||
'万俟' => ' mò qí',
|
||||
'尉迟' => ' yù chí',
|
||||
'单于' => ' chán yú',
|
||||
'不' => ' fǒu',
|
||||
'沈' => ' shěn',
|
||||
'称' => ' chēng',
|
||||
'车' => ' chē',
|
||||
'万' => ' wàn',
|
||||
'汤' => ' tāng',
|
||||
'阿' => ' ā',
|
||||
'丁' => ' dīng',
|
||||
'强' => ' qiáng',
|
||||
'仇' => ' qiú',
|
||||
'叶' => ' yè',
|
||||
'阚' => ' kàn',
|
||||
'乐' => ' yuè',
|
||||
'乜' => ' niè',
|
||||
'陆' => ' lù',
|
||||
'殷' => ' yīn',
|
||||
'牟' => ' móu',
|
||||
'区' => ' ōu',
|
||||
'宿' => ' sù',
|
||||
'俞' => ' yú',
|
||||
'余' => ' yú',
|
||||
'齐' => ' qí',
|
||||
'许' => ' xǔ',
|
||||
'信' => ' xìn',
|
||||
'无' => ' wú',
|
||||
'浣' => ' wǎn',
|
||||
'艾' => ' ài',
|
||||
'浅' => ' qiǎn',
|
||||
'烟' => ' yān',
|
||||
'蓝' => ' lán',
|
||||
'於' => ' yú',
|
||||
'寻' => ' xún',
|
||||
'殳' => ' shū',
|
||||
'思' => ' sī',
|
||||
'鸟' => ' niǎo',
|
||||
'卜' => ' bǔ',
|
||||
'单' => ' shàn',
|
||||
'南' => ' nán',
|
||||
'柏' => ' bǎi',
|
||||
'朴' => ' piáo',
|
||||
'繁' => ' pó',
|
||||
'曾' => ' zēng',
|
||||
'瞿' => ' qú',
|
||||
'缪' => ' miào',
|
||||
'石' => ' shí',
|
||||
'冯' => ' féng',
|
||||
'覃' => ' qín',
|
||||
'幺' => ' yāo',
|
||||
'种' => ' chóng',
|
||||
'折' => ' shè',
|
||||
'燕' => ' yān',
|
||||
'纪' => ' jǐ',
|
||||
'过' => ' guō',
|
||||
'华' => ' huà',
|
||||
'冼' => ' xiǎn',
|
||||
'秘' => ' bì',
|
||||
'重' => ' chóng',
|
||||
'解' => ' xiè',
|
||||
'那' => ' nā',
|
||||
'和' => ' hé',
|
||||
'贾' => ' jiǎ',
|
||||
'塔' => ' tǎ',
|
||||
'盛' => ' shèng',
|
||||
'查' => ' zhā',
|
||||
'盖' => ' gě',
|
||||
'居' => ' jū',
|
||||
'哈' => ' hǎ',
|
||||
'的' => ' dē',
|
||||
'薄' => ' bó',
|
||||
'佴' => ' nài',
|
||||
'六' => ' lù',
|
||||
'都' => ' dū',
|
||||
'翟' => ' zhái',
|
||||
'扎' => ' zā',
|
||||
'藏' => ' zàng',
|
||||
'粘' => ' niàn',
|
||||
'难' => ' nàn',
|
||||
'若' => ' ruò',
|
||||
);
|
||||
8003
vendor/overtrue/pinyin/data/words_0
vendored
Normal file
8003
vendor/overtrue/pinyin/data/words_0
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8003
vendor/overtrue/pinyin/data/words_1
vendored
Normal file
8003
vendor/overtrue/pinyin/data/words_1
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8003
vendor/overtrue/pinyin/data/words_2
vendored
Normal file
8003
vendor/overtrue/pinyin/data/words_2
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8003
vendor/overtrue/pinyin/data/words_3
vendored
Normal file
8003
vendor/overtrue/pinyin/data/words_3
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8003
vendor/overtrue/pinyin/data/words_4
vendored
Normal file
8003
vendor/overtrue/pinyin/data/words_4
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2056
vendor/overtrue/pinyin/data/words_5
vendored
Normal file
2056
vendor/overtrue/pinyin/data/words_5
vendored
Normal file
File diff suppressed because it is too large
Load Diff
42
vendor/overtrue/pinyin/src/DictLoaderInterface.php
vendored
Normal file
42
vendor/overtrue/pinyin/src/DictLoaderInterface.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (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 Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Dict loader interface.
|
||||
*/
|
||||
interface DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* <pre>
|
||||
* [
|
||||
* '响应时间' => "[\t]xiǎng[\t]yìng[\t]shí[\t]jiān",
|
||||
* '长篇连载' => '[\t]cháng[\t]piān[\t]lián[\t]zǎi',
|
||||
* //...
|
||||
* ]
|
||||
* </pre>
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback);
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback);
|
||||
}
|
||||
76
vendor/overtrue/pinyin/src/FileDictLoader.php
vendored
Normal file
76
vendor/overtrue/pinyin/src/FileDictLoader.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (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 Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Dict File loader.
|
||||
*/
|
||||
class FileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* Dict path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $this->path.'/'.sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment)) {
|
||||
$dictionary = (array) include $segment;
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
$surnames = $this->path.'/surnames';
|
||||
|
||||
if (file_exists($surnames)) {
|
||||
$dictionary = (array) include $surnames;
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
142
vendor/overtrue/pinyin/src/GeneratorFileDictLoader.php
vendored
Normal file
142
vendor/overtrue/pinyin/src/GeneratorFileDictLoader.php
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (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 Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
use SplFileObject;
|
||||
use Generator;
|
||||
|
||||
/**
|
||||
* Generator syntax(yield) Dict File loader.
|
||||
*/
|
||||
class GeneratorFileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Data directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* SplFileObjects.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $handles = [];
|
||||
|
||||
/**
|
||||
* surnames.
|
||||
*
|
||||
* @var SplFileObject
|
||||
*/
|
||||
protected static $surnamesHandle;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $this->path.'/'.sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment) && is_file($segment)) {
|
||||
array_push(static::$handles, $this->openFile($segment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new file object.
|
||||
*
|
||||
* @param string $filename file path
|
||||
*
|
||||
* @return SplFileObject
|
||||
*/
|
||||
protected function openFile($filename, $mode = 'r')
|
||||
{
|
||||
return new SplFileObject($filename, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* get Generator syntax.
|
||||
*
|
||||
* @param array $handles SplFileObjects
|
||||
*/
|
||||
protected function getGenerator(array $handles)
|
||||
{
|
||||
foreach ($handles as $handle) {
|
||||
$handle->seek(0);
|
||||
while ($handle->eof() === false) {
|
||||
$string = str_replace(['\'', ' ', PHP_EOL, ','], '', $handle->fgets());
|
||||
|
||||
if (strpos($string, '=>') === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($string, $pinyin) = explode('=>', $string);
|
||||
|
||||
yield $string => $pinyin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the stream.
|
||||
*
|
||||
* @param Generator $generator
|
||||
* @param Closure $callback
|
||||
*
|
||||
* @author Seven Du <shiweidu@outlook.com>
|
||||
*/
|
||||
protected function traversing(Generator $generator, Closure $callback)
|
||||
{
|
||||
foreach ($generator as $string => $pinyin) {
|
||||
$callback([$string => $pinyin]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
$this->traversing($this->getGenerator(static::$handles), $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
if (!static::$surnamesHandle instanceof SplFileObject) {
|
||||
static::$surnamesHandle = $this->openFile($this->path.'/surnames');
|
||||
}
|
||||
|
||||
$this->traversing($this->getGenerator([static::$surnamesHandle]), $callback);
|
||||
}
|
||||
}
|
||||
96
vendor/overtrue/pinyin/src/MemoryFileDictLoader.php
vendored
Normal file
96
vendor/overtrue/pinyin/src/MemoryFileDictLoader.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (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 Overtrue\Pinyin;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Memory Dict File loader.
|
||||
*/
|
||||
class MemoryFileDictLoader implements DictLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Data directory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Words segment name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $segmentName = 'words_%s';
|
||||
|
||||
/**
|
||||
* Segment files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $segments = array();
|
||||
|
||||
/**
|
||||
* Surname cache.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $surnames = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$segment = $path.'/'.sprintf($this->segmentName, $i);
|
||||
|
||||
if (file_exists($segment)) {
|
||||
$this->segments[] = (array) include $segment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function map(Closure $callback)
|
||||
{
|
||||
foreach ($this->segments as $dictionary) {
|
||||
$callback($dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load surname dict.
|
||||
*
|
||||
* @param Closure $callback
|
||||
*/
|
||||
public function mapSurname(Closure $callback)
|
||||
{
|
||||
if (empty($this->surnames)) {
|
||||
$surnames = $this->path.'/surnames';
|
||||
|
||||
if (file_exists($surnames)) {
|
||||
$this->surnames = (array) include $surnames;
|
||||
}
|
||||
}
|
||||
|
||||
$callback($this->surnames);
|
||||
}
|
||||
}
|
||||
309
vendor/overtrue/pinyin/src/Pinyin.php
vendored
Normal file
309
vendor/overtrue/pinyin/src/Pinyin.php
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/pinyin.
|
||||
*
|
||||
* (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 Overtrue\Pinyin;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/*
|
||||
* Chinese to pinyin translator.
|
||||
*
|
||||
* @author overtrue <i@overtrue.me>
|
||||
* @copyright 2015 overtrue <i@overtrue.me>
|
||||
*
|
||||
* @link https://github.com/overtrue/pinyin
|
||||
* @link http://overtrue.me
|
||||
*/
|
||||
|
||||
define('PINYIN_NONE', 'none');
|
||||
define('PINYIN_ASCII', 'ascii');
|
||||
define('PINYIN_UNICODE', 'unicode');
|
||||
|
||||
class Pinyin
|
||||
{
|
||||
const NONE = 'none';
|
||||
const ASCII = 'ascii';
|
||||
const UNICODE = 'unicode';
|
||||
|
||||
/**
|
||||
* Dict loader.
|
||||
*
|
||||
* @var \Overtrue\Pinyin\DictLoaderInterface
|
||||
*/
|
||||
protected $loader;
|
||||
|
||||
/**
|
||||
* Punctuations map.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $punctuations = array(
|
||||
',' => ',',
|
||||
'。' => '.',
|
||||
'!' => '!',
|
||||
'?' => '?',
|
||||
':' => ':',
|
||||
'“' => '"',
|
||||
'”' => '"',
|
||||
'‘' => "'",
|
||||
'’' => "'",
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $loaderName
|
||||
*/
|
||||
public function __construct($loaderName = null)
|
||||
{
|
||||
$this->loader = $loaderName ?: 'Overtrue\\Pinyin\\FileDictLoader';
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function convert($string, $option = self::NONE)
|
||||
{
|
||||
$pinyin = $this->romanize($string);
|
||||
|
||||
return $this->splitWords($pinyin, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string (person name) to pinyin.
|
||||
*
|
||||
* @param string $stringName
|
||||
* @param string $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function name($stringName, $option = self::NONE)
|
||||
{
|
||||
$pinyin = $this->romanize($stringName, true);
|
||||
|
||||
return $this->splitWords($pinyin, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pinyin permalink from string.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function permalink($string, $delimiter = '-')
|
||||
{
|
||||
if (!in_array($delimiter, array('_', '-', '.', ''), true)) {
|
||||
throw new InvalidArgumentException("Delimiter must be one of: '_', '-', '', '.'.");
|
||||
}
|
||||
|
||||
return implode($delimiter, $this->convert($string, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first letters.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function abbr($string, $delimiter = '')
|
||||
{
|
||||
return implode($delimiter, array_map(function ($pinyin) {
|
||||
return $pinyin[0];
|
||||
}, $this->convert($string, false)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chinese phrase to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $delimiter
|
||||
* @param string $option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function phrase($string, $delimiter = ' ', $option = self::NONE)
|
||||
{
|
||||
return implode($delimiter, $this->convert($string, $option));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chinese to pinyin sentense.
|
||||
*
|
||||
* @param string $sentence
|
||||
* @param bool $withTone
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function sentence($sentence, $withTone = false)
|
||||
{
|
||||
$marks = array_keys($this->punctuations);
|
||||
$punctuationsRegex = preg_quote(implode(array_merge($marks, $this->punctuations)), '/');
|
||||
$regex = '/[^üāēīōūǖáéíóúǘǎěǐǒǔǚàèìòùǜa-z0-9'.$punctuationsRegex.'\s_]+/iu';
|
||||
|
||||
$pinyin = preg_replace($regex, '', $this->romanize($sentence));
|
||||
|
||||
$punctuations = array_merge($this->punctuations, array("\t" => ' ', ' ' => ' '));
|
||||
$pinyin = trim(str_replace(array_keys($punctuations), $punctuations, $pinyin));
|
||||
|
||||
return $withTone ? $pinyin : $this->format($pinyin, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loader setter.
|
||||
*
|
||||
* @param \Overtrue\Pinyin\DictLoaderInterface $loader
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLoader(DictLoaderInterface $loader)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return dict loader,.
|
||||
*
|
||||
* @return \Overtrue\Pinyin\DictLoaderInterface
|
||||
*/
|
||||
public function getLoader()
|
||||
{
|
||||
if (!($this->loader instanceof DictLoaderInterface)) {
|
||||
$dataDir = dirname(__DIR__).'/data/';
|
||||
|
||||
$loaderName = $this->loader;
|
||||
$this->loader = new $loaderName($dataDir);
|
||||
}
|
||||
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess.
|
||||
*
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prepare($string)
|
||||
{
|
||||
$string = preg_replace_callback('~[a-z0-9_-]+~i', function ($matches) {
|
||||
return "\t".$matches[0];
|
||||
}, $string);
|
||||
|
||||
return preg_replace("~[^\p{Han}\p{P}\p{Z}\p{M}\p{N}\p{L}\t]~u", '', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Chinese to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param bool $isName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function romanize($string, $isName = false)
|
||||
{
|
||||
$string = $this->prepare($string);
|
||||
|
||||
$dictLoader = $this->getLoader();
|
||||
|
||||
if ($isName) {
|
||||
$string = $this->convertSurname($string, $dictLoader);
|
||||
}
|
||||
|
||||
$dictLoader->map(function ($dictionary) use (&$string) {
|
||||
$string = strtr($string, $dictionary);
|
||||
});
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Chinese Surname to pinyin.
|
||||
*
|
||||
* @param string $string
|
||||
* @param \Overtrue\Pinyin\DictLoaderInterface $dictLoader
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function convertSurname($string, $dictLoader)
|
||||
{
|
||||
$dictLoader->mapSurname(function ($dictionary) use (&$string) {
|
||||
foreach ($dictionary as $surname => $pinyin) {
|
||||
if (strpos($string, $surname) === 0) {
|
||||
$string = $pinyin.mb_substr($string, mb_strlen($surname, 'UTF-8'), mb_strlen($string, 'UTF-8') - 1, 'UTF-8');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split pinyin string to words.
|
||||
*
|
||||
* @param string $pinyin
|
||||
* @param string $option
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function splitWords($pinyin, $option)
|
||||
{
|
||||
$split = array_filter(preg_split('/[^üāēīōūǖáéíóúǘǎěǐǒǔǚàèìòùǜa-z\d]+/iu', $pinyin));
|
||||
|
||||
if ($option !== self::UNICODE) {
|
||||
foreach ($split as $index => $pinyin) {
|
||||
$split[$index] = $this->format($pinyin, $option === self::ASCII);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($split);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format.
|
||||
*
|
||||
* @param string $pinyin
|
||||
* @param bool $tone
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function format($pinyin, $tone = false)
|
||||
{
|
||||
$replacements = array(
|
||||
'üē' => array('ue', 1), 'üé' => array('ue', 2), 'üě' => array('ue', 3), 'üè' => array('ue', 4),
|
||||
'ā' => array('a', 1), 'ē' => array('e', 1), 'ī' => array('i', 1), 'ō' => array('o', 1), 'ū' => array('u', 1), 'ǖ' => array('v', 1),
|
||||
'á' => array('a', 2), 'é' => array('e', 2), 'í' => array('i', 2), 'ó' => array('o', 2), 'ú' => array('u', 2), 'ǘ' => array('v', 2),
|
||||
'ǎ' => array('a', 3), 'ě' => array('e', 3), 'ǐ' => array('i', 3), 'ǒ' => array('o', 3), 'ǔ' => array('u', 3), 'ǚ' => array('v', 3),
|
||||
'à' => array('a', 4), 'è' => array('e', 4), 'ì' => array('i', 4), 'ò' => array('o', 4), 'ù' => array('u', 4), 'ǜ' => array('v', 4),
|
||||
);
|
||||
|
||||
foreach ($replacements as $unicde => $replacement) {
|
||||
if (false !== strpos($pinyin, $unicde)) {
|
||||
$pinyin = str_replace($unicde, $replacement[0], $pinyin).($tone ? $replacement[1] : '');
|
||||
}
|
||||
}
|
||||
|
||||
return $pinyin;
|
||||
}
|
||||
}
|
||||
9
vendor/overtrue/socialite/.github/FUNDING.yml
vendored
Normal file
9
vendor/overtrue/socialite/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: overtrue
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
custom: # Replace with a single custom sponsorship URL
|
||||
9
vendor/overtrue/socialite/.gitignore
vendored
Normal file
9
vendor/overtrue/socialite/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
/vendor
|
||||
composer.phar
|
||||
composer.lock
|
||||
.DS_Store
|
||||
/.idea
|
||||
Thumbs.db
|
||||
/*.php
|
||||
sftp-config.json
|
||||
.php_cs.cache
|
||||
28
vendor/overtrue/socialite/.php_cs
vendored
Normal file
28
vendor/overtrue/socialite/.php_cs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
$header = <<<EOF
|
||||
This file is part of the overtrue/socialite.
|
||||
|
||||
(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,
|
||||
))
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->exclude('vendor')
|
||||
->in(__DIR__)
|
||||
)
|
||||
;
|
||||
13
vendor/overtrue/socialite/.travis.yml
vendored
Normal file
13
vendor/overtrue/socialite/.travis.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
|
||||
sudo: false
|
||||
dist: trusty
|
||||
|
||||
install: travis_retry composer install --no-interaction --prefer-source
|
||||
|
||||
script: vendor/bin/phpunit --verbose
|
||||
21
vendor/overtrue/socialite/LICENSE.txt
vendored
Normal file
21
vendor/overtrue/socialite/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
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.
|
||||
267
vendor/overtrue/socialite/README.md
vendored
Normal file
267
vendor/overtrue/socialite/README.md
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
<h1 align="center"> Socialite</h1>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/overtrue/socialite"><img src="https://travis-ci.org/overtrue/socialite.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/v/stable.svg" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/v/unstable.svg" alt="Latest Unstable Version"></a>
|
||||
<a href="https://scrutinizer-ci.com/g/overtrue/socialite/build-status/master"><img src="https://scrutinizer-ci.com/g/overtrue/socialite/badges/build.png?b=master" alt="Build Status"></a>
|
||||
<a href="https://scrutinizer-ci.com/g/overtrue/socialite/?branch=master"><img src="https://scrutinizer-ci.com/g/overtrue/socialite/badges/quality-score.png?b=master" alt="Scrutinizer Code Quality"></a>
|
||||
<a href="https://scrutinizer-ci.com/g/overtrue/socialite/?branch=master"><img src="https://scrutinizer-ci.com/g/overtrue/socialite/badges/coverage.png?b=master" alt="Code Coverage"></a>
|
||||
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/downloads" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/license" alt="License"></a>
|
||||
</p>
|
||||
|
||||
|
||||
<p align="center">Socialite is an OAuth2 Authentication tool. It is inspired by <a href="https://github.com/laravel/socialite">laravel/socialite</a>, You can easily use it in any PHP project.</p>
|
||||
|
||||
# Requirement
|
||||
|
||||
```
|
||||
PHP >= 5.6
|
||||
```
|
||||
# Installation
|
||||
|
||||
```shell
|
||||
$ composer require "overtrue/socialite" -vvv
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
For Laravel 5: [overtrue/laravel-socialite](https://github.com/overtrue/laravel-socialite)
|
||||
|
||||
`authorize.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Overtrue\Socialite\SocialiteManager;
|
||||
|
||||
$config = [
|
||||
'github' => [
|
||||
'client_id' => 'your-app-id',
|
||||
'client_secret' => 'your-app-secret',
|
||||
'redirect' => 'http://localhost/socialite/callback.php',
|
||||
],
|
||||
];
|
||||
|
||||
$socialite = new SocialiteManager($config);
|
||||
|
||||
$response = $socialite->driver('github')->redirect();
|
||||
|
||||
echo $response;// or $response->send();
|
||||
```
|
||||
|
||||
`callback.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Overtrue\Socialite\SocialiteManager;
|
||||
|
||||
$config = [
|
||||
'github' => [
|
||||
'client_id' => 'your-app-id',
|
||||
'client_secret' => 'your-app-secret',
|
||||
'redirect' => 'http://localhost/socialite/callback.php',
|
||||
],
|
||||
];
|
||||
|
||||
$socialite = new SocialiteManager($config);
|
||||
|
||||
$user = $socialite->driver('github')->user();
|
||||
|
||||
$user->getId(); // 1472352
|
||||
$user->getNickname(); // "overtrue"
|
||||
$user->getUsername(); // "overtrue"
|
||||
$user->getName(); // "安正超"
|
||||
$user->getEmail(); // "anzhengchao@gmail.com"
|
||||
$user->getProviderName(); // GitHub
|
||||
...
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Now we support the following sites:
|
||||
|
||||
`facebook`, `github`, `google`, `linkedin`, `outlook`, `weibo`, `taobao`, `qq`, `wechat`, `douyin`, `baidu`, `feishu`, and `douban`.
|
||||
|
||||
Each driver uses the same configuration keys: `client_id`, `client_secret`, `redirect`.
|
||||
|
||||
Example:
|
||||
```
|
||||
...
|
||||
'weibo' => [
|
||||
'client_id' => 'your-app-id',
|
||||
'client_secret' => 'your-app-secret',
|
||||
'redirect' => 'http://localhost/socialite/callback.php',
|
||||
],
|
||||
...
|
||||
```
|
||||
|
||||
### Scope
|
||||
|
||||
Before redirecting the user, you may also set "scopes" on the request using the scope method. This method will overwrite all existing scopes:
|
||||
|
||||
```php
|
||||
$response = $socialite->driver('github')
|
||||
->scopes(['scope1', 'scope2'])->redirect();
|
||||
|
||||
```
|
||||
|
||||
### Redirect URL
|
||||
|
||||
You may also want to dynamicly set `redirect`,you can use the following methods to change the `redirect` URL:
|
||||
|
||||
```php
|
||||
$socialite->redirect($url);
|
||||
// or
|
||||
$socialite->withRedirectUrl($url)->redirect();
|
||||
// or
|
||||
$socialite->setRedirectUrl($url)->redirect();
|
||||
```
|
||||
|
||||
> WeChat scopes:
|
||||
- `snsapi_base`, `snsapi_userinfo` - Used to Media Platform Authentication.
|
||||
- `snsapi_login` - Used to web Authentication.
|
||||
|
||||
### Additional parameters
|
||||
|
||||
To include any optional parameters in the request, call the with method with an associative array:
|
||||
|
||||
```php
|
||||
$response = $socialite->driver('google')
|
||||
->with(['hd' => 'example.com'])->redirect();
|
||||
```
|
||||
|
||||
### User interface
|
||||
|
||||
#### Standard user api:
|
||||
|
||||
```php
|
||||
|
||||
$user = $socialite->driver('weibo')->user();
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1472352,
|
||||
"nickname": "overtrue",
|
||||
"name": "安正超",
|
||||
"email": "anzhengchao@gmail.com",
|
||||
"avatar": "https://avatars.githubusercontent.com/u/1472352?v=3",
|
||||
"original": {
|
||||
"login": "overtrue",
|
||||
"id": 1472352,
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1472352?v=3",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/overtrue",
|
||||
"html_url": "https://github.com/overtrue",
|
||||
...
|
||||
},
|
||||
"token": {
|
||||
"access_token": "5b1dc56d64fffbd052359f032716cc4e0a1cb9a0",
|
||||
"token_type": "bearer",
|
||||
"scope": "user:email"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can fetch the user attribute as a array keys like these:
|
||||
|
||||
```php
|
||||
$user['id']; // 1472352
|
||||
$user['nickname']; // "overtrue"
|
||||
$user['name']; // "安正超"
|
||||
$user['email']; // "anzhengchao@gmail.com"
|
||||
...
|
||||
```
|
||||
|
||||
Or using the method:
|
||||
|
||||
```php
|
||||
$user->getId();
|
||||
$user->getNickname();
|
||||
$user->getName();
|
||||
$user->getEmail();
|
||||
$user->getAvatar();
|
||||
$user->getOriginal();
|
||||
$user->getToken();// or $user->getAccessToken()
|
||||
$user->getProviderName(); // GitHub/Google/Facebook...
|
||||
```
|
||||
|
||||
#### Get original response from OAuth API
|
||||
|
||||
The `$user->getOriginal()` method will return an array of the API raw response.
|
||||
|
||||
#### Get access token Object
|
||||
|
||||
You can get the access token instance of current session by call `$user->getToken()` or `$user->getAccessToken()` or `$user['token']` .
|
||||
|
||||
|
||||
### Get user with access token
|
||||
|
||||
```php
|
||||
$accessToken = new AccessToken(['access_token' => $accessToken]);
|
||||
$user = $socialite->user($accessToken);
|
||||
```
|
||||
|
||||
|
||||
### Custom Session or Request instance.
|
||||
|
||||
You can set the request with your custom `Request` instance which instanceof `Symfony\Component\HttpFoundation\Request` before you call `driver` method.
|
||||
|
||||
|
||||
```php
|
||||
|
||||
$request = new Request(); // or use AnotherCustomRequest.
|
||||
|
||||
$socialite = new SocialiteManager($config, $request);
|
||||
```
|
||||
|
||||
Or set request to `SocialiteManager` instance:
|
||||
|
||||
```php
|
||||
$socialite->setRequest($request);
|
||||
```
|
||||
|
||||
You can get the request from the `SocialiteManager` instance by `getRequest()`:
|
||||
|
||||
```php
|
||||
$request = $socialite->getRequest();
|
||||
```
|
||||
|
||||
#### Set custom session manager.
|
||||
|
||||
By default, the `SocialiteManager` uses the `Symfony\Component\HttpFoundation\Session\Session` instance as session manager, you can change it as follows:
|
||||
|
||||
```php
|
||||
$session = new YourCustomSessionManager();
|
||||
$socialite->getRequest()->setSession($session);
|
||||
```
|
||||
|
||||
> Your custom session manager must be implement the [`Symfony\Component\HttpFoundation\Session\SessionInterface`](http://api.symfony.com/3.0/Symfony/Component/HttpFoundation/Session/SessionInterface.html).
|
||||
|
||||
Enjoy it! :heart:
|
||||
|
||||
# Reference
|
||||
|
||||
- [Google - OpenID Connect](https://developers.google.com/identity/protocols/OpenIDConnect)
|
||||
- [Facebook - Graph API](https://developers.facebook.com/docs/graph-api)
|
||||
- [Linkedin - Authenticating with OAuth 2.0](https://developer.linkedin.com/docs/oauth2)
|
||||
- [微博 - OAuth 2.0 授权机制说明](http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E)
|
||||
- [QQ - OAuth 2.0 登录QQ](http://wiki.connect.qq.com/oauth2-0%E7%AE%80%E4%BB%8B)
|
||||
- [微信公众平台 - OAuth文档](http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html)
|
||||
- [微信开放平台 - 网站应用微信登录开发指南](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN)
|
||||
- [微信开放平台 - 代公众号发起网页授权](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590&token=&lang=zh_CN)
|
||||
- [豆瓣 - OAuth 2.0 授权机制说明](http://developers.douban.com/wiki/?title=oauth2)
|
||||
- [抖音 - 网站应用开发指南](http://open.douyin.com/platform/doc)
|
||||
- [飞书 - 授权说明](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)
|
||||
|
||||
## PHP 扩展包开发
|
||||
|
||||
> 想知道如何从零开始构建 PHP 扩展包?
|
||||
>
|
||||
> 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package)
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
34
vendor/overtrue/socialite/composer.json
vendored
Normal file
34
vendor/overtrue/socialite/composer.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "overtrue/socialite",
|
||||
"description": "A collection of OAuth 2 packages that extracts from laravel/socialite.",
|
||||
"keywords": [
|
||||
"OAuth",
|
||||
"social",
|
||||
"login",
|
||||
"Weibo",
|
||||
"WeChat",
|
||||
"QQ"
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Overtrue\\Socialite\\": "src/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6",
|
||||
"guzzlehttp/guzzle": "^5.0|^6.0|^7.0",
|
||||
"symfony/http-foundation": "^2.7|^3.0|^4.0|^5.0",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~1.2",
|
||||
"phpunit/phpunit": "^6.0|^7.0|^8.0|^9.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "overtrue",
|
||||
"email": "anzhengchao@gmail.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
18
vendor/overtrue/socialite/phpunit.xml
vendored
Normal file
18
vendor/overtrue/socialite/phpunit.xml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Package Test Suite">
|
||||
<directory suffix=".php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
84
vendor/overtrue/socialite/src/AccessToken.php
vendored
Normal file
84
vendor/overtrue/socialite/src/AccessToken.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
use ArrayAccess;
|
||||
use InvalidArgumentException;
|
||||
use JsonSerializable;
|
||||
|
||||
/**
|
||||
* Class AccessToken.
|
||||
*/
|
||||
class AccessToken implements AccessTokenInterface, ArrayAccess, JsonSerializable
|
||||
{
|
||||
use HasAttributes;
|
||||
|
||||
/**
|
||||
* AccessToken constructor.
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes)
|
||||
{
|
||||
if (empty($attributes['access_token'])) {
|
||||
throw new InvalidArgumentException('The key "access_token" could not be empty.');
|
||||
}
|
||||
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the access token string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
return $this->getAttribute('access_token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the refresh token string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRefreshToken()
|
||||
{
|
||||
return $this->getAttribute('refresh_token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set refresh token into this object.
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
public function setRefreshToken($token)
|
||||
{
|
||||
$this->setAttribute('refresh_token', $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return strval($this->getAttribute('access_token', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->getToken();
|
||||
}
|
||||
}
|
||||
25
vendor/overtrue/socialite/src/AccessTokenInterface.php
vendored
Normal file
25
vendor/overtrue/socialite/src/AccessTokenInterface.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
/**
|
||||
* Interface AccessTokenInterface.
|
||||
*/
|
||||
interface AccessTokenInterface
|
||||
{
|
||||
/**
|
||||
* Return the access token string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getToken();
|
||||
}
|
||||
35
vendor/overtrue/socialite/src/AuthorizeFailedException.php
vendored
Normal file
35
vendor/overtrue/socialite/src/AuthorizeFailedException.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
class AuthorizeFailedException extends \RuntimeException
|
||||
{
|
||||
/**
|
||||
* Response body.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $body;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $body
|
||||
*/
|
||||
public function __construct($message, $body)
|
||||
{
|
||||
parent::__construct($message, -1);
|
||||
|
||||
$this->body = $body;
|
||||
}
|
||||
}
|
||||
180
vendor/overtrue/socialite/src/Config.php
vendored
Normal file
180
vendor/overtrue/socialite/src/Config.php
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
use ArrayAccess;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class Config.
|
||||
*/
|
||||
class Config implements ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Config constructor.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item from an array using "dot" notation.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$config = $this->config;
|
||||
|
||||
if (is_null($key)) {
|
||||
return $config;
|
||||
}
|
||||
if (isset($config[$key])) {
|
||||
return $config[$key];
|
||||
}
|
||||
foreach (explode('.', $key) as $segment) {
|
||||
if (!is_array($config) || !array_key_exists($segment, $config)) {
|
||||
return $default;
|
||||
}
|
||||
$config = $config[$segment];
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an array item to a given value using "dot" notation.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function set($key, $value)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
throw new InvalidArgumentException('Invalid config key.');
|
||||
}
|
||||
|
||||
$keys = explode('.', $key);
|
||||
$config = &$this->config;
|
||||
|
||||
while (count($keys) > 1) {
|
||||
$key = array_shift($keys);
|
||||
if (!isset($config[$key]) || !is_array($config[$key])) {
|
||||
$config[$key] = [];
|
||||
}
|
||||
$config = &$config[$key];
|
||||
}
|
||||
|
||||
$config[array_shift($keys)] = $value;
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given configuration value exists.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return (bool) $this->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists.
|
||||
*
|
||||
* @see http://php.net/manual/en/arrayaccess.offsetexists.php
|
||||
*
|
||||
* @param mixed $offset <p>
|
||||
* An offset to check for.
|
||||
* </p>
|
||||
*
|
||||
* @return bool true on success or false on failure.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value will be casted to boolean if non-boolean was returned
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve.
|
||||
*
|
||||
* @see http://php.net/manual/en/arrayaccess.offsetget.php
|
||||
*
|
||||
* @param mixed $offset <p>
|
||||
* The offset to retrieve.
|
||||
* </p>
|
||||
*
|
||||
* @return mixed Can return all value types
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set.
|
||||
*
|
||||
* @see http://php.net/manual/en/arrayaccess.offsetset.php
|
||||
*
|
||||
* @param mixed $offset <p>
|
||||
* The offset to assign the value to.
|
||||
* </p>
|
||||
* @param mixed $value <p>
|
||||
* The value to set.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset.
|
||||
*
|
||||
* @see http://php.net/manual/en/arrayaccess.offsetunset.php
|
||||
*
|
||||
* @param mixed $offset <p>
|
||||
* The offset to unset.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->set($offset, null);
|
||||
}
|
||||
}
|
||||
27
vendor/overtrue/socialite/src/FactoryInterface.php
vendored
Normal file
27
vendor/overtrue/socialite/src/FactoryInterface.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
/**
|
||||
* Interface FactoryInterface.
|
||||
*/
|
||||
interface FactoryInterface
|
||||
{
|
||||
/**
|
||||
* Get an OAuth provider implementation.
|
||||
*
|
||||
* @param string $driver
|
||||
*
|
||||
* @return \Overtrue\Socialite\ProviderInterface
|
||||
*/
|
||||
public function driver($driver);
|
||||
}
|
||||
135
vendor/overtrue/socialite/src/HasAttributes.php
vendored
Normal file
135
vendor/overtrue/socialite/src/HasAttributes.php
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
/**
|
||||
* Trait HasAttributes.
|
||||
*/
|
||||
trait HasAttributes
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* Return the attributes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the extra attribute.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute($name, $default = null)
|
||||
{
|
||||
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set extra attributes.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttribute($name, $value)
|
||||
{
|
||||
$this->attributes[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the given array onto the user's properties.
|
||||
*
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(array $attributes)
|
||||
{
|
||||
$this->attributes = array_merge($this->attributes, $attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->getAttribute($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->setAttribute($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->attributes[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __get($property)
|
||||
{
|
||||
return $this->getAttribute($property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->getAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return JSON.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJSON()
|
||||
{
|
||||
return json_encode($this->getAttributes(), JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
||||
16
vendor/overtrue/socialite/src/InvalidArgumentException.php
vendored
Normal file
16
vendor/overtrue/socialite/src/InvalidArgumentException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
}
|
||||
16
vendor/overtrue/socialite/src/InvalidStateException.php
vendored
Normal file
16
vendor/overtrue/socialite/src/InvalidStateException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
class InvalidStateException extends \InvalidArgumentException
|
||||
{
|
||||
}
|
||||
31
vendor/overtrue/socialite/src/ProviderInterface.php
vendored
Normal file
31
vendor/overtrue/socialite/src/ProviderInterface.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
interface ProviderInterface
|
||||
{
|
||||
/**
|
||||
* Redirect the user to the authentication page for the provider.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
*/
|
||||
public function redirect();
|
||||
|
||||
/**
|
||||
* Get the User instance for the authenticated user.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return \Overtrue\Socialite\User
|
||||
*/
|
||||
public function user(AccessTokenInterface $token = null);
|
||||
}
|
||||
585
vendor/overtrue/socialite/src/Providers/AbstractProvider.php
vendored
Normal file
585
vendor/overtrue/socialite/src/Providers/AbstractProvider.php
vendored
Normal file
@@ -0,0 +1,585 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use Overtrue\Socialite\AccessToken;
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\AuthorizeFailedException;
|
||||
use Overtrue\Socialite\Config;
|
||||
use Overtrue\Socialite\InvalidStateException;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class AbstractProvider.
|
||||
*/
|
||||
abstract class AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* Provider name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The HTTP request instance.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Driver config.
|
||||
*
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The client ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $clientId;
|
||||
|
||||
/**
|
||||
* The client secret.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $clientSecret;
|
||||
|
||||
/**
|
||||
* @var \Overtrue\Socialite\AccessTokenInterface
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* The redirect URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectUrl;
|
||||
|
||||
/**
|
||||
* The custom parameters to be sent with the request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $parameters = [];
|
||||
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [];
|
||||
|
||||
/**
|
||||
* The separating character for the requested scopes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scopeSeparator = ',';
|
||||
|
||||
/**
|
||||
* The type of the encoding in the query.
|
||||
*
|
||||
* @var int Can be either PHP_QUERY_RFC3986 or PHP_QUERY_RFC1738
|
||||
*/
|
||||
protected $encodingType = PHP_QUERY_RFC1738;
|
||||
|
||||
/**
|
||||
* Indicates if the session state should be utilized.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $stateless = false;
|
||||
|
||||
/**
|
||||
* The options for guzzle\client.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $guzzleOptions = ['http_errors' => false];
|
||||
|
||||
/**
|
||||
* Create a new provider instance.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(Request $request, $config)
|
||||
{
|
||||
// 兼容处理
|
||||
if (!\is_array($config)) {
|
||||
$config = [
|
||||
'client_id' => \func_get_arg(1),
|
||||
'client_secret' => \func_get_arg(2),
|
||||
'redirect' => \func_get_arg(3) ?: null,
|
||||
];
|
||||
}
|
||||
$this->config = new Config($config);
|
||||
$this->request = $request;
|
||||
$this->clientId = $config['client_id'];
|
||||
$this->clientSecret = $config['client_secret'];
|
||||
$this->redirectUrl = isset($config['redirect']) ? $config['redirect'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authentication URL for the provider.
|
||||
*
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getAuthUrl($state);
|
||||
|
||||
/**
|
||||
* Get the token URL for the provider.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getTokenUrl();
|
||||
|
||||
/**
|
||||
* Get the raw user for the given access token.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function getUserByToken(AccessTokenInterface $token);
|
||||
|
||||
/**
|
||||
* Map the raw user array to a Socialite User instance.
|
||||
*
|
||||
* @param array $user
|
||||
*
|
||||
* @return \Overtrue\Socialite\User
|
||||
*/
|
||||
abstract protected function mapUserToObject(array $user);
|
||||
|
||||
/**
|
||||
* Redirect the user of the application to the provider's authentication screen.
|
||||
*
|
||||
* @param string $redirectUrl
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
*/
|
||||
public function redirect($redirectUrl = null)
|
||||
{
|
||||
$state = null;
|
||||
|
||||
if (!is_null($redirectUrl)) {
|
||||
$this->redirectUrl = $redirectUrl;
|
||||
}
|
||||
|
||||
if ($this->usesState()) {
|
||||
$state = $this->makeState();
|
||||
}
|
||||
|
||||
return new RedirectResponse($this->getAuthUrl($state));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function user(AccessTokenInterface $token = null)
|
||||
{
|
||||
if (is_null($token) && $this->hasInvalidState()) {
|
||||
throw new InvalidStateException();
|
||||
}
|
||||
|
||||
$token = $token ?: $this->getAccessToken($this->getCode());
|
||||
|
||||
$user = $this->getUserByToken($token);
|
||||
|
||||
$user = $this->mapUserToObject($user)->merge(['original' => $user]);
|
||||
|
||||
return $user->setToken($token)->setProviderName($this->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set redirect url.
|
||||
*
|
||||
* @param string $redirectUrl
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRedirectUrl($redirectUrl)
|
||||
{
|
||||
$this->redirectUrl = $redirectUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set redirect url.
|
||||
*
|
||||
* @param string $redirectUrl
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withRedirectUrl($redirectUrl)
|
||||
{
|
||||
$this->redirectUrl = $redirectUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the redirect url.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRedirectUrl()
|
||||
{
|
||||
return $this->redirectUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $accessToken
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAccessToken(AccessTokenInterface $accessToken)
|
||||
{
|
||||
$this->accessToken = $accessToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token for the given code.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessTokenInterface
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
if ($this->accessToken) {
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
$guzzleVersion = \defined(ClientInterface::class.'::VERSION') ? \constant(ClientInterface::class.'::VERSION') : 7;
|
||||
|
||||
$postKey = (1 === version_compare($guzzleVersion, '6')) ? 'form_params' : 'body';
|
||||
|
||||
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
$postKey => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the scopes of the requested access.
|
||||
*
|
||||
* @param array $scopes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function scopes(array $scopes)
|
||||
{
|
||||
$this->scopes = $scopes;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request instance.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRequest(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request instance.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the provider should operate as stateless.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function stateless()
|
||||
{
|
||||
$this->stateless = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the custom parameters of the request.
|
||||
*
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function with(array $parameters)
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ReflectionException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
if (empty($this->name)) {
|
||||
$this->name = strstr((new \ReflectionClass(get_class($this)))->getShortName(), 'Provider', true);
|
||||
}
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authentication URL for the provider.
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildAuthUrlFromBase($url, $state)
|
||||
{
|
||||
return $url.'?'.http_build_query($this->getCodeFields($state), '', '&', $this->encodingType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the GET parameters for the code request.
|
||||
*
|
||||
* @param string|null $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getCodeFields($state = null)
|
||||
{
|
||||
$fields = array_merge([
|
||||
'client_id' => $this->config['client_id'],
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
|
||||
'response_type' => 'code',
|
||||
], $this->parameters);
|
||||
|
||||
if ($this->usesState()) {
|
||||
$fields['state'] = $state;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given scopes.
|
||||
*
|
||||
* @param array $scopes
|
||||
* @param string $scopeSeparator
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function formatScopes(array $scopes, $scopeSeparator)
|
||||
{
|
||||
return implode($scopeSeparator, $scopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current request / session has a mismatching "state".
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasInvalidState()
|
||||
{
|
||||
if ($this->isStateless()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$state = $this->request->getSession()->get('state');
|
||||
|
||||
return !(strlen($state) > 0 && $this->request->get('state') === $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the POST fields for the token request.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return [
|
||||
'client_id' => $this->getConfig()->get('client_id'),
|
||||
'client_secret' => $this->getConfig()->get('client_secret'),
|
||||
'code' => $code,
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token from the token response body.
|
||||
*
|
||||
* @param \Psr\Http\Message\StreamInterface|array $body
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessTokenInterface
|
||||
*/
|
||||
protected function parseAccessToken($body)
|
||||
{
|
||||
if (!is_array($body)) {
|
||||
$body = json_decode($body, true);
|
||||
}
|
||||
|
||||
if (empty($body['access_token'])) {
|
||||
throw new AuthorizeFailedException('Authorize Failed: '.json_encode($body, JSON_UNESCAPED_UNICODE), $body);
|
||||
}
|
||||
|
||||
return new AccessToken($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the code from the request.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getCode()
|
||||
{
|
||||
return $this->request->get('code');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a fresh instance of the Guzzle HTTP client.
|
||||
*
|
||||
* @return \GuzzleHttp\Client
|
||||
*/
|
||||
protected function getHttpClient()
|
||||
{
|
||||
return new Client(self::$guzzleOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options for Guzzle HTTP client.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function setGuzzleOptions($config = [])
|
||||
{
|
||||
return self::$guzzleOptions = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the provider is operating with state.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function usesState()
|
||||
{
|
||||
return !$this->stateless;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the provider is operating as stateless.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isStateless()
|
||||
{
|
||||
return !$this->request->hasSession() || $this->stateless;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array item by key.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function arrayItem(array $array, $key, $default = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
return $array;
|
||||
}
|
||||
|
||||
if (isset($array[$key])) {
|
||||
return $array[$key];
|
||||
}
|
||||
|
||||
foreach (explode('.', $key) as $segment) {
|
||||
if (!is_array($array) || !array_key_exists($segment, $array)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
$array = $array[$segment];
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put state to session storage and return it.
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
protected function makeState()
|
||||
{
|
||||
if (!$this->request->hasSession()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$state = sha1(uniqid(mt_rand(1, 1000000), true));
|
||||
$session = $this->request->getSession();
|
||||
|
||||
if (is_callable([$session, 'put'])) {
|
||||
$session->put('state', $state);
|
||||
} elseif (is_callable([$session, 'set'])) {
|
||||
$session->set('state', $state);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
134
vendor/overtrue/socialite/src/Providers/BaiduProvider.php
vendored
Normal file
134
vendor/overtrue/socialite/src/Providers/BaiduProvider.php
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class BaiduProvider.
|
||||
*
|
||||
* @see https://developer.baidu.com/wiki/index.php?title=docs/oauth [OAuth 2.0 授权机制说明]
|
||||
*/
|
||||
class BaiduProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The base url of Weibo API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = 'https://openapi.baidu.com';
|
||||
|
||||
/**
|
||||
* The API version for the request.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version = '2.0';
|
||||
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [''];
|
||||
|
||||
/**
|
||||
* The uid of user authorized.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $uid;
|
||||
|
||||
protected $display = 'popup';
|
||||
|
||||
/**
|
||||
* Get the authentication URL for the provider.
|
||||
*
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase($this->baseUrl.'/oauth/'.$this->version.'/authorize', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getCodeFields($state = null)
|
||||
{
|
||||
return array_merge([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->getConfig()->get('client_id'),
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
|
||||
'display' => $this->display,
|
||||
], $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token URL for the provider.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return $this->baseUrl.'/oauth/'.$this->version.'/token';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Post fields for the token request.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw user for the given access token.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$response = $this->getHttpClient()->get($this->baseUrl.'/rest/'.$this->version.'/passport/users/getInfo', [
|
||||
'query' => [
|
||||
'access_token' => $token->getToken(),
|
||||
],
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the raw user array to a Socialite User instance.
|
||||
*
|
||||
* @param array $user
|
||||
*
|
||||
* @return \Overtrue\Socialite\User
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
$realname = $this->arrayItem($user, 'realname');
|
||||
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'userid'),
|
||||
'nickname' => empty($realname) ? '' : $realname,
|
||||
'name' => $this->arrayItem($user, 'username'),
|
||||
'email' => '',
|
||||
'avatar' => $this->arrayItem($user, 'portrait'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
169
vendor/overtrue/socialite/src/Providers/DouYinProvider.php
vendored
Normal file
169
vendor/overtrue/socialite/src/Providers/DouYinProvider.php
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessToken;
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class DouYinProvider.
|
||||
*
|
||||
* @author haoliang@qiyuankeji.vip
|
||||
*
|
||||
* @see http://open.douyin.com/platform
|
||||
*/
|
||||
class DouYinProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* 抖音接口域名.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = 'https://open.douyin.com';
|
||||
|
||||
/**
|
||||
* 应用授权作用域.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = ['user_info'];
|
||||
|
||||
/**
|
||||
* 获取登录页面地址.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase($this->baseUrl.'/platform/oauth/connect', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权码接口参数.
|
||||
*
|
||||
* @param string|null $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCodeFields($state = null)
|
||||
{
|
||||
$fields = [
|
||||
'client_key' => $this->getConfig()->get('client_id'),
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
|
||||
'response_type' => 'code',
|
||||
];
|
||||
|
||||
if ($this->usesState()) {
|
||||
$fields['state'] = $state;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access_token地址.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return $this->baseUrl.'/oauth/access_token';
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过code获取access_token.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$response = $this->getHttpClient()->get($this->getTokenUrl(), [
|
||||
'query' => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody()->getContents());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取access_token接口参数.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return [
|
||||
'client_key' => $this->getConfig()->get('client_id'),
|
||||
'client_secret' => $this->getConfig()->get('client_secret'),
|
||||
'code' => $code,
|
||||
'grant_type' => 'authorization_code',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化token.
|
||||
*
|
||||
* @param \Psr\Http\Message\StreamInterface|array $body
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessTokenInterface
|
||||
*/
|
||||
protected function parseAccessToken($body)
|
||||
{
|
||||
if (!is_array($body)) {
|
||||
$body = json_decode($body, true);
|
||||
}
|
||||
|
||||
if (empty($body['data']['access_token'])) {
|
||||
throw new AuthorizeFailedException('Authorize Failed: '.json_encode($body, JSON_UNESCAPED_UNICODE), $body);
|
||||
}
|
||||
|
||||
return new AccessToken($body['data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过token 获取用户信息.
|
||||
*
|
||||
* @param AccessTokenInterface $token
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$userUrl = $this->baseUrl.'/oauth/userinfo/';
|
||||
|
||||
$response = $this->getHttpClient()->get(
|
||||
$userUrl,
|
||||
[
|
||||
'query' => [
|
||||
'access_token' => $token->getToken(),
|
||||
'open_id' => $token['open_id'],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化用户信息.
|
||||
*
|
||||
* @param array $user
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'open_id'),
|
||||
'username' => $this->arrayItem($user, 'nickname'),
|
||||
'nickname' => $this->arrayItem($user, 'nickname'),
|
||||
'avatar' => $this->arrayItem($user, 'avatar'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
88
vendor/overtrue/socialite/src/Providers/DoubanProvider.php
vendored
Normal file
88
vendor/overtrue/socialite/src/Providers/DoubanProvider.php
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class DoubanProvider.
|
||||
*
|
||||
* @see http://developers.douban.com/wiki/?title=oauth2 [使用 OAuth 2.0 访问豆瓣 API]
|
||||
*/
|
||||
class DoubanProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase('https://www.douban.com/service/auth2/auth', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return 'https://www.douban.com/service/auth2/token';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$response = $this->getHttpClient()->get('https://api.douban.com/v2/user/~me', [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.$token->getToken(),
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody()->getContents(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'id'),
|
||||
'nickname' => $this->arrayItem($user, 'name'),
|
||||
'name' => $this->arrayItem($user, 'name'),
|
||||
'avatar' => $this->arrayItem($user, 'large_avatar'),
|
||||
'email' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
|
||||
'form_params' => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody()->getContents());
|
||||
}
|
||||
}
|
||||
168
vendor/overtrue/socialite/src/Providers/FacebookProvider.php
vendored
Normal file
168
vendor/overtrue/socialite/src/Providers/FacebookProvider.php
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class FacebookProvider.
|
||||
*
|
||||
* @see https://developers.facebook.com/docs/graph-api [Facebook - Graph API]
|
||||
*/
|
||||
class FacebookProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The base Facebook Graph URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $graphUrl = 'https://graph.facebook.com';
|
||||
|
||||
/**
|
||||
* The Graph API version for the request.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version = 'v3.3';
|
||||
|
||||
/**
|
||||
* The user fields being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fields = ['first_name', 'last_name', 'email', 'gender', 'verified'];
|
||||
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = ['email'];
|
||||
|
||||
/**
|
||||
* Display the dialog in a popup view.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $popup = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase('https://www.facebook.com/'.$this->version.'/dialog/oauth', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return $this->graphUrl.'/oauth/access_token';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token for the given code.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$response = $this->getHttpClient()->get($this->getTokenUrl(), [
|
||||
'query' => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$appSecretProof = hash_hmac('sha256', $token->getToken(), $this->getConfig()->get('client_secret'));
|
||||
|
||||
$response = $this->getHttpClient()->get($this->graphUrl.'/'.$this->version.'/me?access_token='.$token.'&appsecret_proof='.$appSecretProof.'&fields='.implode(',', $this->fields), [
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
$userId = $this->arrayItem($user, 'id');
|
||||
$avatarUrl = $this->graphUrl.'/'.$this->version.'/'.$userId.'/picture';
|
||||
|
||||
$firstName = $this->arrayItem($user, 'first_name');
|
||||
$lastName = $this->arrayItem($user, 'last_name');
|
||||
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'id'),
|
||||
'nickname' => null,
|
||||
'name' => $firstName.' '.$lastName,
|
||||
'email' => $this->arrayItem($user, 'email'),
|
||||
'avatar' => $userId ? $avatarUrl.'?type=normal' : null,
|
||||
'avatar_original' => $userId ? $avatarUrl.'?width=1920' : null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCodeFields($state = null)
|
||||
{
|
||||
$fields = parent::getCodeFields($state);
|
||||
|
||||
if ($this->popup) {
|
||||
$fields['display'] = 'popup';
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user fields to request from Facebook.
|
||||
*
|
||||
* @param array $fields
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fields(array $fields)
|
||||
{
|
||||
$this->fields = $fields;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dialog to be displayed as a popup.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function asPopup()
|
||||
{
|
||||
$this->popup = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
192
vendor/overtrue/socialite/src/Providers/FeiShuProvider.php
vendored
Normal file
192
vendor/overtrue/socialite/src/Providers/FeiShuProvider.php
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessToken;
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\AuthorizeFailedException;
|
||||
use Overtrue\Socialite\InvalidStateException;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class FeiShuProvider.
|
||||
*
|
||||
* @author qijian.song@show.world
|
||||
*
|
||||
* @see https://open.feishu.cn/
|
||||
*/
|
||||
class FeiShuProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* 飞书接口域名.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = 'https://open.feishu.cn';
|
||||
|
||||
/**
|
||||
* 应用授权作用域.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = ['user_info'];
|
||||
|
||||
/**
|
||||
* 获取登录页面地址.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase($this->baseUrl.'/open-apis/authen/v1/index', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权码接口参数.
|
||||
*
|
||||
* @param string|null $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getCodeFields($state = null)
|
||||
{
|
||||
$fields = [
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
'app_id' => $this->getConfig()->get('client_id'),
|
||||
];
|
||||
|
||||
if ($this->usesState()) {
|
||||
$fields['state'] = $state;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 app_access_token 地址.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return $this->baseUrl.'/open-apis/auth/v3/app_access_token/internal';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 app_access_token.
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function getAccessToken($code = '')
|
||||
{
|
||||
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'json' => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody()->getContents());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 app_access_token 接口参数.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return [
|
||||
'app_id' => $this->getConfig()->get('client_id'),
|
||||
'app_secret' => $this->getConfig()->get('client_secret'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化 token.
|
||||
*
|
||||
* @param \Psr\Http\Message\StreamInterface|array $body
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessTokenInterface
|
||||
*/
|
||||
protected function parseAccessToken($body)
|
||||
{
|
||||
if (!is_array($body)) {
|
||||
$body = json_decode($body, true);
|
||||
}
|
||||
|
||||
if (empty($body['app_access_token'])) {
|
||||
throw new AuthorizeFailedException('Authorize Failed: '.json_encode($body, JSON_UNESCAPED_UNICODE), $body);
|
||||
}
|
||||
$data['access_token'] = $body['app_access_token'];
|
||||
|
||||
return new AccessToken($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息.
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function user(AccessTokenInterface $token = null)
|
||||
{
|
||||
if (is_null($token) && $this->hasInvalidState()) {
|
||||
throw new InvalidStateException();
|
||||
}
|
||||
|
||||
$token = $token ?: $this->getAccessToken();
|
||||
|
||||
$user = $this->getUserByToken($token, $this->getCode());
|
||||
$user = $this->mapUserToObject($user)->merge(['original' => $user]);
|
||||
|
||||
return $user->setToken($token)->setProviderName($this->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 token 获取用户信息.
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$userUrl = $this->baseUrl.'/open-apis/authen/v1/access_token';
|
||||
|
||||
$response = $this->getHttpClient()->post(
|
||||
$userUrl,
|
||||
[
|
||||
'json' => [
|
||||
'app_access_token' => $token->getToken(),
|
||||
'code' => $this->getCode(),
|
||||
'grant_type' => 'authorization_code',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$result = json_decode($response->getBody(), true);
|
||||
|
||||
return $result['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化用户信息.
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'open_id'),
|
||||
'username' => $this->arrayItem($user, 'name'),
|
||||
'nickname' => $this->arrayItem($user, 'name'),
|
||||
'avatar' => $this->arrayItem($user, 'avatar_url'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
126
vendor/overtrue/socialite/src/Providers/GitHubProvider.php
vendored
Normal file
126
vendor/overtrue/socialite/src/Providers/GitHubProvider.php
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Exception;
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class GitHubProvider.
|
||||
*/
|
||||
class GitHubProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = ['user:email'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase('https://github.com/login/oauth/authorize', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return 'https://github.com/login/oauth/access_token';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$userUrl = 'https://api.github.com/user';
|
||||
|
||||
$response = $this->getHttpClient()->get(
|
||||
$userUrl,
|
||||
$this->createAuthorizationHeaders($token)
|
||||
);
|
||||
|
||||
$user = json_decode($response->getBody(), true);
|
||||
|
||||
if (in_array('user:email', $this->scopes)) {
|
||||
$user['email'] = $this->getEmailByToken($token);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the email for the given access token.
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getEmailByToken($token)
|
||||
{
|
||||
$emailsUrl = 'https://api.github.com/user/emails';
|
||||
|
||||
try {
|
||||
$response = $this->getHttpClient()->get(
|
||||
$emailsUrl,
|
||||
$this->createAuthorizationHeaders($token)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (json_decode($response->getBody(), true) as $email) {
|
||||
if ($email['primary'] && $email['verified']) {
|
||||
return $email['email'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'id'),
|
||||
'username' => $this->arrayItem($user, 'login'),
|
||||
'nickname' => $this->arrayItem($user, 'login'),
|
||||
'name' => $this->arrayItem($user, 'name'),
|
||||
'email' => $this->arrayItem($user, 'email'),
|
||||
'avatar' => $this->arrayItem($user, 'avatar_url'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default options for an HTTP request.
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function createAuthorizationHeaders(string $token)
|
||||
{
|
||||
return [
|
||||
'headers' => [
|
||||
'Accept' => 'application/vnd.github.v3+json',
|
||||
'Authorization' => sprintf('token %s', $token),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
119
vendor/overtrue/socialite/src/Providers/GoogleProvider.php
vendored
Normal file
119
vendor/overtrue/socialite/src/Providers/GoogleProvider.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class GoogleProvider.
|
||||
*
|
||||
* @see https://developers.google.com/identity/protocols/OpenIDConnect [OpenID Connect]
|
||||
*/
|
||||
class GoogleProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The separating character for the requested scopes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scopeSeparator = ' ';
|
||||
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = [
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
'https://www.googleapis.com/auth/userinfo.profile',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase('https://accounts.google.com/o/oauth2/v2/auth', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return 'https://www.googleapis.com/oauth2/v4/token';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token for the given code.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$guzzleVersion = \defined(ClientInterface::class.'::VERSION') ? \constant(ClientInterface::class.'::VERSION') : 7;
|
||||
$postKey = (1 === version_compare($guzzleVersion, '6')) ? 'form_params' : 'body';
|
||||
|
||||
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
|
||||
$postKey => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the POST fields for the token request.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$response = $this->getHttpClient()->get('https://www.googleapis.com/userinfo/v2/me', [
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Authorization' => 'Bearer '.$token->getToken(),
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'id'),
|
||||
'username' => $this->arrayItem($user, 'email'),
|
||||
'nickname' => $this->arrayItem($user, 'name'),
|
||||
'name' => $this->arrayItem($user, 'name'),
|
||||
'email' => $this->arrayItem($user, 'email'),
|
||||
'avatar' => $this->arrayItem($user, 'picture'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
181
vendor/overtrue/socialite/src/Providers/LinkedinProvider.php
vendored
Normal file
181
vendor/overtrue/socialite/src/Providers/LinkedinProvider.php
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class LinkedinProvider.
|
||||
*
|
||||
* @see https://developer.linkedin.com/docs/oauth2 [Authenticating with OAuth 2.0]
|
||||
*/
|
||||
class LinkedinProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = ['r_liteprofile', 'r_emailaddress'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase('https://www.linkedin.com/oauth/v2/authorization', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token for the given code.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$response = $this->getHttpClient()
|
||||
->post($this->getTokenUrl(), ['form_params' => $this->getTokenFields($code)]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return 'https://www.linkedin.com/oauth/v2/accessToken';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the POST fields for the token request.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$basicProfile = $this->getBasicProfile($token);
|
||||
$emailAddress = $this->getEmailAddress($token);
|
||||
|
||||
return array_merge($basicProfile, $emailAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the basic profile fields for the user.
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getBasicProfile($token)
|
||||
{
|
||||
$url = 'https://api.linkedin.com/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))';
|
||||
|
||||
$response = $this->getHttpClient()->get($url, [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.$token,
|
||||
'X-RestLi-Protocol-Version' => '2.0.0',
|
||||
],
|
||||
]);
|
||||
|
||||
return (array) json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the email address for the user.
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getEmailAddress($token)
|
||||
{
|
||||
$url = 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))';
|
||||
|
||||
$response = $this->getHttpClient()->get($url, [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.$token,
|
||||
'X-RestLi-Protocol-Version' => '2.0.0',
|
||||
],
|
||||
]);
|
||||
|
||||
return (array) $this->arrayItem(json_decode($response->getBody(), true), 'elements.0.handle~');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
$preferredLocale = $this->arrayItem($user, 'firstName.preferredLocale.language').'_'.$this->arrayItem($user, 'firstName.preferredLocale.country');
|
||||
$firstName = $this->arrayItem($user, 'firstName.localized.'.$preferredLocale);
|
||||
$lastName = $this->arrayItem($user, 'lastName.localized.'.$preferredLocale);
|
||||
$name = $firstName.' '.$lastName;
|
||||
|
||||
$images = (array) $this->arrayItem($user, 'profilePicture.displayImage~.elements', []);
|
||||
$avatars = array_filter($images, function ($image) {
|
||||
return $image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['storageSize']['width'] === 100;
|
||||
});
|
||||
$avatar = array_shift($avatars);
|
||||
$originalAvatars = array_filter($images, function ($image) {
|
||||
return $image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['storageSize']['width'] === 800;
|
||||
});
|
||||
$originalAvatar = array_shift($originalAvatars);
|
||||
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'id'),
|
||||
'nickname' => $name,
|
||||
'name' => $name,
|
||||
'email' => $this->arrayItem($user, 'emailAddress'),
|
||||
'avatar' => $avatar ? $this->arrayItem($avatar, 'identifiers.0.identifier') : null,
|
||||
'avatar_original' => $originalAvatar ? $this->arrayItem($originalAvatar, 'identifiers.0.identifier') : null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user fields to request from LinkedIn.
|
||||
*
|
||||
* @param array $fields
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fields(array $fields)
|
||||
{
|
||||
$this->fields = $fields;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the provider is operating as stateless.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isStateless()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
89
vendor/overtrue/socialite/src/Providers/OutlookProvider.php
vendored
Normal file
89
vendor/overtrue/socialite/src/Providers/OutlookProvider.php
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class OutlookProvider.
|
||||
*/
|
||||
class OutlookProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $scopes = ['User.Read'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $scopeSeparator = ' ';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase('https://login.microsoftonline.com/common/oauth2/v2.0/authorize', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$response = $this->getHttpClient()->get(
|
||||
'https://graph.microsoft.com/v1.0/me',
|
||||
['headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Authorization' => 'Bearer '.$token->getToken(),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
return json_decode($response->getBody()->getContents(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'id'),
|
||||
'nickname' => null,
|
||||
'name' => $this->arrayItem($user, 'displayName'),
|
||||
'email' => $this->arrayItem($user, 'userPrincipalName'),
|
||||
'avatar' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return array_merge(parent::getTokenFields($code), [
|
||||
'grant_type' => 'authorization_code',
|
||||
]);
|
||||
}
|
||||
}
|
||||
206
vendor/overtrue/socialite/src/Providers/QQProvider.php
vendored
Normal file
206
vendor/overtrue/socialite/src/Providers/QQProvider.php
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class QQProvider.
|
||||
*
|
||||
* @see http://wiki.connect.qq.com/oauth2-0%E7%AE%80%E4%BB%8B [QQ - OAuth 2.0 登录QQ]
|
||||
*/
|
||||
class QQProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The base url of QQ API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = 'https://graph.qq.com';
|
||||
|
||||
/**
|
||||
* User openid.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $openId;
|
||||
|
||||
/**
|
||||
* get token(openid) with unionid.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $withUnionId = false;
|
||||
|
||||
/**
|
||||
* User unionid.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $unionId;
|
||||
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = ['get_user_info'];
|
||||
|
||||
/**
|
||||
* The uid of user authorized.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $uid;
|
||||
|
||||
/**
|
||||
* Get the authentication URL for the provider.
|
||||
*
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase($this->baseUrl.'/oauth2.0/authorize', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token URL for the provider.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return $this->baseUrl.'/oauth2.0/token';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Post fields for the token request.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token for the given code.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$response = $this->getHttpClient()->get($this->getTokenUrl(), [
|
||||
'query' => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody()->getContents());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token from the token response body.
|
||||
*
|
||||
* @param string $body
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function parseAccessToken($body)
|
||||
{
|
||||
parse_str($body, $token);
|
||||
|
||||
return parent::parseAccessToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public function withUnionId()
|
||||
{
|
||||
$this->withUnionId = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw user for the given access token.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$url = $this->baseUrl.'/oauth2.0/me?access_token='.$token->getToken();
|
||||
$this->withUnionId && $url .= '&unionid=1';
|
||||
|
||||
$response = $this->getHttpClient()->get($url);
|
||||
|
||||
$me = json_decode($this->removeCallback($response->getBody()->getContents()), true);
|
||||
$this->openId = $me['openid'];
|
||||
$this->unionId = isset($me['unionid']) ? $me['unionid'] : '';
|
||||
|
||||
$queries = [
|
||||
'access_token' => $token->getToken(),
|
||||
'openid' => $this->openId,
|
||||
'oauth_consumer_key' => $this->getConfig()->get('client_id'),
|
||||
];
|
||||
|
||||
$response = $this->getHttpClient()->get($this->baseUrl.'/user/get_user_info?'.http_build_query($queries));
|
||||
|
||||
return json_decode($this->removeCallback($response->getBody()->getContents()), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the raw user array to a Socialite User instance.
|
||||
*
|
||||
* @param array $user
|
||||
*
|
||||
* @return \Overtrue\Socialite\User
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->openId,
|
||||
'unionid' => $this->unionId,
|
||||
'nickname' => $this->arrayItem($user, 'nickname'),
|
||||
'name' => $this->arrayItem($user, 'nickname'),
|
||||
'email' => $this->arrayItem($user, 'email'),
|
||||
'avatar' => $this->arrayItem($user, 'figureurl_qq_2'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the fucking callback parentheses.
|
||||
*
|
||||
* @param string $response
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function removeCallback($response)
|
||||
{
|
||||
if (false !== strpos($response, 'callback')) {
|
||||
$lpos = strpos($response, '(');
|
||||
$rpos = strrpos($response, ')');
|
||||
$response = substr($response, $lpos + 1, $rpos - $lpos - 1);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
242
vendor/overtrue/socialite/src/Providers/TaobaoProvider.php
vendored
Normal file
242
vendor/overtrue/socialite/src/Providers/TaobaoProvider.php
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class TaobaoProvider.
|
||||
*
|
||||
* @author mechono <haodouliu@gmail.com>
|
||||
*
|
||||
* @see https://open.taobao.com/doc.htm?docId=102635&docType=1&source=search [Taobao - OAuth 2.0 授权登录]
|
||||
*/
|
||||
class TaobaoProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The base url of Taobao API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = 'https://oauth.taobao.com';
|
||||
|
||||
/**
|
||||
* Taobao API service URL address.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $gatewayUrl = 'https://eco.taobao.com/router/rest';
|
||||
|
||||
/**
|
||||
* The API version for the request.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version = '2.0';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $format = 'json';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signMethod = 'md5';
|
||||
|
||||
/**
|
||||
* Web 对应 PC 端(淘宝 logo )浏览器页面样式;Tmall 对应天猫的浏览器页面样式;Wap 对应无线端的浏览器页面样式。
|
||||
*/
|
||||
protected $view = 'web';
|
||||
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = ['user_info'];
|
||||
|
||||
/**
|
||||
* Get the authentication URL for the provider.
|
||||
*
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase($this->baseUrl.'/authorize', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权码接口参数.
|
||||
*
|
||||
* @param string|null $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCodeFields($state = null)
|
||||
{
|
||||
$fields = [
|
||||
'client_id' => $this->getConfig()->get('client_id'),
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
'view' => $this->view,
|
||||
'response_type' => 'code',
|
||||
];
|
||||
|
||||
if ($this->usesState()) {
|
||||
$fields['state'] = $state;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token URL for the provider.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return $this->baseUrl.'/token';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Post fields for the token request.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code', 'view' => $this->view];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token for the given code.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
|
||||
'query' => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody()->getContents());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token from the token response body.
|
||||
*
|
||||
* @param string $body
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function parseAccessToken($body)
|
||||
{
|
||||
return parent::parseAccessToken($body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw user for the given access token.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$response = $this->getHttpClient()->post($this->getUserInfoUrl($this->gatewayUrl, $token));
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the raw user array to a Socialite User instance.
|
||||
*
|
||||
* @param array $user
|
||||
*
|
||||
* @return \Overtrue\Socialite\User
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'open_id'),
|
||||
'nickname' => $this->arrayItem($user, 'nick'),
|
||||
'name' => $this->arrayItem($user, 'nick'),
|
||||
'avatar' => $this->arrayItem($user, 'avatar'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateSign($params)
|
||||
{
|
||||
ksort($params);
|
||||
|
||||
$stringToBeSigned = $this->getConfig()->get('client_secret');
|
||||
|
||||
foreach ($params as $k => $v) {
|
||||
if (!is_array($v) && '@' != substr($v, 0, 1)) {
|
||||
$stringToBeSigned .= "$k$v";
|
||||
}
|
||||
}
|
||||
|
||||
$stringToBeSigned .= $this->getConfig()->get('client_secret');
|
||||
|
||||
return strtoupper(md5($stringToBeSigned));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
* @param array $apiFields
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getPublicFields(AccessTokenInterface $token, array $apiFields = [])
|
||||
{
|
||||
$fields = [
|
||||
'app_key' => $this->getConfig()->get('client_id'),
|
||||
'sign_method' => $this->signMethod,
|
||||
'session' => $token->getToken(),
|
||||
'timestamp' => date('Y-m-d H:i:s'),
|
||||
'v' => $this->version,
|
||||
'format' => $this->format,
|
||||
];
|
||||
|
||||
$fields = array_merge($apiFields, $fields);
|
||||
$fields['sign'] = $this->generateSign($fields);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getUserInfoUrl($url, AccessTokenInterface $token)
|
||||
{
|
||||
$apiFields = ['method' => 'taobao.miniapp.userInfo.get'];
|
||||
|
||||
$query = http_build_query($this->getPublicFields($token, $apiFields), '', '&', $this->encodingType);
|
||||
|
||||
return $url.'?'.$query;
|
||||
}
|
||||
}
|
||||
234
vendor/overtrue/socialite/src/Providers/WeChatProvider.php
vendored
Normal file
234
vendor/overtrue/socialite/src/Providers/WeChatProvider.php
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\InvalidArgumentException;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
use Overtrue\Socialite\WeChatComponentInterface;
|
||||
|
||||
/**
|
||||
* Class WeChatProvider.
|
||||
*
|
||||
* @see http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html [WeChat - 公众平台OAuth文档]
|
||||
* @see https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN [网站应用微信登录开发指南]
|
||||
*/
|
||||
class WeChatProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The base url of WeChat API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = 'https://api.weixin.qq.com/sns';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected $openId;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected $scopes = ['snsapi_login'];
|
||||
|
||||
/**
|
||||
* Indicates if the session state should be utilized.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $stateless = true;
|
||||
|
||||
/**
|
||||
* Return country code instead of country name.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $withCountryCode = false;
|
||||
|
||||
/**
|
||||
* @var WeChatComponentInterface
|
||||
*/
|
||||
protected $component;
|
||||
|
||||
/**
|
||||
* Return country code instead of country name.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withCountryCode()
|
||||
{
|
||||
$this->withCountryCode = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* WeChat OpenPlatform 3rd component.
|
||||
*
|
||||
* @param WeChatComponentInterface $component
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function component(WeChatComponentInterface $component)
|
||||
{
|
||||
$this->scopes = ['snsapi_base'];
|
||||
|
||||
$this->component = $component;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
public function getAccessToken($code)
|
||||
{
|
||||
$response = $this->getHttpClient()->get($this->getTokenUrl(), [
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
'query' => $this->getTokenFields($code),
|
||||
]);
|
||||
|
||||
return $this->parseAccessToken($response->getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
$path = 'oauth2/authorize';
|
||||
|
||||
if (in_array('snsapi_login', $this->scopes)) {
|
||||
$path = 'qrconnect';
|
||||
}
|
||||
|
||||
return $this->buildAuthUrlFromBase("https://open.weixin.qq.com/connect/{$path}", $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function buildAuthUrlFromBase($url, $state)
|
||||
{
|
||||
$query = http_build_query($this->getCodeFields($state), '', '&', $this->encodingType);
|
||||
|
||||
return $url.'?'.$query.'#wechat_redirect';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getCodeFields($state = null)
|
||||
{
|
||||
if ($this->component) {
|
||||
$this->with(array_merge($this->parameters, ['component_appid' => $this->component->getAppId()]));
|
||||
}
|
||||
|
||||
return array_merge([
|
||||
'appid' => $this->getConfig()->get('client_id'),
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
'response_type' => 'code',
|
||||
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
|
||||
'state' => $state ?: md5(time()),
|
||||
'connect_redirect' => 1,
|
||||
], $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
if ($this->component) {
|
||||
return $this->baseUrl.'/oauth2/component/access_token';
|
||||
}
|
||||
|
||||
return $this->baseUrl.'/oauth2/access_token';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$scopes = explode(',', $token->getAttribute('scope', ''));
|
||||
|
||||
if (in_array('snsapi_base', $scopes)) {
|
||||
return $token->toArray();
|
||||
}
|
||||
|
||||
if (empty($token['openid'])) {
|
||||
throw new InvalidArgumentException('openid of AccessToken is required.');
|
||||
}
|
||||
|
||||
$language = $this->withCountryCode ? null : (isset($this->parameters['lang']) ? $this->parameters['lang'] : 'zh_CN');
|
||||
|
||||
$response = $this->getHttpClient()->get($this->baseUrl.'/userinfo', [
|
||||
'query' => array_filter([
|
||||
'access_token' => $token->getToken(),
|
||||
'openid' => $token['openid'],
|
||||
'lang' => $language,
|
||||
]),
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'openid'),
|
||||
'name' => $this->arrayItem($user, 'nickname'),
|
||||
'nickname' => $this->arrayItem($user, 'nickname'),
|
||||
'avatar' => $this->arrayItem($user, 'headimgurl'),
|
||||
'email' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}.
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return array_filter([
|
||||
'appid' => $this->getConfig()->get('client_id'),
|
||||
'secret' => $this->getConfig()->get('client_secret'),
|
||||
'component_appid' => $this->component ? $this->component->getAppId() : null,
|
||||
'component_access_token' => $this->component ? $this->component->getToken() : null,
|
||||
'code' => $code,
|
||||
'grant_type' => 'authorization_code',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the fucking callback parentheses.
|
||||
*
|
||||
* @param mixed $response
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function removeCallback($response)
|
||||
{
|
||||
if (false !== strpos($response, 'callback')) {
|
||||
$lpos = strpos($response, '(');
|
||||
$rpos = strrpos($response, ')');
|
||||
$response = substr($response, $lpos + 1, $rpos - $lpos - 1);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
214
vendor/overtrue/socialite/src/Providers/WeWorkProvider.php
vendored
Normal file
214
vendor/overtrue/socialite/src/Providers/WeWorkProvider.php
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class WeWorkProvider.
|
||||
*
|
||||
* @author mingyoung <mingyoungcheung@gmail.com>
|
||||
*/
|
||||
class WeWorkProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $agentId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $detailed = false;
|
||||
|
||||
/**
|
||||
* Set agent id.
|
||||
*
|
||||
* @param string $agentId
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAgentId($agentId)
|
||||
{
|
||||
$this->agentId = $agentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $agentId
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function agent($agentId)
|
||||
{
|
||||
return $this->setAgentId($agentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user details.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function detailed()
|
||||
{
|
||||
$this->detailed = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
// 网页授权登录
|
||||
if (!empty($this->scopes)) {
|
||||
return $this->getOAuthUrl($state);
|
||||
}
|
||||
|
||||
// 第三方网页应用登录(扫码登录)
|
||||
return $this->getQrConnectUrl($state);
|
||||
}
|
||||
|
||||
/**
|
||||
* OAuth url.
|
||||
*
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getOAuthUrl($state)
|
||||
{
|
||||
$queries = [
|
||||
'appid' => $this->getConfig()->get('client_id'),
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
'response_type' => 'code',
|
||||
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
|
||||
'agentid' => $this->agentId,
|
||||
'state' => $state,
|
||||
];
|
||||
|
||||
return sprintf('https://open.weixin.qq.com/connect/oauth2/authorize?%s#wechat_redirect', http_build_query($queries));
|
||||
}
|
||||
|
||||
/**
|
||||
* Qr connect url.
|
||||
*
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getQrConnectUrl($state)
|
||||
{
|
||||
$queries = [
|
||||
'appid' => $this->getConfig()->get('client_id'),
|
||||
'agentid' => $this->agentId,
|
||||
'redirect_uri' => $this->redirectUrl,
|
||||
'state' => $state,
|
||||
];
|
||||
|
||||
return 'https://open.work.weixin.qq.com/wwopen/sso/qrConnect?'.http_build_query($queries);
|
||||
}
|
||||
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$userInfo = $this->getUserInfo($token);
|
||||
|
||||
if ($this->detailed && isset($userInfo['user_ticket'])) {
|
||||
return $this->getUserDetail($token, $userInfo['user_ticket']);
|
||||
}
|
||||
|
||||
$this->detailed = false;
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user base info.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getUserInfo(AccessTokenInterface $token)
|
||||
{
|
||||
$response = $this->getHttpClient()->get('https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo', [
|
||||
'query' => array_filter([
|
||||
'access_token' => $token->getToken(),
|
||||
'code' => $this->getCode(),
|
||||
]),
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user detail info.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
* @param $ticket
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getUserDetail(AccessTokenInterface $token, $ticket)
|
||||
{
|
||||
$response = $this->getHttpClient()->post('https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail', [
|
||||
'query' => [
|
||||
'access_token' => $token->getToken(),
|
||||
],
|
||||
'json' => [
|
||||
'user_ticket' => $ticket,
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $user
|
||||
*
|
||||
* @return \Overtrue\Socialite\User
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
if ($this->detailed) {
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'userid'),
|
||||
'name' => $this->arrayItem($user, 'name'),
|
||||
'avatar' => $this->arrayItem($user, 'avatar'),
|
||||
'email' => $this->arrayItem($user, 'email'),
|
||||
]);
|
||||
}
|
||||
|
||||
return new User(array_filter([
|
||||
'id' => $this->arrayItem($user, 'UserId') ?: $this->arrayItem($user, 'OpenId'),
|
||||
'userId' => $this->arrayItem($user, 'UserId'),
|
||||
'openid' => $this->arrayItem($user, 'OpenId'),
|
||||
'deviceId' => $this->arrayItem($user, 'DeviceId'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
126
vendor/overtrue/socialite/src/Providers/WeiboProvider.php
vendored
Normal file
126
vendor/overtrue/socialite/src/Providers/WeiboProvider.php
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite\Providers;
|
||||
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\ProviderInterface;
|
||||
use Overtrue\Socialite\User;
|
||||
|
||||
/**
|
||||
* Class WeiboProvider.
|
||||
*
|
||||
* @see http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E [OAuth 2.0 授权机制说明]
|
||||
*/
|
||||
class WeiboProvider extends AbstractProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* The base url of Weibo API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl = 'https://api.weibo.com';
|
||||
|
||||
/**
|
||||
* The API version for the request.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version = '2';
|
||||
|
||||
/**
|
||||
* The scopes being requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $scopes = ['email'];
|
||||
|
||||
/**
|
||||
* The uid of user authorized.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $uid;
|
||||
|
||||
/**
|
||||
* Get the authentication URL for the provider.
|
||||
*
|
||||
* @param string $state
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return $this->buildAuthUrlFromBase($this->baseUrl.'/oauth2/authorize', $state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token URL for the provider.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return $this->baseUrl.'/'.$this->version.'/oauth2/access_token';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Post fields for the token request.
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokenFields($code)
|
||||
{
|
||||
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw user for the given access token.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
$response = $this->getHttpClient()->get($this->baseUrl.'/'.$this->version.'/users/show.json', [
|
||||
'query' => [
|
||||
'uid' => $token['uid'],
|
||||
'access_token' => $token->getToken(),
|
||||
],
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
]);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the raw user array to a Socialite User instance.
|
||||
*
|
||||
* @param array $user
|
||||
*
|
||||
* @return \Overtrue\Socialite\User
|
||||
*/
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User([
|
||||
'id' => $this->arrayItem($user, 'id'),
|
||||
'nickname' => $this->arrayItem($user, 'screen_name'),
|
||||
'name' => $this->arrayItem($user, 'name'),
|
||||
'email' => $this->arrayItem($user, 'email'),
|
||||
'avatar' => $this->arrayItem($user, 'avatar_large'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
251
vendor/overtrue/socialite/src/SocialiteManager.php
vendored
Normal file
251
vendor/overtrue/socialite/src/SocialiteManager.php
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
|
||||
/**
|
||||
* Class SocialiteManager.
|
||||
*/
|
||||
class SocialiteManager implements FactoryInterface
|
||||
{
|
||||
/**
|
||||
* The configuration.
|
||||
*
|
||||
* @var \Overtrue\Socialite\Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* The request instance.
|
||||
*
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The registered custom driver creators.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $customCreators = [];
|
||||
|
||||
/**
|
||||
* The initial drivers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $initialDrivers = [
|
||||
'facebook' => 'Facebook',
|
||||
'github' => 'GitHub',
|
||||
'google' => 'Google',
|
||||
'linkedin' => 'Linkedin',
|
||||
'weibo' => 'Weibo',
|
||||
'qq' => 'QQ',
|
||||
'wechat' => 'WeChat',
|
||||
'douban' => 'Douban',
|
||||
'wework' => 'WeWork',
|
||||
'outlook' => 'Outlook',
|
||||
'douyin' => 'DouYin',
|
||||
'taobao' => 'Taobao',
|
||||
'feishu' => 'FeiShu',
|
||||
];
|
||||
|
||||
/**
|
||||
* The array of created "drivers".
|
||||
*
|
||||
* @var ProviderInterface[]
|
||||
*/
|
||||
protected $drivers = [];
|
||||
|
||||
/**
|
||||
* SocialiteManager constructor.
|
||||
*
|
||||
* @param array $config
|
||||
* @param Request|null $request
|
||||
*/
|
||||
public function __construct(array $config, Request $request = null)
|
||||
{
|
||||
$this->config = new Config($config);
|
||||
|
||||
if ($this->config->has('guzzle')) {
|
||||
Providers\AbstractProvider::setGuzzleOptions($this->config->get('guzzle'));
|
||||
}
|
||||
|
||||
if ($request) {
|
||||
$this->setRequest($request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config instance.
|
||||
*
|
||||
* @param \Overtrue\Socialite\Config $config
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function config(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a driver instance.
|
||||
*
|
||||
* @param string $driver
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
public function driver($driver)
|
||||
{
|
||||
$driver = strtolower($driver);
|
||||
|
||||
if (!isset($this->drivers[$driver])) {
|
||||
$this->drivers[$driver] = $this->createDriver($driver);
|
||||
}
|
||||
|
||||
return $this->drivers[$driver];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRequest(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request ?: $this->createDefaultRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new driver instance.
|
||||
*
|
||||
* @param string $driver
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
protected function createDriver($driver)
|
||||
{
|
||||
if (isset($this->customCreators[$driver])) {
|
||||
return $this->callCustomCreator($driver);
|
||||
}
|
||||
|
||||
if (isset($this->initialDrivers[$driver])) {
|
||||
$provider = $this->initialDrivers[$driver];
|
||||
$provider = __NAMESPACE__.'\\Providers\\'.$provider.'Provider';
|
||||
|
||||
return $this->buildProvider($provider, $this->formatConfig($this->config->get($driver)));
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Driver [$driver] not supported.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a custom driver creator.
|
||||
*
|
||||
* @param string $driver
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
protected function callCustomCreator($driver)
|
||||
{
|
||||
return $this->customCreators[$driver]($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default request instance.
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
protected function createDefaultRequest()
|
||||
{
|
||||
$request = Request::createFromGlobals();
|
||||
$session = new Session();
|
||||
|
||||
$request->setSession($session);
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom driver creator Closure.
|
||||
*
|
||||
* @param string $driver
|
||||
* @param \Closure $callback
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function extend($driver, Closure $callback)
|
||||
{
|
||||
$driver = strtolower($driver);
|
||||
|
||||
$this->customCreators[$driver] = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the created "drivers".
|
||||
*
|
||||
* @return ProviderInterface[]
|
||||
*/
|
||||
public function getDrivers()
|
||||
{
|
||||
return $this->drivers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an OAuth 2 provider instance.
|
||||
*
|
||||
* @param string $provider
|
||||
* @param array $config
|
||||
*
|
||||
* @return ProviderInterface
|
||||
*/
|
||||
public function buildProvider($provider, $config)
|
||||
{
|
||||
return new $provider($this->getRequest(), $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the server configuration.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function formatConfig(array $config)
|
||||
{
|
||||
return array_merge([
|
||||
'identifier' => $config['client_id'],
|
||||
'secret' => $config['client_secret'],
|
||||
'callback_uri' => $config['redirect'],
|
||||
], $config);
|
||||
}
|
||||
}
|
||||
204
vendor/overtrue/socialite/src/User.php
vendored
Normal file
204
vendor/overtrue/socialite/src/User.php
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
use ArrayAccess;
|
||||
use JsonSerializable;
|
||||
|
||||
/**
|
||||
* Class User.
|
||||
*/
|
||||
class User implements ArrayAccess, UserInterface, JsonSerializable, \Serializable
|
||||
{
|
||||
use HasAttributes;
|
||||
|
||||
/**
|
||||
* User constructor.
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct(array $attributes)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique identifier for the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->getAttribute('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username for the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->getAttribute('username', $this->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nickname / username for the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNickname()
|
||||
{
|
||||
return $this->getAttribute('nickname');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full name of the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->getAttribute('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the e-mail address of the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->getAttribute('email');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the avatar / image URL for the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAvatar()
|
||||
{
|
||||
return $this->getAttribute('avatar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the token on the user.
|
||||
*
|
||||
* @param \Overtrue\Socialite\AccessTokenInterface $token
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setToken(AccessTokenInterface $token)
|
||||
{
|
||||
$this->setAttribute('token', $token->getToken());
|
||||
$this->setAttribute('access_token', $token->getToken());
|
||||
|
||||
if (\is_callable([$token, 'getRefreshToken'])) {
|
||||
$this->setAttribute('refresh_token', $token->getRefreshToken());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $provider
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProviderName($provider)
|
||||
{
|
||||
$this->setAttribute('provider', $provider);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getProviderName()
|
||||
{
|
||||
return $this->getAttribute('provider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authorized token.
|
||||
*
|
||||
* @return \Overtrue\Socialite\AccessToken
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
return new AccessToken([
|
||||
'access_token' => $this->getAccessToken(),
|
||||
'refresh_token' => $this->getAttribute('refresh_token')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user access token.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessToken()
|
||||
{
|
||||
return $this->getAttribute('token') ?: $this->getAttribute('access_token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user refresh token.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRefreshToken()
|
||||
{
|
||||
return $this->getAttribute('refresh_token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original attributes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOriginal()
|
||||
{
|
||||
return $this->getAttribute('original');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the object.
|
||||
*
|
||||
* @see https://php.net/manual/en/serializable.unserialize.php
|
||||
*
|
||||
* @param string $serialized <p>
|
||||
* The string representation of the object.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
$this->attributes = unserialize($serialized) ?: [];
|
||||
}
|
||||
}
|
||||
53
vendor/overtrue/socialite/src/UserInterface.php
vendored
Normal file
53
vendor/overtrue/socialite/src/UserInterface.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
/**
|
||||
* Interface UserInterface.
|
||||
*/
|
||||
interface UserInterface
|
||||
{
|
||||
/**
|
||||
* Get the unique identifier for the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* Get the nickname / username for the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNickname();
|
||||
|
||||
/**
|
||||
* Get the full name of the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Get the e-mail address of the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail();
|
||||
|
||||
/**
|
||||
* Get the avatar / image URL for the user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAvatar();
|
||||
}
|
||||
32
vendor/overtrue/socialite/src/WeChatComponentInterface.php
vendored
Normal file
32
vendor/overtrue/socialite/src/WeChatComponentInterface.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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 Overtrue\Socialite;
|
||||
|
||||
/**
|
||||
* Interface WeChatComponentInterface.
|
||||
*/
|
||||
interface WeChatComponentInterface
|
||||
{
|
||||
/**
|
||||
* Return the open-platform component app id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAppId();
|
||||
|
||||
/**
|
||||
* Return the open-platform component access token string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getToken();
|
||||
}
|
||||
243
vendor/overtrue/socialite/tests/OAuthTest.php
vendored
Normal file
243
vendor/overtrue/socialite/tests/OAuthTest.php
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
|
||||
use Mockery as m;
|
||||
use Overtrue\Socialite\AccessTokenInterface;
|
||||
use Overtrue\Socialite\Providers\AbstractProvider;
|
||||
use Overtrue\Socialite\User;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class OAuthTest extends TestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
m::close();
|
||||
}
|
||||
|
||||
public function testAbstractProviderBackwardCompatible()
|
||||
{
|
||||
$request = Request::create('foo');
|
||||
$request->setSession($session = m::mock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
|
||||
$session->shouldReceive('put')->once();
|
||||
$provider = new OAuthTwoTestProviderStub($request, 'client_id', 'client_secret', 'redirect');
|
||||
|
||||
$this->assertSame('client_id', $provider->getConfig()['client_id']);
|
||||
$this->assertSame('client_secret', $provider->getConfig()['client_secret']);
|
||||
$this->assertSame('redirect', $provider->getConfig()['redirect']);
|
||||
|
||||
$response = $provider->redirect();
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
|
||||
$this->assertSame('http://auth.url', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public function testRedirectGeneratesTheProperSymfonyRedirectResponse()
|
||||
{
|
||||
$request = Request::create('foo');
|
||||
$request->setSession($session = m::mock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
|
||||
$session->shouldReceive('put')->once();
|
||||
$provider = new OAuthTwoTestProviderStub(
|
||||
$request, [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'redirect',
|
||||
]
|
||||
);
|
||||
$response = $provider->redirect();
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
|
||||
$this->assertSame('http://auth.url', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public function testRedirectUrl()
|
||||
{
|
||||
$request = Request::create('foo', 'GET', ['state' => str_repeat('A', 40), 'code' => 'code']);
|
||||
$request->setSession($session = m::mock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
|
||||
|
||||
$provider = new OAuthTwoTestProviderStub(
|
||||
$request, [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
]
|
||||
);
|
||||
$this->assertNull($provider->getRedirectUrl());
|
||||
|
||||
$provider = new OAuthTwoTestProviderStub(
|
||||
$request, [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'redirect_uri',
|
||||
]
|
||||
);
|
||||
$this->assertSame('redirect_uri', $provider->getRedirectUrl());
|
||||
$provider->setRedirectUrl('overtrue.me');
|
||||
$this->assertSame('overtrue.me', $provider->getRedirectUrl());
|
||||
|
||||
$provider->withRedirectUrl('http://overtrue.me');
|
||||
$this->assertSame('http://overtrue.me', $provider->getRedirectUrl());
|
||||
}
|
||||
|
||||
public function testUserReturnsAUserInstanceForTheAuthenticatedRequest()
|
||||
{
|
||||
$request = Request::create('foo', 'GET', ['state' => str_repeat('A', 40), 'code' => 'code']);
|
||||
$request->setSession($session = m::mock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
|
||||
|
||||
$session->shouldReceive('get')->once()->with('state')->andReturn(str_repeat('A', 40));
|
||||
$provider = new OAuthTwoTestProviderStub(
|
||||
$request, [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'redirect_uri',
|
||||
]
|
||||
);
|
||||
$provider->http = m::mock('StdClass');
|
||||
$provider->http->shouldReceive('post')->once()->with(
|
||||
'http://token.url',
|
||||
[
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
'form_params' => [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'code' => 'code',
|
||||
'redirect_uri' => 'redirect_uri',
|
||||
],
|
||||
]
|
||||
)->andReturn($response = m::mock('StdClass'));
|
||||
$response->shouldReceive('getBody')->once()->andReturn('{"access_token":"access_token"}');
|
||||
$user = $provider->user();
|
||||
|
||||
$this->assertInstanceOf('Overtrue\Socialite\User', $user);
|
||||
$this->assertSame('foo', $user->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Overtrue\Socialite\InvalidStateException
|
||||
*/
|
||||
public function testExceptionIsThrownIfStateIsInvalid()
|
||||
{
|
||||
$request = Request::create('foo', 'GET', ['state' => str_repeat('B', 40), 'code' => 'code']);
|
||||
$request->setSession($session = m::mock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
|
||||
$session->shouldReceive('get')->once()->with('state')->andReturn(str_repeat('A', 40));
|
||||
$provider = new OAuthTwoTestProviderStub(
|
||||
$request, [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'redirect',
|
||||
]
|
||||
);
|
||||
$user = $provider->user();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Overtrue\Socialite\AuthorizeFailedException
|
||||
* @expectedExceptionMessage Authorize Failed: {"error":"scope is invalid"}
|
||||
*/
|
||||
public function testExceptionisThrownIfAuthorizeFailed()
|
||||
{
|
||||
$request = Request::create('foo', 'GET', ['state' => str_repeat('A', 40), 'code' => 'code']);
|
||||
$request->setSession($session = m::mock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
|
||||
$session->shouldReceive('get')->once()->with('state')->andReturn(str_repeat('A', 40));
|
||||
$provider = new OAuthTwoTestProviderStub(
|
||||
$request, [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'redirect_uri',
|
||||
]
|
||||
);
|
||||
$provider->http = m::mock('StdClass');
|
||||
$provider->http->shouldReceive('post')->once()->with(
|
||||
'http://token.url',
|
||||
[
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
'form_params' => [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'code' => 'code',
|
||||
'redirect_uri' => 'redirect_uri',
|
||||
],
|
||||
]
|
||||
)->andReturn($response = m::mock('StdClass'));
|
||||
$response->shouldReceive('getBody')->once()->andReturn('{"error":"scope is invalid"}');
|
||||
$user = $provider->user();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Overtrue\Socialite\InvalidStateException
|
||||
*/
|
||||
public function testExceptionIsThrownIfStateIsNotSet()
|
||||
{
|
||||
$request = Request::create('foo', 'GET', ['state' => 'state', 'code' => 'code']);
|
||||
$request->setSession($session = m::mock('Symfony\Component\HttpFoundation\Session\SessionInterface'));
|
||||
$session->shouldReceive('get')->once()->with('state');
|
||||
$provider = new OAuthTwoTestProviderStub(
|
||||
$request, [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'redirect',
|
||||
]
|
||||
);
|
||||
$user = $provider->user();
|
||||
}
|
||||
|
||||
public function testDriverName()
|
||||
{
|
||||
$request = Request::create('foo', 'GET', ['state' => 'state', 'code' => 'code']);
|
||||
$provider = new OAuthTwoTestProviderStub(
|
||||
$request, [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'redirect',
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertSame('OAuthTwoTest', $provider->getName());
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthTwoTestProviderStub extends AbstractProvider
|
||||
{
|
||||
public $http;
|
||||
|
||||
protected function getAuthUrl($state)
|
||||
{
|
||||
return 'http://auth.url';
|
||||
}
|
||||
|
||||
protected function getTokenUrl()
|
||||
{
|
||||
return 'http://token.url';
|
||||
}
|
||||
|
||||
protected function getUserByToken(AccessTokenInterface $token)
|
||||
{
|
||||
return ['id' => 'foo'];
|
||||
}
|
||||
|
||||
protected function mapUserToObject(array $user)
|
||||
{
|
||||
return new User(['id' => $user['id']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a fresh instance of the Guzzle HTTP client.
|
||||
*
|
||||
* @return \GuzzleHttp\Client
|
||||
*/
|
||||
protected function getHttpClient()
|
||||
{
|
||||
if ($this->http) {
|
||||
return $this->http;
|
||||
}
|
||||
|
||||
return $this->http = m::mock('StdClass');
|
||||
}
|
||||
}
|
||||
60
vendor/overtrue/socialite/tests/Providers/WeWorkProviderTest.php
vendored
Normal file
60
vendor/overtrue/socialite/tests/Providers/WeWorkProviderTest.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
|
||||
use Overtrue\Socialite\Providers\WeWorkProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class WeWorkProviderTest extends TestCase
|
||||
{
|
||||
public function testQrConnect()
|
||||
{
|
||||
$response = (new WeWorkProvider(Request::create('foo'), [
|
||||
'client_id' => 'ww100000a5f2191',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'http://www.oa.com',
|
||||
]))
|
||||
->setAgentId('1000000')
|
||||
->stateless()
|
||||
->redirect();
|
||||
|
||||
$this->assertSame('https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=ww100000a5f2191&agentid=1000000&redirect_uri=http%3A%2F%2Fwww.oa.com', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public function testOAuthWithAgentId()
|
||||
{
|
||||
$response = (new WeWorkProvider(Request::create('foo'), [
|
||||
'client_id' => 'CORPID',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'REDIRECT_URI',
|
||||
]))
|
||||
->scopes(['snsapi_base'])
|
||||
->setAgentId('1000000')
|
||||
->stateless()
|
||||
->redirect();
|
||||
|
||||
$this->assertSame('https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&agentid=1000000#wechat_redirect', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public function testOAuthWithoutAgentId()
|
||||
{
|
||||
$response = (new WeWorkProvider(Request::create('foo'), [
|
||||
'client_id' => 'CORPID',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'REDIRECT_URI',
|
||||
]))
|
||||
->scopes(['snsapi_base'])
|
||||
->stateless()
|
||||
->redirect();
|
||||
|
||||
$this->assertSame('https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base#wechat_redirect', $response->getTargetUrl());
|
||||
}
|
||||
}
|
||||
45
vendor/overtrue/socialite/tests/UserTest.php
vendored
Normal file
45
vendor/overtrue/socialite/tests/UserTest.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
|
||||
use Overtrue\Socialite\AccessToken;
|
||||
use Overtrue\Socialite\User;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserTest extends TestCase
|
||||
{
|
||||
public function testJsonserialize()
|
||||
{
|
||||
$this->assertSame('[]', json_encode(new User([])));
|
||||
|
||||
$this->assertSame('{"token":"mock-token"}', json_encode(new User(['token' => new AccessToken(['access_token' => 'mock-token'])])));
|
||||
}
|
||||
|
||||
public function test_it_can_get_refresh_token()
|
||||
{
|
||||
$user = new User([
|
||||
'access_token' => 'mock-token',
|
||||
'refresh_token' => 'fake_refresh',
|
||||
]);
|
||||
|
||||
// 能通过用 User 对象获取 refresh token
|
||||
$this->assertSame('fake_refresh', $user->getRefreshToken());
|
||||
// json 序列化只有 token 字段
|
||||
$this->assertSame('{"access_token":"mock-token","refresh_token":"fake_refresh"}', json_encode($user));
|
||||
|
||||
$user = new User([]);
|
||||
$user->setToken(new AccessToken([
|
||||
'access_token' => 'mock-token',
|
||||
'refresh_token' => 'fake_refresh',
|
||||
]));
|
||||
$this->assertSame('fake_refresh', $user->getRefreshToken());
|
||||
$this->assertSame('{"token":"mock-token","access_token":"mock-token","refresh_token":"fake_refresh"}', json_encode($user));
|
||||
}
|
||||
}
|
||||
137
vendor/overtrue/socialite/tests/WechatProviderTest.php
vendored
Normal file
137
vendor/overtrue/socialite/tests/WechatProviderTest.php
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/socialite.
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
|
||||
use Overtrue\Socialite\Providers\WeChatProvider as RealWeChatProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class WechatProviderTest extends TestCase
|
||||
{
|
||||
public function testWeChatProviderHasCorrectlyRedirectResponse()
|
||||
{
|
||||
$response = (new WeChatProvider(Request::create('foo'), [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'http://localhost/socialite/callback.php',
|
||||
]))->redirect();
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response);
|
||||
$this->assertStringStartsWith('https://open.weixin.qq.com/connect/qrconnect', $response->getTargetUrl());
|
||||
$this->assertRegExp('/redirect_uri=http%3A%2F%2Flocalhost%2Fsocialite%2Fcallback.php/', $response->getTargetUrl());
|
||||
}
|
||||
|
||||
public function testWeChatProviderTokenUrlAndRequestFields()
|
||||
{
|
||||
$provider = new WeChatProvider(Request::create('foo'), [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => 'client_secret',
|
||||
'redirect' => 'http://localhost/socialite/callback.php',
|
||||
]);
|
||||
|
||||
$this->assertSame('https://api.weixin.qq.com/sns/oauth2/access_token', $provider->tokenUrl());
|
||||
$this->assertSame([
|
||||
'appid' => 'client_id',
|
||||
'secret' => 'client_secret',
|
||||
'code' => 'iloveyou',
|
||||
'grant_type' => 'authorization_code',
|
||||
], $provider->tokenFields('iloveyou'));
|
||||
|
||||
$this->assertSame([
|
||||
'appid' => 'client_id',
|
||||
'redirect_uri' => 'http://localhost/socialite/callback.php',
|
||||
'response_type' => 'code',
|
||||
'scope' => 'snsapi_login',
|
||||
'state' => 'wechat-state',
|
||||
'connect_redirect' => 1,
|
||||
], $provider->codeFields('wechat-state'));
|
||||
}
|
||||
|
||||
public function testOpenPlatformComponent()
|
||||
{
|
||||
$provider = new WeChatProvider(Request::create('foo'), [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => null,
|
||||
'redirect' => 'redirect-url',
|
||||
]);
|
||||
$provider->component(new WeChatComponent());
|
||||
$this->assertSame([
|
||||
'appid' => 'client_id',
|
||||
'redirect_uri' => 'redirect-url',
|
||||
'response_type' => 'code',
|
||||
'scope' => 'snsapi_base',
|
||||
'state' => 'state',
|
||||
'connect_redirect' => 1,
|
||||
'component_appid' => 'component-app-id',
|
||||
], $provider->codeFields('state'));
|
||||
|
||||
$this->assertSame([
|
||||
'appid' => 'client_id',
|
||||
'component_appid' => 'component-app-id',
|
||||
'component_access_token' => 'token',
|
||||
'code' => 'simcode',
|
||||
'grant_type' => 'authorization_code',
|
||||
], $provider->tokenFields('simcode'));
|
||||
|
||||
$this->assertSame('https://api.weixin.qq.com/sns/oauth2/component/access_token', $provider->tokenUrl());
|
||||
}
|
||||
|
||||
public function testOpenPlatformComponentWithCustomParameters()
|
||||
{
|
||||
$provider = new WeChatProvider(Request::create('foo'), [
|
||||
'client_id' => 'client_id',
|
||||
'client_secret' => null,
|
||||
'redirect' => 'redirect-url',
|
||||
]);
|
||||
$provider->component(new WeChatComponent());
|
||||
$provider->with(['foo' => 'bar']);
|
||||
|
||||
$fields = $provider->codeFields('wechat-state');
|
||||
|
||||
$this->assertArrayHasKey('foo', $fields);
|
||||
$this->assertSame('bar', $fields['foo']);
|
||||
}
|
||||
}
|
||||
|
||||
trait ProviderTrait
|
||||
{
|
||||
public function tokenUrl()
|
||||
{
|
||||
return $this->getTokenUrl();
|
||||
}
|
||||
|
||||
public function tokenFields($code)
|
||||
{
|
||||
return $this->getTokenFields($code);
|
||||
}
|
||||
|
||||
public function codeFields($state = null)
|
||||
{
|
||||
return $this->getCodeFields($state);
|
||||
}
|
||||
}
|
||||
|
||||
class WeChatProvider extends RealWeChatProvider
|
||||
{
|
||||
use ProviderTrait;
|
||||
}
|
||||
|
||||
class WeChatComponent implements \Overtrue\Socialite\WeChatComponentInterface
|
||||
{
|
||||
public function getAppId()
|
||||
{
|
||||
return 'component-app-id';
|
||||
}
|
||||
|
||||
public function getToken()
|
||||
{
|
||||
return 'token';
|
||||
}
|
||||
}
|
||||
1401
vendor/overtrue/wechat/CHANGELOG.md
vendored
Normal file
1401
vendor/overtrue/wechat/CHANGELOG.md
vendored
Normal file
File diff suppressed because it is too large
Load Diff
67
vendor/overtrue/wechat/CONTRIBUTING.md
vendored
Normal file
67
vendor/overtrue/wechat/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
# 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 -->
|
||||
22
vendor/overtrue/wechat/LICENSE
vendored
Normal file
22
vendor/overtrue/wechat/LICENSE
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
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.
|
||||
|
||||
92
vendor/overtrue/wechat/README.md
vendored
Normal file
92
vendor/overtrue/wechat/README.md
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
<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.
|
||||
|
||||
[](https://github.com/overtrue/wechat/actions)
|
||||
[](https://github.com/overtrue/wechat/actions)
|
||||
[](https://packagist.org/packages/overtrue/wechat)
|
||||
[](https://packagist.org/packages/overtrue/wechat)
|
||||
[](https://scrutinizer-ci.com/g/overtrue/wechat/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/overtrue/wechat/?branch=master)
|
||||
[](https://packagist.org/packages/overtrue/wechat)
|
||||
[](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
|
||||
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fovertrue%2Fwechat?ref=badge_large)
|
||||
59
vendor/overtrue/wechat/composer.json
vendored
Normal file
59
vendor/overtrue/wechat/composer.json
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
39
vendor/overtrue/wechat/src/BasicService/Application.php
vendored
Normal file
39
vendor/overtrue/wechat/src/BasicService/Application.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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,
|
||||
];
|
||||
}
|
||||
123
vendor/overtrue/wechat/src/BasicService/ContentSecurity/Client.php
vendored
Normal file
123
vendor/overtrue/wechat/src/BasicService/ContentSecurity/Client.php
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
31
vendor/overtrue/wechat/src/BasicService/ContentSecurity/ServiceProvider.php
vendored
Normal file
31
vendor/overtrue/wechat/src/BasicService/ContentSecurity/ServiceProvider.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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);
|
||||
};
|
||||
}
|
||||
}
|
||||
207
vendor/overtrue/wechat/src/BasicService/Jssdk/Client.php
vendored
Normal file
207
vendor/overtrue/wechat/src/BasicService/Jssdk/Client.php
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
<?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×tamp=%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');
|
||||
}
|
||||
}
|
||||
33
vendor/overtrue/wechat/src/BasicService/Jssdk/ServiceProvider.php
vendored
Normal file
33
vendor/overtrue/wechat/src/BasicService/Jssdk/ServiceProvider.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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);
|
||||
};
|
||||
}
|
||||
}
|
||||
212
vendor/overtrue/wechat/src/BasicService/Media/Client.php
vendored
Normal file
212
vendor/overtrue/wechat/src/BasicService/Media/Client.php
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
<?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'));
|
||||
}
|
||||
}
|
||||
44
vendor/overtrue/wechat/src/BasicService/Media/ServiceProvider.php
vendored
Normal file
44
vendor/overtrue/wechat/src/BasicService/Media/ServiceProvider.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?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);
|
||||
};
|
||||
}
|
||||
}
|
||||
120
vendor/overtrue/wechat/src/BasicService/QrCode/Client.php
vendored
Normal file
120
vendor/overtrue/wechat/src/BasicService/QrCode/Client.php
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
31
vendor/overtrue/wechat/src/BasicService/QrCode/ServiceProvider.php
vendored
Normal file
31
vendor/overtrue/wechat/src/BasicService/QrCode/ServiceProvider.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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);
|
||||
};
|
||||
}
|
||||
}
|
||||
47
vendor/overtrue/wechat/src/BasicService/Url/Client.php
vendored
Normal file
47
vendor/overtrue/wechat/src/BasicService/Url/Client.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
31
vendor/overtrue/wechat/src/BasicService/Url/ServiceProvider.php
vendored
Normal file
31
vendor/overtrue/wechat/src/BasicService/Url/ServiceProvider.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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);
|
||||
};
|
||||
}
|
||||
}
|
||||
54
vendor/overtrue/wechat/src/Factory.php
vendored
Normal file
54
vendor/overtrue/wechat/src/Factory.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
282
vendor/overtrue/wechat/src/Kernel/AccessToken.php
vendored
Normal file
282
vendor/overtrue/wechat/src/Kernel/AccessToken.php
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
<?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;
|
||||
}
|
||||
271
vendor/overtrue/wechat/src/Kernel/BaseClient.php
vendored
Normal file
271
vendor/overtrue/wechat/src/Kernel/BaseClient.php
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
<?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));
|
||||
});
|
||||
}
|
||||
}
|
||||
64
vendor/overtrue/wechat/src/Kernel/Clauses/Clause.php
vendored
Normal file
64
vendor/overtrue/wechat/src/Kernel/Clauses/Clause.php
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
vendor/overtrue/wechat/src/Kernel/Config.php
vendored
Normal file
23
vendor/overtrue/wechat/src/Kernel/Config.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
40
vendor/overtrue/wechat/src/Kernel/Contracts/AccessTokenInterface.php
vendored
Normal file
40
vendor/overtrue/wechat/src/Kernel/Contracts/AccessTokenInterface.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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;
|
||||
}
|
||||
29
vendor/overtrue/wechat/src/Kernel/Contracts/Arrayable.php
vendored
Normal file
29
vendor/overtrue/wechat/src/Kernel/Contracts/Arrayable.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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();
|
||||
}
|
||||
25
vendor/overtrue/wechat/src/Kernel/Contracts/EventHandlerInterface.php
vendored
Normal file
25
vendor/overtrue/wechat/src/Kernel/Contracts/EventHandlerInterface.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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);
|
||||
}
|
||||
25
vendor/overtrue/wechat/src/Kernel/Contracts/MediaInterface.php
vendored
Normal file
25
vendor/overtrue/wechat/src/Kernel/Contracts/MediaInterface.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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;
|
||||
}
|
||||
35
vendor/overtrue/wechat/src/Kernel/Contracts/MessageInterface.php
vendored
Normal file
35
vendor/overtrue/wechat/src/Kernel/Contracts/MessageInterface.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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;
|
||||
}
|
||||
35
vendor/overtrue/wechat/src/Kernel/Decorators/FinallyResult.php
vendored
Normal file
35
vendor/overtrue/wechat/src/Kernel/Decorators/FinallyResult.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
35
vendor/overtrue/wechat/src/Kernel/Decorators/TerminateResult.php
vendored
Normal file
35
vendor/overtrue/wechat/src/Kernel/Decorators/TerminateResult.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
219
vendor/overtrue/wechat/src/Kernel/Encryptor.php
vendored
Normal file
219
vendor/overtrue/wechat/src/Kernel/Encryptor.php
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
35
vendor/overtrue/wechat/src/Kernel/Events/AccessTokenRefreshed.php
vendored
Normal file
35
vendor/overtrue/wechat/src/Kernel/Events/AccessTokenRefreshed.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
35
vendor/overtrue/wechat/src/Kernel/Events/ApplicationInitialized.php
vendored
Normal file
35
vendor/overtrue/wechat/src/Kernel/Events/ApplicationInitialized.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
35
vendor/overtrue/wechat/src/Kernel/Events/HttpResponseCreated.php
vendored
Normal file
35
vendor/overtrue/wechat/src/Kernel/Events/HttpResponseCreated.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
35
vendor/overtrue/wechat/src/Kernel/Events/ServerGuardResponseCreated.php
vendored
Normal file
35
vendor/overtrue/wechat/src/Kernel/Events/ServerGuardResponseCreated.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
21
vendor/overtrue/wechat/src/Kernel/Exceptions/BadRequestException.php
vendored
Normal file
21
vendor/overtrue/wechat/src/Kernel/Exceptions/BadRequestException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
16
vendor/overtrue/wechat/src/Kernel/Exceptions/DecryptException.php
vendored
Normal file
16
vendor/overtrue/wechat/src/Kernel/Exceptions/DecryptException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
23
vendor/overtrue/wechat/src/Kernel/Exceptions/Exception.php
vendored
Normal file
23
vendor/overtrue/wechat/src/Kernel/Exceptions/Exception.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
52
vendor/overtrue/wechat/src/Kernel/Exceptions/HttpException.php
vendored
Normal file
52
vendor/overtrue/wechat/src/Kernel/Exceptions/HttpException.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
}
|
||||
21
vendor/overtrue/wechat/src/Kernel/Exceptions/InvalidArgumentException.php
vendored
Normal file
21
vendor/overtrue/wechat/src/Kernel/Exceptions/InvalidArgumentException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
21
vendor/overtrue/wechat/src/Kernel/Exceptions/InvalidConfigException.php
vendored
Normal file
21
vendor/overtrue/wechat/src/Kernel/Exceptions/InvalidConfigException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
21
vendor/overtrue/wechat/src/Kernel/Exceptions/RuntimeException.php
vendored
Normal file
21
vendor/overtrue/wechat/src/Kernel/Exceptions/RuntimeException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
21
vendor/overtrue/wechat/src/Kernel/Exceptions/UnboundServiceException.php
vendored
Normal file
21
vendor/overtrue/wechat/src/Kernel/Exceptions/UnboundServiceException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
57
vendor/overtrue/wechat/src/Kernel/Helpers.php
vendored
Normal file
57
vendor/overtrue/wechat/src/Kernel/Helpers.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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;
|
||||
case is_object($data):
|
||||
return $data->{$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'));
|
||||
}
|
||||
}
|
||||
121
vendor/overtrue/wechat/src/Kernel/Http/Response.php
vendored
Normal file
121
vendor/overtrue/wechat/src/Kernel/Http/Response.php
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
86
vendor/overtrue/wechat/src/Kernel/Http/StreamResponse.php
vendored
Normal file
86
vendor/overtrue/wechat/src/Kernel/Http/StreamResponse.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
608
vendor/overtrue/wechat/src/Kernel/Log/LogManager.php
vendored
Normal file
608
vendor/overtrue/wechat/src/Kernel/Log/LogManager.php
vendored
Normal file
@@ -0,0 +1,608 @@
|
||||
<?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\Log;
|
||||
|
||||
use EasyWeChat\Kernel\ServiceContainer;
|
||||
use InvalidArgumentException;
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
use Monolog\Handler\ErrorLogHandler;
|
||||
use Monolog\Handler\FormattableHandlerInterface;
|
||||
use Monolog\Handler\HandlerInterface;
|
||||
use Monolog\Handler\RotatingFileHandler;
|
||||
use Monolog\Handler\SlackWebhookHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogHandler;
|
||||
use Monolog\Handler\WhatFailureGroupHandler;
|
||||
use Monolog\Logger as Monolog;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Class LogManager.
|
||||
*
|
||||
* @author overtrue <i@overtrue.me>
|
||||
*/
|
||||
class LogManager implements LoggerInterface
|
||||
{
|
||||
/**
|
||||
* @var \EasyWeChat\Kernel\ServiceContainer
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* The array of resolved channels.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $channels = [];
|
||||
|
||||
/**
|
||||
* The registered custom driver creators.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $customCreators = [];
|
||||
|
||||
/**
|
||||
* The Log levels.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $levels = [
|
||||
'debug' => Monolog::DEBUG,
|
||||
'info' => Monolog::INFO,
|
||||
'notice' => Monolog::NOTICE,
|
||||
'warning' => Monolog::WARNING,
|
||||
'error' => Monolog::ERROR,
|
||||
'critical' => Monolog::CRITICAL,
|
||||
'alert' => Monolog::ALERT,
|
||||
'emergency' => Monolog::EMERGENCY,
|
||||
];
|
||||
|
||||
/**
|
||||
* LogManager constructor.
|
||||
*
|
||||
* @param \EasyWeChat\Kernel\ServiceContainer $app
|
||||
*/
|
||||
public function __construct(ServiceContainer $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new, on-demand aggregate logger instance.
|
||||
*
|
||||
* @param array $channels
|
||||
* @param string|null $channel
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function stack(array $channels, $channel = null)
|
||||
{
|
||||
return $this->createStackDriver(compact('channels', 'channel'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a log channel instance.
|
||||
*
|
||||
* @param string|null $channel
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function channel($channel = null)
|
||||
{
|
||||
return $this->driver($channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a log driver instance.
|
||||
*
|
||||
* @param string|null $driver
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function driver($driver = null)
|
||||
{
|
||||
return $this->get($driver ?? $this->getDefaultDriver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get the log from the local cache.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function get($name)
|
||||
{
|
||||
try {
|
||||
return $this->channels[$name] ?? ($this->channels[$name] = $this->resolve($name));
|
||||
} catch (\Throwable $e) {
|
||||
$logger = $this->createEmergencyLogger();
|
||||
|
||||
$logger->emergency('Unable to create configured logger. Using emergency logger.', [
|
||||
'exception' => $e,
|
||||
]);
|
||||
|
||||
return $logger;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given log instance by name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function resolve($name)
|
||||
{
|
||||
$config = $this->app['config']->get(\sprintf('log.channels.%s', $name));
|
||||
|
||||
if (is_null($config)) {
|
||||
throw new InvalidArgumentException(\sprintf('Log [%s] is not defined.', $name));
|
||||
}
|
||||
|
||||
if (isset($this->customCreators[$config['driver']])) {
|
||||
return $this->callCustomCreator($config);
|
||||
}
|
||||
|
||||
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
|
||||
|
||||
if (method_exists($this, $driverMethod)) {
|
||||
return $this->{$driverMethod}($config);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(\sprintf('Driver [%s] is not supported.', $config['driver']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an emergency log handler to avoid white screens of death.
|
||||
*
|
||||
* @return \Monolog\Logger
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function createEmergencyLogger()
|
||||
{
|
||||
return new Monolog('EasyWeChat', $this->prepareHandlers([new StreamHandler(
|
||||
\sys_get_temp_dir().'/easywechat/easywechat.log',
|
||||
$this->level(['level' => 'debug'])
|
||||
)]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a custom driver creator.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function callCustomCreator(array $config)
|
||||
{
|
||||
return $this->customCreators[$config['driver']]($this->app, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an aggregate log driver instance.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return \Monolog\Logger
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function createStackDriver(array $config)
|
||||
{
|
||||
$handlers = [];
|
||||
|
||||
foreach ($config['channels'] ?? [] as $channel) {
|
||||
$handlers = \array_merge($handlers, $this->channel($channel)->getHandlers());
|
||||
}
|
||||
|
||||
if ($config['ignore_exceptions'] ?? false) {
|
||||
$handlers = [new WhatFailureGroupHandler($handlers)];
|
||||
}
|
||||
|
||||
return new Monolog($this->parseChannel($config), $handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the single file log driver.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function createSingleDriver(array $config)
|
||||
{
|
||||
return new Monolog($this->parseChannel($config), [
|
||||
$this->prepareHandler(new StreamHandler(
|
||||
$config['path'],
|
||||
$this->level($config),
|
||||
$config['bubble'] ?? true,
|
||||
$config['permission'] ?? null,
|
||||
$config['locking'] ?? false
|
||||
), $config),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the daily file log driver.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected function createDailyDriver(array $config)
|
||||
{
|
||||
return new Monolog($this->parseChannel($config), [
|
||||
$this->prepareHandler(new RotatingFileHandler(
|
||||
$config['path'],
|
||||
$config['days'] ?? 7,
|
||||
$this->level($config),
|
||||
$config['bubble'] ?? true,
|
||||
$config['permission'] ?? null,
|
||||
$config['locking'] ?? false
|
||||
), $config),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the Slack log driver.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected function createSlackDriver(array $config)
|
||||
{
|
||||
return new Monolog($this->parseChannel($config), [
|
||||
$this->prepareHandler(new SlackWebhookHandler(
|
||||
$config['url'],
|
||||
$config['channel'] ?? null,
|
||||
$config['username'] ?? 'EasyWeChat',
|
||||
$config['attachment'] ?? true,
|
||||
$config['emoji'] ?? ':boom:',
|
||||
$config['short'] ?? false,
|
||||
$config['context'] ?? true,
|
||||
$this->level($config),
|
||||
$config['bubble'] ?? true,
|
||||
$config['exclude_fields'] ?? []
|
||||
), $config),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the syslog log driver.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected function createSyslogDriver(array $config)
|
||||
{
|
||||
return new Monolog($this->parseChannel($config), [
|
||||
$this->prepareHandler(new SyslogHandler(
|
||||
'EasyWeChat',
|
||||
$config['facility'] ?? LOG_USER,
|
||||
$this->level($config)
|
||||
), $config),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the "error log" log driver.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected function createErrorlogDriver(array $config)
|
||||
{
|
||||
return new Monolog($this->parseChannel($config), [
|
||||
$this->prepareHandler(
|
||||
new ErrorLogHandler(
|
||||
$config['type'] ?? ErrorLogHandler::OPERATING_SYSTEM,
|
||||
$this->level($config)
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the handlers for usage by Monolog.
|
||||
*
|
||||
* @param array $handlers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareHandlers(array $handlers)
|
||||
{
|
||||
foreach ($handlers as $key => $handler) {
|
||||
$handlers[$key] = $this->prepareHandler($handler);
|
||||
}
|
||||
|
||||
return $handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the handler for usage by Monolog.
|
||||
*
|
||||
* @param \Monolog\Handler\HandlerInterface $handler
|
||||
*
|
||||
* @return \Monolog\Handler\HandlerInterface
|
||||
*/
|
||||
protected function prepareHandler(HandlerInterface $handler, array $config = [])
|
||||
{
|
||||
if (!isset($config['formatter'])) {
|
||||
if ($handler instanceof FormattableHandlerInterface) {
|
||||
$handler->setFormatter($this->formatter());
|
||||
}
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Monolog formatter instance.
|
||||
*
|
||||
* @return \Monolog\Formatter\FormatterInterface
|
||||
*/
|
||||
protected function formatter()
|
||||
{
|
||||
$formatter = new LineFormatter(null, null, true, true);
|
||||
$formatter->includeStacktraces();
|
||||
|
||||
return $formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the log channel from the given configuration.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function parseChannel(array $config)
|
||||
{
|
||||
return $config['name'] ?? 'EasyWeChat';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the string level into a Monolog constant.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function level(array $config)
|
||||
{
|
||||
$level = $config['level'] ?? 'debug';
|
||||
|
||||
if (isset($this->levels[$level])) {
|
||||
return $this->levels[$level];
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException('Invalid log level.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default log driver name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultDriver()
|
||||
{
|
||||
return $this->app['config']['log.default'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default log driver name.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function setDefaultDriver($name)
|
||||
{
|
||||
$this->app['config']['log.default'] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom driver creator Closure.
|
||||
*
|
||||
* @param string $driver
|
||||
* @param \Closure $callback
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function extend($driver, \Closure $callback)
|
||||
{
|
||||
$this->customCreators[$driver] = $callback->bindTo($this, $this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* System is unusable.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function emergency($message, array $context = [])
|
||||
{
|
||||
return $this->driver()->emergency($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action must be taken immediately.
|
||||
*
|
||||
* Example: Entire website down, database unavailable, etc. This should
|
||||
* trigger the SMS alerts and wake you up.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function alert($message, array $context = [])
|
||||
{
|
||||
return $this->driver()->alert($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical conditions.
|
||||
*
|
||||
* Example: Application component unavailable, unexpected exception.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function critical($message, array $context = [])
|
||||
{
|
||||
return $this->driver()->critical($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime errors that do not require immediate action but should typically
|
||||
* be logged and monitored.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function error($message, array $context = [])
|
||||
{
|
||||
return $this->driver()->error($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptional occurrences that are not errors.
|
||||
*
|
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||
* that are not necessarily wrong.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function warning($message, array $context = [])
|
||||
{
|
||||
return $this->driver()->warning($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal but significant events.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function notice($message, array $context = [])
|
||||
{
|
||||
return $this->driver()->notice($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interesting events.
|
||||
*
|
||||
* Example: User logs in, SQL logs.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function info($message, array $context = [])
|
||||
{
|
||||
return $this->driver()->info($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed debug information.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function debug($message, array $context = [])
|
||||
{
|
||||
return $this->driver()->debug($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs with an arbitrary level.
|
||||
*
|
||||
* @param mixed $level
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function log($level, $message, array $context = [])
|
||||
{
|
||||
return $this->driver()->log($level, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically call the default driver instance.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
return $this->driver()->$method(...$parameters);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user