Commit 305fb940 by banice

init

parents
REDIS_HOST=redis.test.senthink.com
REGISTER_IP=127.0.0.1
REGISTER_PORT=1239
GATEWAY_PORT=8283
GATEWAY_IP=0.0.0.0
GATEWAY_COUNT=1
GATEWAY_START_PORT=2130
START_FILE=business|gateway|register
DB_HOST=db.test.senthink.com
DB_USERNAME=cdz
DB_DATABASE=cdzpay
DB_PORT=3306
DB_PASSWORD=senthink123
RABBIT_HOST=rabbitmq.test.senthink.com
RABBIT_PORT=5672
RABBIT_LOGIN=rabbitmq
RABBIT_PASSWORD=senthink123!
RABBIT_VHOST=cdz_gateway
REDIS_PORT=6379
LOG_LEVEL=debug
MESSAGE_API_URL=https://cdzback2.test.senthink.com
MESSAGE_START=L9SEWl3X73v3PBYHtFBZlTlJeBMdO9DDNIwRgHX6SrY
MESSAGE_FAIL=0al-a6kkNsdqFyXPEe97m4jjtbfOXjEC3eCjEsMHJdw
MESSAGE_POWER_EXCEED=rdzTNQ7t4uRXkP0SkuwW10WH77A_CIgYFiJykXJ0utU
MESSAGE_FINISH_USER=f0DDXSxvRTbHyzkCnwnYijtgteFTdfdUR7J8REwTYJ8
MESSAGE_PORT_SHED=C0LvfETe-1rCR_IhOydDGPTUwwYkrWZxgfwMsETrhw8
MESSAGE_CHARGER_TIMEOUT=f0DDXSxvRTbHyzkCnwnYijtgteFTdfdUR7J8REwTYJ8
MESSAGE_RELOAD=t_PyOGCKN_fmD0blw0EuIQs-8sS3qOTmpQay12haPZY
MESSAGE_SHED_TIMEOUT=rdzTNQ7t4uRXkP0SkuwW10WH77A_CIgYFiJykXJ0utU
MESSAGE_CHARGER_SHED=C0LvfETe-1rCR_IhOydDGPTUwwYkrWZxgfwMsETrhw8
LOW_POWER_LIMIT=30
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="Src\" />
<sourceFolder url="file://$MODULE_DIR$/application" isTestSource="false" packagePrefix="Application\" />
<sourceFolder url="file://$MODULE_DIR$/route" isTestSource="false" packagePrefix="Route\" />
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" packagePrefix="Lib\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
<excludeFolder url="file://$MODULE_DIR$/vendor/vlucas/phpdotenv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/workerman/gateway-worker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/workerman/mysql" />
<excludeFolder url="file://$MODULE_DIR$/vendor/workerman/workerman" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/cdz_ota.iml" filepath="$PROJECT_DIR$/.idea/cdz_ota.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/workerman/mysql" />
<path value="$PROJECT_DIR$/vendor/workerman/gateway-worker" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
<path value="$PROJECT_DIR$/vendor/vlucas/phpdotenv" />
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/monolog/monolog" />
<path value="$PROJECT_DIR$/vendor/workerman/workerman" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" />
</project>
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/9/10
* Time: 下午3:50
*/
class Events
{
public static function onConnect($clientId)
{
}
public static function onMessage($clientId,$message)
{
}
public static function onClose($clientId)
{
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/8/10
* Time: 上午10:49
*/
namespace Application\controller;
use Src\log\record\Record;
use Src\log\GatewayLogWriter;
use Src\request\Request;
use Src\response\Response;
use Src\route\Route;
class Controller
{
/**
* @var Record
*/
protected $logRecord;
/**
* 数据帧解析
* @var array
*/
protected $parameterMap = [];
/**
* 客户端id
*
* @var string
*/
protected $clientId;
/**
* 接受到的消息
*
* @var string
*/
protected $message;
/**
* 请求对象
*
* @var Request
*/
protected $request;
/**
* 是否加密
*
* @var bool
*/
protected $encrypt = false;
/**
* 数据帧基本结构
*
* @var array
*/
protected $defaultHeader = [
'preamble' => 2,
'headerFlag' => 1,
'headerCtrl' => 1,
'length' => 2,
'nonce' => 1,
];
/**
* 数据头
*
* @var string
*/
protected $preamble;
/**
* 是否加密字段
*
* @var string
*/
protected $headerFlag;
/**
* 消息命令
*
* @var string
*/
protected $headerCtrl;
/**
* 数据帧长度
*
* @var string
*/
protected $length;
/**
* 数据帧中的随机字符
*
* @var string
*/
protected $nonce;
/**
* 数据头
*
* @var array
*/
protected $headerBody = [];
/**
* 响应实例
*
* @var Response
*/
protected $response;
/**
* @var int
*/
protected $plc = 0;
protected $route;
protected $command = '';
/**
* Controller constructor.
* @param string $clientId
* @param string $message
* @param string $command
*/
public function __construct(string $clientId, string $message, string $command)
{
$this->clientId = $clientId;
$this->message = $message;
$this->parseHeader($message, $this->defaultHeader);
$this->command = $command;
$this->parseBody();
$this->response = new Response($this->clientId, $this->headerFlag);
$this->encrypt = $this->headerFlag === '05' ? true : false;
$this->logRecord = new Record();
$this->logRecord->setBody($this->message);
$this->logRecord->setCommand($command);
if (isset($_SESSION['station_id'])) {
$this->logRecord->setStationId($_SESSION['station_id']);
}
if (isset($_SESSION['gateway_id'])) {
$this->logRecord->setGatewayId($_SESSION['gateway_id']);
}
}
public function __destruct()
{
if($this->command === '00')
{
return ;
}
$logWriter = new GatewayLogWriter();
if($this->response->getResponseMessage() !== '')
{
$this->logRecord->setResponse($this->response->getResponseMessage());
}
$logWriter->write($this->logRecord);
}
/**
*
*/
private function parseBody()
{
$tmp = [];
foreach ($this->parameterMap[$this->command] as $key => $length) {
$start = $this->plc;
$length *= 2;
$tmp[$key] = substr($this->message, $start, $length);
$this->plc += $length;
}
$this->request = new Request($tmp);
}
private function parseHeader(string $message, array $map)
{
$parameters = [];
$start = 0;
foreach ($map as $key => $length) {
$length *= 2;
$parameters[$key] = substr($message, $start, $length);
if (property_exists($this, $key)) {
$this->$key = $parameters[$key];
}
$start += $length;
$this->plc += $length;
}
return $parameters;
}
/**
* Get 数据帧解析
*
* @return array
*/
public function getParameterMap()
{
return $this->parameterMap;
}
/**
* Set 数据帧解析
*
* @param array $parameterMap 数据帧解析
*
* @return self
*/
public function setParameterMap(array $parameterMap)
{
$this->parameterMap = $parameterMap;
return $this;
}
/**
* Get 客户端id
*
* @return string
*/
public function getClientId()
{
return $this->clientId;
}
/**
* Set 客户端id
*
* @param string $clientId 客户端id
*
* @return self
*/
public function setClientId(string $clientId)
{
$this->clientId = $clientId;
return $this;
}
/**
* Get 接受到的消息
*
* @return string
*/
public function getMessage()
{
return $this->message;
}
/**
* Set 接受到的消息
*
* @param string $message 接受到的消息
*
* @return self
*/
public function setMessage(string $message)
{
$this->message = $message;
return $this;
}
/**
* Get 请求对象
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Set 请求对象
*
* @param Request $request 请求对象
*
* @return self
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* Get 是否加密
*
* @return bool
*/
public function getEncrypt()
{
return $this->encrypt;
}
/**
* Set 是否加密
*
* @param bool $encrypt 是否加密
*
* @return self
*/
public function setEncrypt(bool $encrypt)
{
$this->encrypt = $encrypt;
return $this;
}
/**
* Get 数据帧基本结构
*
* @return array
*/
public function getDefaultHeader()
{
return $this->defaultHeader;
}
/**
* Set 数据帧基本结构
*
* @param array $defaultHeader 数据帧基本结构
*
* @return self
*/
public function setDefaultHeader(array $defaultHeader)
{
$this->defaultHeader = $defaultHeader;
return $this;
}
/**
* Get 响应实例
*
* @return Response
*/
public function getResponse()
{
return $this->response;
}
/**
* Set 响应实例
*
* @param Response $response 响应实例
*
* @return self
*/
public function setResponse(Response $response)
{
$this->response = $response;
return $this;
}
/**
* Get 消息命令
*
* @return string
*/
public function getHeaderCtrl()
{
return $this->headerCtrl;
}
/**
* Set 消息命令
*
* @param string $headerCtrl 消息命令
*
* @return self
*/
public function setHeaderCtrl(string $headerCtrl)
{
$this->headerCtrl = $headerCtrl;
return $this;
}
/**
* Get 数据帧长度
*
* @return string
*/
public function getLength()
{
return $this->length;
}
/**
* Set 数据帧长度
*
* @param string $length 数据帧长度
*
* @return self
*/
public function setLength(string $length)
{
$this->length = $length;
return $this;
}
/**
* Get 数据头
*
* @return string
*/
public function getPreamble()
{
return $this->preamble;
}
/**
* Set 数据头
*
* @param string $preamble 数据头
*
* @return self
*/
public function setPreamble(string $preamble)
{
$this->preamble = $preamble;
return $this;
}
/**
* Get 数据帧中的随机字符
*
* @return string
*/
public function getNonce()
{
return $this->nonce;
}
/**
* Set 数据帧中的随机字符
*
* @param string $nonce 数据帧中的随机字符
*
* @return self
*/
public function setNonce(string $nonce)
{
$this->nonce = $nonce;
return $this;
}
}
\ No newline at end of file
<?php
namespace Application\route;
use Application\controller\Controller;
use Src\route\Route;
class Api
{
public $route = [];
public function boot()
{
}
/**
* 添加路由
*
* @param string $command
* @param string $controllerAction
* @param array $middlewar
* @return void
*/
public function add(string $command, string $controllerAction, array $middleware = [])
{
$route = new Route();
$route->setCommand($command);
if (strstr($controllerAction, '@')) {
$controllerAction = explode('@', $controllerAction);
try {
$controllerName = 'Application\\controller\\' . $controllerAction[0];
$controller = new \ReflectionClass($controllerName);
$route->setController($controller);
$methods = $controller->getMethods(\ReflectionMethod::IS_PUBLIC);
$result = false;
foreach ($methods as $method) {
if ($method->name === $controllerAction[1]) {
$result = true;
}
}
if ($result === false) {
exit($controllerAction[1] . 'method not find');
}
$route->setMethod($controllerAction[1]);
unset($controller);
} catch (\ReflectionException $exception) {
exit(__CLASS__ . '-->' . $exception->getMessage());
}
$this->route[$command] = $route;
} else {
exit('wrong route missing controller');
}
unset($route);
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/8/10
* Time: 上午11:05
*/
$worker = new \GatewayWorker\BusinessWorker();
$worker->name = 'cdz_business';
$worker->count = 1;
$worker->registerAddress = getenv('REGISTER_IP') . ':' . getenv('REGISTER_PORT');
//$logs = fopen('./storage/logs/business.log','w');
//fclose($logs);
//\GatewayWorker\BusinessWorker::$stdoutFile = './storage/stdout.log';
//\GatewayWorker\BusinessWorker::$logFile = './storage/logs/business.log';
if (!defined('GLOBAL_START')) {
\Workerman\Worker::runAll();
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/8/10
* Time: 上午11:05
*/
$gateway = new \GatewayWorker\Gateway('bintohex://0.0.0.0:' . getenv('GATEWAY_PORT'));
$gateway->name = 'cdz';
$gateway->count = getenv('GATEWAY_COUNT');
$gateway->lanIp = getenv('GATEWAY_IP');
$gateway->startPort = getenv('GATEWAY_START_PORT');
$gateway->registerAddress = getenv('REGISTER_IP') . ':' . getenv('REGISTER_PORT');
// 心跳间隔
$gateway->pingInterval = 50;
// 心跳数据
$gateway->pingNotResponseLimit = 2;
if (!defined('GLOBAL_START')) {
\Workerman\Worker::runAll();
}
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/8/10
* Time: 上午11:05
*/
$register = new \GatewayWorker\Register('text://' . getenv('REGISTER_IP') . ':' . getenv('REGISTER_PORT'));
if (!defined('GLOBAL_START')) {
\Workerman\Worker::runAll();
}
\ No newline at end of file
{
"require": {
"workerman/gateway-worker": "^3.0",
"workerman/mysql": "^1.0",
"monolog/monolog": "^1.23",
"guzzlehttp/guzzle": "^6.3",
"vlucas/phpdotenv": "^2.5"
},
"autoload": {
"psr-4": {
"Application\\": "application/",
"Lib\\":"lib/",
"Route\\":"route/",
"Src\\" :"src/"
}
}
}
This diff is collapsed. Click to expand it.
# 充电桩ota升级
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/9/10
* Time: 下午3:59
*/
namespace Src\file;
class Open
{
private $path = '';
private $file = null;
private $fileSize = 0;
public function __construct(string $path)
{
$this->path = $path;
}
public function open()
{
$this->file = fopen($this->path, "rb");
if ($this->file === false) {
return false;
}
$this->fileSize = filesize($this->path);
return true;
}
/**
* @return string
*/
public function getPath(): string
{
return $this->path;
}
/**
* @param string $path
*/
public function setPath(string $path): void
{
$this->path = $path;
}
/**
* @return null
*/
public function getFile()
{
return $this->file;
}
/**
* @param null $file
*/
public function setFile($file): void
{
$this->file = $file;
}
/**
* @return int
*/
public function getFileSize(): int
{
return $this->fileSize;
}
/**
* @param int $fileSize
*/
public function setFileSize(int $fileSize): void
{
$this->fileSize = $fileSize;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/9/10
* Time: 下午4:00
*/
namespace Src\file;
class Read
{
private $openFile;
public function __construct(Open $open)
{
$this->openFile = $open;
}
public function read($length = -1)
{
if($length === -1)
{
return fread($this->openFile->getFile(),$this->openFile->getFileSize());
}else{
}
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/8/10
* Time: 上午10:28
*/
namespace Src\route;
use Application\controller\Controller;
use \ReflectionClass;
/**
* 路由类
*/
class Route
{
/**
* 命令
*
* @var string
*/
private $command;
/**
* 控制器
*
* @var \ReflectionClass
*/
private $controller;
/**
* 控制器方法
*
* @var string
*/
private $method;
/**
* 路由组
* @var string
*/
private $group;
/**
* 中间件验证
*
* @var array
*/
private $middleware = [];
/**
* Get 中间件验证
*
* @return array
*/
public function getMiddleware()
{
return $this->middleware;
}
/**
* Set 中间件验证
*
* @param array $middleware 中间件验证
*
* @return self
*/
public function setMiddleware(array $middleware)
{
$this->middleware = $middleware;
return $this;
}
/**
* Get 路由组
*
* @return string
*/
public function getGroup()
{
return $this->group;
}
/**
* Set 路由组
*
* @param string $group 路由组
*
* @return self
*/
public function setGroup(string $group)
{
$this->group = $group;
return $this;
}
/**
* Get 控制器方法
*
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Set 控制器方法
*
* @param string $method 控制器方法
*
* @return self
*/
public function setMethod(string $method)
{
$this->method = $method;
return $this;
}
/**
* @return \ReflectionClass
*/
public function getController(): \ReflectionClass
{
return $this->controller;
}
/**
* @param \ReflectionClass $controller
*/
public function setController(\ReflectionClass $controller): void
{
$this->controller = $controller;
}
/**
* Get 命令
*
* @return string
*/
public function getCommand()
{
return $this->command;
}
/**
* Set 命令
*
* @param string $command 命令
*
* @return self
*/
public function setCommand(string $command)
{
$this->command = $command;
return $this;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: banice
* Date: 2018/8/10
* Time: 上午9:10
*/
ini_set('display_errors', 'on');
date_default_timezone_set('Asia/Shanghai');
// 检查扩展
if (!extension_loaded('pcntl')) {
exit("Please install pcntl extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
}
if (!extension_loaded('posix')) {
exit("Please install posix extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
}
// 标记是全局启动
define('GLOBAL_START', 1);
require_once __DIR__ . '/vendor/autoload.php';
$dotenv = new \Dotenv\Dotenv(__DIR__);
$dotenv->load();
$startGateway = getenv('START_FILE');
$startGateway = explode('|', $startGateway);
foreach (glob(__DIR__ . '/application/start*.php') as $startFile) {
foreach ($startGateway as $name) {
if (strstr($startFile, $name)) {
require_once $startFile;
}
}
}
\Workerman\Worker::runAll();
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit4b3289a8eb5b5105a933b763a0fe0fe5::getLoader();
Copyright (c) Nils Adermann, Jordi Boggiano
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.
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
);
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Workerman\\MySQL\\' => array($vendorDir . '/workerman/mysql/src'),
'Workerman\\' => array($vendorDir . '/workerman/workerman'),
'Src\\' => array($baseDir . '/src'),
'Route\\' => array($baseDir . '/route'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'Lib\\' => array($baseDir . '/lib'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'GatewayWorker\\' => array($vendorDir . '/workerman/gateway-worker/src'),
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
'Application\\' => array($baseDir . '/application'),
);
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit4b3289a8eb5b5105a933b763a0fe0fe5
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit4b3289a8eb5b5105a933b763a0fe0fe5', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit4b3289a8eb5b5105a933b763a0fe0fe5', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit4b3289a8eb5b5105a933b763a0fe0fe5::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit4b3289a8eb5b5105a933b763a0fe0fe5::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire4b3289a8eb5b5105a933b763a0fe0fe5($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire4b3289a8eb5b5105a933b763a0fe0fe5($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit4b3289a8eb5b5105a933b763a0fe0fe5
{
public static $files = array (
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
);
public static $prefixLengthsPsr4 = array (
'W' =>
array (
'Workerman\\MySQL\\' => 16,
'Workerman\\' => 10,
),
'S' =>
array (
'Src\\' => 4,
),
'R' =>
array (
'Route\\' => 6,
),
'P' =>
array (
'Psr\\Log\\' => 8,
'Psr\\Http\\Message\\' => 17,
),
'M' =>
array (
'Monolog\\' => 8,
),
'L' =>
array (
'Lib\\' => 4,
),
'G' =>
array (
'GuzzleHttp\\Psr7\\' => 16,
'GuzzleHttp\\Promise\\' => 19,
'GuzzleHttp\\' => 11,
'GatewayWorker\\' => 14,
),
'D' =>
array (
'Dotenv\\' => 7,
),
'A' =>
array (
'Application\\' => 12,
),
);
public static $prefixDirsPsr4 = array (
'Workerman\\MySQL\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/mysql/src',
),
'Workerman\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/workerman',
),
'Src\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
'Route\\' =>
array (
0 => __DIR__ . '/../..' . '/route',
),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
),
'Psr\\Http\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Monolog\\' =>
array (
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
),
'Lib\\' =>
array (
0 => __DIR__ . '/../..' . '/lib',
),
'GuzzleHttp\\Psr7\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
),
'GuzzleHttp\\Promise\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
),
'GuzzleHttp\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
),
'GatewayWorker\\' =>
array (
0 => __DIR__ . '/..' . '/workerman/gateway-worker/src',
),
'Dotenv\\' =>
array (
0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src',
),
'Application\\' =>
array (
0 => __DIR__ . '/../..' . '/application',
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit4b3289a8eb5b5105a933b763a0fe0fe5::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit4b3289a8eb5b5105a933b763a0fe0fe5::$prefixDirsPsr4;
}, null, ClassLoader::class);
}
}
Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
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.
Guzzle, PHP HTTP client
=======================
[![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
[![Build Status](https://img.shields.io/travis/guzzle/guzzle.svg?style=flat-square)](https://travis-ci.org/guzzle/guzzle)
[![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)
Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
trivial to integrate with web services.
- Simple interface for building query strings, POST requests, streaming large
uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
etc...
- Can send both synchronous and asynchronous requests using the same interface.
- Uses PSR-7 interfaces for requests, responses, and streams. This allows you
to utilize other PSR-7 compatible libraries with Guzzle.
- Abstracts away the underlying HTTP transport, allowing you to write
environment and transport agnostic code; i.e., no hard dependency on cURL,
PHP streams, sockets, or non-blocking event loops.
- Middleware system allows you to augment and compose client behavior.
```php
$client = new \GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');
echo $res->getStatusCode();
// 200
echo $res->getHeaderLine('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// '{"id": 1420053, "name": "guzzle", ...}'
// Send an asynchronous request.
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
$promise = $client->sendAsync($request)->then(function ($response) {
echo 'I completed! ' . $response->getBody();
});
$promise->wait();
```
## Help and docs
- [Documentation](http://guzzlephp.org/)
- [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle)
- [Gitter](https://gitter.im/guzzle/guzzle)
## Installing Guzzle
The recommended way to install Guzzle is through
[Composer](http://getcomposer.org).
```bash
# Install Composer
curl -sS https://getcomposer.org/installer | php
```
Next, run the Composer command to install the latest stable version of Guzzle:
```bash
php composer.phar require guzzlehttp/guzzle
```
After installing, you need to require Composer's autoloader:
```php
require 'vendor/autoload.php';
```
You can then later update Guzzle using composer:
```bash
composer.phar update
```
## Version Guidance
| Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version |
|---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------|
| 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >= 5.3.3 |
| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >= 5.4 |
| 5.x | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >= 5.4 |
| 6.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >= 5.5 |
[guzzle-3-repo]: https://github.com/guzzle/guzzle3
[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3
[guzzle-6-repo]: https://github.com/guzzle/guzzle
[guzzle-3-docs]: http://guzzle3.readthedocs.org/en/latest/
[guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/
[guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/
{
"name": "guzzlehttp/guzzle",
"type": "library",
"description": "Guzzle is a PHP HTTP client library",
"keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"],
"homepage": "http://guzzlephp.org/",
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.5",
"guzzlehttp/psr7": "^1.4",
"guzzlehttp/promises": "^1.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
"psr/log": "^1.0"
},
"autoload": {
"files": ["src/functions_include.php"],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"GuzzleHttp\\Tests\\": "tests/"
}
},
"suggest": {
"psr/log": "Required for using the Log middleware"
},
"extra": {
"branch-alias": {
"dev-master": "6.3-dev"
}
}
}
<?php
namespace GuzzleHttp;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
/**
* Client interface for sending HTTP requests.
*/
interface ClientInterface
{
const VERSION = '6.3.3';
/**
* Send an HTTP request.
*
* @param RequestInterface $request Request to send
* @param array $options Request options to apply to the given
* request and to the transfer.
*
* @return ResponseInterface
* @throws GuzzleException
*/
public function send(RequestInterface $request, array $options = []);
/**
* Asynchronously send an HTTP request.
*
* @param RequestInterface $request Request to send
* @param array $options Request options to apply to the given
* request and to the transfer.
*
* @return PromiseInterface
*/
public function sendAsync(RequestInterface $request, array $options = []);
/**
* Create and send an HTTP request.
*
* Use an absolute path to override the base path of the client, or a
* relative path to append to the base path of the client. The URL can
* contain the query string as well.
*
* @param string $method HTTP method.
* @param string|UriInterface $uri URI object or string.
* @param array $options Request options to apply.
*
* @return ResponseInterface
* @throws GuzzleException
*/
public function request($method, $uri, array $options = []);
/**
* Create and send an asynchronous HTTP request.
*
* Use an absolute path to override the base path of the client, or a
* relative path to append to the base path of the client. The URL can
* contain the query string as well. Use an array to provide a URL
* template and additional variables to use in the URL template expansion.
*
* @param string $method HTTP method
* @param string|UriInterface $uri URI object or string.
* @param array $options Request options to apply.
*
* @return PromiseInterface
*/
public function requestAsync($method, $uri, array $options = []);
/**
* Get a client configuration option.
*
* These options include default request options of the client, a "handler"
* (if utilized by the concrete client), and a "base_uri" if utilized by
* the concrete client.
*
* @param string|null $option The config option to retrieve.
*
* @return mixed
*/
public function getConfig($option = null);
}
<?php
namespace GuzzleHttp\Cookie;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Cookie jar that stores cookies as an array
*/
class CookieJar implements CookieJarInterface
{
/** @var SetCookie[] Loaded cookie data */
private $cookies = [];
/** @var bool */
private $strictMode;
/**
* @param bool $strictMode Set to true to throw exceptions when invalid
* cookies are added to the cookie jar.
* @param array $cookieArray Array of SetCookie objects or a hash of
* arrays that can be used with the SetCookie
* constructor
*/
public function __construct($strictMode = false, $cookieArray = [])
{
$this->strictMode = $strictMode;
foreach ($cookieArray as $cookie) {
if (!($cookie instanceof SetCookie)) {
$cookie = new SetCookie($cookie);
}
$this->setCookie($cookie);
}
}
/**
* Create a new Cookie jar from an associative array and domain.
*
* @param array $cookies Cookies to create the jar from
* @param string $domain Domain to set the cookies to
*
* @return self
*/
public static function fromArray(array $cookies, $domain)
{
$cookieJar = new self();
foreach ($cookies as $name => $value) {
$cookieJar->setCookie(new SetCookie([
'Domain' => $domain,
'Name' => $name,
'Value' => $value,
'Discard' => true
]));
}
return $cookieJar;
}
/**
* @deprecated
*/
public static function getCookieValue($value)
{
return $value;
}
/**
* Evaluate if this cookie should be persisted to storage
* that survives between requests.
*
* @param SetCookie $cookie Being evaluated.
* @param bool $allowSessionCookies If we should persist session cookies
* @return bool
*/
public static function shouldPersist(
SetCookie $cookie,
$allowSessionCookies = false
) {
if ($cookie->getExpires() || $allowSessionCookies) {
if (!$cookie->getDiscard()) {
return true;
}
}
return false;
}
/**
* Finds and returns the cookie based on the name
*
* @param string $name cookie name to search for
* @return SetCookie|null cookie that was found or null if not found
*/
public function getCookieByName($name)
{
// don't allow a null name
if ($name === null) {
return null;
}
foreach ($this->cookies as $cookie) {
if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
return $cookie;
}
}
}
public function toArray()
{
return array_map(function (SetCookie $cookie) {
return $cookie->toArray();
}, $this->getIterator()->getArrayCopy());
}
public function clear($domain = null, $path = null, $name = null)
{
if (!$domain) {
$this->cookies = [];
return;
} elseif (!$path) {
$this->cookies = array_filter(
$this->cookies,
function (SetCookie $cookie) use ($path, $domain) {
return !$cookie->matchesDomain($domain);
}
);
} elseif (!$name) {
$this->cookies = array_filter(
$this->cookies,
function (SetCookie $cookie) use ($path, $domain) {
return !($cookie->matchesPath($path) &&
$cookie->matchesDomain($domain));
}
);
} else {
$this->cookies = array_filter(
$this->cookies,
function (SetCookie $cookie) use ($path, $domain, $name) {
return !($cookie->getName() == $name &&
$cookie->matchesPath($path) &&
$cookie->matchesDomain($domain));
}
);
}
}
public function clearSessionCookies()
{
$this->cookies = array_filter(
$this->cookies,
function (SetCookie $cookie) {
return !$cookie->getDiscard() && $cookie->getExpires();
}
);
}
public function setCookie(SetCookie $cookie)
{
// If the name string is empty (but not 0), ignore the set-cookie
// string entirely.
$name = $cookie->getName();
if (!$name && $name !== '0') {
return false;
}
// Only allow cookies with set and valid domain, name, value
$result = $cookie->validate();
if ($result !== true) {
if ($this->strictMode) {
throw new \RuntimeException('Invalid cookie: ' . $result);
} else {
$this->removeCookieIfEmpty($cookie);
return false;
}
}
// Resolve conflicts with previously set cookies
foreach ($this->cookies as $i => $c) {
// Two cookies are identical, when their path, and domain are
// identical.
if ($c->getPath() != $cookie->getPath() ||
$c->getDomain() != $cookie->getDomain() ||
$c->getName() != $cookie->getName()
) {
continue;
}
// The previously set cookie is a discard cookie and this one is
// not so allow the new cookie to be set
if (!$cookie->getDiscard() && $c->getDiscard()) {
unset($this->cookies[$i]);
continue;
}
// If the new cookie's expiration is further into the future, then
// replace the old cookie
if ($cookie->getExpires() > $c->getExpires()) {
unset($this->cookies[$i]);
continue;
}
// If the value has changed, we better change it
if ($cookie->getValue() !== $c->getValue()) {
unset($this->cookies[$i]);
continue;
}
// The cookie exists, so no need to continue
return false;
}
$this->cookies[] = $cookie;
return true;
}
public function count()
{
return count($this->cookies);
}
public function getIterator()
{
return new \ArrayIterator(array_values($this->cookies));
}
public function extractCookies(
RequestInterface $request,
ResponseInterface $response
) {
if ($cookieHeader = $response->getHeader('Set-Cookie')) {
foreach ($cookieHeader as $cookie) {
$sc = SetCookie::fromString($cookie);
if (!$sc->getDomain()) {
$sc->setDomain($request->getUri()->getHost());
}
if (0 !== strpos($sc->getPath(), '/')) {
$sc->setPath($this->getCookiePathFromRequest($request));
}
$this->setCookie($sc);
}
}
}
/**
* Computes cookie path following RFC 6265 section 5.1.4
*
* @link https://tools.ietf.org/html/rfc6265#section-5.1.4
*
* @param RequestInterface $request
* @return string
*/
private function getCookiePathFromRequest(RequestInterface $request)
{
$uriPath = $request->getUri()->getPath();
if ('' === $uriPath) {
return '/';
}
if (0 !== strpos($uriPath, '/')) {
return '/';
}
if ('/' === $uriPath) {
return '/';
}
if (0 === $lastSlashPos = strrpos($uriPath, '/')) {
return '/';
}
return substr($uriPath, 0, $lastSlashPos);
}
public function withCookieHeader(RequestInterface $request)
{
$values = [];
$uri = $request->getUri();
$scheme = $uri->getScheme();
$host = $uri->getHost();
$path = $uri->getPath() ?: '/';
foreach ($this->cookies as $cookie) {
if ($cookie->matchesPath($path) &&
$cookie->matchesDomain($host) &&
!$cookie->isExpired() &&
(!$cookie->getSecure() || $scheme === 'https')
) {
$values[] = $cookie->getName() . '='
. $cookie->getValue();
}
}
return $values
? $request->withHeader('Cookie', implode('; ', $values))
: $request;
}
/**
* If a cookie already exists and the server asks to set it again with a
* null value, the cookie must be deleted.
*
* @param SetCookie $cookie
*/
private function removeCookieIfEmpty(SetCookie $cookie)
{
$cookieValue = $cookie->getValue();
if ($cookieValue === null || $cookieValue === '') {
$this->clear(
$cookie->getDomain(),
$cookie->getPath(),
$cookie->getName()
);
}
}
}
<?php
namespace GuzzleHttp\Cookie;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Stores HTTP cookies.
*
* It extracts cookies from HTTP requests, and returns them in HTTP responses.
* CookieJarInterface instances automatically expire contained cookies when
* necessary. Subclasses are also responsible for storing and retrieving
* cookies from a file, database, etc.
*
* @link http://docs.python.org/2/library/cookielib.html Inspiration
*/
interface CookieJarInterface extends \Countable, \IteratorAggregate
{
/**
* Create a request with added cookie headers.
*
* If no matching cookies are found in the cookie jar, then no Cookie
* header is added to the request and the same request is returned.
*
* @param RequestInterface $request Request object to modify.
*
* @return RequestInterface returns the modified request.
*/
public function withCookieHeader(RequestInterface $request);
/**
* Extract cookies from an HTTP response and store them in the CookieJar.
*
* @param RequestInterface $request Request that was sent
* @param ResponseInterface $response Response that was received
*/
public function extractCookies(
RequestInterface $request,
ResponseInterface $response
);
/**
* Sets a cookie in the cookie jar.
*
* @param SetCookie $cookie Cookie to set.
*
* @return bool Returns true on success or false on failure
*/
public function setCookie(SetCookie $cookie);
/**
* Remove cookies currently held in the cookie jar.
*
* Invoking this method without arguments will empty the whole cookie jar.
* If given a $domain argument only cookies belonging to that domain will
* be removed. If given a $domain and $path argument, cookies belonging to
* the specified path within that domain are removed. If given all three
* arguments, then the cookie with the specified name, path and domain is
* removed.
*
* @param string $domain Clears cookies matching a domain
* @param string $path Clears cookies matching a domain and path
* @param string $name Clears cookies matching a domain, path, and name
*
* @return CookieJarInterface
*/
public function clear($domain = null, $path = null, $name = null);
/**
* Discard all sessions cookies.
*
* Removes cookies that don't have an expire field or a have a discard
* field set to true. To be called when the user agent shuts down according
* to RFC 2965.
*/
public function clearSessionCookies();
/**
* Converts the cookie jar to an array.
*
* @return array
*/
public function toArray();
}
<?php
namespace GuzzleHttp\Cookie;
/**
* Persists non-session cookies using a JSON formatted file
*/
class FileCookieJar extends CookieJar
{
/** @var string filename */
private $filename;
/** @var bool Control whether to persist session cookies or not. */
private $storeSessionCookies;
/**
* Create a new FileCookieJar object
*
* @param string $cookieFile File to store the cookie data
* @param bool $storeSessionCookies Set to true to store session cookies
* in the cookie jar.
*
* @throws \RuntimeException if the file cannot be found or created
*/
public function __construct($cookieFile, $storeSessionCookies = false)
{
$this->filename = $cookieFile;
$this->storeSessionCookies = $storeSessionCookies;
if (file_exists($cookieFile)) {
$this->load($cookieFile);
}
}
/**
* Saves the file when shutting down
*/
public function __destruct()
{
$this->save($this->filename);
}
/**
* Saves the cookies to a file.
*
* @param string $filename File to save
* @throws \RuntimeException if the file cannot be found or created
*/
public function save($filename)
{
$json = [];
foreach ($this as $cookie) {
/** @var SetCookie $cookie */
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
$json[] = $cookie->toArray();
}
}
$jsonStr = \GuzzleHttp\json_encode($json);
if (false === file_put_contents($filename, $jsonStr)) {
throw new \RuntimeException("Unable to save file {$filename}");
}
}
/**
* Load cookies from a JSON formatted file.
*
* Old cookies are kept unless overwritten by newly loaded ones.
*
* @param string $filename Cookie file to load.
* @throws \RuntimeException if the file cannot be loaded.
*/
public function load($filename)
{
$json = file_get_contents($filename);
if (false === $json) {
throw new \RuntimeException("Unable to load file {$filename}");
} elseif ($json === '') {
return;
}
$data = \GuzzleHttp\json_decode($json, true);
if (is_array($data)) {
foreach (json_decode($json, true) as $cookie) {
$this->setCookie(new SetCookie($cookie));
}
} elseif (strlen($data)) {
throw new \RuntimeException("Invalid cookie file: {$filename}");
}
}
}
<?php
namespace GuzzleHttp\Cookie;
/**
* Persists cookies in the client session
*/
class SessionCookieJar extends CookieJar
{
/** @var string session key */
private $sessionKey;
/** @var bool Control whether to persist session cookies or not. */
private $storeSessionCookies;
/**
* Create a new SessionCookieJar object
*
* @param string $sessionKey Session key name to store the cookie
* data in session
* @param bool $storeSessionCookies Set to true to store session cookies
* in the cookie jar.
*/
public function __construct($sessionKey, $storeSessionCookies = false)
{
$this->sessionKey = $sessionKey;
$this->storeSessionCookies = $storeSessionCookies;
$this->load();
}
/**
* Saves cookies to session when shutting down
*/
public function __destruct()
{
$this->save();
}
/**
* Save cookies to the client session
*/
public function save()
{
$json = [];
foreach ($this as $cookie) {
/** @var SetCookie $cookie */
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
$json[] = $cookie->toArray();
}
}
$_SESSION[$this->sessionKey] = json_encode($json);
}
/**
* Load the contents of the client session into the data array
*/
protected function load()
{
if (!isset($_SESSION[$this->sessionKey])) {
return;
}
$data = json_decode($_SESSION[$this->sessionKey], true);
if (is_array($data)) {
foreach ($data as $cookie) {
$this->setCookie(new SetCookie($cookie));
}
} elseif (strlen($data)) {
throw new \RuntimeException("Invalid cookie data");
}
}
}
<?php
namespace GuzzleHttp\Exception;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Exception when an HTTP error occurs (4xx or 5xx error)
*/
class BadResponseException extends RequestException
{
public function __construct(
$message,
RequestInterface $request,
ResponseInterface $response = null,
\Exception $previous = null,
array $handlerContext = []
) {
if (null === $response) {
@trigger_error(
'Instantiating the ' . __CLASS__ . ' class without a Response is deprecated since version 6.3 and will be removed in 7.0.',
E_USER_DEPRECATED
);
}
parent::__construct($message, $request, $response, $previous, $handlerContext);
}
}
<?php
namespace GuzzleHttp\Exception;
/**
* Exception when a client error is encountered (4xx codes)
*/
class ClientException extends BadResponseException {}
<?php
namespace GuzzleHttp\Exception;
use Psr\Http\Message\RequestInterface;
/**
* Exception thrown when a connection cannot be established.
*
* Note that no response is present for a ConnectException
*/
class ConnectException extends RequestException
{
public function __construct(
$message,
RequestInterface $request,
\Exception $previous = null,
array $handlerContext = []
) {
parent::__construct($message, $request, null, $previous, $handlerContext);
}
/**
* @return null
*/
public function getResponse()
{
return null;
}
/**
* @return bool
*/
public function hasResponse()
{
return false;
}
}
<?php
namespace GuzzleHttp\Exception;
/**
* @method string getMessage()
* @method \Throwable|null getPrevious()
* @method mixed getCode()
* @method string getFile()
* @method int getLine()
* @method array getTrace()
* @method string getTraceAsString()
*/
interface GuzzleException {}
<?php
namespace GuzzleHttp\Exception;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Promise\PromiseInterface;
use Psr\Http\Message\UriInterface;
/**
* HTTP Request exception
*/
class RequestException extends TransferException
{
/** @var RequestInterface */
private $request;
/** @var ResponseInterface */
private $response;
/** @var array */
private $handlerContext;
public function __construct(
$message,
RequestInterface $request,
ResponseInterface $response = null,
\Exception $previous = null,
array $handlerContext = []
) {
// Set the code of the exception if the response is set and not future.
$code = $response && !($response instanceof PromiseInterface)
? $response->getStatusCode()
: 0;
parent::__construct($message, $code, $previous);
$this->request = $request;
$this->response = $response;
$this->handlerContext = $handlerContext;
}
/**
* Wrap non-RequestExceptions with a RequestException
*
* @param RequestInterface $request
* @param \Exception $e
*
* @return RequestException
*/
public static function wrapException(RequestInterface $request, \Exception $e)
{
return $e instanceof RequestException
? $e
: new RequestException($e->getMessage(), $request, null, $e);
}
/**
* Factory method to create a new exception with a normalized error message
*
* @param RequestInterface $request Request
* @param ResponseInterface $response Response received
* @param \Exception $previous Previous exception
* @param array $ctx Optional handler context.
*
* @return self
*/
public static function create(
RequestInterface $request,
ResponseInterface $response = null,
\Exception $previous = null,
array $ctx = []
) {
if (!$response) {
return new self(
'Error completing request',
$request,
null,
$previous,
$ctx
);
}
$level = (int) floor($response->getStatusCode() / 100);
if ($level === 4) {
$label = 'Client error';
$className = ClientException::class;
} elseif ($level === 5) {
$label = 'Server error';
$className = ServerException::class;
} else {
$label = 'Unsuccessful request';
$className = __CLASS__;
}
$uri = $request->getUri();
$uri = static::obfuscateUri($uri);
// Client Error: `GET /` resulted in a `404 Not Found` response:
// <html> ... (truncated)
$message = sprintf(
'%s: `%s %s` resulted in a `%s %s` response',
$label,
$request->getMethod(),
$uri,
$response->getStatusCode(),
$response->getReasonPhrase()
);
$summary = static::getResponseBodySummary($response);
if ($summary !== null) {
$message .= ":\n{$summary}\n";
}
return new $className($message, $request, $response, $previous, $ctx);
}
/**
* Get a short summary of the response
*
* Will return `null` if the response is not printable.
*
* @param ResponseInterface $response
*
* @return string|null
*/
public static function getResponseBodySummary(ResponseInterface $response)
{
$body = $response->getBody();
if (!$body->isSeekable()) {
return null;
}
$size = $body->getSize();
if ($size === 0) {
return null;
}
$summary = $body->read(120);
$body->rewind();
if ($size > 120) {
$summary .= ' (truncated...)';
}
// Matches any printable character, including unicode characters:
// letters, marks, numbers, punctuation, spacing, and separators.
if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
return null;
}
return $summary;
}
/**
* Obfuscates URI if there is an username and a password present
*
* @param UriInterface $uri
*
* @return UriInterface
*/
private static function obfuscateUri($uri)
{
$userInfo = $uri->getUserInfo();
if (false !== ($pos = strpos($userInfo, ':'))) {
return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
}
return $uri;
}
/**
* Get the request that caused the exception
*
* @return RequestInterface
*/
public function getRequest()
{
return $this->request;
}
/**
* Get the associated response
*
* @return ResponseInterface|null
*/
public function getResponse()
{
return $this->response;
}
/**
* Check if a response was received
*
* @return bool
*/
public function hasResponse()
{
return $this->response !== null;
}
/**
* Get contextual information about the error from the underlying handler.
*
* The contents of this array will vary depending on which handler you are
* using. It may also be just an empty array. Relying on this data will
* couple you to a specific handler, but can give more debug information
* when needed.
*
* @return array
*/
public function getHandlerContext()
{
return $this->handlerContext;
}
}
<?php
namespace GuzzleHttp\Exception;
use Psr\Http\Message\StreamInterface;
/**
* Exception thrown when a seek fails on a stream.
*/
class SeekException extends \RuntimeException implements GuzzleException
{
private $stream;
public function __construct(StreamInterface $stream, $pos = 0, $msg = '')
{
$this->stream = $stream;
$msg = $msg ?: 'Could not seek the stream to position ' . $pos;
parent::__construct($msg);
}
/**
* @return StreamInterface
*/
public function getStream()
{
return $this->stream;
}
}
<?php
namespace GuzzleHttp\Exception;
/**
* Exception when a server error is encountered (5xx codes)
*/
class ServerException extends BadResponseException {}
<?php
namespace GuzzleHttp\Exception;
class TooManyRedirectsException extends RequestException {}
<?php
namespace GuzzleHttp\Exception;
class TransferException extends \RuntimeException implements GuzzleException {}
<?php
namespace GuzzleHttp\Handler;
use Psr\Http\Message\RequestInterface;
interface CurlFactoryInterface
{
/**
* Creates a cURL handle resource.
*
* @param RequestInterface $request Request
* @param array $options Transfer options
*
* @return EasyHandle
* @throws \RuntimeException when an option cannot be applied
*/
public function create(RequestInterface $request, array $options);
/**
* Release an easy handle, allowing it to be reused or closed.
*
* This function must call unset on the easy handle's "handle" property.
*
* @param EasyHandle $easy
*/
public function release(EasyHandle $easy);
}
<?php
namespace GuzzleHttp\Handler;
use GuzzleHttp\Psr7;
use Psr\Http\Message\RequestInterface;
/**
* HTTP handler that uses cURL easy handles as a transport layer.
*
* When using the CurlHandler, custom curl options can be specified as an
* associative array of curl option constants mapping to values in the
* **curl** key of the "client" key of the request.
*/
class CurlHandler
{
/** @var CurlFactoryInterface */
private $factory;
/**
* Accepts an associative array of options:
*
* - factory: Optional curl factory used to create cURL handles.
*
* @param array $options Array of options to use with the handler
*/
public function __construct(array $options = [])
{
$this->factory = isset($options['handle_factory'])
? $options['handle_factory']
: new CurlFactory(3);
}
public function __invoke(RequestInterface $request, array $options)
{
if (isset($options['delay'])) {
usleep($options['delay'] * 1000);
}
$easy = $this->factory->create($request, $options);
curl_exec($easy->handle);
$easy->errno = curl_errno($easy->handle);
return CurlFactory::finish($this, $easy, $this->factory);
}
}
<?php
namespace GuzzleHttp\Handler;
use GuzzleHttp\Promise as P;
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Psr7;
use Psr\Http\Message\RequestInterface;
/**
* Returns an asynchronous response using curl_multi_* functions.
*
* When using the CurlMultiHandler, custom curl options can be specified as an
* associative array of curl option constants mapping to values in the
* **curl** key of the provided request options.
*
* @property resource $_mh Internal use only. Lazy loaded multi-handle.
*/
class CurlMultiHandler
{
/** @var CurlFactoryInterface */
private $factory;
private $selectTimeout;
private $active;
private $handles = [];
private $delays = [];
/**
* This handler accepts the following options:
*
* - handle_factory: An optional factory used to create curl handles
* - select_timeout: Optional timeout (in seconds) to block before timing
* out while selecting curl handles. Defaults to 1 second.
*
* @param array $options
*/
public function __construct(array $options = [])
{
$this->factory = isset($options['handle_factory'])
? $options['handle_factory'] : new CurlFactory(50);
$this->selectTimeout = isset($options['select_timeout'])
? $options['select_timeout'] : 1;
}
public function __get($name)
{
if ($name === '_mh') {
return $this->_mh = curl_multi_init();
}
throw new \BadMethodCallException();
}
public function __destruct()
{
if (isset($this->_mh)) {
curl_multi_close($this->_mh);
unset($this->_mh);
}
}
public function __invoke(RequestInterface $request, array $options)
{
$easy = $this->factory->create($request, $options);
$id = (int) $easy->handle;
$promise = new Promise(
[$this, 'execute'],
function () use ($id) {
return $this->cancel($id);
}
);
$this->addRequest(['easy' => $easy, 'deferred' => $promise]);
return $promise;
}
/**
* Ticks the curl event loop.
*/
public function tick()
{
// Add any delayed handles if needed.
if ($this->delays) {
$currentTime = microtime(true);
foreach ($this->delays as $id => $delay) {
if ($currentTime >= $delay) {
unset($this->delays[$id]);
curl_multi_add_handle(
$this->_mh,
$this->handles[$id]['easy']->handle
);
}
}
}
// Step through the task queue which may add additional requests.
P\queue()->run();
if ($this->active &&
curl_multi_select($this->_mh, $this->selectTimeout) === -1
) {
// Perform a usleep if a select returns -1.
// See: https://bugs.php.net/bug.php?id=61141
usleep(250);
}
while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
$this->processMessages();
}
/**
* Runs until all outstanding connections have completed.
*/
public function execute()
{
$queue = P\queue();
while ($this->handles || !$queue->isEmpty()) {
// If there are no transfers, then sleep for the next delay
if (!$this->active && $this->delays) {
usleep($this->timeToNext());
}
$this->tick();
}
}
private function addRequest(array $entry)
{
$easy = $entry['easy'];
$id = (int) $easy->handle;
$this->handles[$id] = $entry;
if (empty($easy->options['delay'])) {
curl_multi_add_handle($this->_mh, $easy->handle);
} else {
$this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000);
}
}
/**
* Cancels a handle from sending and removes references to it.
*
* @param int $id Handle ID to cancel and remove.
*
* @return bool True on success, false on failure.
*/
private function cancel($id)
{
// Cannot cancel if it has been processed.
if (!isset($this->handles[$id])) {
return false;
}
$handle = $this->handles[$id]['easy']->handle;
unset($this->delays[$id], $this->handles[$id]);
curl_multi_remove_handle($this->_mh, $handle);
curl_close($handle);
return true;
}
private function processMessages()
{
while ($done = curl_multi_info_read($this->_mh)) {
$id = (int) $done['handle'];
curl_multi_remove_handle($this->_mh, $done['handle']);
if (!isset($this->handles[$id])) {
// Probably was cancelled.
continue;
}
$entry = $this->handles[$id];
unset($this->handles[$id], $this->delays[$id]);
$entry['easy']->errno = $done['result'];
$entry['deferred']->resolve(
CurlFactory::finish(
$this,
$entry['easy'],
$this->factory
)
);
}
}
private function timeToNext()
{
$currentTime = microtime(true);
$nextTime = PHP_INT_MAX;
foreach ($this->delays as $time) {
if ($time < $nextTime) {
$nextTime = $time;
}
}
return max(0, $nextTime - $currentTime) * 1000000;
}
}
<?php
namespace GuzzleHttp\Handler;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
/**
* Represents a cURL easy handle and the data it populates.
*
* @internal
*/
final class EasyHandle
{
/** @var resource cURL resource */
public $handle;
/** @var StreamInterface Where data is being written */
public $sink;
/** @var array Received HTTP headers so far */
public $headers = [];
/** @var ResponseInterface Received response (if any) */
public $response;
/** @var RequestInterface Request being sent */
public $request;
/** @var array Request options */
public $options = [];
/** @var int cURL error number (if any) */
public $errno = 0;
/** @var \Exception Exception during on_headers (if any) */
public $onHeadersException;
/**
* Attach a response to the easy handle based on the received headers.
*
* @throws \RuntimeException if no headers have been received.
*/
public function createResponse()
{
if (empty($this->headers)) {
throw new \RuntimeException('No headers have been received');
}
// HTTP-version SP status-code SP reason-phrase
$startLine = explode(' ', array_shift($this->headers), 3);
$headers = \GuzzleHttp\headers_from_lines($this->headers);
$normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
if (!empty($this->options['decode_content'])
&& isset($normalizedKeys['content-encoding'])
) {
$headers['x-encoded-content-encoding']
= $headers[$normalizedKeys['content-encoding']];
unset($headers[$normalizedKeys['content-encoding']]);
if (isset($normalizedKeys['content-length'])) {
$headers['x-encoded-content-length']
= $headers[$normalizedKeys['content-length']];
$bodyLength = (int) $this->sink->getSize();
if ($bodyLength) {
$headers[$normalizedKeys['content-length']] = $bodyLength;
} else {
unset($headers[$normalizedKeys['content-length']]);
}
}
}
// Attach a response to the easy handle with the parsed headers.
$this->response = new Response(
$startLine[1],
$headers,
$this->sink,
substr($startLine[0], 5),
isset($startLine[2]) ? (string) $startLine[2] : null
);
}
public function __get($name)
{
$msg = $name === 'handle'
? 'The EasyHandle has been released'
: 'Invalid property: ' . $name;
throw new \BadMethodCallException($msg);
}
}
<?php
namespace GuzzleHttp\Handler;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\TransferStats;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Handler that returns responses or throw exceptions from a queue.
*/
class MockHandler implements \Countable
{
private $queue = [];
private $lastRequest;
private $lastOptions;
private $onFulfilled;
private $onRejected;
/**
* Creates a new MockHandler that uses the default handler stack list of
* middlewares.
*
* @param array $queue Array of responses, callables, or exceptions.
* @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
* @param callable $onRejected Callback to invoke when the return value is rejected.
*
* @return HandlerStack
*/
public static function createWithMiddleware(
array $queue = null,
callable $onFulfilled = null,
callable $onRejected = null
) {
return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
}
/**
* The passed in value must be an array of
* {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
* callables, or Promises.
*
* @param array $queue
* @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
* @param callable $onRejected Callback to invoke when the return value is rejected.
*/
public function __construct(
array $queue = null,
callable $onFulfilled = null,
callable $onRejected = null
) {
$this->onFulfilled = $onFulfilled;
$this->onRejected = $onRejected;
if ($queue) {
call_user_func_array([$this, 'append'], $queue);
}
}
public function __invoke(RequestInterface $request, array $options)
{
if (!$this->queue) {
throw new \OutOfBoundsException('Mock queue is empty');
}
if (isset($options['delay'])) {
usleep($options['delay'] * 1000);
}
$this->lastRequest = $request;
$this->lastOptions = $options;
$response = array_shift($this->queue);
if (isset($options['on_headers'])) {
if (!is_callable($options['on_headers'])) {
throw new \InvalidArgumentException('on_headers must be callable');
}
try {
$options['on_headers']($response);
} catch (\Exception $e) {
$msg = 'An error was encountered during the on_headers event';
$response = new RequestException($msg, $request, $response, $e);
}
}
if (is_callable($response)) {
$response = call_user_func($response, $request, $options);
}
$response = $response instanceof \Exception
? \GuzzleHttp\Promise\rejection_for($response)
: \GuzzleHttp\Promise\promise_for($response);
return $response->then(
function ($value) use ($request, $options) {
$this->invokeStats($request, $options, $value);
if ($this->onFulfilled) {
call_user_func($this->onFulfilled, $value);
}
if (isset($options['sink'])) {
$contents = (string) $value->getBody();
$sink = $options['sink'];
if (is_resource($sink)) {
fwrite($sink, $contents);
} elseif (is_string($sink)) {
file_put_contents($sink, $contents);
} elseif ($sink instanceof \Psr\Http\Message\StreamInterface) {
$sink->write($contents);
}
}
return $value;
},
function ($reason) use ($request, $options) {
$this->invokeStats($request, $options, null, $reason);
if ($this->onRejected) {
call_user_func($this->onRejected, $reason);
}
return \GuzzleHttp\Promise\rejection_for($reason);
}
);
}
/**
* Adds one or more variadic requests, exceptions, callables, or promises
* to the queue.
*/
public function append()
{
foreach (func_get_args() as $value) {
if ($value instanceof ResponseInterface
|| $value instanceof \Exception
|| $value instanceof PromiseInterface
|| is_callable($value)
) {
$this->queue[] = $value;
} else {
throw new \InvalidArgumentException('Expected a response or '
. 'exception. Found ' . \GuzzleHttp\describe_type($value));
}
}
}
/**
* Get the last received request.
*
* @return RequestInterface
*/
public function getLastRequest()
{
return $this->lastRequest;
}
/**
* Get the last received request options.
*
* @return array
*/
public function getLastOptions()
{
return $this->lastOptions;
}
/**
* Returns the number of remaining items in the queue.
*
* @return int
*/
public function count()
{
return count($this->queue);
}
private function invokeStats(
RequestInterface $request,
array $options,
ResponseInterface $response = null,
$reason = null
) {
if (isset($options['on_stats'])) {
$stats = new TransferStats($request, $response, 0, $reason);
call_user_func($options['on_stats'], $stats);
}
}
}
<?php
namespace GuzzleHttp\Handler;
use GuzzleHttp\RequestOptions;
use Psr\Http\Message\RequestInterface;
/**
* Provides basic proxies for handlers.
*/
class Proxy
{
/**
* Sends synchronous requests to a specific handler while sending all other
* requests to another handler.
*
* @param callable $default Handler used for normal responses
* @param callable $sync Handler used for synchronous responses.
*
* @return callable Returns the composed handler.
*/
public static function wrapSync(
callable $default,
callable $sync
) {
return function (RequestInterface $request, array $options) use ($default, $sync) {
return empty($options[RequestOptions::SYNCHRONOUS])
? $default($request, $options)
: $sync($request, $options);
};
}
/**
* Sends streaming requests to a streaming compatible handler while sending
* all other requests to a default handler.
*
* This, for example, could be useful for taking advantage of the
* performance benefits of curl while still supporting true streaming
* through the StreamHandler.
*
* @param callable $default Handler used for non-streaming responses
* @param callable $streaming Handler used for streaming responses
*
* @return callable Returns the composed handler.
*/
public static function wrapStreaming(
callable $default,
callable $streaming
) {
return function (RequestInterface $request, array $options) use ($default, $streaming) {
return empty($options['stream'])
? $default($request, $options)
: $streaming($request, $options);
};
}
}
<?php
namespace GuzzleHttp;
use Psr\Http\Message\RequestInterface;
/**
* Creates a composed Guzzle handler function by stacking middlewares on top of
* an HTTP handler function.
*/
class HandlerStack
{
/** @var callable */
private $handler;
/** @var array */
private $stack = [];
/** @var callable|null */
private $cached;
/**
* Creates a default handler stack that can be used by clients.
*
* The returned handler will wrap the provided handler or use the most
* appropriate default handler for your system. The returned HandlerStack has
* support for cookies, redirects, HTTP error exceptions, and preparing a body
* before sending.
*
* The returned handler stack can be passed to a client in the "handler"
* option.
*
* @param callable $handler HTTP handler function to use with the stack. If no
* handler is provided, the best handler for your
* system will be utilized.
*
* @return HandlerStack
*/
public static function create(callable $handler = null)
{
$stack = new self($handler ?: choose_handler());
$stack->push(Middleware::httpErrors(), 'http_errors');
$stack->push(Middleware::redirect(), 'allow_redirects');
$stack->push(Middleware::cookies(), 'cookies');
$stack->push(Middleware::prepareBody(), 'prepare_body');
return $stack;
}
/**
* @param callable $handler Underlying HTTP handler.
*/
public function __construct(callable $handler = null)
{
$this->handler = $handler;
}
/**
* Invokes the handler stack as a composed handler
*
* @param RequestInterface $request
* @param array $options
*/
public function __invoke(RequestInterface $request, array $options)
{
$handler = $this->resolve();
return $handler($request, $options);
}
/**
* Dumps a string representation of the stack.
*
* @return string
*/
public function __toString()
{
$depth = 0;
$stack = [];
if ($this->handler) {
$stack[] = "0) Handler: " . $this->debugCallable($this->handler);
}
$result = '';
foreach (array_reverse($this->stack) as $tuple) {
$depth++;
$str = "{$depth}) Name: '{$tuple[1]}', ";
$str .= "Function: " . $this->debugCallable($tuple[0]);
$result = "> {$str}\n{$result}";
$stack[] = $str;
}
foreach (array_keys($stack) as $k) {
$result .= "< {$stack[$k]}\n";
}
return $result;
}
/**
* Set the HTTP handler that actually returns a promise.
*
* @param callable $handler Accepts a request and array of options and
* returns a Promise.
*/
public function setHandler(callable $handler)
{
$this->handler = $handler;
$this->cached = null;
}
/**
* Returns true if the builder has a handler.
*
* @return bool
*/
public function hasHandler()
{
return (bool) $this->handler;
}
/**
* Unshift a middleware to the bottom of the stack.
*
* @param callable $middleware Middleware function
* @param string $name Name to register for this middleware.
*/
public function unshift(callable $middleware, $name = null)
{
array_unshift($this->stack, [$middleware, $name]);
$this->cached = null;
}
/**
* Push a middleware to the top of the stack.
*
* @param callable $middleware Middleware function
* @param string $name Name to register for this middleware.
*/
public function push(callable $middleware, $name = '')
{
$this->stack[] = [$middleware, $name];
$this->cached = null;
}
/**
* Add a middleware before another middleware by name.
*
* @param string $findName Middleware to find
* @param callable $middleware Middleware function
* @param string $withName Name to register for this middleware.
*/
public function before($findName, callable $middleware, $withName = '')
{
$this->splice($findName, $withName, $middleware, true);
}
/**
* Add a middleware after another middleware by name.
*
* @param string $findName Middleware to find
* @param callable $middleware Middleware function
* @param string $withName Name to register for this middleware.
*/
public function after($findName, callable $middleware, $withName = '')
{
$this->splice($findName, $withName, $middleware, false);
}
/**
* Remove a middleware by instance or name from the stack.
*
* @param callable|string $remove Middleware to remove by instance or name.
*/
public function remove($remove)
{
$this->cached = null;
$idx = is_callable($remove) ? 0 : 1;
$this->stack = array_values(array_filter(
$this->stack,
function ($tuple) use ($idx, $remove) {
return $tuple[$idx] !== $remove;
}
));
}
/**
* Compose the middleware and handler into a single callable function.
*
* @return callable
*/
public function resolve()
{
if (!$this->cached) {
if (!($prev = $this->handler)) {
throw new \LogicException('No handler has been specified');
}
foreach (array_reverse($this->stack) as $fn) {
$prev = $fn[0]($prev);
}
$this->cached = $prev;
}
return $this->cached;
}
/**
* @param $name
* @return int
*/
private function findByName($name)
{
foreach ($this->stack as $k => $v) {
if ($v[1] === $name) {
return $k;
}
}
throw new \InvalidArgumentException("Middleware not found: $name");
}
/**
* Splices a function into the middleware list at a specific position.
*
* @param $findName
* @param $withName
* @param callable $middleware
* @param $before
*/
private function splice($findName, $withName, callable $middleware, $before)
{
$this->cached = null;
$idx = $this->findByName($findName);
$tuple = [$middleware, $withName];
if ($before) {
if ($idx === 0) {
array_unshift($this->stack, $tuple);
} else {
$replacement = [$tuple, $this->stack[$idx]];
array_splice($this->stack, $idx, 1, $replacement);
}
} elseif ($idx === count($this->stack) - 1) {
$this->stack[] = $tuple;
} else {
$replacement = [$this->stack[$idx], $tuple];
array_splice($this->stack, $idx, 1, $replacement);
}
}
/**
* Provides a debug string for a given callable.
*
* @param array|callable $fn Function to write as a string.
*
* @return string
*/
private function debugCallable($fn)
{
if (is_string($fn)) {
return "callable({$fn})";
}
if (is_array($fn)) {
return is_string($fn[0])
? "callable({$fn[0]}::{$fn[1]})"
: "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
}
return 'callable(' . spl_object_hash($fn) . ')';
}
}
<?php
namespace GuzzleHttp;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Formats log messages using variable substitutions for requests, responses,
* and other transactional data.
*
* The following variable substitutions are supported:
*
* - {request}: Full HTTP request message
* - {response}: Full HTTP response message
* - {ts}: ISO 8601 date in GMT
* - {date_iso_8601} ISO 8601 date in GMT
* - {date_common_log} Apache common log date using the configured timezone.
* - {host}: Host of the request
* - {method}: Method of the request
* - {uri}: URI of the request
* - {version}: Protocol version
* - {target}: Request target of the request (path + query + fragment)
* - {hostname}: Hostname of the machine that sent the request
* - {code}: Status code of the response (if available)
* - {phrase}: Reason phrase of the response (if available)
* - {error}: Any error messages (if available)
* - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
* - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
* - {req_headers}: Request headers
* - {res_headers}: Response headers
* - {req_body}: Request body
* - {res_body}: Response body
*/
class MessageFormatter
{
/**
* Apache Common Log Format.
* @link http://httpd.apache.org/docs/2.4/logs.html#common
* @var string
*/
const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
/** @var string Template used to format log messages */
private $template;
/**
* @param string $template Log message template
*/
public function __construct($template = self::CLF)
{
$this->template = $template ?: self::CLF;
}
/**
* Returns a formatted message string.
*
* @param RequestInterface $request Request that was sent
* @param ResponseInterface $response Response that was received
* @param \Exception $error Exception that was received
*
* @return string
*/
public function format(
RequestInterface $request,
ResponseInterface $response = null,
\Exception $error = null
) {
$cache = [];
return preg_replace_callback(
'/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
function (array $matches) use ($request, $response, $error, &$cache) {
if (isset($cache[$matches[1]])) {
return $cache[$matches[1]];
}
$result = '';
switch ($matches[1]) {
case 'request':
$result = Psr7\str($request);
break;
case 'response':
$result = $response ? Psr7\str($response) : '';
break;
case 'req_headers':
$result = trim($request->getMethod()
. ' ' . $request->getRequestTarget())
. ' HTTP/' . $request->getProtocolVersion() . "\r\n"
. $this->headers($request);
break;
case 'res_headers':
$result = $response ?
sprintf(
'HTTP/%s %d %s',
$response->getProtocolVersion(),
$response->getStatusCode(),
$response->getReasonPhrase()
) . "\r\n" . $this->headers($response)
: 'NULL';
break;
case 'req_body':
$result = $request->getBody();
break;
case 'res_body':
$result = $response ? $response->getBody() : 'NULL';
break;
case 'ts':
case 'date_iso_8601':
$result = gmdate('c');
break;
case 'date_common_log':
$result = date('d/M/Y:H:i:s O');
break;
case 'method':
$result = $request->getMethod();
break;
case 'version':
$result = $request->getProtocolVersion();
break;
case 'uri':
case 'url':
$result = $request->getUri();
break;
case 'target':
$result = $request->getRequestTarget();
break;
case 'req_version':
$result = $request->getProtocolVersion();
break;
case 'res_version':
$result = $response
? $response->getProtocolVersion()
: 'NULL';
break;
case 'host':
$result = $request->getHeaderLine('Host');
break;
case 'hostname':
$result = gethostname();
break;
case 'code':
$result = $response ? $response->getStatusCode() : 'NULL';
break;
case 'phrase':
$result = $response ? $response->getReasonPhrase() : 'NULL';
break;
case 'error':
$result = $error ? $error->getMessage() : 'NULL';
break;
default:
// handle prefixed dynamic headers
if (strpos($matches[1], 'req_header_') === 0) {
$result = $request->getHeaderLine(substr($matches[1], 11));
} elseif (strpos($matches[1], 'res_header_') === 0) {
$result = $response
? $response->getHeaderLine(substr($matches[1], 11))
: 'NULL';
}
}
$cache[$matches[1]] = $result;
return $result;
},
$this->template
);
}
private function headers(MessageInterface $message)
{
$result = '';
foreach ($message->getHeaders() as $name => $values) {
$result .= $name . ': ' . implode(', ', $values) . "\r\n";
}
return trim($result);
}
}
<?php
namespace GuzzleHttp;
use GuzzleHttp\Cookie\CookieJarInterface;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Psr7;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
/**
* Functions used to create and wrap handlers with handler middleware.
*/
final class Middleware
{
/**
* Middleware that adds cookies to requests.
*
* The options array must be set to a CookieJarInterface in order to use
* cookies. This is typically handled for you by a client.
*
* @return callable Returns a function that accepts the next handler.
*/
public static function cookies()
{
return function (callable $handler) {
return function ($request, array $options) use ($handler) {
if (empty($options['cookies'])) {
return $handler($request, $options);
} elseif (!($options['cookies'] instanceof CookieJarInterface)) {
throw new \InvalidArgumentException('cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface');
}
$cookieJar = $options['cookies'];
$request = $cookieJar->withCookieHeader($request);
return $handler($request, $options)
->then(
function ($response) use ($cookieJar, $request) {
$cookieJar->extractCookies($request, $response);
return $response;
}
);
};
};
}
/**
* Middleware that throws exceptions for 4xx or 5xx responses when the
* "http_error" request option is set to true.
*
* @return callable Returns a function that accepts the next handler.
*/
public static function httpErrors()
{
return function (callable $handler) {
return function ($request, array $options) use ($handler) {
if (empty($options['http_errors'])) {
return $handler($request, $options);
}
return $handler($request, $options)->then(
function (ResponseInterface $response) use ($request, $handler) {
$code = $response->getStatusCode();
if ($code < 400) {
return $response;
}
throw RequestException::create($request, $response);
}
);
};
};
}
/**
* Middleware that pushes history data to an ArrayAccess container.
*
* @param array|\ArrayAccess $container Container to hold the history (by reference).
*
* @return callable Returns a function that accepts the next handler.
* @throws \InvalidArgumentException if container is not an array or ArrayAccess.
*/
public static function history(&$container)
{
if (!is_array($container) && !$container instanceof \ArrayAccess) {
throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess');
}
return function (callable $handler) use (&$container) {
return function ($request, array $options) use ($handler, &$container) {
return $handler($request, $options)->then(
function ($value) use ($request, &$container, $options) {
$container[] = [
'request' => $request,
'response' => $value,
'error' => null,
'options' => $options
];
return $value;
},
function ($reason) use ($request, &$container, $options) {
$container[] = [
'request' => $request,
'response' => null,
'error' => $reason,
'options' => $options
];
return \GuzzleHttp\Promise\rejection_for($reason);
}
);
};
};
}
/**
* Middleware that invokes a callback before and after sending a request.
*
* The provided listener cannot modify or alter the response. It simply
* "taps" into the chain to be notified before returning the promise. The
* before listener accepts a request and options array, and the after
* listener accepts a request, options array, and response promise.
*
* @param callable $before Function to invoke before forwarding the request.
* @param callable $after Function invoked after forwarding.
*
* @return callable Returns a function that accepts the next handler.
*/
public static function tap(callable $before = null, callable $after = null)
{
return function (callable $handler) use ($before, $after) {
return function ($request, array $options) use ($handler, $before, $after) {
if ($before) {
$before($request, $options);
}
$response = $handler($request, $options);
if ($after) {
$after($request, $options, $response);
}
return $response;
};
};
}
/**
* Middleware that handles request redirects.
*
* @return callable Returns a function that accepts the next handler.
*/
public static function redirect()
{
return function (callable $handler) {
return new RedirectMiddleware($handler);
};
}
/**
* Middleware that retries requests based on the boolean result of
* invoking the provided "decider" function.
*
* If no delay function is provided, a simple implementation of exponential
* backoff will be utilized.
*
* @param callable $decider Function that accepts the number of retries,
* a request, [response], and [exception] and
* returns true if the request is to be retried.
* @param callable $delay Function that accepts the number of retries and
* returns the number of milliseconds to delay.
*
* @return callable Returns a function that accepts the next handler.
*/
public static function retry(callable $decider, callable $delay = null)
{
return function (callable $handler) use ($decider, $delay) {
return new RetryMiddleware($decider, $handler, $delay);
};
}
/**
* Middleware that logs requests, responses, and errors using a message
* formatter.
*
* @param LoggerInterface $logger Logs messages.
* @param MessageFormatter $formatter Formatter used to create message strings.
* @param string $logLevel Level at which to log requests.
*
* @return callable Returns a function that accepts the next handler.
*/
public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
{
return function (callable $handler) use ($logger, $formatter, $logLevel) {
return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
return $handler($request, $options)->then(
function ($response) use ($logger, $request, $formatter, $logLevel) {
$message = $formatter->format($request, $response);
$logger->log($logLevel, $message);
return $response;
},
function ($reason) use ($logger, $request, $formatter) {
$response = $reason instanceof RequestException
? $reason->getResponse()
: null;
$message = $formatter->format($request, $response, $reason);
$logger->notice($message);
return \GuzzleHttp\Promise\rejection_for($reason);
}
);
};
};
}
/**
* This middleware adds a default content-type if possible, a default
* content-length or transfer-encoding header, and the expect header.
*
* @return callable
*/
public static function prepareBody()
{
return function (callable $handler) {
return new PrepareBodyMiddleware($handler);
};
}
/**
* Middleware that applies a map function to the request before passing to
* the next handler.
*
* @param callable $fn Function that accepts a RequestInterface and returns
* a RequestInterface.
* @return callable
*/
public static function mapRequest(callable $fn)
{
return function (callable $handler) use ($fn) {
return function ($request, array $options) use ($handler, $fn) {
return $handler($fn($request), $options);
};
};
}
/**
* Middleware that applies a map function to the resolved promise's
* response.
*
* @param callable $fn Function that accepts a ResponseInterface and
* returns a ResponseInterface.
* @return callable
*/
public static function mapResponse(callable $fn)
{
return function (callable $handler) use ($fn) {
return function ($request, array $options) use ($handler, $fn) {
return $handler($request, $options)->then($fn);
};
};
}
}
<?php
namespace GuzzleHttp;
use GuzzleHttp\Promise\PromisorInterface;
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\Promise\EachPromise;
/**
* Sends and iterator of requests concurrently using a capped pool size.
*
* The pool will read from an iterator until it is cancelled or until the
* iterator is consumed. When a request is yielded, the request is sent after
* applying the "request_options" request options (if provided in the ctor).
*
* When a function is yielded by the iterator, the function is provided the
* "request_options" array that should be merged on top of any existing
* options, and the function MUST then return a wait-able promise.
*/
class Pool implements PromisorInterface
{
/** @var EachPromise */
private $each;
/**
* @param ClientInterface $client Client used to send the requests.
* @param array|\Iterator $requests Requests or functions that return
* requests to send concurrently.
* @param array $config Associative array of options
* - concurrency: (int) Maximum number of requests to send concurrently
* - options: Array of request options to apply to each request.
* - fulfilled: (callable) Function to invoke when a request completes.
* - rejected: (callable) Function to invoke when a request is rejected.
*/
public function __construct(
ClientInterface $client,
$requests,
array $config = []
) {
// Backwards compatibility.
if (isset($config['pool_size'])) {
$config['concurrency'] = $config['pool_size'];
} elseif (!isset($config['concurrency'])) {
$config['concurrency'] = 25;
}
if (isset($config['options'])) {
$opts = $config['options'];
unset($config['options']);
} else {
$opts = [];
}
$iterable = \GuzzleHttp\Promise\iter_for($requests);
$requests = function () use ($iterable, $client, $opts) {
foreach ($iterable as $key => $rfn) {
if ($rfn instanceof RequestInterface) {
yield $key => $client->sendAsync($rfn, $opts);
} elseif (is_callable($rfn)) {
yield $key => $rfn($opts);
} else {
throw new \InvalidArgumentException('Each value yielded by '
. 'the iterator must be a Psr7\Http\Message\RequestInterface '
. 'or a callable that returns a promise that fulfills '
. 'with a Psr7\Message\Http\ResponseInterface object.');
}
}
};
$this->each = new EachPromise($requests(), $config);
}
public function promise()
{
return $this->each->promise();
}
/**
* Sends multiple requests concurrently and returns an array of responses
* and exceptions that uses the same ordering as the provided requests.
*
* IMPORTANT: This method keeps every request and response in memory, and
* as such, is NOT recommended when sending a large number or an
* indeterminate number of requests concurrently.
*
* @param ClientInterface $client Client used to send the requests
* @param array|\Iterator $requests Requests to send concurrently.
* @param array $options Passes through the options available in
* {@see GuzzleHttp\Pool::__construct}
*
* @return array Returns an array containing the response or an exception
* in the same order that the requests were sent.
* @throws \InvalidArgumentException if the event format is incorrect.
*/
public static function batch(
ClientInterface $client,
$requests,
array $options = []
) {
$res = [];
self::cmpCallback($options, 'fulfilled', $res);
self::cmpCallback($options, 'rejected', $res);
$pool = new static($client, $requests, $options);
$pool->promise()->wait();
ksort($res);
return $res;
}
private static function cmpCallback(array &$options, $name, array &$results)
{
if (!isset($options[$name])) {
$options[$name] = function ($v, $k) use (&$results) {
$results[$k] = $v;
};
} else {
$currentFn = $options[$name];
$options[$name] = function ($v, $k) use (&$results, $currentFn) {
$currentFn($v, $k);
$results[$k] = $v;
};
}
}
}
<?php
namespace GuzzleHttp;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use Psr\Http\Message\RequestInterface;
/**
* Prepares requests that contain a body, adding the Content-Length,
* Content-Type, and Expect headers.
*/
class PrepareBodyMiddleware
{
/** @var callable */
private $nextHandler;
/**
* @param callable $nextHandler Next handler to invoke.
*/
public function __construct(callable $nextHandler)
{
$this->nextHandler = $nextHandler;
}
/**
* @param RequestInterface $request
* @param array $options
*
* @return PromiseInterface
*/
public function __invoke(RequestInterface $request, array $options)
{
$fn = $this->nextHandler;
// Don't do anything if the request has no body.
if ($request->getBody()->getSize() === 0) {
return $fn($request, $options);
}
$modify = [];
// Add a default content-type if possible.
if (!$request->hasHeader('Content-Type')) {
if ($uri = $request->getBody()->getMetadata('uri')) {
if ($type = Psr7\mimetype_from_filename($uri)) {
$modify['set_headers']['Content-Type'] = $type;
}
}
}
// Add a default content-length or transfer-encoding header.
if (!$request->hasHeader('Content-Length')
&& !$request->hasHeader('Transfer-Encoding')
) {
$size = $request->getBody()->getSize();
if ($size !== null) {
$modify['set_headers']['Content-Length'] = $size;
} else {
$modify['set_headers']['Transfer-Encoding'] = 'chunked';
}
}
// Add the expect header if needed.
$this->addExpectHeader($request, $options, $modify);
return $fn(Psr7\modify_request($request, $modify), $options);
}
private function addExpectHeader(
RequestInterface $request,
array $options,
array &$modify
) {
// Determine if the Expect header should be used
if ($request->hasHeader('Expect')) {
return;
}
$expect = isset($options['expect']) ? $options['expect'] : null;
// Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
if ($expect === false || $request->getProtocolVersion() < 1.1) {
return;
}
// The expect header is unconditionally enabled
if ($expect === true) {
$modify['set_headers']['Expect'] = '100-Continue';
return;
}
// By default, send the expect header when the payload is > 1mb
if ($expect === null) {
$expect = 1048576;
}
// Always add if the body cannot be rewound, the size cannot be
// determined, or the size is greater than the cutoff threshold
$body = $request->getBody();
$size = $body->getSize();
if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
$modify['set_headers']['Expect'] = '100-Continue';
}
}
}
<?php
namespace GuzzleHttp;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\TooManyRedirectsException;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
/**
* Request redirect middleware.
*
* Apply this middleware like other middleware using
* {@see GuzzleHttp\Middleware::redirect()}.
*/
class RedirectMiddleware
{
const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
public static $defaultSettings = [
'max' => 5,
'protocols' => ['http', 'https'],
'strict' => false,
'referer' => false,
'track_redirects' => false,
];
/** @var callable */
private $nextHandler;
/**
* @param callable $nextHandler Next handler to invoke.
*/
public function __construct(callable $nextHandler)
{
$this->nextHandler = $nextHandler;
}
/**
* @param RequestInterface $request
* @param array $options
*
* @return PromiseInterface
*/
public function __invoke(RequestInterface $request, array $options)
{
$fn = $this->nextHandler;
if (empty($options['allow_redirects'])) {
return $fn($request, $options);
}
if ($options['allow_redirects'] === true) {
$options['allow_redirects'] = self::$defaultSettings;
} elseif (!is_array($options['allow_redirects'])) {
throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
} else {
// Merge the default settings with the provided settings
$options['allow_redirects'] += self::$defaultSettings;
}
if (empty($options['allow_redirects']['max'])) {
return $fn($request, $options);
}
return $fn($request, $options)
->then(function (ResponseInterface $response) use ($request, $options) {
return $this->checkRedirect($request, $options, $response);
});
}
/**
* @param RequestInterface $request
* @param array $options
* @param ResponseInterface|PromiseInterface $response
*
* @return ResponseInterface|PromiseInterface
*/
public function checkRedirect(
RequestInterface $request,
array $options,
ResponseInterface $response
) {
if (substr($response->getStatusCode(), 0, 1) != '3'
|| !$response->hasHeader('Location')
) {
return $response;
}
$this->guardMax($request, $options);
$nextRequest = $this->modifyRequest($request, $options, $response);
if (isset($options['allow_redirects']['on_redirect'])) {
call_user_func(
$options['allow_redirects']['on_redirect'],
$request,
$response,
$nextRequest->getUri()
);
}
/** @var PromiseInterface|ResponseInterface $promise */
$promise = $this($nextRequest, $options);
// Add headers to be able to track history of redirects.
if (!empty($options['allow_redirects']['track_redirects'])) {
return $this->withTracking(
$promise,
(string) $nextRequest->getUri(),
$response->getStatusCode()
);
}
return $promise;
}
private function withTracking(PromiseInterface $promise, $uri, $statusCode)
{
return $promise->then(
function (ResponseInterface $response) use ($uri, $statusCode) {
// Note that we are pushing to the front of the list as this
// would be an earlier response than what is currently present
// in the history header.
$historyHeader = $response->getHeader(self::HISTORY_HEADER);
$statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
array_unshift($historyHeader, $uri);
array_unshift($statusHeader, $statusCode);
return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
}
);
}
private function guardMax(RequestInterface $request, array &$options)
{
$current = isset($options['__redirect_count'])
? $options['__redirect_count']
: 0;
$options['__redirect_count'] = $current + 1;
$max = $options['allow_redirects']['max'];
if ($options['__redirect_count'] > $max) {
throw new TooManyRedirectsException(
"Will not follow more than {$max} redirects",
$request
);
}
}
/**
* @param RequestInterface $request
* @param array $options
* @param ResponseInterface $response
*
* @return RequestInterface
*/
public function modifyRequest(
RequestInterface $request,
array $options,
ResponseInterface $response
) {
// Request modifications to apply.
$modify = [];
$protocols = $options['allow_redirects']['protocols'];
// Use a GET request if this is an entity enclosing request and we are
// not forcing RFC compliance, but rather emulating what all browsers
// would do.
$statusCode = $response->getStatusCode();
if ($statusCode == 303 ||
($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict'])
) {
$modify['method'] = 'GET';
$modify['body'] = '';
}
$modify['uri'] = $this->redirectUri($request, $response, $protocols);
Psr7\rewind_body($request);
// Add the Referer header if it is told to do so and only
// add the header if we are not redirecting from https to http.
if ($options['allow_redirects']['referer']
&& $modify['uri']->getScheme() === $request->getUri()->getScheme()
) {
$uri = $request->getUri()->withUserInfo('', '');
$modify['set_headers']['Referer'] = (string) $uri;
} else {
$modify['remove_headers'][] = 'Referer';
}
// Remove Authorization header if host is different.
if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
$modify['remove_headers'][] = 'Authorization';
}
return Psr7\modify_request($request, $modify);
}
/**
* Set the appropriate URL on the request based on the location header
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param array $protocols
*
* @return UriInterface
*/
private function redirectUri(
RequestInterface $request,
ResponseInterface $response,
array $protocols
) {
$location = Psr7\UriResolver::resolve(
$request->getUri(),
new Psr7\Uri($response->getHeaderLine('Location'))
);
// Ensure that the redirect URI is allowed based on the protocols.
if (!in_array($location->getScheme(), $protocols)) {
throw new BadResponseException(
sprintf(
'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
$location,
implode(', ', $protocols)
),
$request,
$response
);
}
return $location;
}
}
<?php
namespace GuzzleHttp;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Psr7;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Middleware that retries requests based on the boolean result of
* invoking the provided "decider" function.
*/
class RetryMiddleware
{
/** @var callable */
private $nextHandler;
/** @var callable */
private $decider;
/**
* @param callable $decider Function that accepts the number of retries,
* a request, [response], and [exception] and
* returns true if the request is to be
* retried.
* @param callable $nextHandler Next handler to invoke.
* @param callable $delay Function that accepts the number of retries
* and [response] and returns the number of
* milliseconds to delay.
*/
public function __construct(
callable $decider,
callable $nextHandler,
callable $delay = null
) {
$this->decider = $decider;
$this->nextHandler = $nextHandler;
$this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
}
/**
* Default exponential backoff delay function.
*
* @param $retries
*
* @return int
*/
public static function exponentialDelay($retries)
{
return (int) pow(2, $retries - 1);
}
/**
* @param RequestInterface $request
* @param array $options
*
* @return PromiseInterface
*/
public function __invoke(RequestInterface $request, array $options)
{
if (!isset($options['retries'])) {
$options['retries'] = 0;
}
$fn = $this->nextHandler;
return $fn($request, $options)
->then(
$this->onFulfilled($request, $options),
$this->onRejected($request, $options)
);
}
private function onFulfilled(RequestInterface $req, array $options)
{
return function ($value) use ($req, $options) {
if (!call_user_func(
$this->decider,
$options['retries'],
$req,
$value,
null
)) {
return $value;
}
return $this->doRetry($req, $options, $value);
};
}
private function onRejected(RequestInterface $req, array $options)
{
return function ($reason) use ($req, $options) {
if (!call_user_func(
$this->decider,
$options['retries'],
$req,
null,
$reason
)) {
return \GuzzleHttp\Promise\rejection_for($reason);
}
return $this->doRetry($req, $options);
};
}
private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
{
$options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
return $this($request, $options);
}
}
<?php
namespace GuzzleHttp;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
/**
* Represents data at the point after it was transferred either successfully
* or after a network error.
*/
final class TransferStats
{
private $request;
private $response;
private $transferTime;
private $handlerStats;
private $handlerErrorData;
/**
* @param RequestInterface $request Request that was sent.
* @param ResponseInterface $response Response received (if any)
* @param null $transferTime Total handler transfer time.
* @param mixed $handlerErrorData Handler error data.
* @param array $handlerStats Handler specific stats.
*/
public function __construct(
RequestInterface $request,
ResponseInterface $response = null,
$transferTime = null,
$handlerErrorData = null,
$handlerStats = []
) {
$this->request = $request;
$this->response = $response;
$this->transferTime = $transferTime;
$this->handlerErrorData = $handlerErrorData;
$this->handlerStats = $handlerStats;
}
/**
* @return RequestInterface
*/
public function getRequest()
{
return $this->request;
}
/**
* Returns the response that was received (if any).
*
* @return ResponseInterface|null
*/
public function getResponse()
{
return $this->response;
}
/**
* Returns true if a response was received.
*
* @return bool
*/
public function hasResponse()
{
return $this->response !== null;
}
/**
* Gets handler specific error data.
*
* This might be an exception, a integer representing an error code, or
* anything else. Relying on this value assumes that you know what handler
* you are using.
*
* @return mixed
*/
public function getHandlerErrorData()
{
return $this->handlerErrorData;
}
/**
* Get the effective URI the request was sent to.
*
* @return UriInterface
*/
public function getEffectiveUri()
{
return $this->request->getUri();
}
/**
* Get the estimated time the request was being transferred by the handler.
*
* @return float Time in seconds.
*/
public function getTransferTime()
{
return $this->transferTime;
}
/**
* Gets an array of all of the handler specific transfer data.
*
* @return array
*/
public function getHandlerStats()
{
return $this->handlerStats;
}
/**
* Get a specific handler statistic from the handler by name.
*
* @param string $stat Handler specific transfer stat to retrieve.
*
* @return mixed|null
*/
public function getHandlerStat($stat)
{
return isset($this->handlerStats[$stat])
? $this->handlerStats[$stat]
: null;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment