Merge branch 'feature/push' into dev

This commit is contained in:
luofei 2023-06-30 17:28:06 +08:00
commit be8ff6ed03
116 changed files with 9155 additions and 11 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ app/controller/api/Test.php
public/protocol.html
/runtime/*
runtime/swoole.pid
cert_crmeb copy.key

1
.user.ini Normal file
View File

@ -0,0 +1 @@
open_basedir=/www/wwwroot/shop-php/:/tmp/

View File

@ -34,7 +34,7 @@ class SystemNoticeConfigDao extends BaseDao
public function getNoticeStatusByConstKey(string $key)
{
$value = $this->getModel()::getDb()->where('const_key',$key)->field('notice_sys,notice_wechat,notice_routine,notice_sms')->find();
$value = $this->getModel()::getDb()->where('const_key',$key)->field('notice_sys,notice_wechat,notice_routine,notice_sms,notice_app')->find();
return $value;
}

View File

@ -0,0 +1,200 @@
<?php
namespace app\common\service;
use app\common\model\system\merchant\Merchant;
use app\common\model\user\User;
use app\common\repositories\store\order\StoreGroupOrderRepository;
use app\common\repositories\store\order\StoreOrderRepository;
use JPush\Client;
class JgPush
{
public $client;
public $push;
public function __construct()
{
$this->client = new Client('b5f679f4357018605ea6fd2e', 'c4fb573758f8d7058d697c54');
$this->push = $this->client->push();
}
public function init($type, $data)
{
switch ($type) {
case 'ADMIN_PAY_SUCCESS_CODE':
$groupOrder = app()->make(StoreGroupOrderRepository::class)->get($data['group_order_id']);
if ($groupOrder) {
foreach ($groupOrder->orderList as $order) {
$route = "/pages/admin/orderDetail/index?id={$order['order_id']}&mer_id={$order['mer_id']}";
$merUserId = Merchant::where('mer_id', $order->mer_id)->value('uid');
$jgRegisterId = User::where('uid', $merUserId)->value('jg_register_id');
if (empty($jgRegisterId)) {
continue;
}
$this->addRegistrationId($jgRegisterId);
$this->androidNotification('您有新的订单,请注意查看。', ['extras' => ['route' => $route]]);
$this->iosNotification('您有新的订单,请注意查看。', ['extras' => ['route' => $route]]);
}
}
break;
case 'DELIVER_GOODS_CODE':
case 'ORDER_DELIVER_SUCCESS':
$order = app()->make(StoreOrderRepository::class)->get($data['order_id']);
if ($order) {
$route = "/pages/order_details/index?order_id={$order['order_id']}";
$jgRegisterId = User::where('uid', $order['uid'])->value('jg_register_id');
if (!empty($jgRegisterId)) {
$this->addRegistrationId($jgRegisterId);
$this->androidNotification('您的订单已发货,请注意查看。', ['extras' => ['route' => $route]]);
$this->iosNotification('您的订单已发货,请注意查看。', ['extras' => ['route' => $route]]);
}
}
break;
default:
break;
}
$this->setPlatform();
return $this;
}
/**
* 设置推送平台
* @param array $platform
*/
public function setPlatform(array $platform = ['android', 'ios'])
{
$this->push->setPlatform($platform);
}
/**
* 设置推送设备
* @param $registrationId
* @return void
*/
public function addRegistrationId($registrationId)
{
$this->push->addRegistrationId($registrationId);
}
/**
* 给所有平台推送相同的 alert 消息
* @param $alert
* @return void
*/
public function setNotificationAlert($alert)
{
$this->push->setNotificationAlert($alert);
}
/**
* ios平台通知
* @param $alert
* @param $extras
* @return void
* @example $extras = ['sound' => 'sound', 'badge' => '+1', 'extras' => ['key' => 'value']
*/
public function iosNotification($alert, $extras = [])
{
$this->push->iosNotification($alert, $extras);
}
/**
* android平台通知
* @param $alert
* @param $extras
* @return void
* @example $extras = ['alert' => 'alert', 'title' => 'title', 'extras' => ['key' => 'value']
*/
public function androidNotification($alert, $extras = [])
{
$this->push->androidNotification($alert, $extras);
}
/**
* iOS VOIP 功能
* @param $extras
* @return void
* @example $extras = ['key' => 'value'] //任意自定义 key/value 对,会透传给 APP
*/
public function voip($extras = [])
{
$this->push->voip($extras);
}
/**
* @param $msg_content
* @param array $extras
* @return void
* @example $extras = ['title' => 'title', 'content_type' => 'text', 'extras' => ['key' => 'value']
*/
public function message($msg_content, array $extras = [])
{
$this->push->message($msg_content, $extras);
}
/**
* 推送可选项
* @param array $options
* @return void
* @example $options = ['sendno' => 100, 'time_to_live' => 1, 'apns_production' => false, 'big_push_duration' => 1]
* sendno: 表示推送序号,纯粹用来作为 API 调用标识,
* time_to_live: 表示离线消息保留时长()
* apns_production: 表示APNs是否生产环境
* big_push_duration: 表示定速推送时长(分钟),又名缓慢推送
*/
public function options(array $options = [])
{
$this->push->options($options);
}
/**
* 发送短信通知
* @param int $delay 延迟时间,单位秒
* @param string $templateId 模板id
* @param array $param 模板参数
* @return void
*/
public function setSms(int $delay, string $templateId, array $param = [])
{
$this->push->setSms($delay, $templateId, $param);
}
/**
* 设备标签
* @param $tag
*/
public function addTag($tag)
{
$this->push->addTag($tag);
}
/**
* 设备标签AND
* @param $tag
*/
public function addTagAnd($tag)
{
$this->push->addTagAnd($tag);
}
/**
* 设备别名
* @param $alias
*/
public function addAlias($alias)
{
$this->push->addAlias($alias);
}
/**
* 所有用户
*/
public function addAllAudience()
{
$this->push->addAllAudience();
}
}

View File

@ -393,6 +393,25 @@ class Auth extends BaseController
return app('json')->success();
}
/**
* 绑定极光register_id
* @param UserRepository $repository
* @return mixed
* @throws DataNotFoundException
* @throws DbException
* @throws ModelNotFoundException
*/
public function bindJg(UserRepository $repository)
{
$phone = $this->request->param('phone');
$jgRegisterId = $this->request->param('jg_register_id');
$user = $repository->accountByUser($phone);
if ($user->save(['jg_register_id' => $jgRegisterId]) === false) {
return app('json')->fail('绑定失败');
}
return app('json')->success();
}
public function getCaptcha()
{
$codeBuilder = new CaptchaBuilder(null, new PhraseBuilder(4));

View File

@ -54,7 +54,8 @@
"ucloud/ufile-php-sdk": "^1.0",
"swoole/ide-helper": "^4.8",
"alibabacloud/dysmsapi-20170525": "2.0.9",
"fastknife/ajcaptcha": "^1.1"
"fastknife/ajcaptcha": "^1.1",
"vlucas/phpdotenv": "^5.3"
},
"require-dev": {
"symfony/var-dumper": "^4.2",

237
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a1e4f8118804a0d4921567860412c9a2",
"content-hash": "cdcb3c1e7a4e523b77afdea055928bd4",
"packages": [
{
"name": "adbario/php-dot-notation",
@ -1038,6 +1038,74 @@
"homepage": "https://github.com/firebase/php-jwt",
"time": "2020-02-24T23:15:03+00:00"
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
"reference": "a878d45c1914464426dc94da61c9e1d36ae262a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/a878d45c1914464426dc94da61c9e1d36ae262a8",
"reference": "a878d45c1914464426dc94da61c9e1d36ae262a8",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9"
},
"require-dev": {
"phpunit/phpunit": "^8.5.28 || ^9.5.21"
},
"type": "library",
"autoload": {
"psr-4": {
"GrahamCampbell\\ResultType\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "An Implementation Of The Result Type",
"keywords": [
"Graham Campbell",
"GrahamCampbell",
"Result Type",
"Result-Type",
"result"
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
"type": "tidelift"
}
],
"time": "2022-07-30T15:56:11+00:00"
},
{
"name": "gregwar/captcha",
"version": "v1.1.8",
@ -2851,6 +2919,87 @@
],
"time": "2020-07-19T09:51:35+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.1",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "dd3a383e599f49777d8b628dadbb90cae435b87e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e",
"reference": "dd3a383e599f49777d8b628dadbb90cae435b87e",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": true
},
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh"
},
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
"source": "https://github.com/schmittjoh/php-option/tree/1.9.1"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
"type": "tidelift"
}
],
"time": "2023-02-25T19:38:58+00:00"
},
{
"name": "pimple/pimple",
"version": "v3.3.0",
@ -5220,6 +5369,92 @@
},
"time": "2018-05-08T02:37:11+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.3.0",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "b3eac5c7ac896e52deab4a99068e3f4ab12d9e56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/b3eac5c7ac896e52deab4a99068e3f4ab12d9e56",
"reference": "b3eac5c7ac896e52deab4a99068e3f4ab12d9e56",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"ext-pcre": "*",
"graham-campbell/result-type": "^1.0.1",
"php": "^7.1.3 || ^8.0",
"phpoption/phpoption": "^1.7.4",
"symfony/polyfill-ctype": "^1.17",
"symfony/polyfill-mbstring": "^1.17",
"symfony/polyfill-php80": "^1.17"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"ext-filter": "*",
"phpunit/phpunit": "^7.5.20 || ^8.5.14 || ^9.5.1"
},
"suggest": {
"ext-filter": "Required to use the boolean validator."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.3-dev"
}
},
"autoload": {
"psr-4": {
"Dotenv\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"homepage": "https://gjcampbell.co.uk/"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "https://vancelucas.com/"
}
],
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
"keywords": [
"dotenv",
"env",
"environment"
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
"source": "https://github.com/vlucas/phpdotenv/tree/v5.3.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
"type": "tidelift"
}
],
"time": "2021-01-20T15:23:13+00:00"
},
{
"name": "xaboy/form-builder",
"version": "2.0.15",

View File

@ -17,7 +17,7 @@ use think\swoole\websocket\socketio\Parser;
return [
'server' => [
'host' => env('SWOOLE_HOST', '0.0.0.0'), // 监听地址
'port' => env('SWOOLE_PORT', 8325), // 监听端口
'port' => env('SWOOLE_PORT', 8324), // 监听端口
'mode' => SWOOLE_PROCESS, // 运行模式 默认为SWOOLE_PROCESS
'sock_type' => SWOOLE_SOCK_TCP, // sock type 默认为SWOOLE_SOCK_TCP
'options' => [

View File

@ -15,9 +15,11 @@ namespace crmeb\jobs;
use app\common\repositories\system\notice\SystemNoticeConfigRepository;
use app\common\service\JgPush;
use crmeb\interfaces\JobInterface;
use crmeb\services\SmsService;
use crmeb\services\WechatTemplateMessageService;
use crmeb\utils\DingTalk;
use think\facade\Log;
class SendSmsJob implements JobInterface
@ -26,6 +28,17 @@ class SendSmsJob implements JobInterface
public function fire($job, $data)
{
$status = app()->make(SystemNoticeConfigRepository::class)->getNoticeStatusByConstKey($data['tempId']);
if ($status['notice_app'] == 1) {
try {
/** @var JgPush $push */
$push = app()->make(JgPush::class);
Log::info('app推送发送数据' . var_export($data, 1));
$push->init($data['tempId'], $data)->push->send();
} catch (\Exception $e) {
Log::info('app推送消息发送失败' . var_export($data, 1) . $e->getMessage());
DingTalk::exception($e, 'app推送消息发送失败' . var_export($data, 1));
}
}
if ($status['notice_sms'] == 1) {
try {
SmsService::sendMessage($data);

View File

@ -603,6 +603,8 @@ Route::group('api/', function () {
Route::post('auth/mp', 'api.Auth/mpAuth');
//绑定小程序账号
Route::post('auth/bindMp', 'api.Auth/bindMp');
//绑定极光register_id
Route::post('auth/bindJg', 'api.Auth/bindJg');
//app授权
Route::post('auth/app', 'api.Auth/appAuth');
//apple授权

View File

@ -41,6 +41,7 @@ return array(
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
'Payment\\' => array($vendorDir . '/riverslei/payment/src'),
'Overtrue\\Socialite\\' => array($vendorDir . '/overtrue/socialite/src'),
@ -54,16 +55,19 @@ return array(
'League\\Flysystem\\Cached\\' => array($vendorDir . '/league/flysystem-cached-adapter/src'),
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
'Joypack\\Tencent\\Map\\' => array($vendorDir . '/joypack/tencent-map/src'),
'JPush\\' => array($vendorDir . '/jpush/jpush/src/JPush'),
'Intervention\\Image\\' => array($vendorDir . '/intervention/image/src/Intervention/Image'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'Gregwar\\' => array($vendorDir . '/gregwar/captcha/src/Gregwar'),
'GrahamCampbell\\ResultType\\' => array($vendorDir . '/graham-campbell/result-type/src'),
'FormBuilder\\' => array($vendorDir . '/xaboy/form-builder/src'),
'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
'Fastknife\\' => array($vendorDir . '/fastknife/ajcaptcha/src'),
'Endroid\\QrCode\\' => array($vendorDir . '/endroid/qr-code/src'),
'EasyWeChat\\' => array($vendorDir . '/overtrue/wechat/src'),
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
'Darabonba\\OpenApi\\' => array($vendorDir . '/alibabacloud/darabonba-openapi/src'),
'Darabonba\\GatewaySpi\\' => array($vendorDir . '/alibabacloud/gateway-spi/src'),

View File

@ -147,6 +147,7 @@ class ComposerStaticInitb1229d2685c190533aa1234015613f09
'Psr\\Http\\Client\\' => 16,
'Psr\\Container\\' => 14,
'Psr\\Cache\\' => 10,
'PhpOption\\' => 10,
'PhpOffice\\PhpSpreadsheet\\' => 25,
'Payment\\' => 8,
),
@ -172,6 +173,7 @@ class ComposerStaticInitb1229d2685c190533aa1234015613f09
'J' =>
array (
'Joypack\\Tencent\\Map\\' => 20,
'JPush\\' => 6,
),
'I' =>
array (
@ -183,6 +185,7 @@ class ComposerStaticInitb1229d2685c190533aa1234015613f09
'GuzzleHttp\\Promise\\' => 19,
'GuzzleHttp\\' => 11,
'Gregwar\\' => 8,
'GrahamCampbell\\ResultType\\' => 26,
),
'F' =>
array (
@ -197,6 +200,7 @@ class ComposerStaticInitb1229d2685c190533aa1234015613f09
),
'D' =>
array (
'Dotenv\\' => 7,
'Doctrine\\Common\\Lexer\\' => 22,
'Darabonba\\OpenApi\\' => 18,
'Darabonba\\GatewaySpi\\' => 21,
@ -370,6 +374,10 @@ class ComposerStaticInitb1229d2685c190533aa1234015613f09
array (
0 => __DIR__ . '/..' . '/psr/cache/src',
),
'PhpOption\\' =>
array (
0 => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption',
),
'PhpOffice\\PhpSpreadsheet\\' =>
array (
0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet',
@ -422,6 +430,10 @@ class ComposerStaticInitb1229d2685c190533aa1234015613f09
array (
0 => __DIR__ . '/..' . '/joypack/tencent-map/src',
),
'JPush\\' =>
array (
0 => __DIR__ . '/..' . '/jpush/jpush/src/JPush',
),
'Intervention\\Image\\' =>
array (
0 => __DIR__ . '/..' . '/intervention/image/src/Intervention/Image',
@ -442,6 +454,10 @@ class ComposerStaticInitb1229d2685c190533aa1234015613f09
array (
0 => __DIR__ . '/..' . '/gregwar/captcha/src/Gregwar',
),
'GrahamCampbell\\ResultType\\' =>
array (
0 => __DIR__ . '/..' . '/graham-campbell/result-type/src',
),
'FormBuilder\\' =>
array (
0 => __DIR__ . '/..' . '/xaboy/form-builder/src',
@ -462,6 +478,10 @@ class ComposerStaticInitb1229d2685c190533aa1234015613f09
array (
0 => __DIR__ . '/..' . '/overtrue/wechat/src',
),
'Dotenv\\' =>
array (
0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src',
),
'Doctrine\\Common\\Lexer\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',

View File

@ -1086,6 +1086,77 @@
"homepage": "https://github.com/firebase/php-jwt",
"install-path": "../firebase/php-jwt"
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.0",
"version_normalized": "1.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
"reference": "a878d45c1914464426dc94da61c9e1d36ae262a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/a878d45c1914464426dc94da61c9e1d36ae262a8",
"reference": "a878d45c1914464426dc94da61c9e1d36ae262a8",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9"
},
"require-dev": {
"phpunit/phpunit": "^8.5.28 || ^9.5.21"
},
"time": "2022-07-30T15:56:11+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"GrahamCampbell\\ResultType\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "An Implementation Of The Result Type",
"keywords": [
"Graham Campbell",
"GrahamCampbell",
"Result Type",
"Result-Type",
"result"
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
"type": "tidelift"
}
],
"install-path": "../graham-campbell/result-type"
},
{
"name": "gregwar/captcha",
"version": "v1.1.8",
@ -1614,6 +1685,56 @@
],
"install-path": "../joypack/tencent-map"
},
{
"name": "jpush/jpush",
"version": "v3.6.8",
"version_normalized": "3.6.8.0",
"source": {
"type": "git",
"url": "https://github.com/jpush/jpush-api-php-client.git",
"reference": "ebb191e8854a35c3fb7a6626028b3a23132cbe2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jpush/jpush-api-php-client/zipball/ebb191e8854a35c3fb7a6626028b3a23132cbe2c",
"reference": "ebb191e8854a35c3fb7a6626028b3a23132cbe2c",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"time": "2021-08-12T07:43:39+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"JPush\\": "src/JPush/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "JPush",
"email": "support@jpush.cn",
"homepage": "https://www.jpush.cn/",
"role": "Developer"
}
],
"description": "JPush API PHP Client",
"homepage": "https://github.com/jpush/jpush-api-php-client",
"support": {
"issues": "https://github.com/jpush/jpush-api-php-client/issues",
"source": "https://github.com/jpush/jpush-api-php-client/tree/v3.6.8"
},
"install-path": "../jpush/jpush"
},
{
"name": "khanamiryan/qrcode-detector-decoder",
"version": "1.0.3",
@ -2977,6 +3098,90 @@
],
"install-path": "../phpoffice/phpspreadsheet"
},
{
"name": "phpoption/phpoption",
"version": "1.9.1",
"version_normalized": "1.9.1.0",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "dd3a383e599f49777d8b628dadbb90cae435b87e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e",
"reference": "dd3a383e599f49777d8b628dadbb90cae435b87e",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12"
},
"time": "2023-02-25T19:38:58+00:00",
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": true
},
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh"
},
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
"source": "https://github.com/schmittjoh/php-option/tree/1.9.1"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
"type": "tidelift"
}
],
"install-path": "../phpoption/phpoption"
},
{
"name": "pimple/pimple",
"version": "v3.3.0",
@ -5674,6 +5879,95 @@
},
"install-path": "../ucloud/ufile-php-sdk"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.3.0",
"version_normalized": "5.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "b3eac5c7ac896e52deab4a99068e3f4ab12d9e56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/b3eac5c7ac896e52deab4a99068e3f4ab12d9e56",
"reference": "b3eac5c7ac896e52deab4a99068e3f4ab12d9e56",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"ext-pcre": "*",
"graham-campbell/result-type": "^1.0.1",
"php": "^7.1.3 || ^8.0",
"phpoption/phpoption": "^1.7.4",
"symfony/polyfill-ctype": "^1.17",
"symfony/polyfill-mbstring": "^1.17",
"symfony/polyfill-php80": "^1.17"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"ext-filter": "*",
"phpunit/phpunit": "^7.5.20 || ^8.5.14 || ^9.5.1"
},
"suggest": {
"ext-filter": "Required to use the boolean validator."
},
"time": "2021-01-20T15:23:13+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.3-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Dotenv\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"homepage": "https://gjcampbell.co.uk/"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "https://vancelucas.com/"
}
],
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
"keywords": [
"dotenv",
"env",
"environment"
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
"source": "https://github.com/vlucas/phpdotenv/tree/v5.3.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
"type": "tidelift"
}
],
"install-path": "../vlucas/phpdotenv"
},
{
"name": "xaboy/form-builder",
"version": "2.0.15",

View File

@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => 'topthink/think',
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => NULL,
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'fa730cf99f81c843e926f633ad856764fdcf6f49',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -172,6 +172,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
'graham-campbell/result-type' => array(
'pretty_version' => 'v1.1.0',
'version' => '1.1.0.0',
'reference' => 'a878d45c1914464426dc94da61c9e1d36ae262a8',
'type' => 'library',
'install_path' => __DIR__ . '/../graham-campbell/result-type',
'aliases' => array(),
'dev_requirement' => false,
),
'gregwar/captcha' => array(
'pretty_version' => 'v1.1.8',
'version' => '1.1.8.0',
@ -367,6 +376,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
'jpush/jpush' => array(
'pretty_version' => 'v3.6.8',
'version' => '3.6.8.0',
'reference' => 'ebb191e8854a35c3fb7a6626028b3a23132cbe2c',
'type' => 'library',
'install_path' => __DIR__ . '/../jpush/jpush',
'aliases' => array(),
'dev_requirement' => false,
),
'khanamiryan/qrcode-detector-decoder' => array(
'pretty_version' => '1.0.3',
'version' => '1.0.3.0',
@ -538,6 +556,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
'phpoption/phpoption' => array(
'pretty_version' => '1.9.1',
'version' => '1.9.1.0',
'reference' => 'dd3a383e599f49777d8b628dadbb90cae435b87e',
'type' => 'library',
'install_path' => __DIR__ . '/../phpoption/phpoption',
'aliases' => array(),
'dev_requirement' => false,
),
'pimple/pimple' => array(
'pretty_version' => 'v3.3.0',
'version' => '3.3.0.0',
@ -863,9 +890,9 @@
'dev_requirement' => false,
),
'topthink/think' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => NULL,
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'fa730cf99f81c843e926f633ad856764fdcf6f49',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -943,6 +970,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
'vlucas/phpdotenv' => array(
'pretty_version' => 'v5.3.0',
'version' => '5.3.0.0',
'reference' => 'b3eac5c7ac896e52deab4a99068e3f4ab12d9e56',
'type' => 'library',
'install_path' => __DIR__ . '/../vlucas/phpdotenv',
'aliases' => array(),
'dev_requirement' => false,
),
'xaboy/form-builder' => array(
'pretty_version' => '2.0.15',
'version' => '2.0.15.0',

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2020-2022 Graham Campbell <hello@gjcampbell.co.uk>
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.

View File

@ -0,0 +1,33 @@
{
"name": "graham-campbell/result-type",
"description": "An Implementation Of The Result Type",
"keywords": ["result", "result-type", "Result", "Result Type", "Result-Type", "Graham Campbell", "GrahamCampbell"],
"license": "MIT",
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9"
},
"require-dev": {
"phpunit/phpunit": "^8.5.28 || ^9.5.21"
},
"autoload": {
"psr-4": {
"GrahamCampbell\\ResultType\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"GrahamCampbell\\Tests\\ResultType\\": "tests/"
}
},
"config": {
"preferred-install": "dist"
}
}

View File

@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
/*
* This file is part of Result Type.
*
* (c) Graham Campbell <hello@gjcampbell.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace GrahamCampbell\ResultType;
use PhpOption\None;
use PhpOption\Some;
/**
* @template T
* @template E
* @extends \GrahamCampbell\ResultType\Result<T,E>
*/
final class Error extends Result
{
/**
* @var E
*/
private $value;
/**
* Internal constructor for an error value.
*
* @param E $value
*
* @return void
*/
private function __construct($value)
{
$this->value = $value;
}
/**
* Create a new error value.
*
* @template F
*
* @param F $value
*
* @return \GrahamCampbell\ResultType\Result<T,F>
*/
public static function create($value)
{
return new self($value);
}
/**
* Get the success option value.
*
* @return \PhpOption\Option<T>
*/
public function success()
{
return None::create();
}
/**
* Map over the success value.
*
* @template S
*
* @param callable(T):S $f
*
* @return \GrahamCampbell\ResultType\Result<S,E>
*/
public function map(callable $f)
{
return self::create($this->value);
}
/**
* Flat map over the success value.
*
* @template S
* @template F
*
* @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f
*
* @return \GrahamCampbell\ResultType\Result<S,F>
*/
public function flatMap(callable $f)
{
/** @var \GrahamCampbell\ResultType\Result<S,F> */
return self::create($this->value);
}
/**
* Get the error option value.
*
* @return \PhpOption\Option<E>
*/
public function error()
{
return Some::create($this->value);
}
/**
* Map over the error value.
*
* @template F
*
* @param callable(E):F $f
*
* @return \GrahamCampbell\ResultType\Result<T,F>
*/
public function mapError(callable $f)
{
return self::create($f($this->value));
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/*
* This file is part of Result Type.
*
* (c) Graham Campbell <hello@gjcampbell.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace GrahamCampbell\ResultType;
/**
* @template T
* @template E
*/
abstract class Result
{
/**
* Get the success option value.
*
* @return \PhpOption\Option<T>
*/
abstract public function success();
/**
* Map over the success value.
*
* @template S
*
* @param callable(T):S $f
*
* @return \GrahamCampbell\ResultType\Result<S,E>
*/
abstract public function map(callable $f);
/**
* Flat map over the success value.
*
* @template S
* @template F
*
* @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f
*
* @return \GrahamCampbell\ResultType\Result<S,F>
*/
abstract public function flatMap(callable $f);
/**
* Get the error option value.
*
* @return \PhpOption\Option<E>
*/
abstract public function error();
/**
* Map over the error value.
*
* @template F
*
* @param callable(E):F $f
*
* @return \GrahamCampbell\ResultType\Result<T,F>
*/
abstract public function mapError(callable $f);
}

View File

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/*
* This file is part of Result Type.
*
* (c) Graham Campbell <hello@gjcampbell.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace GrahamCampbell\ResultType;
use PhpOption\None;
use PhpOption\Some;
/**
* @template T
* @template E
* @extends \GrahamCampbell\ResultType\Result<T,E>
*/
final class Success extends Result
{
/**
* @var T
*/
private $value;
/**
* Internal constructor for a success value.
*
* @param T $value
*
* @return void
*/
private function __construct($value)
{
$this->value = $value;
}
/**
* Create a new error value.
*
* @template S
*
* @param S $value
*
* @return \GrahamCampbell\ResultType\Result<S,E>
*/
public static function create($value)
{
return new self($value);
}
/**
* Get the success option value.
*
* @return \PhpOption\Option<T>
*/
public function success()
{
return Some::create($this->value);
}
/**
* Map over the success value.
*
* @template S
*
* @param callable(T):S $f
*
* @return \GrahamCampbell\ResultType\Result<S,E>
*/
public function map(callable $f)
{
return self::create($f($this->value));
}
/**
* Flat map over the success value.
*
* @template S
* @template F
*
* @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f
*
* @return \GrahamCampbell\ResultType\Result<S,F>
*/
public function flatMap(callable $f)
{
return $f($this->value);
}
/**
* Get the error option value.
*
* @return \PhpOption\Option<E>
*/
public function error()
{
return None::create();
}
/**
* Map over the error value.
*
* @template F
*
* @param callable(E):F $f
*
* @return \GrahamCampbell\ResultType\Result<T,F>
*/
public function mapError(callable $f)
{
return self::create($this->value);
}
}

18
vendor/jpush/jpush/.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
*.pyc
log/
build/
bin/
.pydevproject
.tar
.zip
.buildpath
.project
.settings/
.idea
composer.lock
vendor/
*.log
vendor.tar.gz
composer.phar
vendor.zip
examples/config.php

13
vendor/jpush/jpush/.travis.yml vendored Normal file
View File

@ -0,0 +1,13 @@
language: php
php:
- '5.3'
- '5.4'
- '5.5'
- '5.6'
- '7.0'
before_script:
- composer install
script:
- ./vendor/bin/phpunit tests

21
vendor/jpush/jpush/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 极光
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.

121
vendor/jpush/jpush/README.md vendored Normal file
View File

@ -0,0 +1,121 @@
# JPush API PHP Client
这是 JPush REST API 的 PHP 版本封装开发包,是由极光推送官方提供的,一般支持最新的 API 功能。
对应的 REST API 文档: https://docs.jiguang.cn/jpush/server/push/server_overview/
> 支持的 PHP 版本: 5.3.3 5.6.x, 7.x
> 若需要兼容 PHP 5.3.3 以下版本,可以使用 [v3 分支的代码](https://github.com/jpush/jpush-api-php-client/tree/v3)。
因为运行 Composer 需要 PHP 5.3.2+ 以上版本,所以其不提供 Composer 支持,
也可以[点击链接](https://github.com/jpush/jpush-api-php-client/releases)下载 v3.4.x 版本源码。
## Installation
#### 使用 Composer 安装
- 执行 `$ php composer.phar install``$ composer install` 进行安装。
#### 直接下载源码安装
> 直接下载源代码也是一种安装 SDK 的方法,不过因为有版本更新的维护问题,所以这种安装方式**十分不推荐**,但由于种种原因导致无法使用 Composer所以我们也提供了这种情况下的备选方案。
- 下载源代码包,解压到项目中
- 在项目中引入 autoload
```php
require 'path_to_sdk/autoload.php';
```
## Usage
- [Init API](https://github.com/jpush/jpush-api-php-client/blob/master/doc/api.md#init-api)
- [Push API](https://github.com/jpush/jpush-api-php-client/blob/master/doc/api.md#push-api)
- [Report API](https://github.com/jpush/jpush-api-php-client/blob/master/doc/api.md#report-api)
- [Device API](https://github.com/jpush/jpush-api-php-client/blob/master/doc/api.md#device-api)
- [Schedule API](https://github.com/jpush/jpush-api-php-client/blob/master/doc/api.md#schedule-api)
- [Exception Handle](https://github.com/jpush/jpush-api-php-client/blob/master/doc/api.md#schedule-api)
- [HTTP/2 Support](https://github.com/jpush/jpush-api-php-client/blob/master/doc/http2.md)
- [Group Push](https://github.com/jpush/jpush-api-php-client/blob/master/doc/grouppush.md)
#### 初始化
```php
use JPush\Client as JPush;
...
...
$client = new JPush($app_key, $master_secret);
...
```
OR
```php
$client = new \JPush\Client($app_key, $master_secret);
```
#### 简单推送
```php
$client->push()
->setPlatform('all')
->addAllAudience()
->setNotificationAlert('Hello, JPush')
->send();
```
#### 异常处理
```php
$pusher = $client->push();
$pusher->setPlatform('all');
$pusher->addAllAudience();
$pusher->setNotificationAlert('Hello, JPush');
try {
$pusher->send();
} catch (\JPush\Exceptions\JPushException $e) {
// try something else here
print $e;
}
```
## Examples
**注意: 这只是使用样例, 不应该直接用于实际环境中!!**
在下载的中的 [examples](https://github.com/jpush/jpush-api-php-client/tree/master/examples) 文件夹有简单示例代码, 开发者可以参考其中的样例快速了解该库的使用方法。
**简单使用方法**
先填写对应的appKey和masterSecret可以额外设定Registration_id。
若要运行 push_example.php 中的示例代码:
``` bash
# 假定当前目录为 JPush 源码所在的根目录
$ php examples/push_example.php
```
> 同时也可编辑相关的示例文件,更改参数查看执行效果
## Testing
```bash
# 编辑 tests/bootstrap.php 文件,填入必须的变量值
# OR 设置相应的环境变量
# 运行全部测试用例
$ composer tests
# 运行某一具体测试用例
$ composer tests/JPush/xxTest.php
```
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/jpush/jpush-api-php-client.
## License
The library is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).

10
vendor/jpush/jpush/autoload.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
function classLoader($class)
{
$path = str_replace('\\', DIRECTORY_SEPARATOR, $class);
$file = __DIR__ . '/src/' . $path . '.php';
if (file_exists($file)) {
require_once $file;
}
}
spl_autoload_register('classLoader');

31
vendor/jpush/jpush/composer.json vendored Normal file
View File

@ -0,0 +1,31 @@
{
"name": "jpush/jpush",
"type": "library",
"description": "JPush API PHP Client",
"homepage": "https://github.com/jpush/jpush-api-php-client",
"license": "MIT",
"authors": [
{
"name": "JPush",
"email": "support@jpush.cn",
"homepage": "https://www.jpush.cn/",
"role": "Developer"
}
],
"require": {
"php": ">=5.3.3",
"ext-curl": "*"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"autoload" : {
"psr-4": {"JPush\\": "src/JPush/"}
},
"autoload-dev": {
"psr-4": { "JPush\\Tests\\": "tests/" }
},
"scripts": {
"test": "vendor/bin/phpunit"
}
}

494
vendor/jpush/jpush/doc/api.md vendored Normal file
View File

@ -0,0 +1,494 @@
# 目录
- [Init API](#init-api)
- [Push API](#push-api)
- [Report API](#report-api)
- [Device API](#device-api)
- [Schedule API](#schedule-api)
- [Exception Handle](#schedule-api)
> **注PHP SDK 全面支持 namespaces 但为方便表达,以下例子都不使用 use 语句**
## Init API
在调用推送之前,我们必须先初始化 JPushClient调用以下代码可以进行快速初始化
```php
$client = new \JPush\Client($app_key, $master_secret);
```
在初始化 JPushClient 的时候,可以指定**日志路径**
```php
$client = new \JPush\Client($app_key, $master_secret, $log_path);
```
> 默认日志路径为 `./jpush.log`,即保存在当前运行目录,如果想关闭日志,可以指定为 `null`
## Push API
在初始化 JPushClient 后,调用以下代码将返回一个推送 Payload 构建器它提供丰富的API来帮助你构建 PushPayload。
```php
$push = $client->push();
```
通过 [JPush Push API](https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/) 我们知道,一个 PushPayload 是由以下几个部分构成的:
- Cid
- Platform
- Audience
- Notification
- Message
- SmsContent
- Options
#### Cid
```php
$push->setCid($cid);
```
#### Platform
```php
$push->setPlatform('all');
// OR
$push->setPlatform('ios', 'android');
// OR
$push->setPlatform(['ios', 'android']);
```
#### Audience
```php
$push->addAllAudience();
```
```php
$push->addTag('tag1');
// OR
$push->addTag(['tag1', 'tag2']);
```
其他诸如 `addAlias()`, `addRegistrationId()`, `addTagAnd()`, `addTagNot()`, `addSegmentId()`, `addAbtest()` 的使用方法与 `addTag()` 类似,在此不做赘述。
#### Notification
```php
// 简单地给所有平台推送相同的 alert 消息
$push->setNotificationAlert('alert');
```
**iOS Notification**
```php
// iosNotification($alert = '', array $notification = array())
// 数组 $notification 的键支持 'sound', 'badge', 'content-available', 'mutable-content', category', 'extras', 'thread-id' 中的一个或多个
// 调用示例
$push->iosNotification();
// OR
$push->iosNotification('hello');
// OR
$push->iosNotification('hello', [
'sound' => 'sound',
'badge' => '+1',
'extras' => [
'key' => 'value'
]
]);
```
参数说明:
| 参数 | 说明 |
| --- | --- |
| alert |表示通知内容,会覆盖上级统一指定的 alert 信息;默认内容可以为空字符串,表示不展示到通知栏, 支持字符串和数组两种形式 |
| sound | 表示通知提示声音,默认填充为空字符串 |
| badge | 表示应用角标,把角标数字改为指定的数字;为 0 表示清除,支持 '+1','-1' 这样的字符串,表示在原有的 badge 基础上进行增减,默认填充为 '+1' |
| content-available | 表示推送唤醒,仅接受 true 表示为 Background Remote Notification若不填默认表示普通的 Remote Notification |
| mutable-content | 表示通知扩展, 仅接受 true 表示支持 iOS10 的 UNNotificationServiceExtension, 若不填默认表示普通的 Remote Notification |
| category | IOS8才支持。设置 APNs payload 中的 'category' 字段值 |
| thread-id | 表示通知分组ios 的远程通知通过该属性来对通知进行分组,同一个 thread-id 的通知归为一组 |
| extras | 表示扩展字段,接受一个数组,自定义 Key/value 信息以供业务使用 |
**Android Notification**
```php
// androidNotification($alert = '', array $notification = array())
// 调用示例同 IOS数组 $notification 的键支持 'title', 'builder_id', 'priority', 'category', 'style', 'alert_type', 'big_text', 'inbox', 'big_pic_path', 'large_icon', 'intent', 'extras' 中的一个或多个
```
参数说明:
| 参数 | 说明 |
| --- | --- |
| alert | 表示通知内容,会覆盖上级统一指定的 alert 信息;默认内容可以为空字符串,表示不展示到通知栏 |
| title | 表示通知标题,会替换通知里原来展示 App 名称的地方 |
| builder_id | 表示通知栏样式 ID |
| priority | 表示通知栏展示优先级,默认为 0范围为 -22 ,其他值将会被忽略而采用默认值 |
| category | 表示通知栏条目过滤或排序,完全依赖 rom 厂商对 category 的处理策略 |
| style | 表示通知栏样式类型,默认为 0还有123可选用来指定选择哪种通知栏样式其他值无效。有三种可选分别为 bigText=1Inbox=2bigPicture=3 |
| alert_type | 表示通知提醒方式, 可选范围为 -17 ,对应 Notification.DEFAULT_ALL = -1 或者 Notification.DEFAULT_SOUND = 1 Notification.DEFAULT_VIBRATE = 2 Notification.DEFAULT_LIGHTS = 4 的任意 “or” 组合。默认按照 -1 处理。 |
| big_text | 表示大文本通知栏样式,当 style = 1 时可用,内容会被通知栏以大文本的形式展示出来,支持 api 16 以上的 rom |
| inbox | 表示文本条目通知栏样式,接受一个数组,当 style = 2 时可用,数组的每个 key 对应的 value 会被当作文本条目逐条展示,支持 api 16 以上的 rom |
| big_pic_path | 表示大图片通知栏样式,当 style = 3 时可用,可以是网络图片 url或本地图片的 path目前支持 .jpg 和 .png 后缀的图片。图片内容会被通知栏以大图片的形式展示出来。如果是 httphttps 的 url会自动下载如果要指定开发者准备的本地图片就填 sdcard 的相对路径,支持 api 16 以上的 rom |
| large_icon | 表示通知栏大图标,图标路径可以是以 http 或 https 开头的网络图片,如:"http:jiguang.cn/logo.png",图标大小不超过 30k 也可以是位于 drawable 资源文件夹的图标路径,如:"R.drawable.lg_icon"|
| intent | 表示扩展字段,接受一个数组,自定义 Key/value 信息以供业务使用 |
| extras | 表示扩展字段,接受一个数组,自定义 Key/value 信息以供业务使用 |
**WinPhone Notification**
```php
$push->addWinPhoneNotification($alert=null, $title=null, $_open_page=null, $extras=null)
```
参数说明:
| 参数 | 说明 |
| --- | --- |
| alert | 表示通知内容,会覆盖上级统一指定的 alert 信息;内容为空则不展示到通知栏 |
| title | 通知标题,会填充到 toast 类型 text1 字段上 |
| _open_page | 点击打开的页面名称 |
#### Message
```php
// message($msg_content, array $msg = array())
// 数组 $msg 的键支持 'title', 'content_type', 'extras' 中的一个或多个
// 调用示例
$push->message('Hello JPush');
// OR
$push->message('Hello JPush', [
'title' => 'Hello',
'content_type' => 'text',
'extras' => [
'key' => 'value'
]
]);
```
参数说明:
| 参数 | 说明 |
| --- | --- |
| msg_content | 消息内容本身 |
| title | 消息标题 |
| content_type | 消息内容类型 |
| extras | 表示扩展字段,接受一个数组,自定义 Key/value 信息以供业务使用 |
#### Sms Message
```php
$push->setSms($delay_time, $temp_id, array $temp_para = [])
```
参数说明:
* delay_time: 表示短信发送的延迟时间,单位为秒,不能超过 24 小时(即大于等于 0 小于等于 86400)。仅对 android 平台有效。
* temp_id: 短信补充的内容模板 ID。没有填写该字段即表示不使用短信补充功能。
* temp_para: 短信模板中的参数
##### 已弃用
```php
$push->setSmsMessage($content, $delay_time)
```
参数说明:
* content: 短信文本,不超过 480 字符
* delay_time: 表示短信发送的延迟时间,单位为秒,不能超过 24 小时(即大于等于 0 小于等于 86400)。仅对 android 平台有效。默认为 0表示立即发送短信
#### Options
```php
// options(array $opts = array())
// 数组 $opts 的键支持 'sendno', 'time_to_live', 'override_msg_id', 'apns_production', 'big_push_duration', 'apns_collapse_id' 中的一个或多个
```
参数说明:
| 可选项 | 说明 |
| --- | --- |
| sendno | 表示推送序号,纯粹用来作为 API 调用标识API 返回时被原样返回,以方便 API 调用方匹配请求与返回 |
| time_to_live | 表示离线消息保留时长(秒),推送当前用户不在线时,为该用户保留多长时间的离线消息,以便其上线时再次推送。默认 86400 1 天),最长 10 天。设置为 0 表示不保留离线消息,只有推送当前在线的用户可以收到 |
| override_msg_id | 表示要覆盖的消息ID如果当前的推送要覆盖之前的一条推送这里填写前一条推送的 msg_id 就会产生覆盖效果 |
| apns_production | 表示 APNs 是否生产环境True 表示推送生产环境False 表示要推送开发环境;如果不指定则默认为推送开发环境 |
| apns_collapse_id | APNs 新通知如果匹配到当前通知中心有相同 apns-collapse-id 字段的通知则会用新通知内容来更新它并使其置于通知中心首位collapse id 长度不可超过 64 bytes|
| big_push_duration | 表示定速推送时长(分钟),又名缓慢推送,把原本尽可能快的推送速度,降低下来,给定的 n 分钟内均匀地向这次推送的目标用户推送。最大值为1400.未设置则不是定速推送 |
#### Common Method
```php
// 发送推送
// 该方法内部将自动调用构建方法获得当前构建对象,并转化为 JSON 向 JPush 服务器发送请求
$push->send();
```
> 构建 PushPayload 的 API 每一次都会返回自身的引用,所以我们可用使用链式调用的方法提高代码的简洁性,如:
```php
$response = $push()
->setCid('xxxxxx')
->setPlatform(['ios', 'android'])
->addTag(['tag1', 'tag2'])
->setNotificationAlert('Hello, JPush')
->iosNotification('hello', [
'sound' => 'sound',
'badge' => '+1',
'extras' => [
'key' => 'value'
]
])
->androidNotification('hello')
->message('Hello JPush', [
'title' => 'Hello',
'content_type' => 'text',
'extras' => [
'key' => 'value'
]
])
->send();
// OR 也可以提前准备好所有的参数,然后链式调用,这样代码可读性更好一点
$cid = 'xxxxxx';
$platform = array('ios', 'android');
$alert = 'Hello JPush';
$tag = array('tag1', 'tag2');
$regId = array('rid1', 'rid2');
$ios_notification = array(
'sound' => 'hello jpush',
'badge' => 2,
'content-available' => true,
'category' => 'jiguang',
'extras' => array(
'key' => 'value',
'jiguang'
),
);
$android_notification = array(
'title' => 'hello jpush',
'builder_id' => 2,
'extras' => array(
'key' => 'value',
'jiguang'
),
);
$content = 'Hello World';
$message = array(
'title' => 'hello jpush',
'content_type' => 'text',
'extras' => array(
'key' => 'value',
'jiguang'
),
);
$options = array(
'sendno' => 100,
'time_to_live' => 100,
'override_msg_id' => 100,
'big_push_duration' => 100
);
$response = $push->setCid($cid)
->setPlatform($platform)
->addTag($tag)
->addRegistrationId($regId)
->iosNotification($alert, $ios_notification)
->androidNotification($alert, $android_notification)
->message($content, $message)
->options($options)
->send();
```
#### 获取 Cid
```php
$push->getCid($count = 1, $type = 'push');
```
## Report API
```php
$report = $client->report();
```
#### 获取送达统计
```php
$report->getReceived('msg_id');
// OR
$report->getReceived(['msg_id1', 'msg_id2']);
```
#### 送达状态查询
```php
$msg_id0 = 66666666666;
$report->getMessageStatus($msg_id0, 'rid0');
# OR
$report->getMessageStatus($msg_id0, ['rid0', 'rid1']);
#OR
$report->getMessageStatus($msg_id0, ['rid0', 'rid1'], '2017-12-21');
```
#### 获取消息统计
```php
// getMessages(getMessages($msgIds));
// 消息统计与送达统计一样,接受一个数组的参数,在这里不做赘述
```
#### 获取用户统计
调用一下代码可以获得用户统计
```php
$report->getUsers($time_unit, $start, $duration)
```
参数说明:
- time_unit:`String` 时间单位, 可取值HOUR, DAY, MONTH
- start:`String` 起始时间
- 如果单位是小时则起始时间是小时包含天格式例2014-06-11 09
- 如果单位是天则起始时间是日期格式例2014-06-11
- 如果单位是月则起始时间是日期格式例2014-06
- duration:`String` 持续时长
- 如果单位是天,则是持续的天数。以此类推
- 只支持查询60天以内的用户信息对于time_unit为HOUR的只支持输出当天的统计结果。
## Device API
```php
$device = $client->device();
```
#### 操作 Device(registration_id)
```php
// 查询指定设备的别名与标签
$device->getDevices($registration_id);
// 更新指定设备的别名与标签
// 更新 Alias
$device->updateAlias($registration_id, 'alias');
// 添加 tag, 支持字符串和数组两种参数
$device->addTags($registration_id, 'tag');
// OR
$device->addTags($registration_id, ['tag1', 'tag2']);
// 移除 tag支持字符串和数组两种参数
$device->removeTags($registration_id, 'tags');
// OR
$device->removeTags($registration_id, ['tag1', 'tag2']);
// 清空所有 tag
$device->clearTags($registration_id);
// 更新 mobile
$device->updateMoblie($registration_id, '13800138000');
// 取消手机绑定
$device->clearMobile($registration_id);
// getDevicesStatus($registrationId)
// 获取在线用户的登录状态VIP专属接口,支持字符串和数组两种参数
$device->getDevicesStatus('rid');
// OR
$device->getDevicesStatus(['rid1', 'rid2']);
```
#### 操作标签
```php
// 获取标签列表
$device->getTags()
// 判断指定设备是否在指定标签之下
$device->isDeviceInTag($registrationId, $tag);
// 更新标签
// 为标签添加设备,支持字符串和数组两种参数
$device->addDevicesToTag($tag, 'rid');
$device->addDevicesToTag($tag, ['rid1', 'rid2']);
// 为标签移除设备,支持字符串和数组两种参数
$device->removeDevicesFromTag($tag, 'rid');
$device->removeDevicesFromTag($tag, ['rid1', 'rid2']);
// 删除标签
$device->deleteTag('tag');
```
#### 操作别名
```php
// 获取指定别名下的设备
$device->getAliasDevices('alias');
// 删除别名
$device->deleteAlias('alias');
```
## Schedule API
```php
$schedule = $client->schedule();
```
#### 创建定时任务
定时任务分为Single与Periodical两种可以通过调用以下方法创建定时任务
```php
$schedule->createSingleSchedule($name, $push_payload, $trigger)
$schedule->createPeriodicalSchedule($name, $push_payload, $trigger)
```
参数说明:
- name: `String` 定时任务的名称
- push_payload: `PushPayload` Push的构建对象通过Push模块的`build()`方法获得
- trigger: `Array` 触发器对象
#### 更新定时任务
```php
$schedule->updateSingleSchedule($schedule_id, $name=null, $enabled=null, $push_payload=null, $trigger=null)
$schedule->updatePeriodicalSchedule($schedule_id, $name=null, $enabled=null, $push_payload=null, $trigger=null)
```
#### 其他
```php
// 获取定时任务列表
$schedule->getSchedules($page=1);
// 获取指定定时任务
$schedule->getSchedule($schedule_id);
// 删除指定定时任务
$schedule->deleteSchedule($schedule_id);
// 获取定时任务对应的所有 msg_id
$schedule->getMsgIds($schedule_id);
```
## Exception Handle
当 API 请求发生错误时SDK 将抛出异常Pushpayload 具体错误代码请参考[ API 错误代码表](https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#_19)。
PHP SDK 主要抛出两个异常 `\JPush\Exceptions\APIConnectionException``\JPush\Exceptions\APIRequestException` 分别对应请求连接产生的异常和请求响应的异常。
这两种异常都需要捕获,为简单起见,也可以捕获他们的父类异常 `JPush\Exceptions\JPushException`(见 README。另外 APIRequestException 异常还提供其他方法供开发者调用。
```php
try {
$pusher->send();
} catch (\JPush\Exceptions\APIConnectionException $e) {
// try something here
print $e;
} catch (\JPush\Exceptions\APIRequestException $e) {
// try something here
print $e;
}
```

28
vendor/jpush/jpush/doc/grouppush.md vendored Normal file
View File

@ -0,0 +1,28 @@
# JPush Group Push
## 获取 Group Key 和 Group Master Secret
```php
$group_key = 'xxxx';
$group_master_secret = 'xxxx';
```
## 初始化
**注Group Key 需拼接 'group-' 使用**
```php
$client = new \JPush\Client('group-' . $group_key, $group_master_secret);
```
## 简单群组推送
```php
$client->push()
->setPlatform('all')
->addAllAudience()
->setNotificationAlert('Hello, JPush')
->send();
```
> [Example](https://github.com/jpush/jpush-api-php-client/blob/master/examples/push_example.php)

56
vendor/jpush/jpush/doc/http2.md vendored Normal file
View File

@ -0,0 +1,56 @@
# JPush API PHP Client With HTTP/2 Support
> JPush API PHP Client 全面支持 HTTP/2
> **要求 PHP >= 5.5.24**
> 但由于 libcurl 对于 HTTP/2 的实现依赖于第三方库 [nghttp2](https://github.com/nghttp2/nghttp2) 所以如果要支持 HTTP/2 需要做一些其他的配置。
### 安装 nghttp2
系统依赖仅针对 Ubuntu 14.04 LTS (trusty) 和 Debian 7.0 (wheezy) 或以上版本,其他系统版本请按照 nghttp2 的文档来操作:
> From Ubuntu 15.10, spdylay has been available as a package named libspdylay-dev. For the earlier Ubuntu release, you need to build it yourself: http://tatsuhiro-t.github.io/spdylay/
详细情况请查看 [nghttp2 的文档](https://github.com/nghttp2/nghttp2#requirements)。
```bash
# Get build requirements
# Some of these are used for the Python bindings
# this package also installs
$ sudo apt-get install g++ make binutils autoconf automake autotools-dev libtool pkg-config \
zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
libjemalloc-dev cython python3-dev python-setuptools
# Build nghttp2 from source
$ git clone https://github.com/tatsuhiro-t/nghttp2.git
$ cd nghttp2
$ autoreconf -i
$ automake
$ autoconf
$ ./configure
$ make
$ sudo make install
```
### 升级 curl 至最新版本
```bash
$ sudo apt-get build-dep curl
# 请根据当前的 curl 官网中的最新版本(https://curl.haxx.se/download/)替换下面的相应位置
$ wget https://curl.haxx.se/download/curl-7.x.x.tar.bz2
$ tar -xvjf curl-7.x.x.tar.bz2
$ cd curl-7.x.x
$ ./configure --with-nghttp2=/usr/local --with-ssl
$ make
$ sudo make install
```
### 测试
##### 命令行测试
命令行运行 `$ curl --version`,若输出中的 Features 栏中有 `HTTP2` 一项则证明配置成功。
##### 样例测试
运行样例 `$ php examples/devices/get_devices_example.php`,若输出中的 HTTP 版本是 HTTP/2 则证明已经在使用 HTTP2 发送请求和接受响应了。
##### 测试测试
运行测试 `$./vendor/bin/phpunit tests/JPush/DevicePayloadTest.php`,若打印出的 http headers 中的 HTTP 版本是 HTTP/2 则证明已经在使用 HTTP2 发送请求和接受响应了。

View File

@ -0,0 +1,14 @@
<?php
// 这只是使用样例,不应该直接用于实际生产环境中 !!
require 'config.php';
use JPush\AdminClient as Admin;
$admin = new Admin($dev_key, $dev_secret);
$response = $admin->createApp('aaa', 'cn.jpush.app');
print_r($response);
$appKey = $response['body']['app_key'];
$response = $admin->deleteApp($appKey);
print_r($response);

View File

@ -0,0 +1,35 @@
<?php
// 这只是使用样例,不应该直接用于实际生产环境中 !!
require 'config.php';
$singlePayloads = array(
array(
'platform' => 'all',
'target' => 'regid1',
'notification' => array(
'alert' => 'NotificationAlert1'
)
),
array(
'platform' => 'all',
'target' => 'regid2',
'notification' => array(
'alert' => 'NotificationAlert2'
)
)
);
$push_payload = $client -> push();
try {
$response = $push_payload -> batchPushByRegid($singlePayloads);
print_r($response);
$response = $push_payload -> batchPushByAlias($singlePayloads);
print_r($response);
} catch (\JPush\Exceptions\APIConnectionException $e) {
// try something here
print $e;
} catch (\JPush\Exceptions\APIRequestException $e) {
// try something here
print $e;
}

View File

@ -0,0 +1,7 @@
<?php
require 'config.php';
$response = $client->push()->getCid();
print_r($response);

View File

@ -0,0 +1,10 @@
<?php
require __DIR__ . '/../autoload.php';
use JPush\Client as JPush;
$app_key = getenv('app_key');
$master_secret = getenv('master_secret');
$registration_id = getenv('registration_id');
$client = new JPush($app_key, $master_secret);

View File

@ -0,0 +1,5 @@
<?php
require __DIR__ . '/../config.php';
$response = $client->device()->deleteAlias('alias');
print_r($response);

View File

@ -0,0 +1,5 @@
<?php
require __DIR__ . '/../config.php';
$response = $client->device()->deleteTag('tag');
print_r($response);

View File

@ -0,0 +1,6 @@
<?php
require __DIR__ . '/../config.php';
// 更新 Alias
$response = $client->device()->getAliasDevices('alias');
print_r($response);

View File

@ -0,0 +1,6 @@
<?php
require __DIR__ . '/../config.php';
// 获取指定设备的 Mobile,Alias,Tags 等信息
$response = $client->device()->getDevices($registration_id);
print_r($response);

View File

@ -0,0 +1,13 @@
<?php
require __DIR__ . '/../config.php';
// 获取用户在线状态VIP专属接口
try {
$response = $client->device()->getDevicesStatus($registration_id);
} catch(\JPush\Exceptions\APIRequestException $e) {
print $e;
print $e->getHttpCode();
print $e->getHeaders();
}
print_r($response);

View File

@ -0,0 +1,6 @@
<?php
require __DIR__ . '/../config.php';
// 获取Tag列表
$response = $client->device()->getDevices($registration_id);
print_r($response);

View File

@ -0,0 +1,56 @@
<?php
require __DIR__ . '/../config.php';
// 更新 Alias
$result = $client->device()->getDevices($registration_id);
print "before update alias = " . $result['body']['alias'] . "\n";
print 'updating alias ... response = ';
$response = $client->device()->updateAlias($registration_id, 'jpush_alias');
print_r($response);
$result = $client->device()->getDevices($registration_id);
print "after update alias = " . $result['body']['alias'] . "\n\n";
// 添加 tag
$result = $client->device()->getDevices($registration_id);
print "before add tags = [" . implode(',', $result['body']['tags']) . "]\n";
print 'add tag1 tag2 ... response = ';
$response = $client->device()->addTags($registration_id, 'tag0');
print_r($response);
$response = $client->device()->addTags($registration_id, ['tag1', 'tag2']);
print_r($response);
$result = $client->device()->getDevices($registration_id);
print "after add tags = [" . implode(',', $result['body']['tags']) . "]\n\n";
// 移除 tag
$result = $client->device()->getDevices($registration_id);
print "before remove tags = [" . implode(',', $result['body']['tags']) . "]\n";
print 'removing tag1 tag2 ... response = ';
$response = $client->device()->removeTags($registration_id, 'tag0');
print_r($response);
$response = $client->device()->removeTags($registration_id, ['tag1', 'tag2']);
print_r($response);
$result = $client->device()->getDevices($registration_id);
print "after remove tags = [" . implode(',', $result['body']['tags']) . "]\n\n";
// 更新 mobile
$result = $client->device()->getDevices($registration_id);
print "before update mobile = " . $result['body']['mobile'] . "\n";
print 'updating mobile ... response = ';
$response = $client->device()->updateMoblie($registration_id, '13800138000');
print_r($response);
$result = $client->device()->getDevices($registration_id);
print "after update mobile = " . $result['body']['mobile'] . "\n\n";

View File

@ -0,0 +1,29 @@
<?php
require __DIR__ . '/../config.php';
// 为一个标签添加设备
$result = $client->device()->isDeviceInTag($registration_id, 'tag');
$r = $result['body']['result'] ? 'true' : 'false';
print "before add device = " . $r . "\n";
print 'adding device ... response = ';
$response = $client->device()->addDevicesToTag('tag', $registration_id);
print_r($response);
$result = $client->device()->isDeviceInTag($registration_id, 'tag');
$r = $result['body']['result'] ? 'true' : 'false';
print "after add tags = " . $r . "\n\n";
// 为一个标签删除设备
$result = $client->device()->isDeviceInTag($registration_id, 'tag');
$r = $result['body']['result'] ? 'true' : 'false';
print "before remove device = " . $r . "\n";
print 'removing device ... response = ';
$response = $client->device()->removeDevicesFromTag('tag', $registration_id);
print_r($response);
$result = $client->device()->isDeviceInTag($registration_id, 'tag');
$r = $result['body']['result'] ? 'true' : 'false';
print "after remove device = " . $r . "\n\n";

View File

@ -0,0 +1,24 @@
<?php
require __DIR__ . '/../autoload.php';
use JPush\Client as JPush;
$group_key = 'xxxx';
$group_master_secret = 'xxxx';
$client = new JPush('group-' . $group_key, $group_master_secret, null);
$push_payload = $client->push()
->setPlatform('all')
->addAllAudience()
->setNotificationAlert('Hi, JPush');
try {
$response = $push_payload->send();
print_r($response);
} catch (\JPush\Exceptions\APIConnectionException $e) {
// try something here
print $e;
} catch (\JPush\Exceptions\APIRequestException $e) {
// try something here
print $e;
}

View File

@ -0,0 +1,36 @@
<?php
// 这只是使用样例,不应该直接用于实际生产环境中 !!
require 'config.php';
try {
$payload = $client->push()
->setPlatform(array('ios', 'android'))
// ->addAlias('alias')
->addTag(array('tag1', 'tag2'))
// ->addRegistrationId($registration_id)
->setNotificationAlert('Hi, JPush')
->androidNotification('Hello HUAWEI', array(
'title' => 'huawei demo',
// ---------------------------------------------------
// `uri_activity` 字段用于指定想要打开的 activity.
// 值为 activity 节点的 “android:name” 属性值。
'uri_activity' => 'cn.jpush.android.ui.OpenClickActivity',
// ---------------------------------------------------
'extras' => array(
'key' => 'value',
'jiguang'
),
));
// ->send();
print_r($payload->build());
} catch (\JPush\Exceptions\APIConnectionException $e) {
// try something here
print $e;
} catch (\JPush\Exceptions\APIRequestException $e) {
// try something here
print $e;
}

View File

@ -0,0 +1,140 @@
<?php
// 这只是使用样例,不应该直接用于实际生产环境中 !!
require __DIR__ . '/../autoload.php';
use JPush\Client as JPush;
// 这里填写appKey,masterSecret以及registration_id
$app_key = 'e5c0d34f58732cf09b2d4d74';
$master_secret = '4cdda6d3c8b029941dbc5cb3';
$registration_id = ('registration_id');
$client = new JPush($app_key, $master_secret);
// 简单推送示例
// 这只是使用样例,不应该直接用于实际生产环境中 !!
// $push_payload = $client->push()
// ->setPlatform('all')
// ->addAllAudience()
// ->setNotificationAlert('Hi, JPush');
// try {
// $response = $push_payload->send();
// print_r($response);
// } catch (\JPush\Exceptions\APIConnectionException $e) {
// // try something here
// print $e;
// } catch (\JPush\Exceptions\APIRequestException $e) {
// // try something here
// print $e;
// }
// 完整的推送示例
// 这只是使用样例,不应该直接用于实际生产环境中 !!
try {
$response = $client->push()
->setPlatform(array('ios', 'android'))
// 一般情况下,关于 audience 的设置只需要调用 addAlias、addTag、addTagAnd 或 addRegistrationId
// 这四个方法中的某一个即可,这里仅作为示例,当然全部调用也可以,多项 audience 调用表示其结果的交集
// 即是说一般情况下,下面三个方法和没有列出的 addTagAnd 一共四个,只适用一个便可满足大多数的场景需求
// ->addAlias('alias')
// ->addTag(array('tag1', 'tag2'))
// ->addRegistrationId($registration_id)
->addAllAudience()
->setNotificationAlert('Test custom')
->iosNotification('Hello IOS', array(
'sound' => 'sound.caf',
// 'badge' => '+1',
// 'content-available' => true,
// 'mutable-content' => true,
'category' => 'jiguang',
'extras' => array(
'key' => 'value',
'jiguang'
),
))
->androidNotification('Hello Android', array(
'title' => 'hello jpush',
// 'builder_id' => 2,
'extras' => array(
'key' => 'value',
'jiguang'
),
))
// voip可以传输任意键值对可用作自定义
->voip(array(
'test123' => 'val1',
'jsontest' => 2,
'booleantest' => true
))
->message('message content', array(
'title' => 'hello jpush',
// 'content_type' => 'text',
'extras' => array(
'key' => 'value',
'jiguang'
),
))
->options(array(
// sendno: 表示推送序号,纯粹用来作为 API 调用标识,
// API 返回时被原样返回,以方便 API 调用方匹配请求与返回
// 这里设置为 100 仅作为示例
// 'sendno' => 100,
// time_to_live: 表示离线消息保留时长(秒)
// 推送当前用户不在线时,为该用户保留多长时间的离线消息,以便其上线时再次推送。
// 默认 86400 1 天),最长 10 天。设置为 0 表示不保留离线消息,只有推送当前在线的用户可以收到
// 这里设置为 1 仅作为示例
'time_to_live' => 1,
// apns_production: 表示APNs是否生产环境
// True 表示推送生产环境False 表示要推送开发环境;如果不指定则默认为推送开发环境
// 'apns_production' => false,
// big_push_duration: 表示定速推送时长(分钟),又名缓慢推送,把原本尽可能快的推送速度,降低下来,
// 给定的 n 分钟内均匀地向这次推送的目标用户推送。最大值为1400.未设置则不是定速推送
// 这里设置为 1 仅作为示例
// 'big_push_duration' => 1
))
->setSmsMessage(array(
'delay_time' => 60,
'signid' => 154,
'temp_id' => 1,
'temp_para' => array(
'code' => 357
),
'active_filter' => false
))
// custom可自定义最外层参数如skd未支持部分文档功能用户可自行写入
// 这里仅作为例子展示
// ->custom(array(
// 'sms_message' => array(
// 'active_filter' => false,
// 'delay_time' => 60,
// 'signid' => 154,
// 'temp_id' => 1,
// 'temp_para' => array(
// 'code' => 357
// )),
// 'options' => array(
// 'apns_production' => false,
// 'time_to_live' => 62000,
// )
// ))
->send();
print_r($response);
} catch (\JPush\Exceptions\APIConnectionException $e) {
// try something here
print $e;
} catch (\JPush\Exceptions\APIRequestException $e) {
// try something here
print $e;
}

View File

@ -0,0 +1,5 @@
<?php
require __DIR__ . '/../config.php';
$result = $client->report()->getMessagesDetail('149646415212');
print_r($result);

View File

@ -0,0 +1,5 @@
<?php
require __DIR__ . '/../config.php';
$result = $client->report()->getReceivedDetail('149646415212');
print_r($result);

View File

@ -0,0 +1,26 @@
<?php
// 这只是使用样例不应该直接用于实际生产环境中 !!
require 'config.php';
$payload = $client->push()
->setPlatform("all")
->addAllAudience()
->setNotificationAlert("Hi, 这是一条定时发送的消息")
->build();
// 创建一个2016-12-22 13:45:00触发的定时任务
$response = $client->schedule()->createSingleSchedule("每天14点发送的定时任务", $payload, array("time"=>"2016-12-22 13:45:00"));
print_r($response);
// 创建一个每天14点发送的定时任务
$response = $client->schedule()->createPeriodicalSchedule("每天14点发送的定时任务", $payload,
array(
"start"=>"2016-12-22 13:45:00",
"end"=>"2016-12-25 13:45:00",
"time"=>"14:00:00",
"time_unit"=>"DAY",
"frequency"=>1
));
print_r($response);

View File

@ -0,0 +1,24 @@
<?php
// 这只是使用样例,不应该直接用于实际生产环境中 !!
require 'config.php';
use JPush\Client as JPush;
// 简单推送示例
// 这只是使用样例,不应该直接用于实际生产环境中 !!
$client = new JPush($app_key, $master_secret, null, null, 'BJ');
$push_payload = $client->push()
->setPlatform('all')
->addAllAudience()
->setNotificationAlert('Hi, JPush');
try {
$response = $push_payload->send();
print_r($response);
} catch (\JPush\Exceptions\APIConnectionException $e) {
// try something here
print $e;
} catch (\JPush\Exceptions\APIRequestException $e) {
// try something here
print $e;
}

15
vendor/jpush/jpush/phpunit.xml.dist vendored Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false"
syntaxCheck="true"
bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite name="JPush PHP SDK Test Suite">
<directory>tests/JPush</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,42 @@
<?php
namespace JPush;
class AdminClient {
const ADMIN_URL = 'https://admin.jpush.cn/v1/app/';
private $devKey;
private $devSecret;
private $retryTimes;
private $logFile;
function __construct($devKey, $devSecret) {
if (!is_string($devKey) || !is_string($devSecret)) {
throw new InvalidArgumentException("Invalid devKey or devSecret");
}
$this->devKey = $devKey;
$this->devSecret = $devSecret;
$this->retryTimes = 1;
$this->logFile = null;
}
public function getAuthStr() { return $this->devKey . ":" . $this->devSecret; }
public function getRetryTimes() { return $this->retryTimes; }
public function getLogFile() { return $this->logFile; }
public function createApp($appName, $androidPackage, $groupName=null) {
$url = AdminClient::ADMIN_URL;
$body = [
'app_name' => $appName,
'android_package'=> $androidPackage,
'group_name' => $groupName
];
return Http::post($this, $url, $body);
}
public function deleteApp($appKey) {
$url = AdminClient::ADMIN_URL . $appKey . '/delete';
return Http::post($this, $url, []);
}
}

71
vendor/jpush/jpush/src/JPush/Client.php vendored Normal file
View File

@ -0,0 +1,71 @@
<?php
namespace JPush;
use InvalidArgumentException;
class Client {
private $appKey;
private $masterSecret;
private $retryTimes;
private $logFile;
private $zone;
private static $zones = [
'DEFAULT' => [
'push' => 'https://api.jpush.cn/v3/',
'report' => 'https://report.jpush.cn/v3/',
'device' => 'https://device.jpush.cn/v3/devices/',
'alias' => 'https://device.jpush.cn/v3/aliases/',
'tag' => 'https://device.jpush.cn/v3/tags/',
'schedule' => 'https://api.jpush.cn/v3/schedules'
],
'BJ' => [
'push' => 'https://bjapi.push.jiguang.cn/v3/',
'report' => 'https://bjapi.push.jiguang.cn/v3/report/',
'device' => 'https://bjapi.push.jiguang.cn/v3/device/',
'alias' => 'https://bjapi.push.jiguang.cn/v3/device/aliases/',
'tag' => 'https://bjapi.push.jiguang.cn/v3/device/tags/',
'schedules' => 'https://bjapi.push.jiguang.cn/v3/push/schedules'
]
];
public function __construct($appKey, $masterSecret, $logFile=Config::DEFAULT_LOG_FILE, $retryTimes=Config::DEFAULT_MAX_RETRY_TIMES, $zone = null) {
if (!is_string($appKey) || !is_string($masterSecret)) {
throw new InvalidArgumentException("Invalid appKey or masterSecret");
}
$this->appKey = $appKey;
$this->masterSecret = $masterSecret;
if (!is_null($retryTimes)) {
$this->retryTimes = $retryTimes;
} else {
$this->retryTimes = 1;
}
$this->logFile = $logFile;
if (!is_null($zone) && in_array(strtoupper($zone), array_keys(self::$zones))) {
$this->zone = strtoupper($zone);
} else {
$this->zone = null;
}
}
public function push() { return new PushPayload($this); }
public function report() { return new ReportPayload($this); }
public function device() { return new DevicePayload($this); }
public function schedule() { return new SchedulePayload($this);}
public function getAuthStr() { return $this->appKey . ":" . $this->masterSecret; }
public function getRetryTimes() { return $this->retryTimes; }
public function getLogFile() { return $this->logFile; }
public function is_group() {
$str = substr($this->appKey, 0, 6);
return $str === 'group-';
}
public function makeURL($key) {
if (is_null($this->zone)) {
return self::$zones['DEFAULT'][$key];
} else {
return self::$zones[$this->zone][$key];
}
}
}

16
vendor/jpush/jpush/src/JPush/Config.php vendored Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace JPush;
class Config {
const DISABLE_SOUND = "_disable_Sound";
const DISABLE_BADGE = 0x10000;
const USER_AGENT = 'JPush-API-PHP-Client';
const CONNECT_TIMEOUT = 20;
const READ_TIMEOUT = 120;
const DEFAULT_MAX_RETRY_TIMES = 3;
const DEFAULT_LOG_FILE = "./jpush.log";
const HTTP_GET = 'GET';
const HTTP_POST = 'POST';
const HTTP_DELETE = 'DELETE';
const HTTP_PUT = 'PUT';
}

View File

@ -0,0 +1,224 @@
<?php
namespace JPush;
use InvalidArgumentException;
class DevicePayload {
private $client;
/**
* DevicePayload constructor.
* @param $client JPush
*/
public function __construct($client)
{
$this->client = $client;
}
public function getDevices($registrationId) {
$url = $this->client->makeURL('device') . $registrationId;
return Http::get($this->client, $url);
}
public function updateAlias($registration_id, $alias) {
return $this->updateDevice($registration_id, $alias);
}
public function addTags($registration_id, $tags) {
$tags = is_array($tags) ? $tags : array($tags);
return $this->updateDevice($registration_id, null, null, $tags);
}
public function removeTags($registration_id, $tags) {
$tags = is_array($tags) ? $tags : array($tags);
return $this->updateDevice($registration_id, null, null, null, $tags);
}
public function updateMoblie($registration_id, $mobile) {
return $this->updateDevice($registration_id, null, $mobile);
}
public function clearMobile($registrationId) {
$url = $this->client->makeURL('device') . $registrationId;
return Http::post($this->client, $url, ['mobile' => '']);
}
public function clearTags($registrationId) {
$url = $this->client->makeURL('device') . $registrationId;
return Http::post($this->client, $url, ['tags' => '']);
}
public function updateDevice($registrationId, $alias = null, $mobile = null, $addTags = null, $removeTags = null) {
$payload = array();
if (!is_string($registrationId)) {
throw new InvalidArgumentException('Invalid registration_id');
}
$aliasIsNull = is_null($alias);
$mobileIsNull = is_null($mobile);
$addTagsIsNull = is_null($addTags);
$removeTagsIsNull = is_null($removeTags);
if ($aliasIsNull && $addTagsIsNull && $removeTagsIsNull && $mobileIsNull) {
throw new InvalidArgumentException("alias, addTags, removeTags not all null");
}
if (!$aliasIsNull) {
if (is_string($alias)) {
$payload['alias'] = $alias;
} else {
throw new InvalidArgumentException("Invalid alias string");
}
}
if (!$mobileIsNull) {
if (is_string($mobile)) {
$payload['mobile'] = $mobile;
} else {
throw new InvalidArgumentException("Invalid mobile string");
}
}
$tags = array();
if (!$addTagsIsNull) {
if (is_array($addTags)) {
$tags['add'] = $addTags;
} else {
throw new InvalidArgumentException("Invalid addTags array");
}
}
if (!$removeTagsIsNull) {
if (is_array($removeTags)) {
$tags['remove'] = $removeTags;
} else {
throw new InvalidArgumentException("Invalid removeTags array");
}
}
if (count($tags) > 0) {
$payload['tags'] = $tags;
}
$url = $this->client->makeURL('device') . $registrationId;
return Http::post($this->client, $url, $payload);
}
public function getTags() {
$url = $this->client->makeURL('tag');
return Http::get($this->client, $url);
}
public function isDeviceInTag($registrationId, $tag) {
if (!is_string($registrationId)) {
throw new InvalidArgumentException("Invalid registration_id");
}
if (!is_string($tag)) {
throw new InvalidArgumentException("Invalid tag");
}
$url = $this->client->makeURL('tag') . $tag . '/registration_ids/' . $registrationId;
return Http::get($this->client, $url);
}
public function addDevicesToTag($tag, $addDevices) {
$device = is_array($addDevices) ? $addDevices : array($addDevices);
return $this->updateTag($tag, $device, null);
}
public function removeDevicesFromTag($tag, $removeDevices) {
$device = is_array($removeDevices) ? $removeDevices : array($removeDevices);
return $this->updateTag($tag, null, $device);
}
public function updateTag($tag, $addDevices = null, $removeDevices = null) {
if (!is_string($tag)) {
throw new InvalidArgumentException("Invalid tag");
}
$addDevicesIsNull = is_null($addDevices);
$removeDevicesIsNull = is_null($removeDevices);
if ($addDevicesIsNull && $removeDevicesIsNull) {
throw new InvalidArgumentException("Either or both addDevices and removeDevices must be set.");
}
$registrationId = array();
if (!$addDevicesIsNull) {
if (is_array($addDevices)) {
$registrationId['add'] = $addDevices;
} else {
throw new InvalidArgumentException("Invalid addDevices");
}
}
if (!$removeDevicesIsNull) {
if (is_array($removeDevices)) {
$registrationId['remove'] = $removeDevices;
} else {
throw new InvalidArgumentException("Invalid removeDevices");
}
}
$url = $this->client->makeURL('tag') . $tag;
$payload = array('registration_ids'=>$registrationId);
return Http::post($this->client, $url, $payload);
}
public function deleteTag($tag) {
if (!is_string($tag)) {
throw new InvalidArgumentException("Invalid tag");
}
$url = $this->client->makeURL('tag') . $tag;
return Http::delete($this->client, $url);
}
public function getAliasDevices($alias, $platform = null) {
if (!is_string($alias)) {
throw new InvalidArgumentException("Invalid alias");
}
$url = $this->client->makeURL('alias') . $alias;
if (!is_null($platform)) {
if (is_array($platform)) {
$isFirst = true;
foreach($platform as $item) {
if ($isFirst) {
$url = $url . '?platform=' . $item;
$isFirst = false;
} else {
$url = $url . ',' . $item;
}
}
} else if (is_string($platform)) {
$url = $url . '?platform=' . $platform;
} else {
throw new InvalidArgumentException("Invalid platform");
}
}
return Http::get($this->client, $url);
}
public function deleteAlias($alias) {
if (!is_string($alias)) {
throw new InvalidArgumentException("Invalid alias");
}
$url = $this->client->makeURL('alias') . $alias;
return Http::delete($this->client, $url);
}
public function getDevicesStatus($registrationId) {
if (!is_array($registrationId) && !is_string($registrationId)) {
throw new InvalidArgumentException('Invalid registration_id');
}
if (is_string($registrationId)) {
$registrationId = explode(',', $registrationId);
}
$payload = array();
if (count($registrationId) <= 0) {
throw new InvalidArgumentException('Invalid registration_id');
}
$payload['registration_ids'] = $registrationId;
$url = $this->client->makeURL('device') . 'status';
return Http::post($this->client, $url, $payload);
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace JPush\Exceptions;
class APIConnectionException extends JPushException {
function __toString() {
return "\n" . __CLASS__ . " -- {$this->message} \n";
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace JPush\Exceptions;
class APIRequestException extends JPushException {
private $http_code;
private $headers;
private static $expected_keys = array('code', 'message');
function __construct($response){
$this->http_code = $response['http_code'];
$this->headers = $response['headers'];
$body = json_decode($response['body'], true);
if (key_exists('error', $body)) {
$this->code = $body['error']['code'];
$this->message = $body['error']['message'];
} else {
$this->code = $body['code'];
$this->message = $body['message'];
}
}
public function __toString() {
return "\n" . __CLASS__ . " -- [{$this->code}]: {$this->message} \n";
}
public function getHttpCode() {
return $this->http_code;
}
public function getHeaders() {
return $this->headers;
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace JPush\Exceptions;
class JPushException extends \Exception {
function __construct($message) {
parent::__construct($message);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace JPush\Exceptions;
class ServiceNotAvaliable extends JPushException {
private $http_code;
private $headers;
function __construct($response){
$this->http_code = $response['http_code'];
$this->headers = $response['headers'];
$this->message = $response['body'];
}
function __toString() {
return "\n" . __CLASS__ . " -- [{$this->http_code}]: {$this->message} \n";
}
public function getHttpCode() {
return $this->http_code;
}
public function getHeaders() {
return $this->headers;
}
}

139
vendor/jpush/jpush/src/JPush/Http.php vendored Normal file
View File

@ -0,0 +1,139 @@
<?php
namespace JPush;
use JPush\Exceptions\APIConnectionException;
use JPush\Exceptions\APIRequestException;
use JPush\Exceptions\ServiceNotAvaliable;
final class Http {
public static function get($client, $url) {
$response = self::sendRequest($client, $url, Config::HTTP_GET, $body=null);
return self::processResp($response);
}
public static function post($client, $url, $body) {
$response = self::sendRequest($client, $url, Config::HTTP_POST, $body);
return self::processResp($response);
}
public static function put($client, $url, $body) {
$response = self::sendRequest($client, $url, Config::HTTP_PUT, $body);
return self::processResp($response);
}
public static function delete($client, $url) {
$response = self::sendRequest($client, $url, Config::HTTP_DELETE, $body=null);
return self::processResp($response);
}
private static function sendRequest($client, $url, $method, $body=null, $times=1) {
self::log($client, "Send " . $method . " " . $url . ", body:" . json_encode($body) . ", times:" . $times);
if (!defined('CURL_HTTP_VERSION_2_0')) {
define('CURL_HTTP_VERSION_2_0', 3);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_USERAGENT, Config::USER_AGENT);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, Config::CONNECT_TIMEOUT); // 连接建立最长耗时
curl_setopt($ch, CURLOPT_TIMEOUT, Config::READ_TIMEOUT); // 请求最长耗时
// 设置SSL版本 1=CURL_SSLVERSION_TLSv1, 不指定使用默认值,curl会自动获取需要使用的CURL版本
// curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// 如果报证书相关失败,可以考虑取消注释掉该行,强制指定证书版本
//curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'TLSv1');
// 设置Basic认证
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $client->getAuthStr());
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
// 设置Post参数
if ($method === Config::HTTP_POST) {
curl_setopt($ch, CURLOPT_POST, true);
} else if ($method === Config::HTTP_DELETE || $method === Config::HTTP_PUT) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
}
if (!is_null($body)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Connection: Keep-Alive'
));
$output = curl_exec($ch);
$response = array();
$errorCode = curl_errno($ch);
// $msg = '';
// $data = json_decode($body, true);
// if (isset($data['options']['sendno'])) {
// $sendno = $data['options']['sendno'];
// $msg = 'sendno: ' . $sendno;
// }
$msg = '';
if (isset($body['options']['sendno'])) {
$sendno = $body['options']['sendno'];
$msg = 'sendno: ' . $sendno;
}
if ($errorCode) {
$retries = $client->getRetryTimes();
if ($times < $retries) {
return self::sendRequest($client, $url, $method, $body, ++$times);
} else {
if ($errorCode === 28) {
throw new APIConnectionException($msg . "Response timeout. Your request has probably be received by JPush Server,please check that whether need to be pushed again." );
} elseif ($errorCode === 56) {
// resolve error[56 Problem (2) in the Chunked-Encoded data]
throw new APIConnectionException($msg . "Response timeout, maybe cause by old CURL version. Your request has probably be received by JPush Server, please check that whether need to be pushed again.");
} else {
throw new APIConnectionException("$msg . Connect timeout. Please retry later. Error:" . $errorCode . " " . curl_error($ch));
}
}
} else {
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header_text = substr($output, 0, $header_size);
$body = substr($output, $header_size);
$headers = array();
foreach (explode("\r\n", $header_text) as $i => $line) {
if (!empty($line)) {
if ($i === 0) {
$headers[0] = $line;
} else if (strpos($line, ": ")) {
list ($key, $value) = explode(': ', $line);
$headers[$key] = $value;
}
}
}
$response['headers'] = $headers;
$response['body'] = $body;
$response['http_code'] = $httpCode;
}
curl_close($ch);
return $response;
}
public static function processResp($response) {
$data = json_decode($response['body'], true);
if ($response['http_code'] === 200) {
$result = array();
$result['body'] = $data;
$result['http_code'] = $response['http_code'];
$result['headers'] = $response['headers'];
return $result;
} elseif (is_null($data)) {
throw new ServiceNotAvaliable($response);
} else {
throw new APIRequestException($response);
}
}
public static function log($client, $content) {
if (!is_null($client->getLogFile())) {
error_log($content . "\r\n", 3, $client->getLogFile());
}
}
}

View File

@ -0,0 +1,761 @@
<?php
namespace JPush;
use InvalidArgumentException;
class PushPayload {
private static $EFFECTIVE_DEVICE_TYPES = array('ios', 'android', 'winphone');
private $client;
private $url;
private $cid;
private $platform;
private $audience;
private $tags;
private $tagAnds;
private $tagNots;
private $alias;
private $registrationIds;
private $segmentIds;
private $abtests;
private $notificationAlert;
private $iosNotification;
private $androidNotification;
private $winPhoneNotification;
private $voip;
private $smsMessage;
private $message;
private $options;
private $custom;
/**
* PushPayload constructor.
* @param $client JPush
*/
function __construct($client) {
$this->client = $client;
$url = $this->client->is_group() ? 'grouppush' : 'push';
$this->url = $this->client->makeURL('push') . $url;
}
public function getCid($count = 1, $type = 'push') {
$url = $this->client->makeURL('push') . 'push/cid?count=' . $count . '&type=' . $type;
return Http::get($this->client, $url);
}
public function setCid($cid) {
$this->cid = trim($cid);
return $this;
}
public function setPlatform($platform) {
# $required_keys = array('all', 'android', 'ios', 'winphone');
if (is_string($platform)) {
$ptf = strtolower($platform);
if ('all' === $ptf) {
$this->platform = 'all';
} elseif (in_array($ptf, self::$EFFECTIVE_DEVICE_TYPES)) {
$this->platform = array($ptf);
}
} elseif (is_array($platform)) {
$ptf = array_map('strtolower', $platform);
$this->platform = array_intersect($ptf, self::$EFFECTIVE_DEVICE_TYPES);
}
return $this;
}
public function setAudience($all) {
if (strtolower($all) === 'all') {
$this->addAllAudience();
return $this;
} else {
throw new InvalidArgumentException('Invalid audience value');
}
}
public function addAllAudience() {
$this->audience = "all";
return $this;
}
public function addTag($tag) {
return $this->updateAudience('tags', $tag, 'tag');
}
public function addTagAnd($tag) {
return $this->updateAudience('tagAnds', $tag, 'tag_and');
}
public function addTagNot($tag) {
return $this->updateAudience('tagNots', $tag, 'tag_not');
}
public function addAlias($alias) {
return $this->updateAudience('alias', $alias, 'alias');
}
public function addRegistrationId($registrationId) {
return $this->updateAudience('registrationIds', $registrationId, 'registration_id');
}
public function addSegmentId($segmentId) {
return $this->updateAudience('segmentIds', $segmentId, 'segment');
}
public function addAbtest($abtest) {
return $this->updateAudience('abtests', $abtest, 'abtest');
}
private function updateAudience($key, $value, $name) {
if (is_null($this->$key)) {
$this->$key = array();
}
if (is_array($value)) {
foreach($value as $v) {
if (!is_string($v)) {
throw new InvalidArgumentException("Invalid $name value");
}
if (!in_array($v, $this->$key)) {
array_push($this->$key, $v);
}
}
} else if (is_string($value)) {
if (!in_array($value, $this->$key)) {
array_push($this->$key, $value);
}
} else {
throw new InvalidArgumentException("Invalid $name value");
}
return $this;
}
public function setNotificationAlert($alert) {
if (!is_string($alert)) {
throw new InvalidArgumentException("Invalid alert value");
}
$this->notificationAlert = $alert;
return $this;
}
public function addWinPhoneNotification($alert=null, $title=null, $_open_page=null, $extras=null) {
$winPhone = array();
if (!is_null($alert)) {
if (!is_string($alert)) {
throw new InvalidArgumentException("Invalid winphone notification");
}
$winPhone['alert'] = $alert;
}
if (!is_null($title)) {
if (!is_string($title)) {
throw new InvalidArgumentException("Invalid winphone title notification");
}
if(strlen($title) > 0) {
$winPhone['title'] = $title;
}
}
if (!is_null($_open_page)) {
if (!is_string($_open_page)) {
throw new InvalidArgumentException("Invalid winphone _open_page notification");
}
if (strlen($_open_page) > 0) {
$winPhone['_open_page'] = $_open_page;
}
}
if (!is_null($extras)) {
if (!is_array($extras)) {
throw new InvalidArgumentException("Invalid winphone extras notification");
}
if (count($extras) > 0) {
$winPhone['extras'] = $extras;
}
}
if (count($winPhone) <= 0) {
throw new InvalidArgumentException("Invalid winphone notification");
}
$this->winPhoneNotification = $winPhone;
return $this;
}
public function setSms($delay_time, $temp_id, array $temp_para = []) {
$sms = array();
$sms['temp_id'] = $temp_id;
$sms['delay_time'] = ($delay_time === 0 || (is_int($delay_time) && $delay_time > 0 && $delay_time <= 86400)) ? $delay_time : 0;
if (!empty($temp_para)) {
$sms['temp_para'] = $temp_para;
}
$this->smsMessage = $sms;
return $this;
}
public function build() {
$payload = array();
// validate platform
if (is_null($this->platform)) {
throw new InvalidArgumentException("platform must be set");
}
$payload["platform"] = $this->platform;
if (!is_null($this->cid)) {
$payload['cid'] = $this->cid;
}
// validate audience
$audience = array();
if (!is_null($this->tags)) {
$audience["tag"] = $this->tags;
}
if (!is_null($this->tagAnds)) {
$audience["tag_and"] = $this->tagAnds;
}
if (!is_null($this->tagNots)) {
$audience["tag_not"] = $this->tagNots;
}
if (!is_null($this->alias)) {
$audience["alias"] = $this->alias;
}
if (!is_null($this->registrationIds)) {
$audience["registration_id"] = $this->registrationIds;
}
if (!is_null($this->segmentIds)) {
$audience["segment"] = $this->segmentIds;
}
if (!is_null($this->abtests)) {
$audience["abtest"] = $this->abtests;
}
if (is_null($this->audience) && count($audience) <= 0) {
throw new InvalidArgumentException("audience must be set");
} else if (!is_null($this->audience) && count($audience) > 0) {
throw new InvalidArgumentException("you can't add tags/alias/registration_id/tag_and when audience='all'");
} else if (is_null($this->audience)) {
$payload["audience"] = $audience;
} else {
$payload["audience"] = $this->audience;
}
// validate notification
$notification = array();
if (!is_null($this->notificationAlert)) {
$notification['alert'] = $this->notificationAlert;
}
if (!is_null($this->androidNotification)) {
$notification['android'] = $this->androidNotification;
if (is_null($this->androidNotification['alert'])) {
if (is_null($this->notificationAlert)) {
throw new InvalidArgumentException("Android alert can not be null");
} else {
$notification['android']['alert'] = $this->notificationAlert;
}
}
}
if (!is_null($this->iosNotification)) {
$notification['ios'] = $this->iosNotification;
if (is_null($this->iosNotification['alert'])) {
if (is_null($this->notificationAlert)) {
throw new InvalidArgumentException("iOS alert can not be null");
} else {
$notification['ios']['alert'] = $this->notificationAlert;
}
}
}
if (!is_null($this->winPhoneNotification)) {
$notification['winphone'] = $this->winPhoneNotification;
if (is_null($this->winPhoneNotification['alert'])) {
if (is_null($this->winPhoneNotification)) {
throw new InvalidArgumentException("WinPhone alert can not be null");
} else {
$notification['winphone']['alert'] = $this->notificationAlert;
}
}
}
if (!is_null($this->voip)) {
$notification['voip'] = $this->voip;
}
if (count($notification) > 0) {
$payload['notification'] = $notification;
}
if (!is_null($this->message)) {
$payload['message'] = $this->message;
}
if (!array_key_exists('notification', $payload) && !array_key_exists('message', $payload)) {
throw new InvalidArgumentException('notification and message can not all be null');
}
if (!is_null($this->smsMessage)) {
$payload['sms_message'] = $this->smsMessage;
}
if (is_null($this->options)) {
$this->options();
}
$payload['options'] = $this->options;
if (!is_null($this->custom)) {
foreach($this->custom as $key=>$val) {
$payload[$key] = $val;
}
}
return $payload;
}
public function toJSON() {
$payload = $this->build();
return json_encode($payload);
}
public function printJSON() {
echo $this->toJSON();
return $this;
}
public function send() {
return Http::post($this->client, $this->url, $this->build());
}
public function validate() {
$url = $this->client->makeURL('push') . '/push/validate';
return Http::post($this->client, $url, $this->build());
}
private function generateSendno() {
return rand(100000, getrandmax());
}
# new methods
public function iosNotification($alert = '', array $notification = array()) {
$ios = array();
$ios['alert'] = (is_string($alert) || is_array($alert)) ? $alert : '';
if (!empty($notification)) {
if (isset($notification['sound'])) {
if (is_string($notification['sound']) || is_array($notification['sound'])) {
$ios['sound'] = $notification['sound'];
} else {
unset($notification['sound']);
}
}
if (isset($notification['content-available'])) {
if (is_bool($notification['content-available'])) {
$ios['content-available'] = $notification['content-available'];
} else {
unset($notification['content-available']);
}
}
if (isset($notification['mutable-content'])) {
if (is_bool($notification['mutable-content'])) {
$ios['mutable-content'] = $notification['mutable-content'];
} else {
unset($notification['mutable-content']);
}
}
if (isset($notification['extras'])) {
if (is_array($notification['extras']) && !empty($notification['extras'])) {
$ios['extras'] = $notification['extras'];
} else {
unset($notification['extras']);
}
}
$ios = array_merge($notification, $ios);
}
if (!isset($ios['sound'])) {
$ios['sound'] = '';
}
if (!isset($ios['badge'])) {
$ios['badge'] = '+1';
}
$this->iosNotification = $ios;
return $this;
}
public function androidNotification($alert = '', array $notification = array()) {
$android = array();
$android['alert'] = is_string($alert) ? $alert : '';
if (!empty($notification)) {
if (isset($notification['builder_id'])) {
if (is_int($notification['builder_id'])) {
$android['builder_id'] = $notification['builder_id'];
} else {
unset($notification['builder_id']);
}
}
if (isset($notification['priority'])) {
if (is_int($notification['priority'])) {
$android['priority'] = $notification['priority'];
} else {
unset($notification['priority']);
}
}
if (isset($notification['style'])) {
if (is_int($notification['style'])) {
$android['style'] = $notification['style'];
} else {
unset($notification['style']);
}
}
if (isset($notification['alert_type'])) {
if (is_int($notification['alert_type'])) {
$android['alert_type'] = $notification['alert_type'];
} else {
unset($notification['alert_type']);
}
}
if (isset($notification['inbox'])) {
if (is_array($notification['inbox']) && !empty($notification['inbox'])) {
$android['inbox'] = $notification['inbox'];
} else {
unset($notification['inbox']);
}
}
if (isset($notification['intent'])) {
if (is_array($notification['intent']) && !empty($notification['intent'])) {
$android['intent'] = $notification['intent'];
} else {
unset($notification['intent']);
}
}
if (isset($notification['extras'])) {
if (is_array($notification['extras']) && !empty($notification['extras'])) {
$android['extras'] = $notification['extras'];
} else {
unset($notification['extras']);
}
}
$android = array_merge($notification, $android);
}
$this->androidNotification = $android;
return $this;
}
/**
* Voip in notification
* could add any custom key/value into it
*/
public function voip (array $extras = array()) {
$voip = array();
if(!empty($extras)) {
foreach($extras as $key=>$val) {
$voip[$key] = $val;
}
}
$voip = array_merge($extras, $voip);
$this->voip=$voip;
return $this;
}
public function message($msg_content, array $msg = array()) {
# $required_keys = array('title', 'content_type', 'extras');
if (is_string($msg_content)) {
$message = array();
$message['msg_content'] = $msg_content;
if (!empty($msg)) {
if (isset($msg['title']) && is_string($msg['title'])) {
$message['title'] = $msg['title'];
}
if (isset($msg['content_type']) && is_string($msg['content_type'])) {
$message['content_type'] = $msg['content_type'];
}
if (isset($msg['extras']) && is_array($msg['extras']) && !empty($msg['extras'])) {
$message['extras'] = $msg['extras'];
}
}
$this->message = $message;
}
return $this;
}
public function options(array $opts = array()) {
# $required_keys = array('sendno', 'time_to_live', 'override_msg_id', 'apns_production', 'apns_collapse_id', 'big_push_duration');
$options = array();
if (isset($opts['sendno'])) {
$options['sendno'] = $opts['sendno'];
} else {
$options['sendno'] = $this->generateSendno();
}
if (isset($opts['time_to_live']) && $opts['time_to_live'] <= 864000 && $opts['time_to_live'] >= 0) {
$options['time_to_live'] = $opts['time_to_live'];
}
if (isset($opts['override_msg_id'])) {
$options['override_msg_id'] = $opts['override_msg_id'];
}
if (isset($opts['apns_production'])) {
$options['apns_production'] = (bool)$opts['apns_production'];
} else {
$options['apns_production'] = false;
}
if (isset($opts['apns_collapse_id'])) {
$options['apns_collapse_id'] = $opts['apns_collapse_id'];
}
if (isset($opts['big_push_duration']) && $opts['big_push_duration'] <= 1400 && $opts['big_push_duration'] >= 0) {
$options['big_push_duration'] = $opts['big_push_duration'];
}
$options = array_merge($opts, $options);
$this->options = $options;
return $this;
}
public function custom (array $extras = array()) {
if(!empty($extras)) {
$this->custom=$extras;
}
return $this;
}
###############################################################################
############# 以下函数已过期,不推荐使用,仅作为兼容接口存在 #########################
###############################################################################
public function addIosNotification($alert=null, $sound=null, $badge=null, $content_available=null, $category=null, $extras=null) {
$ios = array();
if (!is_null($alert)) {
if (!is_string($alert) && !is_array($alert)) {
throw new InvalidArgumentException("Invalid ios alert value");
}
$ios['alert'] = $alert;
}
if (!is_null($sound)) {
if (!is_string($sound)) {
throw new InvalidArgumentException("Invalid ios sound value");
}
if ($sound !== Config::DISABLE_SOUND) {
$ios['sound'] = $sound;
}
} else {
// 默认sound为''
$ios['sound'] = '';
}
if (!is_null($badge)) {
if (is_string($badge) && !preg_match("/^[+-]{1}[0-9]{1,3}$/", $badge)) {
if (!is_int($badge)) {
throw new InvalidArgumentException("Invalid ios badge value");
}
}
if ($badge != Config::DISABLE_BADGE) {
$ios['badge'] = $badge;
}
} else {
// 默认badge为'+1'
$ios['badge'] = '+1';
}
if (!is_null($content_available)) {
if (!is_bool($content_available)) {
throw new InvalidArgumentException("Invalid ios content-available value");
}
$ios['content-available'] = $content_available;
}
if (!is_null($category)) {
if (!is_string($category)) {
throw new InvalidArgumentException("Invalid ios category value");
}
if (strlen($category)) {
$ios['category'] = $category;
}
}
if (!is_null($extras)) {
if (!is_array($extras)) {
throw new InvalidArgumentException("Invalid ios extras value");
}
if (count($extras) > 0) {
$ios['extras'] = $extras;
}
}
if (count($ios) <= 0) {
throw new InvalidArgumentException("Invalid iOS notification");
}
$this->iosNotification = $ios;
return $this;
}
public function addAndroidNotification($alert=null, $title=null, $builderId=null, $extras=null) {
$android = array();
if (!is_null($alert)) {
if (!is_string($alert)) {
throw new InvalidArgumentException("Invalid android alert value");
}
$android['alert'] = $alert;
}
if (!is_null($title)) {
if(!is_string($title)) {
throw new InvalidArgumentException("Invalid android title value");
}
if(strlen($title) > 0) {
$android['title'] = $title;
}
}
if (!is_null($builderId)) {
if (!is_int($builderId)) {
throw new InvalidArgumentException("Invalid android builder_id value");
}
$android['builder_id'] = $builderId;
}
if (!is_null($extras)) {
if (!is_array($extras)) {
throw new InvalidArgumentException("Invalid android extras value");
}
if (count($extras) > 0) {
$android['extras'] = $extras;
}
}
if (count($android) <= 0) {
throw new InvalidArgumentException("Invalid android notification");
}
$this->androidNotification = $android;
return $this;
}
public function setMessage($msg_content, $title=null, $content_type=null, $extras=null) {
$message = array();
if (is_null($msg_content) || !is_string($msg_content)) {
throw new InvalidArgumentException("Invalid message content");
} else {
$message['msg_content'] = $msg_content;
}
if (!is_null($title)) {
if (!is_string($title)) {
throw new InvalidArgumentException("Invalid message title");
}
$message['title'] = $title;
}
if (!is_null($content_type)) {
if (!is_string($content_type)) {
throw new InvalidArgumentException("Invalid message content type");
}
$message["content_type"] = $content_type;
}
if (!is_null($extras)) {
if (!is_array($extras)) {
throw new InvalidArgumentException("Invalid message extras");
}
if (count($extras) > 0) {
$message['extras'] = $extras;
}
}
$this->message = $message;
return $this;
}
public function setSmsMessage($smsMessage) {
$this->smsMessage = $smsMessage;
return $this;
}
public function setOptions($sendno=null, $time_to_live=null, $override_msg_id=null, $apns_production=null, $big_push_duration=null) {
$options = array();
if (!is_null($sendno)) {
if (!is_int($sendno)) {
throw new InvalidArgumentException('Invalid option sendno');
}
$options['sendno'] = $sendno;
} else {
$options['sendno'] = $this->generateSendno();
}
if (!is_null($time_to_live)) {
if (!is_int($time_to_live) || $time_to_live < 0 || $time_to_live > 864000) {
throw new InvalidArgumentException('Invalid option time to live, it must be a int and in [0, 864000]');
}
$options['time_to_live'] = $time_to_live;
}
if (!is_null($override_msg_id)) {
if (!is_long($override_msg_id)) {
throw new InvalidArgumentException('Invalid option override msg id');
}
$options['override_msg_id'] = $override_msg_id;
}
if (!is_null($apns_production)) {
if (!is_bool($apns_production)) {
throw new InvalidArgumentException('Invalid option apns production');
}
$options['apns_production'] = $apns_production;
} else {
$options['apns_production'] = false;
}
if (!is_null($big_push_duration)) {
if (!is_int($big_push_duration) || $big_push_duration < 0 || $big_push_duration > 1440) {
throw new InvalidArgumentException('Invalid option big push duration, it must be a int and in [0, 1440]');
}
$options['big_push_duration'] = $big_push_duration;
}
$this->options = $options;
return $this;
}
/*
针对RegID方式批量单推
https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#vip
*/
public function batchPushByRegid(array $singlePayloads) {
$body = array(
"pushlist"=>array()
);
$response = $this -> getCid(count($singlePayloads), 'push');
$cidlist = $response['body']['cidlist'];
foreach ($cidlist as $i => $cid) {
$body["pushlist"][$cid] = $singlePayloads[$i];
}
$url = $this->client->makeURL('push') . 'push/batch/regid/single';
return Http::post($this->client, $url, $body);
}
/*
针对Alias方式批量单推
https://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#vip
*/
public function batchPushByAlias(array $singlePayloads) {
$body = array(
"pushlist"=>array()
);
$response = $this -> getCid(count($singlePayloads), 'push');
$cidlist = $response['body']['cidlist'];
foreach ($cidlist as $i => $cid) {
$body["pushlist"][$cid] = $singlePayloads[$i];
}
$url = $this->client->makeURL('push') . 'push/batch/alias/single';
return Http::post($this->client, $url, $body);
}
}

View File

@ -0,0 +1,109 @@
<?php
namespace JPush;
use InvalidArgumentException;
class ReportPayload {
private static $EFFECTIVE_TIME_UNIT = array('HOUR', 'DAY', 'MONTH');
private $client;
/**
* ReportPayload constructor.
* @param $client JPush
*/
public function __construct($client)
{
$this->client = $client;
}
public function getReceived($msgIds) {
$queryParams = '?msg_ids=';
if (is_array($msgIds) && !empty($msgIds)) {
$msgIdsStr = implode(',', $msgIds);
$queryParams .= $msgIdsStr;
} elseif (is_string($msgIds)) {
$queryParams .= $msgIds;
} else {
throw new InvalidArgumentException("Invalid msg_ids");
}
$url = $this->client->makeURL('report') . 'received' . $queryParams;
return Http::get($this->client, $url);
}
/*
送达统计详情(新)
https://docs.jiguang.cn/jpush/server/push/rest_api_v3_report/#_7
*/
public function getReceivedDetail($msgIds) {
$queryParams = '?msg_ids=';
if (is_array($msgIds) && !empty($msgIds)) {
$msgIdsStr = implode(',', $msgIds);
$queryParams .= $msgIdsStr;
} elseif (is_string($msgIds)) {
$queryParams .= $msgIds;
} else {
throw new InvalidArgumentException("Invalid msg_ids");
}
$url = $this->client->makeURL('report') . 'received/detail' . $queryParams;
return Http::get($this->client, $url);
}
public function getMessageStatus($msgId, $rids, $data = null) {
$url = $this->client->makeURL('report') . 'status/message';
$registrationIds = is_array($rids) ? $rids : array($rids);
$body = [
'msg_id' => $msgId,
'registration_ids' => $registrationIds
];
if (!is_null($data)) {
$body['data'] = $data;
}
return Http::post($this->client, $url, $body);
}
public function getMessages($msgIds) {
$queryParams = '?msg_ids=';
if (is_array($msgIds) && !empty($msgIds)) {
$msgIdsStr = implode(',', $msgIds);
$queryParams .= $msgIdsStr;
} elseif (is_string($msgIds)) {
$queryParams .= $msgIds;
} else {
throw new InvalidArgumentException("Invalid msg_ids");
}
$url = $this->client->makeURL('report') . 'messages/' .$queryParams;
return Http::get($this->client, $url);
}
/*
消息统计详情VIP 专属接口,新)
https://docs.jiguang.cn/jpush/server/push/rest_api_v3_report/#vip_1
*/
public function getMessagesDetail($msgIds) {
$queryParams = '?msg_ids=';
if (is_array($msgIds) && !empty($msgIds)) {
$msgIdsStr = implode(',', $msgIds);
$queryParams .= $msgIdsStr;
} elseif (is_string($msgIds)) {
$queryParams .= $msgIds;
} else {
throw new InvalidArgumentException("Invalid msg_ids");
}
$url = $this->client->makeURL('report') . 'messages/detail' .$queryParams;
return Http::get($this->client, $url);
}
public function getUsers($time_unit, $start, $duration) {
$time_unit = strtoupper($time_unit);
if (!in_array($time_unit, self::$EFFECTIVE_TIME_UNIT)) {
throw new InvalidArgumentException('Invalid time unit');
}
$url = $this->client->makeURL('report') . 'users/?time_unit=' . $time_unit . '&start=' . $start . '&duration=' . $duration;
return Http::get($this->client, $url);
}
}

View File

@ -0,0 +1,182 @@
<?php
namespace JPush;
use InvalidArgumentException;
class SchedulePayload {
private $client;
/**
* SchedulePayload constructor.
* @param $client JPush
*/
public function __construct($client) {
$this->client = $client;
}
public function createSingleSchedule($name, $push_payload, $trigger) {
if (!is_string($name)) {
throw new InvalidArgumentException('Invalid schedule name');
}
if (!is_array($push_payload)) {
throw new InvalidArgumentException('Invalid schedule push payload');
}
if (!is_array($trigger)) {
throw new InvalidArgumentException('Invalid schedule trigger');
}
$payload = array();
$payload['name'] = $name;
$payload['enabled'] = true;
$payload['trigger'] = array("single"=>$trigger);
$payload['push'] = $push_payload;
$url = $this->client->makeURL('schedule');
return Http::post($this->client, $url, $payload);
}
public function createPeriodicalSchedule($name, $push_payload, $trigger) {
if (!is_string($name)) {
throw new InvalidArgumentException('Invalid schedule name');
}
if (!is_array($push_payload)) {
throw new InvalidArgumentException('Invalid schedule push payload');
}
if (!is_array($trigger)) {
throw new InvalidArgumentException('Invalid schedule trigger');
}
$payload = array();
$payload['name'] = $name;
$payload['enabled'] = true;
$payload['trigger'] = array("periodical"=>$trigger);
$payload['push'] = $push_payload;
$url = $this->client->makeURL('schedule');
return Http::post($this->client, $url, $payload);
}
public function updateSingleSchedule($schedule_id, $name=null, $enabled=null, $push_payload=null, $trigger=null) {
if (!is_string($schedule_id)) {
throw new InvalidArgumentException('Invalid schedule id');
}
$payload = array();
if (!is_null($name)) {
if (!is_string($name)) {
throw new InvalidArgumentException('Invalid schedule name');
} else {
$payload['name'] = $name;
}
}
if (!is_null($enabled)) {
if (!is_bool($enabled)) {
throw new InvalidArgumentException('Invalid schedule enable');
} else {
$payload['enabled'] = $enabled;
}
}
if (!is_null($push_payload)) {
if (!is_array($push_payload)) {
throw new InvalidArgumentException('Invalid schedule push payload');
} else {
$payload['push'] = $push_payload;
}
}
if (!is_null($trigger)) {
if (!is_array($trigger)) {
throw new InvalidArgumentException('Invalid schedule trigger');
} else {
$payload['trigger'] = array("single"=>$trigger);
}
}
if (count($payload) <= 0) {
throw new InvalidArgumentException('Invalid schedule, name, enabled, trigger, push can not all be null');
}
$url = $this->client->makeURL('schedule') . "/" . $schedule_id;
return Http::put($this->client, $url, $payload);
}
public function updatePeriodicalSchedule($schedule_id, $name=null, $enabled=null, $push_payload=null, $trigger=null) {
if (!is_string($schedule_id)) {
throw new InvalidArgumentException('Invalid schedule id');
}
$payload = array();
if (!is_null($name)) {
if (!is_string($name)) {
throw new InvalidArgumentException('Invalid schedule name');
} else {
$payload['name'] = $name;
}
}
if (!is_null($enabled)) {
if (!is_bool($enabled)) {
throw new InvalidArgumentException('Invalid schedule enable');
} else {
$payload['enabled'] = $enabled;
}
}
if (!is_null($push_payload)) {
if (!is_array($push_payload)) {
throw new InvalidArgumentException('Invalid schedule push payload');
} else {
$payload['push'] = $push_payload;
}
}
if (!is_null($trigger)) {
if (!is_array($trigger)) {
throw new InvalidArgumentException('Invalid schedule trigger');
} else {
$payload['trigger'] = array("periodical"=>$trigger);
}
}
if (count($payload) <= 0) {
throw new InvalidArgumentException('Invalid schedule, name, enabled, trigger, push can not all be null');
}
$url = $this->client->makeURL('schedule') . "/" . $schedule_id;
return Http::put($this->client, $url, $payload);
}
public function getSchedules($page = 1) {
if (!is_int($page)) {
$page = 1;
}
$url = $this->client->makeURL('schedule') . "?page=" . $page;
return Http::get($this->client, $url);
}
public function getSchedule($schedule_id) {
if (!is_string($schedule_id)) {
throw new InvalidArgumentException('Invalid schedule id');
}
$url = $this->client->makeURL('schedule') . "/" . $schedule_id;
return Http::get($this->client, $url);
}
public function deleteSchedule($schedule_id) {
if (!is_string($schedule_id)) {
throw new InvalidArgumentException('Invalid schedule id');
}
$url = $this->client->makeURL('schedule') . "/" . $schedule_id;
return Http::delete($this->client, $url);
}
public function getMsgIds($schedule_id) {
if (!is_string($schedule_id)) {
throw new InvalidArgumentException('Invalid schedule id');
}
$url = $this->client->makeURL('schedule') . '/' . $schedule_id . '/msg_ids';
return Http::get($this->client, $url);
}
}

View File

@ -0,0 +1,4 @@
<?php
namespace JPush;
const VERSION = '3.6.5';

View File

@ -0,0 +1,131 @@
<?php
namespace JPush\Tests;
use PHPUnit\Framework\TestCase;
class DevicePayloadTest extends TestCase {
protected function setUp() {
global $client;
$this->device = $client->device();
$this->test_tag = 'jpush_tag';
}
function testGetDevices() {
global $registration_id;
$response = $this->device->getDevices($registration_id);
$this->assertEquals('200', $response['http_code']);
echo "HTTP HEADERS ARE: ";
print_r($response['headers']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(3, count($body));
$this->assertArrayHasKey('tags', $body);
$this->assertArrayHasKey('alias', $body);
$this->assertArrayHasKey('mobile', $body);
$this->assertTrue(is_array($body['tags']));
}
/**
* @expectedException \JPush\Exceptions\APIRequestException
* @expectedExceptionCode 7002
*/
function testGetDevicesWithInvalidRid() {
$response = $this->device->getDevices('INVALID_REGISTRATION_ID');
}
function testUpdateDevicesAlias() {
global $registration_id;
$result = $this->device->getDevices($registration_id);
$old_alias = $result['body']['alias'];
if ($old_alias == null) {
$old_alias = '';
}
$new_alias = 'jpush_alias';
if ($old_alias == $new_alias) {
$new_alias = $new_alias . time();
}
$response = $this->device->updateAlias($registration_id, $new_alias);
$this->assertEquals('200', $response['http_code']);
$response = $this->device->updateAlias($registration_id, $old_alias);
$this->assertEquals('200', $response['http_code']);
}
function testUpdateDevicesTags() {
global $registration_id;
$new_tag = $this->test_tag;
$response = $this->device->addTags($registration_id, array($new_tag));
$this->assertEquals('200', $response['http_code']);
$response = $this->device->removeTags($registration_id, array($new_tag));
$this->assertEquals('200', $response['http_code']);
}
function testGetTags() {
$response = $this->device->getTags();
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(1, count($body));
$this->assertArrayHasKey('tags', $body);
}
function testIsDeviceInTag() {
global $registration_id;
$test_tag = $this->test_tag;
$this->device->addTags($registration_id, array($test_tag));
$response = $this->device->isDeviceInTag($registration_id, $test_tag);
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertTrue($body['result']);
$this->device->removeTags($registration_id, array($test_tag));
$response = $this->device->isDeviceInTag($registration_id, $test_tag);
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertFalse($body['result']);
}
function testUpdateTag() {
global $registration_id;
$test_tag = $this->test_tag;
$response = $this->device->addDevicesToTag($test_tag, array($registration_id));
$this->assertEquals('200', $response['http_code']);
$response = $this->device->removeDevicesFromTag($test_tag, array($registration_id));
$this->assertEquals('200', $response['http_code']);
}
function testDeleteTag() {}
function testGetAliasDevices() {
$test_tag = $this->test_tag;
$response = $this->device->getAliasDevices($test_tag);
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(1, count($body));
$this->assertArrayHasKey('registration_ids', $body);
}
function testDeleteAlias() {}
function testGetDevicesStatus() {
global $registration_id;
$response = $this->device->getDevicesStatus($registration_id);
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(1, count($body));
}
}

View File

@ -0,0 +1,315 @@
<?php
namespace JPush\Tests;
use PHPUnit\Framework\TestCase;
class PushPayloadTest extends TestCase {
protected function setUp() {
global $client;
$this->payload = $client->push()
->setPlatform('all')
->addAllAudience()
->setNotificationAlert('Hello JPush');
$this->payload_without_audience = $client->push()
->setPlatform('all')
->setNotificationAlert('Hello JPush');
}
public function testSimplePushToAll() {
$payload = $this->payload;
$result = $payload->build();
$this->assertTrue(is_array($result));
$this->assertEquals(4, count($result));
$this->assertArrayHasKey('platform', $result);
$this->assertArrayHasKey('audience', $result);
$this->assertArrayHasKey('notification', $result);
$this->assertArrayHasKey('options', $result);
}
public function testSetPlatform() {
$payload = $this->payload;
$result = $payload->build();
$this->assertEquals('all', $result['platform']);
$result = $payload->setPlatform('ios')->build();
$this->assertTrue(is_array($result['platform']));
$this->assertEquals(1, count($result['platform']));
$this->assertTrue(in_array('ios', $result['platform']));
$result = $payload->setPlatform(array('ios', 'android', 'blackberry'))->build();
$this->assertTrue(is_array($result['platform']));
$this->assertEquals(2, count($result['platform']));
$this->assertFalse(in_array('blackberry', $result['platform']));
}
public function testSetAudience() {
$result = $this->payload->build();
$this->assertEquals('all', $result['audience']);
}
public function testAddTag() {
$payload = $this->payload_without_audience;
$result = $payload->addTag('hello')->build();
$audience = $result['audience'];
$this->assertTrue(is_array($audience['tag']));
$this->assertEquals(1, count($audience['tag']));
$result = $payload->addTag(array('jpush', 'jiguang'))->build();
$this->assertEquals(3, count($result['audience']['tag']));
}
public function testAddTag2() {
$payload = $this->payload_without_audience;
$result = $payload->addTagAnd(array('jpush', 'jiguang'))->build();
$audience = $result['audience'];
$this->assertTrue(is_array($audience['tag_and']));
$this->assertEquals(2, count($audience['tag_and']));
$result = $payload->addTagAnd('hello')->build();
$this->assertEquals(3, count($result['audience']['tag_and']));
}
public function testAddTagAnd1() {
$payload = $this->payload_without_audience;
$result = $payload->addTagAnd('hello')->build();
$audience = $result['audience'];
$this->assertTrue(is_array($audience['tag_and']));
$this->assertEquals(1, count($audience['tag_and']));
$result = $payload->addTagAnd(array('jpush', 'jiguang'))->build();
$this->assertEquals(3, count($result['audience']['tag_and']));
}
public function testAddTagAnd2() {
$payload = $this->payload_without_audience;
$result = $payload->addTagAnd(array('jpush', 'jiguang'))->build();
$audience = $result['audience'];
$this->assertTrue(is_array($audience['tag_and']));
$this->assertEquals(2, count($audience['tag_and']));
$result = $payload->addTagAnd('hello')->build();
$this->assertEquals(3, count($result['audience']['tag_and']));
}
public function testAddRegistrationId1() {
$payload = $this->payload_without_audience;
$result = $payload->addRegistrationId('hello')->build();
$audience = $result['audience'];
$this->assertTrue(is_array($audience['registration_id']));
$this->assertEquals(1, count($audience['registration_id']));
$result = $payload->addRegistrationId(array('jpush', 'jiguang'))->build();
$this->assertEquals(3, count($result['audience']['registration_id']));
}
public function testAddRegistrationId2() {
$payload = $this->payload_without_audience;
$result = $payload->addRegistrationId(array('jpush', 'jiguang'))->build();
$audience = $result['audience'];
$this->assertTrue(is_array($audience['registration_id']));
$this->assertEquals(2, count($audience['registration_id']));
$result = $payload->addRegistrationId('hello')->build();
$this->assertEquals(3, count($result['audience']['registration_id']));
}
public function testSetNotificationAlert() {
$result = $this->payload->build();
$notification = $result['notification'];
$this->assertTrue(is_array($notification));
$this->assertEquals(1, count($notification));
$this->assertEquals('Hello JPush', $result['notification']['alert']);
}
public function testIosNotification() {
$payload = $this->payload;
$result = $payload->iosNotification()->build();
$ios = $result['notification']['ios'];
$this->assertTrue(is_array($ios));
$this->assertEquals(3, count($ios));
$this->assertArrayHasKey('alert', $ios);
$this->assertArrayHasKey('sound', $ios);
$this->assertArrayHasKey('badge', $ios);
$this->assertEquals('', $ios['alert']);
$this->assertEquals('', $ios['sound']);
$this->assertEquals('+1', $ios['badge']);
$result = $payload->iosNotification('hello')->build();
$ios = $result['notification']['ios'];
$this->assertEquals('hello', $result['notification']['ios']['alert']);
}
public function testIosNotificationWithArray() {
$payload = $this->payload;
$alert_array = array(
'alert_k1' => 'alert_v1',
'alert_k2' => 'alert_v2',
'alert_k3' => 'alert_v3',
'alert_k4' => 'alert_v4'
);
$array = array(
'sound' => 'jpush.caf',
'badge' => 2,
'content-available' => true,
'category' => 'jiguang',
'extras' => array(
'key' => 'value',
'jiguang'
),
'invalid_key' => 'invalid_value'
);
$result = $payload->iosNotification($alert_array, $array)->build();
$ios = $result['notification']['ios'];
$this->assertTrue(is_array($ios['alert']));
$this->assertEquals(6, count($ios));
$this->assertFalse(array_key_exists('invalid_key', $ios));
}
public function testAndroidNotification() {
$payload = $this->payload;
$result = $payload->androidNotification()->build();
$android = $result['notification']['android'];
$this->assertTrue(is_array($android));
$this->assertEquals(1, count($android));
$this->assertArrayHasKey('alert', $android);
$this->assertEquals('', $android['alert']);
$result = $payload->androidNotification('hello')->build();
$android = $result['notification']['android'];
$this->assertEquals('hello', $result['notification']['android']['alert']);
}
public function testAndroidNotificationWithArray() {
$payload = $this->payload;
$array = array(
'title' => 'hello jpush',
'builder_id' => 2,
'extras' => array(
'key' => 'value',
'jiguang'
),
'invalid_key' => 'invalid_value'
);
$result = $payload->androidNotification('', $array)->build();
$android = $result['notification']['android'];
$this->assertEquals(4, count($android));
$this->assertFalse(array_key_exists('invalid_key', $android));
}
public function testSetSmsMessage() {
$payload = $this->payload;
$smsMessage = array(
'delay_time' => 60,
'signid' => 154,
'temp_id' => 1,
'temp_para' => array(
'code' => 357
),
'active_filter' => false
);
$result = $payload->setSmsMessage($smsMessage)->build();
$sms = $result['sms_message'];
$this->assertTrue(is_array($sms));
$this->assertEquals(5, count($sms));
$this->assertEquals(60, $sms['delay_time']);
$this->assertEquals(154, $sms['signid']);
$this->assertEquals(1, $sms['temp_id']);
}
public function testMessage() {
$payload = $this->payload;
$result = $payload->message('Hello JPush')->build();
$message = $result['message'];
$this->assertTrue(is_array($message));
$this->assertEquals(1, count($message));
$this->assertEquals('Hello JPush', $message['msg_content']);
$array = array(
'title' => 'hello jpush',
'content_type' => '',
'extras' => array(
'key' => 'value',
'jiguang'
),
'invalid_key' => 'invalid_value'
);
$result = $payload->message('Hello JPush', $array)->build();
}
public function testOptions() {
$payload = $this->payload;
$result = $payload->options()->build();
$this->assertTrue(array_key_exists('options', $result));
$this->assertEquals(false, $result['options']['apns_production']);
$this->assertTrue(array_key_exists('sendno', $result['options']));
$array = array(
'sendno' => 100,
'time_to_live' => 100,
'apns_production' => true,
'override_msg_id' => 100,
'big_push_duration' => 100
);
$result = $payload->options($array)->build();
$options = $result['options'];
$this->assertEquals(5, count($options));
$this->assertArrayHasKey('apns_production', $options);
$this->assertEquals(true, $options['apns_production']);
}
public function testPushToAll() {
$payload = $this->payload;
$platform = array('ios', 'android', 'blackberry');
$ios_alert = array(
'k1' => 'v1',
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4'
);
$ios_notification = array(
'sound' => 'jpush.caf',
'badge' => 2,
'content-available' => true,
'category' => 'jiguang',
'extras' => array(
'key' => 'value',
'jiguang'
),
'invalid_key' => 'invalid_value'
);
$android_notification = array(
'title' => 'hello jpush',
'builder_id' => 2,
'extras' => array(
'key' => 'value',
'jiguang'
),
'invalid_key' => 'invalid_value'
);
$message = array(
'title' => 'hello jpush',
'content_type' => '',
'extras' => array(
'key' => 'value',
'jiguang'
),
'invalid_key' => 'invalid_value'
);
$result = $payload->setPlatform($platform)
->iosNotification($ios_alert, $ios_notification)
->androidNotification('Hello Android', $android_notification)
->message('Hello JPush', $message)
->build();
$response = $payload->send();
$this->assertEquals('200', $response['http_code']);
echo "HTTP HEADERS ARE: ";
print_r($response['headers']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(2, count($body));
$this->assertArrayHasKey('sendno', $body);
$this->assertArrayHasKey('msg_id', $body);
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace JPush\Tests;
use PHPUnit\Framework\TestCase;
class ReportPayloadTest extends TestCase {
protected function setUp() {
global $client;
$this->payload = $client->push()
->setPlatform('all')
->addAllAudience()
->setNotificationAlert('Hello JPush');
$this->reporter = $client->report();
}
public function testPusher0() {
$payload = $this->payload;
$response = $payload->send();
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(2, count($body));
$this->assertArrayHasKey('sendno', $body);
$this->assertArrayHasKey('msg_id', $body);
sleep(10);
return $body['msg_id'];
}
public function testPusher1() {
$payload = $this->payload;
$response = $payload->send();
$this->assertEquals('200', $response['http_code']);
sleep(10);
return $response['body']['msg_id'];
}
/**
* @depends testPusher0
* @depends testPusher1
*/
public function testGetReceived($msg_id_0, $msg_id_1) {
$response = $this->reporter->getReceived($msg_id_0);
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(1, count($body));
$this->assertTrue(is_array($body[0]));
$this->assertArrayHasKey('msg_id', $body[0]);
$response = $this->reporter->getReceived(array($msg_id_0, $msg_id_1));
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(2, count($body));
}
/**
* @depends testPusher0
* @depends testPusher1
*/
public function testGetMessages($msg_id_0, $msg_id_1) {
$response = $this->reporter->getMessages($msg_id_0);
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(1, count($body));
$this->assertTrue(is_array($body[0]));
$this->assertEquals(4, count($body[0]));
$this->assertArrayHasKey('msg_id', $body[0]);
$response = $this->reporter->getMessages(array($msg_id_0, $msg_id_1));
$this->assertEquals('200', $response['http_code']);
$body = $response['body'];
$this->assertTrue(is_array($body));
$this->assertEquals(2, count($body));
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace JPush\Tests;
use PHPUnit\Framework\TestCase;
class SchedulePayloadTest extends TestCase {
protected function setUp() {
global $client;
$this->schedule = $client->schedule();
}
public function testGetSchedules() {
$schedule = $this->schedule;
$response = $schedule->getSchedules();
$this->assertEquals('200', $response['http_code']);
}
}

View File

@ -0,0 +1,8 @@
<?php
use JPush\Client;
$app_key = getenv('app_key');
$master_secret = getenv('master_secret');
$client = new Client($app_key, $master_secret);
$registration_id = getenv('registration_id');

201
vendor/phpoption/phpoption/LICENSE vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,52 @@
{
"name": "phpoption/phpoption",
"description": "Option Type for PHP",
"keywords": ["php", "option", "language", "type"],
"license": "Apache-2.0",
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh"
},
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12"
},
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"autoload-dev": {
"psr-4": {
"PhpOption\\Tests\\": "tests/PhpOption/Tests/"
}
},
"config": {
"allow-plugins": {
"bamarni/composer-bin-plugin": true
},
"preferred-install": "dist"
},
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": true
},
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@ -0,0 +1,175 @@
<?php
/*
* Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace PhpOption;
use Traversable;
/**
* @template T
*
* @extends Option<T>
*/
final class LazyOption extends Option
{
/** @var callable(mixed...):(Option<T>) */
private $callback;
/** @var array<int, mixed> */
private $arguments;
/** @var Option<T>|null */
private $option;
/**
* @template S
* @param callable(mixed...):(Option<S>) $callback
* @param array<int, mixed> $arguments
*
* @return LazyOption<S>
*/
public static function create($callback, array $arguments = []): self
{
return new self($callback, $arguments);
}
/**
* @param callable(mixed...):(Option<T>) $callback
* @param array<int, mixed> $arguments
*/
public function __construct($callback, array $arguments = [])
{
if (!is_callable($callback)) {
throw new \InvalidArgumentException('Invalid callback given');
}
$this->callback = $callback;
$this->arguments = $arguments;
}
public function isDefined(): bool
{
return $this->option()->isDefined();
}
public function isEmpty(): bool
{
return $this->option()->isEmpty();
}
public function get()
{
return $this->option()->get();
}
public function getOrElse($default)
{
return $this->option()->getOrElse($default);
}
public function getOrCall($callable)
{
return $this->option()->getOrCall($callable);
}
public function getOrThrow(\Exception $ex)
{
return $this->option()->getOrThrow($ex);
}
public function orElse(Option $else)
{
return $this->option()->orElse($else);
}
public function ifDefined($callable)
{
$this->option()->forAll($callable);
}
public function forAll($callable)
{
return $this->option()->forAll($callable);
}
public function map($callable)
{
return $this->option()->map($callable);
}
public function flatMap($callable)
{
return $this->option()->flatMap($callable);
}
public function filter($callable)
{
return $this->option()->filter($callable);
}
public function filterNot($callable)
{
return $this->option()->filterNot($callable);
}
public function select($value)
{
return $this->option()->select($value);
}
public function reject($value)
{
return $this->option()->reject($value);
}
/**
* @return Traversable<T>
*/
public function getIterator(): Traversable
{
return $this->option()->getIterator();
}
public function foldLeft($initialValue, $callable)
{
return $this->option()->foldLeft($initialValue, $callable);
}
public function foldRight($initialValue, $callable)
{
return $this->option()->foldRight($initialValue, $callable);
}
/**
* @return Option<T>
*/
private function option(): Option
{
if (null === $this->option) {
/** @var mixed */
$option = call_user_func_array($this->callback, $this->arguments);
if ($option instanceof Option) {
$this->option = $option;
} else {
throw new \RuntimeException(sprintf('Expected instance of %s', Option::class));
}
}
return $this->option;
}
}

View File

@ -0,0 +1,136 @@
<?php
/*
* Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace PhpOption;
use EmptyIterator;
/**
* @extends Option<mixed>
*/
final class None extends Option
{
/** @var None|null */
private static $instance;
/**
* @return None
*/
public static function create(): self
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function get()
{
throw new \RuntimeException('None has no value.');
}
public function getOrCall($callable)
{
return $callable();
}
public function getOrElse($default)
{
return $default;
}
public function getOrThrow(\Exception $ex)
{
throw $ex;
}
public function isEmpty(): bool
{
return true;
}
public function isDefined(): bool
{
return false;
}
public function orElse(Option $else)
{
return $else;
}
public function ifDefined($callable)
{
// Just do nothing in that case.
}
public function forAll($callable)
{
return $this;
}
public function map($callable)
{
return $this;
}
public function flatMap($callable)
{
return $this;
}
public function filter($callable)
{
return $this;
}
public function filterNot($callable)
{
return $this;
}
public function select($value)
{
return $this;
}
public function reject($value)
{
return $this;
}
public function getIterator(): EmptyIterator
{
return new EmptyIterator();
}
public function foldLeft($initialValue, $callable)
{
return $initialValue;
}
public function foldRight($initialValue, $callable)
{
return $initialValue;
}
private function __construct()
{
}
}

View File

@ -0,0 +1,434 @@
<?php
/*
* Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace PhpOption;
use ArrayAccess;
use IteratorAggregate;
/**
* @template T
*
* @implements IteratorAggregate<T>
*/
abstract class Option implements IteratorAggregate
{
/**
* Creates an option given a return value.
*
* This is intended for consuming existing APIs and allows you to easily
* convert them to an option. By default, we treat ``null`` as the None
* case, and everything else as Some.
*
* @template S
*
* @param S $value The actual return value.
* @param S $noneValue The value which should be considered "None"; null by
* default.
*
* @return Option<S>
*/
public static function fromValue($value, $noneValue = null)
{
if ($value === $noneValue) {
return None::create();
}
return new Some($value);
}
/**
* Creates an option from an array's value.
*
* If the key does not exist in the array, the array is not actually an
* array, or the array's value at the given key is null, None is returned.
* Otherwise, Some is returned wrapping the value at the given key.
*
* @template S
*
* @param array<string|int,S>|ArrayAccess<string|int,S>|null $array A potential array or \ArrayAccess value.
* @param string $key The key to check.
*
* @return Option<S>
*/
public static function fromArraysValue($array, $key)
{
if (!(is_array($array) || $array instanceof ArrayAccess) || !isset($array[$key])) {
return None::create();
}
return new Some($array[$key]);
}
/**
* Creates a lazy-option with the given callback.
*
* This is also a helper constructor for lazy-consuming existing APIs where
* the return value is not yet an option. By default, we treat ``null`` as
* None case, and everything else as Some.
*
* @template S
*
* @param callable $callback The callback to evaluate.
* @param array $arguments The arguments for the callback.
* @param S $noneValue The value which should be considered "None";
* null by default.
*
* @return LazyOption<S>
*/
public static function fromReturn($callback, array $arguments = [], $noneValue = null)
{
return new LazyOption(static function () use ($callback, $arguments, $noneValue) {
/** @var mixed */
$return = call_user_func_array($callback, $arguments);
if ($return === $noneValue) {
return None::create();
}
return new Some($return);
});
}
/**
* Option factory, which creates new option based on passed value.
*
* If value is already an option, it simply returns. If value is callable,
* LazyOption with passed callback created and returned. If Option
* returned from callback, it returns directly. On other case value passed
* to Option::fromValue() method.
*
* @template S
*
* @param Option<S>|callable|S $value
* @param S $noneValue Used when $value is mixed or
* callable, for None-check.
*
* @return Option<S>|LazyOption<S>
*/
public static function ensure($value, $noneValue = null)
{
if ($value instanceof self) {
return $value;
} elseif (is_callable($value)) {
return new LazyOption(static function () use ($value, $noneValue) {
/** @var mixed */
$return = $value();
if ($return instanceof self) {
return $return;
} else {
return self::fromValue($return, $noneValue);
}
});
} else {
return self::fromValue($value, $noneValue);
}
}
/**
* Lift a function so that it accepts Option as parameters.
*
* We return a new closure that wraps the original callback. If any of the
* parameters passed to the lifted function is empty, the function will
* return a value of None. Otherwise, we will pass all parameters to the
* original callback and return the value inside a new Option, unless an
* Option is returned from the function, in which case, we use that.
*
* @template S
*
* @param callable $callback
* @param mixed $noneValue
*
* @return callable
*/
public static function lift($callback, $noneValue = null)
{
return static function () use ($callback, $noneValue) {
/** @var array<int, mixed> */
$args = func_get_args();
$reduced_args = array_reduce(
$args,
/** @param bool $status */
static function ($status, self $o) {
return $o->isEmpty() ? true : $status;
},
false
);
// if at least one parameter is empty, return None
if ($reduced_args) {
return None::create();
}
$args = array_map(
/** @return T */
static function (self $o) {
// it is safe to do so because the fold above checked
// that all arguments are of type Some
/** @var T */
return $o->get();
},
$args
);
return self::ensure(call_user_func_array($callback, $args), $noneValue);
};
}
/**
* Returns the value if available, or throws an exception otherwise.
*
* @throws \RuntimeException If value is not available.
*
* @return T
*/
abstract public function get();
/**
* Returns the value if available, or the default value if not.
*
* @template S
*
* @param S $default
*
* @return T|S
*/
abstract public function getOrElse($default);
/**
* Returns the value if available, or the results of the callable.
*
* This is preferable over ``getOrElse`` if the computation of the default
* value is expensive.
*
* @template S
*
* @param callable():S $callable
*
* @return T|S
*/
abstract public function getOrCall($callable);
/**
* Returns the value if available, or throws the passed exception.
*
* @param \Exception $ex
*
* @return T
*/
abstract public function getOrThrow(\Exception $ex);
/**
* Returns true if no value is available, false otherwise.
*
* @return bool
*/
abstract public function isEmpty();
/**
* Returns true if a value is available, false otherwise.
*
* @return bool
*/
abstract public function isDefined();
/**
* Returns this option if non-empty, or the passed option otherwise.
*
* This can be used to try multiple alternatives, and is especially useful
* with lazy evaluating options:
*
* ```php
* $repo->findSomething()
* ->orElse(new LazyOption(array($repo, 'findSomethingElse')))
* ->orElse(new LazyOption(array($repo, 'createSomething')));
* ```
*
* @param Option<T> $else
*
* @return Option<T>
*/
abstract public function orElse(self $else);
/**
* This is similar to map() below except that the return value has no meaning;
* the passed callable is simply executed if the option is non-empty, and
* ignored if the option is empty.
*
* In all cases, the return value of the callable is discarded.
*
* ```php
* $comment->getMaybeFile()->ifDefined(function($file) {
* // Do something with $file here.
* });
* ```
*
* If you're looking for something like ``ifEmpty``, you can use ``getOrCall``
* and ``getOrElse`` in these cases.
*
* @deprecated Use forAll() instead.
*
* @param callable(T):mixed $callable
*
* @return void
*/
abstract public function ifDefined($callable);
/**
* This is similar to map() except that the return value of the callable has no meaning.
*
* The passed callable is simply executed if the option is non-empty, and ignored if the
* option is empty. This method is preferred for callables with side-effects, while map()
* is intended for callables without side-effects.
*
* @param callable(T):mixed $callable
*
* @return Option<T>
*/
abstract public function forAll($callable);
/**
* Applies the callable to the value of the option if it is non-empty,
* and returns the return value of the callable wrapped in Some().
*
* If the option is empty, then the callable is not applied.
*
* ```php
* (new Some("foo"))->map('strtoupper')->get(); // "FOO"
* ```
*
* @template S
*
* @param callable(T):S $callable
*
* @return Option<S>
*/
abstract public function map($callable);
/**
* Applies the callable to the value of the option if it is non-empty, and
* returns the return value of the callable directly.
*
* In contrast to ``map``, the return value of the callable is expected to
* be an Option itself; it is not automatically wrapped in Some().
*
* @template S
*
* @param callable(T):Option<S> $callable must return an Option
*
* @return Option<S>
*/
abstract public function flatMap($callable);
/**
* If the option is empty, it is returned immediately without applying the callable.
*
* If the option is non-empty, the callable is applied, and if it returns true,
* the option itself is returned; otherwise, None is returned.
*
* @param callable(T):bool $callable
*
* @return Option<T>
*/
abstract public function filter($callable);
/**
* If the option is empty, it is returned immediately without applying the callable.
*
* If the option is non-empty, the callable is applied, and if it returns false,
* the option itself is returned; otherwise, None is returned.
*
* @param callable(T):bool $callable
*
* @return Option<T>
*/
abstract public function filterNot($callable);
/**
* If the option is empty, it is returned immediately.
*
* If the option is non-empty, and its value does not equal the passed value
* (via a shallow comparison ===), then None is returned. Otherwise, the
* Option is returned.
*
* In other words, this will filter all but the passed value.
*
* @param T $value
*
* @return Option<T>
*/
abstract public function select($value);
/**
* If the option is empty, it is returned immediately.
*
* If the option is non-empty, and its value does equal the passed value (via
* a shallow comparison ===), then None is returned; otherwise, the Option is
* returned.
*
* In other words, this will let all values through except the passed value.
*
* @param T $value
*
* @return Option<T>
*/
abstract public function reject($value);
/**
* Binary operator for the initial value and the option's value.
*
* If empty, the initial value is returned. If non-empty, the callable
* receives the initial value and the option's value as arguments.
*
* ```php
*
* $some = new Some(5);
* $none = None::create();
* $result = $some->foldLeft(1, function($a, $b) { return $a + $b; }); // int(6)
* $result = $none->foldLeft(1, function($a, $b) { return $a + $b; }); // int(1)
*
* // This can be used instead of something like the following:
* $option = Option::fromValue($integerOrNull);
* $result = 1;
* if ( ! $option->isEmpty()) {
* $result += $option->get();
* }
* ```
*
* @template S
*
* @param S $initialValue
* @param callable(S, T):S $callable
*
* @return S
*/
abstract public function foldLeft($initialValue, $callable);
/**
* foldLeft() but with reversed arguments for the callable.
*
* @template S
*
* @param S $initialValue
* @param callable(T, S):S $callable
*
* @return S
*/
abstract public function foldRight($initialValue, $callable);
}

View File

@ -0,0 +1,169 @@
<?php
/*
* Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace PhpOption;
use ArrayIterator;
/**
* @template T
*
* @extends Option<T>
*/
final class Some extends Option
{
/** @var T */
private $value;
/**
* @param T $value
*/
public function __construct($value)
{
$this->value = $value;
}
/**
* @template U
*
* @param U $value
*
* @return Some<U>
*/
public static function create($value): self
{
return new self($value);
}
public function isDefined(): bool
{
return true;
}
public function isEmpty(): bool
{
return false;
}
public function get()
{
return $this->value;
}
public function getOrElse($default)
{
return $this->value;
}
public function getOrCall($callable)
{
return $this->value;
}
public function getOrThrow(\Exception $ex)
{
return $this->value;
}
public function orElse(Option $else)
{
return $this;
}
public function ifDefined($callable)
{
$this->forAll($callable);
}
public function forAll($callable)
{
$callable($this->value);
return $this;
}
public function map($callable)
{
return new self($callable($this->value));
}
public function flatMap($callable)
{
/** @var mixed */
$rs = $callable($this->value);
if (!$rs instanceof Option) {
throw new \RuntimeException('Callables passed to flatMap() must return an Option. Maybe you should use map() instead?');
}
return $rs;
}
public function filter($callable)
{
if (true === $callable($this->value)) {
return $this;
}
return None::create();
}
public function filterNot($callable)
{
if (false === $callable($this->value)) {
return $this;
}
return None::create();
}
public function select($value)
{
if ($this->value === $value) {
return $this;
}
return None::create();
}
public function reject($value)
{
if ($this->value === $value) {
return None::create();
}
return $this;
}
/**
* @return ArrayIterator<int, T>
*/
public function getIterator(): ArrayIterator
{
return new ArrayIterator([$this->value]);
}
public function foldLeft($initialValue, $callable)
{
return $callable($initialValue, $this->value);
}
public function foldRight($initialValue, $callable)
{
return $callable($this->value, $initialValue);
}
}

2
vendor/services.php vendored
View File

@ -1,5 +1,5 @@
<?php
// This file is automatically generated at:2023-05-09 10:51:01
// This file is automatically generated at:2023-06-06 12:49:17
declare (strict_types = 1);
return array (
0 => 'think\\queue\\Service',

30
vendor/vlucas/phpdotenv/LICENSE vendored Normal file
View File

@ -0,0 +1,30 @@
BSD 3-Clause License
Copyright (c) 2014, Graham Campbell.
Copyright (c) 2013, Vance Lucas.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

53
vendor/vlucas/phpdotenv/composer.json vendored Normal file
View File

@ -0,0 +1,53 @@
{
"name": "vlucas/phpdotenv",
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
"keywords": ["env", "dotenv", "environment"],
"license": "BSD-3-Clause",
"authors": [
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"homepage": "https://gjcampbell.co.uk/"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "https://vancelucas.com/"
}
],
"require": {
"php": "^7.1.3 || ^8.0",
"ext-pcre": "*",
"graham-campbell/result-type": "^1.0.1",
"phpoption/phpoption": "^1.7.4",
"symfony/polyfill-ctype": "^1.17",
"symfony/polyfill-mbstring": "^1.17",
"symfony/polyfill-php80": "^1.17"
},
"require-dev": {
"ext-filter": "*",
"bamarni/composer-bin-plugin": "^1.4.1",
"phpunit/phpunit": "^7.5.20 || ^8.5.14 || ^9.5.1"
},
"autoload": {
"psr-4": {
"Dotenv\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Dotenv\\Tests\\": "tests/Dotenv/"
}
},
"suggest": {
"ext-filter": "Required to use the boolean validator."
},
"config": {
"preferred-install": "dist"
},
"extra": {
"branch-alias": {
"dev-master": "5.3-dev"
}
}
}

267
vendor/vlucas/phpdotenv/src/Dotenv.php vendored Normal file
View File

@ -0,0 +1,267 @@
<?php
declare(strict_types=1);
namespace Dotenv;
use Dotenv\Exception\InvalidPathException;
use Dotenv\Loader\Loader;
use Dotenv\Loader\LoaderInterface;
use Dotenv\Parser\Parser;
use Dotenv\Parser\ParserInterface;
use Dotenv\Repository\Adapter\ArrayAdapter;
use Dotenv\Repository\Adapter\PutenvAdapter;
use Dotenv\Repository\RepositoryBuilder;
use Dotenv\Repository\RepositoryInterface;
use Dotenv\Store\StoreBuilder;
use Dotenv\Store\StoreInterface;
use Dotenv\Store\StringStore;
class Dotenv
{
/**
* The store instance.
*
* @var \Dotenv\Store\StoreInterface
*/
private $store;
/**
* The parser instance.
*
* @var \Dotenv\Parser\ParserInterface
*/
private $parser;
/**
* The loader instance.
*
* @var \Dotenv\Loader\LoaderInterface
*/
private $loader;
/**
* The repository instance.
*
* @var \Dotenv\Repository\RepositoryInterface
*/
private $repository;
/**
* Create a new dotenv instance.
*
* @param \Dotenv\Store\StoreInterface $store
* @param \Dotenv\Parser\ParserInterface $parser
* @param \Dotenv\Loader\LoaderInterface $loader
* @param \Dotenv\Repository\RepositoryInterface $repository
*
* @return void
*/
public function __construct(
StoreInterface $store,
ParserInterface $parser,
LoaderInterface $loader,
RepositoryInterface $repository
) {
$this->store = $store;
$this->parser = $parser;
$this->loader = $loader;
$this->repository = $repository;
}
/**
* Create a new dotenv instance.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
* @param string|null $fileEncoding
*
* @return \Dotenv\Dotenv
*/
public static function create(RepositoryInterface $repository, $paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
{
$builder = $names === null ? StoreBuilder::createWithDefaultName() : StoreBuilder::createWithNoNames();
foreach ((array) $paths as $path) {
$builder = $builder->addPath($path);
}
foreach ((array) $names as $name) {
$builder = $builder->addName($name);
}
if ($shortCircuit) {
$builder = $builder->shortCircuit();
}
return new self($builder->fileEncoding($fileEncoding)->make(), new Parser(), new Loader(), $repository);
}
/**
* Create a new mutable dotenv instance with default repository.
*
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
* @param string|null $fileEncoding
*
* @return \Dotenv\Dotenv
*/
public static function createMutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
{
$repository = RepositoryBuilder::createWithDefaultAdapters()->make();
return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
}
/**
* Create a new mutable dotenv instance with default repository with the putenv adapter.
*
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
* @param string|null $fileEncoding
*
* @return \Dotenv\Dotenv
*/
public static function createUnsafeMutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
{
$repository = RepositoryBuilder::createWithDefaultAdapters()
->addAdapter(PutenvAdapter::class)
->make();
return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
}
/**
* Create a new immutable dotenv instance with default repository.
*
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
* @param string|null $fileEncoding
*
* @return \Dotenv\Dotenv
*/
public static function createImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
{
$repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();
return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
}
/**
* Create a new immutable dotenv instance with default repository with the putenv adapter.
*
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
* @param string|null $fileEncoding
*
* @return \Dotenv\Dotenv
*/
public static function createUnsafeImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
{
$repository = RepositoryBuilder::createWithDefaultAdapters()
->addAdapter(PutenvAdapter::class)
->immutable()
->make();
return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
}
/**
* Create a new dotenv instance with an array backed repository.
*
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
* @param string|null $fileEncoding
*
* @return \Dotenv\Dotenv
*/
public static function createArrayBacked($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
{
$repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make();
return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
}
/**
* Parse the given content and resolve nested variables.
*
* This method behaves just like load(), only without mutating your actual
* environment. We do this by using an array backed repository.
*
* @param string $content
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
public static function parse(string $content)
{
$repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make();
$phpdotenv = new self(new StringStore($content), new Parser(), new Loader(), $repository);
return $phpdotenv->load();
}
/**
* Read and load environment file(s).
*
* @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
public function load()
{
$entries = $this->parser->parse($this->store->read());
return $this->loader->load($this->repository, $entries);
}
/**
* Read and load environment file(s), silently failing if no files can be read.
*
* @throws \Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
public function safeLoad()
{
try {
return $this->load();
} catch (InvalidPathException $e) {
// suppressing exception
return [];
}
}
/**
* Required ensures that the specified variables exist, and returns a new validator object.
*
* @param string|string[] $variables
*
* @return \Dotenv\Validator
*/
public function required($variables)
{
return (new Validator($this->repository, (array) $variables))->required();
}
/**
* Returns a new validator object that won't check if the specified variables exist.
*
* @param string|string[] $variables
*
* @return \Dotenv\Validator
*/
public function ifPresent($variables)
{
return new Validator($this->repository, (array) $variables);
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Dotenv\Exception;
use Throwable;
interface ExceptionInterface extends Throwable
{
//
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Dotenv\Exception;
use InvalidArgumentException;
final class InvalidEncodingException extends InvalidArgumentException implements ExceptionInterface
{
//
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Dotenv\Exception;
use InvalidArgumentException;
final class InvalidFileException extends InvalidArgumentException implements ExceptionInterface
{
//
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Dotenv\Exception;
use InvalidArgumentException;
final class InvalidPathException extends InvalidArgumentException implements ExceptionInterface
{
//
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Dotenv\Exception;
use RuntimeException;
final class ValidationException extends RuntimeException implements ExceptionInterface
{
//
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Dotenv\Loader;
use Dotenv\Parser\Entry;
use Dotenv\Parser\Value;
use Dotenv\Repository\RepositoryInterface;
final class Loader implements LoaderInterface
{
/**
* Load the given entries into the repository.
*
* We'll substitute any nested variables, and send each variable to the
* repository, with the effect of actually mutating the environment.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param \Dotenv\Parser\Entry[] $entries
*
* @return array<string,string|null>
*/
public function load(RepositoryInterface $repository, array $entries)
{
return \array_reduce($entries, static function (array $vars, Entry $entry) use ($repository) {
$name = $entry->getName();
$value = $entry->getValue()->map(static function (Value $value) use ($repository) {
return Resolver::resolve($repository, $value);
});
if ($value->isDefined()) {
$inner = $value->get();
if ($repository->set($name, $inner)) {
return \array_merge($vars, [$name => $inner]);
}
} else {
if ($repository->clear($name)) {
return \array_merge($vars, [$name => null]);
}
}
return $vars;
}, []);
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Dotenv\Loader;
use Dotenv\Repository\RepositoryInterface;
interface LoaderInterface
{
/**
* Load the given entries into the repository.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param \Dotenv\Parser\Entry[] $entries
*
* @return array<string,string|null>
*/
public function load(RepositoryInterface $repository, array $entries);
}

View File

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Dotenv\Loader;
use Dotenv\Parser\Value;
use Dotenv\Repository\RepositoryInterface;
use Dotenv\Util\Regex;
use Dotenv\Util\Str;
use PhpOption\Option;
final class Resolver
{
/**
* This class is a singleton.
*
* @codeCoverageIgnore
*
* @return void
*/
private function __construct()
{
//
}
/**
* Resolve the nested variables in the given value.
*
* Replaces ${varname} patterns in the allowed positions in the variable
* value by an existing environment variable.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param \Dotenv\Parser\Value $value
*
* @return string
*/
public static function resolve(RepositoryInterface $repository, Value $value)
{
return \array_reduce($value->getVars(), static function (string $s, int $i) use ($repository) {
return Str::substr($s, 0, $i).self::resolveVariable($repository, Str::substr($s, $i));
}, $value->getChars());
}
/**
* Resolve a single nested variable.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param string $str
*
* @return string
*/
private static function resolveVariable(RepositoryInterface $repository, string $str)
{
return Regex::replaceCallback(
'/\A\${([a-zA-Z0-9_.]+)}/',
static function (array $matches) use ($repository) {
return Option::fromValue($repository->get($matches[1]))
->getOrElse($matches[0]);
},
$str,
1
)->success()->getOrElse($str);
}
}

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Dotenv\Parser;
use PhpOption\Option;
final class Entry
{
/**
* The entry name.
*
* @var string
*/
private $name;
/**
* The entry value.
*
* @var \Dotenv\Parser\Value|null
*/
private $value;
/**
* Create a new entry instance.
*
* @param string $name
* @param \Dotenv\Parser\Value|null $value
*
* @return void
*/
public function __construct(string $name, Value $value = null)
{
$this->name = $name;
$this->value = $value;
}
/**
* Get the entry name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Get the entry value.
*
* @return \PhpOption\Option<\Dotenv\Parser\Value>
*/
public function getValue()
{
/** @var \PhpOption\Option<\Dotenv\Parser\Value> */
return Option::fromValue($this->value);
}
}

View File

@ -0,0 +1,293 @@
<?php
declare(strict_types=1);
namespace Dotenv\Parser;
use Dotenv\Util\Regex;
use Dotenv\Util\Str;
use GrahamCampbell\ResultType\Error;
use GrahamCampbell\ResultType\Result;
use GrahamCampbell\ResultType\Success;
final class EntryParser
{
private const INITIAL_STATE = 0;
private const UNQUOTED_STATE = 1;
private const SINGLE_QUOTED_STATE = 2;
private const DOUBLE_QUOTED_STATE = 3;
private const ESCAPE_SEQUENCE_STATE = 4;
private const WHITESPACE_STATE = 5;
private const COMMENT_STATE = 6;
private const REJECT_STATES = [self::SINGLE_QUOTED_STATE, self::DOUBLE_QUOTED_STATE, self::ESCAPE_SEQUENCE_STATE];
/**
* This class is a singleton.
*
* @codeCoverageIgnore
*
* @return void
*/
private function __construct()
{
//
}
/**
* Parse a raw entry into a proper entry.
*
* That is, turn a raw environment variable entry into a name and possibly
* a value. We wrap the answer in a result type.
*
* @param string $entry
*
* @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry,string>
*/
public static function parse(string $entry)
{
return self::splitStringIntoParts($entry)->flatMap(static function (array $parts) {
[$name, $value] = $parts;
return self::parseName($name)->flatMap(static function (string $name) use ($value) {
/** @var Result<Value|null,string> */
$parsedValue = $value === null ? Success::create(null) : self::parseValue($value);
return $parsedValue->map(static function (?Value $value) use ($name) {
return new Entry($name, $value);
});
});
});
}
/**
* Split the compound string into parts.
*
* @param string $line
*
* @return \GrahamCampbell\ResultType\Result<array{string,string|null},string>
*/
private static function splitStringIntoParts(string $line)
{
/** @var array{string,string|null} */
$result = Str::pos($line, '=')->map(static function () use ($line) {
return \array_map('trim', \explode('=', $line, 2));
})->getOrElse([$line, null]);
if ($result[0] === '') {
return Error::create(self::getErrorMessage('an unexpected equals', $line));
}
/** @var \GrahamCampbell\ResultType\Result<array{string,string|null},string> */
return Success::create($result);
}
/**
* Parse the given variable name.
*
* That is, strip the optional quotes and leading "export" from the
* variable name. We wrap the answer in a result type.
*
* @param string $name
*
* @return \GrahamCampbell\ResultType\Result<string,string>
*/
private static function parseName(string $name)
{
if (Str::len($name) > 8 && Str::substr($name, 0, 6) === 'export' && \ctype_space(Str::substr($name, 6, 1))) {
$name = \ltrim(Str::substr($name, 6));
}
if (self::isQuotedName($name)) {
$name = Str::substr($name, 1, -1);
}
if (!self::isValidName($name)) {
return Error::create(self::getErrorMessage('an invalid name', $name));
}
return Success::create($name);
}
/**
* Is the given variable name quoted?
*
* @param string $name
*
* @return bool
*/
private static function isQuotedName(string $name)
{
if (Str::len($name) < 3) {
return false;
}
$first = Str::substr($name, 0, 1);
$last = Str::substr($name, -1, 1);
return ($first === '"' && $last === '"') || ($first === '\'' && $last === '\'');
}
/**
* Is the given variable name valid?
*
* @param string $name
*
* @return bool
*/
private static function isValidName(string $name)
{
return Regex::matches('~\A[a-zA-Z0-9_.]+\z~', $name)->success()->getOrElse(false);
}
/**
* Parse the given variable value.
*
* This has the effect of stripping quotes and comments, dealing with
* special characters, and locating nested variables, but not resolving
* them. Formally, we run a finite state automaton with an output tape: a
* transducer. We wrap the answer in a result type.
*
* @param string $value
*
* @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string>
*/
private static function parseValue(string $value)
{
if (\trim($value) === '') {
return Success::create(Value::blank());
}
return \array_reduce(\iterator_to_array(Lexer::lex($value)), static function (Result $data, string $token) {
return $data->flatMap(static function (array $data) use ($token) {
return self::processToken($data[1], $token)->map(static function (array $val) use ($data) {
return [$data[0]->append($val[0], $val[1]), $val[2]];
});
});
}, Success::create([Value::blank(), self::INITIAL_STATE]))->flatMap(static function (array $result) {
if (in_array($result[1], self::REJECT_STATES, true)) {
return Error::create('a missing closing quote');
}
return Success::create($result[0]);
})->mapError(static function (string $err) use ($value) {
return self::getErrorMessage($err, $value);
});
}
/**
* Process the given token.
*
* @param int $state
* @param string $token
*
* @return \GrahamCampbell\ResultType\Result<array{string,bool,int},string>
*/
private static function processToken(int $state, string $token)
{
switch ($state) {
case self::INITIAL_STATE:
if ($token === '\'') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::SINGLE_QUOTED_STATE]);
} elseif ($token === '"') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::DOUBLE_QUOTED_STATE]);
} elseif ($token === '#') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::COMMENT_STATE]);
} elseif ($token === '$') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, true, self::UNQUOTED_STATE]);
} else {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, false, self::UNQUOTED_STATE]);
}
case self::UNQUOTED_STATE:
if ($token === '#') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::COMMENT_STATE]);
} elseif (\ctype_space($token)) {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::WHITESPACE_STATE]);
} elseif ($token === '$') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, true, self::UNQUOTED_STATE]);
} else {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, false, self::UNQUOTED_STATE]);
}
case self::SINGLE_QUOTED_STATE:
if ($token === '\'') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::WHITESPACE_STATE]);
} else {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, false, self::SINGLE_QUOTED_STATE]);
}
case self::DOUBLE_QUOTED_STATE:
if ($token === '"') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::WHITESPACE_STATE]);
} elseif ($token === '\\') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::ESCAPE_SEQUENCE_STATE]);
} elseif ($token === '$') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, true, self::DOUBLE_QUOTED_STATE]);
} else {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
}
case self::ESCAPE_SEQUENCE_STATE:
if ($token === '"' || $token === '\\') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
} elseif ($token === '$') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
} else {
$first = Str::substr($token, 0, 1);
if (\in_array($first, ['f', 'n', 'r', 't', 'v'], true)) {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create([\stripcslashes('\\'.$first).Str::substr($token, 1), false, self::DOUBLE_QUOTED_STATE]);
} else {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Error::create('an unexpected escape sequence');
}
}
case self::WHITESPACE_STATE:
if ($token === '#') {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::COMMENT_STATE]);
} elseif (!\ctype_space($token)) {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Error::create('unexpected whitespace');
} else {
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::WHITESPACE_STATE]);
}
case self::COMMENT_STATE:
/** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
return Success::create(['', false, self::COMMENT_STATE]);
default:
throw new \Error('Parser entered invalid state.');
}
}
/**
* Generate a friendly error message.
*
* @param string $cause
* @param string $subject
*
* @return string
*/
private static function getErrorMessage(string $cause, string $subject)
{
return \sprintf(
'Encountered %s at [%s].',
$cause,
\strtok($subject, "\n")
);
}
}

View File

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace Dotenv\Parser;
final class Lexer
{
/**
* The regex for each type of token.
*
* @var string
*/
private const PATTERNS = [
'[\r\n]{1,1000}', '[^\S\r\n]{1,1000}', '\\\\', '\'', '"', '\\#', '\\$', '([^(\s\\\\\'"\\#\\$)]|\\(|\\)){1,1000}',
];
/**
* This class is a singleton.
*
* @codeCoverageIgnore
*
* @return void
*/
private function __construct()
{
//
}
/**
* Convert content into a token stream.
*
* Multibyte string processing is not needed here, and nether is error
* handling, for performance reasons.
*
* @param string $content
*
* @return \Generator<string>
*/
public static function lex(string $content)
{
static $regex;
if ($regex === null) {
$regex = '(('.\implode(')|(', self::PATTERNS).'))A';
}
$tokens = [];
$offset = 0;
while (isset($content[$offset])) {
if (!\preg_match($regex, $content, $matches, 0, $offset)) {
throw new \Error(\sprintf('Lexer encountered unexpected character [%s].', $content[$offset]));
}
$offset += \strlen($matches[0]);
yield $matches[0];
}
}
}

View File

@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace Dotenv\Parser;
use Dotenv\Util\Regex;
use Dotenv\Util\Str;
final class Lines
{
/**
* This class is a singleton.
*
* @codeCoverageIgnore
*
* @return void
*/
private function __construct()
{
//
}
/**
* Process the array of lines of environment variables.
*
* This will produce an array of raw entries, one per variable.
*
* @param string[] $lines
*
* @return string[]
*/
public static function process(array $lines)
{
$output = [];
$multiline = false;
$multilineBuffer = [];
foreach ($lines as $line) {
[$multiline, $line, $multilineBuffer] = self::multilineProcess($multiline, $line, $multilineBuffer);
if (!$multiline && !self::isCommentOrWhitespace($line)) {
$output[] = $line;
}
}
return $output;
}
/**
* Used to make all multiline variable process.
*
* @param bool $multiline
* @param string $line
* @param string[] $buffer
*
* @return array{bool,string,string[]}
*/
private static function multilineProcess(bool $multiline, string $line, array $buffer)
{
// check if $line can be multiline variable
if ($started = self::looksLikeMultilineStart($line)) {
$multiline = true;
}
if ($multiline) {
\array_push($buffer, $line);
if (self::looksLikeMultilineStop($line, $started)) {
$multiline = false;
$line = \implode("\n", $buffer);
$buffer = [];
}
}
return [$multiline, $line, $buffer];
}
/**
* Determine if the given line can be the start of a multiline variable.
*
* @param string $line
*
* @return bool
*/
private static function looksLikeMultilineStart(string $line)
{
return Str::pos($line, '="')->map(static function () use ($line) {
return self::looksLikeMultilineStop($line, true) === false;
})->getOrElse(false);
}
/**
* Determine if the given line can be the start of a multiline variable.
*
* @param string $line
* @param bool $started
*
* @return bool
*/
private static function looksLikeMultilineStop(string $line, bool $started)
{
if ($line === '"') {
return true;
}
return Regex::occurences('/(?=([^\\\\]"))/', \str_replace('\\\\', '', $line))->map(static function (int $count) use ($started) {
return $started ? $count > 1 : $count >= 1;
})->success()->getOrElse(false);
}
/**
* Determine if the line in the file is a comment or whitespace.
*
* @param string $line
*
* @return bool
*/
private static function isCommentOrWhitespace(string $line)
{
$line = \trim($line);
return $line === '' || (isset($line[0]) && $line[0] === '#');
}
}

View File

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Dotenv\Parser;
use Dotenv\Exception\InvalidFileException;
use Dotenv\Util\Regex;
use GrahamCampbell\ResultType\Result;
use GrahamCampbell\ResultType\Success;
final class Parser implements ParserInterface
{
/**
* Parse content into an entry array.
*
* @param string $content
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return \Dotenv\Parser\Entry[]
*/
public function parse(string $content)
{
return Regex::split("/(\r\n|\n|\r)/", $content)->mapError(static function () {
return 'Could not split into separate lines.';
})->flatMap(static function (array $lines) {
return self::process(Lines::process($lines));
})->mapError(static function (string $error) {
throw new InvalidFileException(\sprintf('Failed to parse dotenv file. %s', $error));
})->success()->get();
}
/**
* Convert the raw entries into proper entries.
*
* @param string[] $entries
*
* @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry[],string>
*/
private static function process(array $entries)
{
/** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry[],string> */
return \array_reduce($entries, static function (Result $result, string $raw) {
return $result->flatMap(static function (array $entries) use ($raw) {
return EntryParser::parse($raw)->map(static function (Entry $entry) use ($entries) {
return \array_merge($entries, [$entry]);
});
});
}, Success::create([]));
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Dotenv\Parser;
interface ParserInterface
{
/**
* Parse content into an entry array.
*
* @param string $content
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return \Dotenv\Parser\Entry[]
*/
public function parse(string $content);
}

View File

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace Dotenv\Parser;
use Dotenv\Util\Str;
final class Value
{
/**
* The string representation of the parsed value.
*
* @var string
*/
private $chars;
/**
* The locations of the variables in the value.
*
* @var int[]
*/
private $vars;
/**
* Internal constructor for a value.
*
* @param string $chars
* @param int[] $vars
*
* @return void
*/
private function __construct(string $chars, array $vars)
{
$this->chars = $chars;
$this->vars = $vars;
}
/**
* Create an empty value instance.
*
* @return \Dotenv\Parser\Value
*/
public static function blank()
{
return new self('', []);
}
/**
* Create a new value instance, appending the characters.
*
* @param string $chars
* @param bool $var
*
* @return \Dotenv\Parser\Value
*/
public function append(string $chars, bool $var)
{
return new self(
$this->chars.$chars,
$var ? \array_merge($this->vars, [Str::len($this->chars)]) : $this->vars
);
}
/**
* Get the string representation of the parsed value.
*
* @return string
*/
public function getChars()
{
return $this->chars;
}
/**
* Get the locations of the variables in the value.
*
* @return int[]
*/
public function getVars()
{
$vars = $this->vars;
\rsort($vars);
return $vars;
}
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
interface AdapterInterface extends ReaderInterface, WriterInterface
{
/**
* Create a new instance of the adapter, if it is available.
*
* @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
*/
public static function create();
}

View File

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
use PhpOption\None;
use PhpOption\Option;
use PhpOption\Some;
final class ApacheAdapter implements AdapterInterface
{
/**
* Create a new apache adapter instance.
*
* @return void
*/
private function __construct()
{
//
}
/**
* Create a new instance of the adapter, if it is available.
*
* @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
*/
public static function create()
{
if (self::isSupported()) {
/** @var \PhpOption\Option<AdapterInterface> */
return Some::create(new self());
}
return None::create();
}
/**
* Determines if the adapter is supported.
*
* This happens if PHP is running as an Apache module.
*
* @return bool
*/
private static function isSupported()
{
return \function_exists('apache_getenv') && \function_exists('apache_setenv');
}
/**
* Read an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string>
*/
public function read(string $name)
{
/** @var \PhpOption\Option<string> */
return Option::fromValue(apache_getenv($name))->filter(static function ($value) {
return \is_string($value) && $value !== '';
});
}
/**
* Write to an environment variable, if possible.
*
* @param string $name
* @param string $value
*
* @return bool
*/
public function write(string $name, string $value)
{
return apache_setenv($name, $value);
}
/**
* Delete an environment variable, if possible.
*
* @param string $name
*
* @return bool
*/
public function delete(string $name)
{
return apache_setenv($name, '');
}
}

View File

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
use PhpOption\Option;
use PhpOption\Some;
final class ArrayAdapter implements AdapterInterface
{
/**
* The variables and their values.
*
* @var array<string,string>
*/
private $variables;
/**
* Create a new array adapter instance.
*
* @return void
*/
private function __construct()
{
$this->variables = [];
}
/**
* Create a new instance of the adapter, if it is available.
*
* @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
*/
public static function create()
{
/** @var \PhpOption\Option<AdapterInterface> */
return Some::create(new self());
}
/**
* Read an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string>
*/
public function read(string $name)
{
return Option::fromArraysValue($this->variables, $name);
}
/**
* Write to an environment variable, if possible.
*
* @param string $name
* @param string $value
*
* @return bool
*/
public function write(string $name, string $value)
{
$this->variables[$name] = $value;
return true;
}
/**
* Delete an environment variable, if possible.
*
* @param string $name
*
* @return bool
*/
public function delete(string $name)
{
unset($this->variables[$name]);
return true;
}
}

View File

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
use PhpOption\Option;
use PhpOption\Some;
final class EnvConstAdapter implements AdapterInterface
{
/**
* Create a new env const adapter instance.
*
* @return void
*/
private function __construct()
{
//
}
/**
* Create a new instance of the adapter, if it is available.
*
* @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
*/
public static function create()
{
/** @var \PhpOption\Option<AdapterInterface> */
return Some::create(new self());
}
/**
* Read an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string>
*/
public function read(string $name)
{
/** @var \PhpOption\Option<string> */
return Option::fromArraysValue($_ENV, $name)
->map(static function ($value) {
if ($value === false) {
return 'false';
}
if ($value === true) {
return 'true';
}
return $value;
})->filter(static function ($value) {
return \is_string($value);
});
}
/**
* Write to an environment variable, if possible.
*
* @param string $name
* @param string $value
*
* @return bool
*/
public function write(string $name, string $value)
{
$_ENV[$name] = $value;
return true;
}
/**
* Delete an environment variable, if possible.
*
* @param string $name
*
* @return bool
*/
public function delete(string $name)
{
unset($_ENV[$name]);
return true;
}
}

View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
final class GuardedWriter implements WriterInterface
{
/**
* The inner writer to use.
*
* @var \Dotenv\Repository\Adapter\WriterInterface
*/
private $writer;
/**
* The variable name allow list.
*
* @var string[]
*/
private $allowList;
/**
* Create a new guarded writer instance.
*
* @param \Dotenv\Repository\Adapter\WriterInterface $writer
* @param string[] $allowList
*
* @return void
*/
public function __construct(WriterInterface $writer, array $allowList)
{
$this->writer = $writer;
$this->allowList = $allowList;
}
/**
* Write to an environment variable, if possible.
*
* @param string $name
* @param string $value
*
* @return bool
*/
public function write(string $name, string $value)
{
// Don't set non-allowed variables
if (!$this->isAllowed($name)) {
return false;
}
// Set the value on the inner writer
return $this->writer->write($name, $value);
}
/**
* Delete an environment variable, if possible.
*
* @param string $name
*
* @return bool
*/
public function delete(string $name)
{
// Don't clear non-allowed variables
if (!$this->isAllowed($name)) {
return false;
}
// Set the value on the inner writer
return $this->writer->delete($name);
}
/**
* Determine if the given variable is allowed.
*
* @param string $name
*
* @return bool
*/
private function isAllowed(string $name)
{
return \in_array($name, $this->allowList, true);
}
}

View File

@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
final class ImmutableWriter implements WriterInterface
{
/**
* The inner writer to use.
*
* @var \Dotenv\Repository\Adapter\WriterInterface
*/
private $writer;
/**
* The inner reader to use.
*
* @var \Dotenv\Repository\Adapter\ReaderInterface
*/
private $reader;
/**
* The record of loaded variables.
*
* @var array<string,string>
*/
private $loaded;
/**
* Create a new immutable writer instance.
*
* @param \Dotenv\Repository\Adapter\WriterInterface $writer
* @param \Dotenv\Repository\Adapter\ReaderInterface $reader
*
* @return void
*/
public function __construct(WriterInterface $writer, ReaderInterface $reader)
{
$this->writer = $writer;
$this->reader = $reader;
$this->loaded = [];
}
/**
* Write to an environment variable, if possible.
*
* @param string $name
* @param string $value
*
* @return bool
*/
public function write(string $name, string $value)
{
// Don't overwrite existing environment variables
// Ruby's dotenv does this with `ENV[key] ||= value`
if ($this->isExternallyDefined($name)) {
return false;
}
// Set the value on the inner writer
if (!$this->writer->write($name, $value)) {
return false;
}
// Record that we have loaded the variable
$this->loaded[$name] = '';
return true;
}
/**
* Delete an environment variable, if possible.
*
* @param string $name
*
* @return bool
*/
public function delete(string $name)
{
// Don't clear existing environment variables
if ($this->isExternallyDefined($name)) {
return false;
}
// Clear the value on the inner writer
if (!$this->writer->delete($name)) {
return false;
}
// Leave the variable as fair game
unset($this->loaded[$name]);
return true;
}
/**
* Determine if the given variable is externally defined.
*
* That is, is it an "existing" variable.
*
* @param string $name
*
* @return bool
*/
private function isExternallyDefined(string $name)
{
return $this->reader->read($name)->isDefined() && !isset($this->loaded[$name]);
}
}

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
use PhpOption\None;
final class MultiReader implements ReaderInterface
{
/**
* The set of readers to use.
*
* @var \Dotenv\Repository\Adapter\ReaderInterface[]
*/
private $readers;
/**
* Create a new multi-reader instance.
*
* @param \Dotenv\Repository\Adapter\ReaderInterface[] $readers
*
* @return void
*/
public function __construct(array $readers)
{
$this->readers = $readers;
}
/**
* Read an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string>
*/
public function read(string $name)
{
foreach ($this->readers as $reader) {
$result = $reader->read($name);
if ($result->isDefined()) {
return $result;
}
}
return None::create();
}
}

View File

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
final class MultiWriter implements WriterInterface
{
/**
* The set of writers to use.
*
* @var \Dotenv\Repository\Adapter\WriterInterface[]
*/
private $writers;
/**
* Create a new multi-writer instance.
*
* @param \Dotenv\Repository\Adapter\WriterInterface[] $writers
*
* @return void
*/
public function __construct(array $writers)
{
$this->writers = $writers;
}
/**
* Write to an environment variable, if possible.
*
* @param string $name
* @param string $value
*
* @return bool
*/
public function write(string $name, string $value)
{
foreach ($this->writers as $writers) {
if (!$writers->write($name, $value)) {
return false;
}
}
return true;
}
/**
* Delete an environment variable, if possible.
*
* @param string $name
*
* @return bool
*/
public function delete(string $name)
{
foreach ($this->writers as $writers) {
if (!$writers->delete($name)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace Dotenv\Repository\Adapter;
use PhpOption\None;
use PhpOption\Option;
use PhpOption\Some;
final class PutenvAdapter implements AdapterInterface
{
/**
* Create a new putenv adapter instance.
*
* @return void
*/
private function __construct()
{
//
}
/**
* Create a new instance of the adapter, if it is available.
*
* @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface>
*/
public static function create()
{
if (self::isSupported()) {
/** @var \PhpOption\Option<AdapterInterface> */
return Some::create(new self());
}
return None::create();
}
/**
* Determines if the adapter is supported.
*
* @return bool
*/
private static function isSupported()
{
return \function_exists('getenv') && \function_exists('putenv');
}
/**
* Read an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string>
*/
public function read(string $name)
{
/** @var \PhpOption\Option<string> */
return Option::fromValue(\getenv($name), false)->filter(static function ($value) {
return \is_string($value);
});
}
/**
* Write to an environment variable, if possible.
*
* @param string $name
* @param string $value
*
* @return bool
*/
public function write(string $name, string $value)
{
\putenv("$name=$value");
return true;
}
/**
* Delete an environment variable, if possible.
*
* @param string $name
*
* @return bool
*/
public function delete(string $name)
{
\putenv($name);
return true;
}
}

Some files were not shown because too many files have changed in this diff Show More