Commit c39fe132 by duwenjun

Merge branch 'dev/ver/cloud_1.0.0' into 'master'

Dev/ver/cloud 1.0.0

See merge request !5
parents 452ab34c 94f8cd52
Showing with 3254 additions and 249 deletions
......@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Http\Queue\RabbitQueueModel;
use App\Model\CommonModel;
use App\Model\InquiryItemsAssignModel;
use App\Model\InquiryItemsReportModel;
......@@ -67,7 +68,17 @@ class ApiController extends Controller
}
//关闭单条询价明细
public function ApiInquiryItemsClose($input, $id){
Export((new InquiryItemsModel())->inquiryItemsClose($input));
$res = (new InquiryItemsModel())->inquiryItemsClose($input);
// 加入队列, 后续会关闭云芯询价
$RabbitQueueModel = new RabbitQueueModel();
$queue_data = [
"queue_route_key" => '/inquiry/closeautoinquiry',
"inquiry_items_id" => $input['inquiry_items_id'],
];
$RabbitQueueModel->push("cloud_common_queue", json_encode($queue_data));
Export($res);
}
//询价: 选中 撤销 确认 -》 报价
public function ApiQuoteSure($input, $id){
......@@ -213,6 +224,18 @@ class ApiController extends Controller
Export((new QuoteModel())->cancel($input));
}
// 关闭报价
public function ApiCloseQuote($input, $id)
{
Export((new QuoteModel())->close($input));
}
// 删除报价
public function ApiDelQuote($input, $id)
{
Export((new QuoteModel())->del($input));
}
// 新增报价
public function ApiAddQuote($input, $id)
{
......
<?php
namespace App\Http\Queue;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
class RabbitQueueModel
{
private $connection;
public function __construct($connect_name = '')
{
if ($connect_name) {
$config = Config('rabbitmq.connections.' . $connect_name);
} else {
$default_connect_name = Config('rabbitmq.default');
$config = Config('rabbitmq.connections.' . $default_connect_name);
}
// 创建rabbitmq链接
$this->connection = new AMQPStreamConnection(
$config['host'], $config['port'], $config['login'], $config['password'], $config['vhost']
);
}
// 队列推送
public function push($queue_name, $content)
{
$channel = $this->connection->channel();
$channel->queue_declare($queue_name, false, true, false, false);
$message = new AMQPMessage($content);
$channel->basic_publish($message, '', $queue_name); // 推送消息
$channel->close();
$this->connection->close();
return true;
}
}
\ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: duwenjun
* Date: 2021/5/13
* Time: 11:52 AM
*/
namespace App\Http\Utils;
interface AppType
{
const APPTYPE_YUNXIN = 'yunxin'; // 云芯平台
}
\ No newline at end of file
<?php
namespace App\Http\Utils;
class RequestHelper implements AppType
{
public static $client_type = '';
public static $user = null;
public static function setClientType($client_type)
{
self::$client_type = $client_type;
}
public static function getClientType()
{
return self::$client_type;
}
public static function setUser($user)
{
self::$user = $user;
}
public static function getUser()
{
return self::$user;
}
}
\ No newline at end of file
......@@ -141,7 +141,7 @@ class CommonModel extends Model
//模糊搜索sku型号
public function SkuList($k,$types = 1)
{
$url = config("website.search_url")."/search/spu/think?spu_name=".$k;
$url = Config('website.search_url'). "/search/spu/think?spu_name=".$k;
$res = post_curl($url,[]);
$resArr = \GuzzleHttp\json_decode($res,true);
$temp = [];
......
......@@ -142,7 +142,7 @@ class InquiryItemsModel extends Model
}
if (@$input["inquiry_items_id"]){
$del = $this->where(["id"=>$input["inquiry_items_id"]])->delete();
$del = (new InquiryItemsReportModel())->where(["id"=>$input["inquiry_items_id"]])->delete(); #同步输出报价表
$del = (new InquiryItemsReportModel())->where(["inquiry_items_id"=>$input["inquiry_items_id"]])->delete(); #同步输出报价表
}
return [0,"删除成功"];
}
......@@ -238,13 +238,20 @@ class InquiryItemsModel extends Model
"update_time"=>time() ,
];
if (isset($b['is_quote']) && $b['is_quote'] == 'on') { // 无需报价,询价状态变为已确定
$insertArr['is_quote'] = -1;
$insertArr['status'] = 5;
}
$inquiryItemsId = $this->insertGetId($insertArr);
#插入销售报价表
(new InquiryItemsReportModel())->insert([
"inquiry_id"=>$b["inquiry_id"] ,
"inquiry_items_id"=>$inquiryItemsId ,
]);
$reportArr = [];
$reportArr['inquiry_id'] = $b['inquiry_id'];
$reportArr['inquiry_items_id'] = $inquiryItemsId;
$reportArr['create_time'] = time();
(new InquiryItemsReportModel())->insert($reportArr);
#插入指定领取人
if ($b["pur_s"] !== "" ){
......@@ -324,6 +331,16 @@ class InquiryItemsModel extends Model
"create_time"=>time() ,
"update_time"=>time() ,
];
// 无需报价没勾选,则默认为待报价
if (isset($b['is_quote']) && $b['is_quote'] == 'on') {
$updateArr['is_quote'] = -1;
$updateArr['status'] = 5;
} else {
$updateArr['is_quote'] = 1;
$updateArr['status'] = 1;
}
$this->where("id",$inquiry_items_id)->update($updateArr);
#清空指定人
......@@ -432,6 +449,14 @@ class InquiryItemsModel extends Model
"create_time"=>time() ,
"update_time"=>time() ,
];
if ($b['is_quote'] == '否') {
$insertArr['is_quote'] = -1;
$insertArr['status'] = 5;
} else {
$insertArr['is_quote'] = 1;
}
#判断是否已经存在此品牌+型号
$check = $this->where(["inquiry_id"=>$inquiry_id,"goods_name"=>$b["goods_name"],"brand_name"=>$b["brand_name"]])->count();
if ($check >0){
......@@ -495,6 +520,7 @@ class InquiryItemsModel extends Model
"target_price", //可空
"batch",
"delivery_time",
"is_quote",
"assign", //指定采购,可空
];
$filer_need = [ //必填字段
......@@ -509,12 +535,17 @@ class InquiryItemsModel extends Model
foreach ($excel as $k=>$v) {
if ($k == 0 ) continue; //排除第一行表头
if ($k == 0 || $k == 1) continue; //排除第一、二行表头
$temp = [];
$c = 0;
foreach ($v as $a=>$b){
if ($a >=7) continue;
if ($a > 7) continue;
if (empty(trim($b))) $c ++;
if ($filer[$a] == 'target_price') {
$b = number_format($b, 6);
}
$temp[$filer[$a]] = trim($b);
}
if ($c == count($v)) continue;
......@@ -528,7 +559,7 @@ class InquiryItemsModel extends Model
foreach ($goodsInfo as $a2=>&$b2){
$b2["inquiry_id"] = $inquiry_id;
$hang = $a2+2; #对应excel第几行
$hang = $a2+3; #对应excel第几行
$lie = 0;
foreach ($b2 as $key=>$val) {
$lie = $lie+1;
......
......@@ -208,7 +208,7 @@ class InquiryItemsReportModel extends Model
#更新当前报价单状态
$QuoteModel->where("id",$data["quote_id"])->update(["status"=>$status]); #更新报价选中
#更新关联除了当前询价单 此询价明细的报价单状态
$QuoteModel->where("inquiry_items_id",$inquiry_items_id)->whereNotIn("id",[$quote_id])->whereNotIn("status",[-1,0])->update(["status"=>1]); #重启其他所有状态为 已报价
$QuoteModel->where("inquiry_items_id",$inquiry_items_id)->whereNotIn("id",[$quote_id])->whereNotIn("status",[-1,0, 9])->update(["status"=>1]); #重启其他所有状态为 已报价
#更新询价明细表状态
(new InquiryItemsModel())->where("id",$inquiry_items_id)->update(["status"=>$status == 2 ? 3:5]);
......@@ -229,7 +229,7 @@ class InquiryItemsReportModel extends Model
$itemsIdArr = explode(",",$input["inquiry_items_ids"]);
$InquiryItemsModel = new InquiryItemsModel();
if ($types == 1){
$check = $InquiryItemsModel->whereIn("status",[3,4,5])->whereIn("id",$itemsIdArr)->count();
$check = $InquiryItemsModel->whereIn("status",[-2,3,4,5])->whereIn("id",$itemsIdArr)->count();
}else{
$check = $InquiryItemsModel->where("status",5)->whereIn("id",$itemsIdArr)->count();
}
......
......@@ -2,6 +2,7 @@
namespace App\Model;
use App\Http\Queue\RabbitQueueModel;
use App\map\InquiryMap;
use App\map\OperationLogMap;
use App\map\QuoteMap;
......@@ -186,7 +187,7 @@ class InquiryModel extends Model
}
}
$v['is_quote_val'] = $v['is_quote'] == 1 ? '是' : '否';
}
$data = $list['data'];
if (@$input["is_export"] == 1) { #如果是导出
......@@ -295,7 +296,7 @@ class InquiryModel extends Model
/**
* 询价池展示条件:
* 1. 询价单启用状态;
* 2. 询价明细需过滤已关闭、已确认和被指定的类型
* 2. 询价明细需过滤无需报价、已关闭、已确认和被指定的类型
* 3. 询价明细分配表需过滤当前用户或不存在的(未指定或领取的)
*/
if ($type == 1) {
......@@ -466,6 +467,40 @@ class InquiryModel extends Model
$data["com_name"] = $user[0]["name"];
}
$check = $inqueryId = $this->where("id", $input["inquiry_id"])->update($data);
// 检查询价明细是否需要报价,若无需报价则更新询价明细报价表
$inquiry_items = (new InquiryItemsModel())->where("inquiry_id",$inquiry_id)->get()->toArray();
foreach ($inquiry_items as $item) {
if ($item['is_quote'] == 1) continue;
$reportArr = [];
$reportArr['goods_name'] = $item['goods_name'];
$reportArr['goods_name_pro'] = $item['goods_name'];
$reportArr['brand_id'] = $item['brand_id'];
$reportArr['brand_name'] = $item['brand_name'];
$reportArr['supplier_name'] = '猎芯联营';
$reportArr['inquiry_number'] = $item['inquiry_number'];
$reportArr['currency'] = $input["delivery_place"];
if ($input["delivery_place"] == 1) {
$reportArr['price_rmb'] = $item['target_price'];
} else {
$reportArr['price_origin'] = $item['target_price'];
}
$reportArr['delivery_time'] = $item['delivery_time'];
$reportArr['class_id2'] = $item['class_id2'];
$reportArr['class_id2_name'] = $item['class_id2_name'];
$reportArr['batch'] = $item['batch'];
(new InquiryItemsReportModel())->where("inquiry_items_id", $item['id'])->update($reportArr);
}
// 加入队列, 后续会把询价单分发到云芯的供应商
$RabbitQueueModel = new RabbitQueueModel();
$RabbitQueueModel->push("frq_add_inquiry", $input["inquiry_id"]);
return [0, "操作成功"];
}
......
......@@ -2,7 +2,7 @@
namespace App\Model;
use ClassPreloader\Config;
use App\Http\Utils\RequestHelper;
use Illuminate\Database\Eloquent\Model;
use League\Flysystem\Exception;
use Request;
......@@ -26,7 +26,6 @@ class LoginModel extends Model
if (@$_REQUEST["debug"] == 100){
return ["user_id"=>1000,"user_name"=>"admin"];
}
$Redis= \RedisDB::connection();
//来自不一样的端,判断是否已经登录
$source = $request->header("source"); //来源端:内部后台:pc 云芯系统:yunxin App: app 小程序:h5_app
......@@ -34,15 +33,42 @@ class LoginModel extends Model
if ($token == ""){
Export(1001,"参数 token 不得为空");
}
$userInfoKey = $Redis->keys('frq_login_'.$token."*");
$userInfo = count($userInfoKey) ? $Redis->get($userInfoKey[0]) : "";
if (empty($userInfo)){
Export(1001,"请先登录");
if($source == 'yunxin'){
$cloud_user_info = $this->getCloudSupplierCmsInfo($token);
$userInfo = ['user_id' => $cloud_user_info['userId'], "user_name" => $cloud_user_info['name'], 'email' => $cloud_user_info['email']];
// 设置请求平台类型
RequestHelper::setClientType($source);
// 设置请求用户信息
RequestHelper::setUser($cloud_user_info);
} else {
$Redis= \RedisDB::connection();
$userInfoKey = $Redis->keys('frq_login_'.$token."*");
$userInfo = count($userInfoKey) ? $Redis->get($userInfoKey[0]) : "";
if (empty($userInfo)){
Export(1001,"请先登录");
}
$userInfo = \GuzzleHttp\json_decode($userInfo,true);
}
$userInfo = \GuzzleHttp\json_decode($userInfo,true);
return $userInfo;
}
// 获取云芯用户信息
private function getCloudSupplierCmsInfo($token)
{
$http = new \GuzzleHttp\Client;
$url = Config('website.cloud_domain') . "/api/user/getsupplier.cmsinfo";
$response = $http->get($url, ['query' => ['token'=>$token]]);
$stringBody = (string)$response->getBody();
$res = json_decode($stringBody, true);
if ($res && is_array($res) && ($res['err_code'] === 0)){
return $res['data'];
} else {
Export(1001,"请先登录");
}
}
/*
* 生成token ,目前只有pc端要调用
*/
......@@ -65,7 +91,7 @@ class LoginModel extends Model
public function setLoginCookie($userId, $skey, $header, $expire)
{
setcookie('oa_user_id', $userId, $expire, '/', Config::get('website.cookieDomain'));
setcookie('oa_user_id', $userId, $expire, '/', Config('website.cookieDomain'));
}
/*
......
......@@ -15,7 +15,7 @@ class SearchModel
private $SERVICE_URL;
public function __construct()
{
$this->SERVICE_URL = "http://so12.ichunt.com/";
$this->SERVICE_URL = Config('website.search_url');
}
/*
......
......@@ -23,5 +23,4 @@ class SupplierChannelComModel extends Model
echo json_encode($brandInfo,JSON_UNESCAPED_UNICODE) ;
exit();
}
}
......@@ -10,6 +10,12 @@ class UserModel extends Model
protected $table='user_info';
public $timestamps = false;
public static function getUsersByIds($uids)
{
return self::whereIn('userId', $uids)->select('userId','name','email')->get()->toArray();
}
public function DepartmentUserInfo($Department=''){
if(!$Department) return false;
$OrganizationModel=new OrganizationModel();
......
......@@ -3,13 +3,14 @@ namespace App\map;
//询价枚举
class InquiryMap{
//状态 -1:已关闭 1:待报价 2:已报价 3:已选中 4:已领取 5:已确认
//状态 -1:已关闭 1:待报价 2:已报价 3:已选中 4:已领取 5:已确认 10:已成单
const status_close = -1;
const status_ready = 1;
const status_replay = 2;
const status_check = 3;
const status_fix = 4;
const status_sure = 5;
const status_order = 10;
static $status =[
self::status_close => "已关闭",
self::status_ready => "待报价",
......@@ -17,6 +18,7 @@ class InquiryMap{
self::status_check => "已选中",
self::status_fix => "已领取",
self::status_sure => "已确认",
self::status_order => "已成单",
];
//币种 1:CNY 2:USD 3: 港币 4:欧元 5:英磅
......
......@@ -4,17 +4,23 @@ namespace App\map;
//报价枚举
class QuoteMap{
//状态 -1:已撤销 0:草稿 1:已报价 2:已选中 3:已确认
const status_close = -1;
const status_cancel = -1;
const status_cao = 0;
const status_ready = 1;
const status_fix = 2;
const status_sure = 3;
const status_order = 4;
const status_close = 5;
const status_delete = 9;
static $status =[
self::status_close => "已撤销",
self::status_cancel => "已撤销",
self::status_cao => "草稿",
self::status_ready => "已报价",
self::status_fix => "已选中",
self::status_sure => "已确认",
self::status_order => "已成单",
self::status_close => "已关闭",
self::status_delete => "已删除",
];
//可用报价状态
......
......@@ -10,7 +10,6 @@
"guzzlehttp/guzzle": "^6.0",
"predis/predis": "^1.1",
"maatwebsite/excel": "~2.0.0",
"vladimir-yuldashev/laravel-queue-rabbitmq": "5.2",
"artisaninweb/laravel-soap": "0.2.*",
"hprose/hprose": "^2.0",
"barryvdh/laravel-dompdf": "^0.8.2",
......@@ -25,9 +24,6 @@
"symfony/dom-crawler": "2.8.*|3.0.*"
},
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
}
......
......@@ -157,10 +157,7 @@ return [
App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
VladimirYuldashev\LaravelQueueRabbitMQ\LaravelQueueRabbitMQServiceProvider::class,
Artisaninweb\SoapWrapper\ServiceProvider::class,
Barryvdh\DomPDF\ServiceProvider::class,
Jenssegers\Mongodb\MongodbServiceProvider::class,
......
......@@ -2,41 +2,56 @@
return [
'quote_status' => [ // 报价状态
0 => '草稿',
1 => '已报价',
1 => '已报价',
2 => '已选中',
3 => '已确认',
4 => '已成单',
5 => '已关闭',
9 => '已删除',
],
'quote_tax_rate' => '0.13', // 报价税率
'import_quote_map' => [ // 导入报价映射
'inquiry_item_id',
'goods_name',
'brand_name',
'inquiry_number',
'target_price',
'delivery_place',
'delivery_time',
'supplier_name',
'quote_number',
'currency',
'price_origin',
'price_rmb',
'price_other',
'batch',
'delivery_time',
'quote_delivery_time',
],
//币种 1:CNY 2:USD 3: 港币 4:欧元 5:英磅
//币种 1:CNY 2:USD 3: 港币 4:欧元 5:英磅
'currency' => [
1 => '人民币',
2 => '美元',
3 => '港币',
4 => '欧元',
5 => '英',
5 => '英',
],
'currency_sign' => [
'currency_sign' => [ // 货币符号
1 => '¥',
2 => '$',
3 => 'HK$',
4 => '€',
5 => '£',
5 => '£',
],
'show_supplier_list' => [
strtolower("digikey")
],
'quote_source' => [ // 报价来源
1 => '猎芯内部',
3 => '云芯商家',
],
];
<?php
return [
'default' => "cloud",
'connections' => [
'cloud' => [
'host' => get_resource_config_section('rabbit', 'rabbit')['host'],
'port' => get_resource_config_section('rabbit', 'rabbit')['port'],
'login' => get_resource_config_section('rabbit', 'rabbit')['user'],
'password' => get_resource_config_section('rabbit', 'rabbit')['passwd'],
'vhost' => get_resource_config_section('rabbit', 'rabbit')['vhost']
]
]
];
......@@ -13,12 +13,7 @@ return [
'search'=> $domain_config['domain']['login_domain'] . '/api/search',
'dashboard'=> $domain_config['domain']['login_domain'] . '/dashboard',
],
////////////////////
"search_url"=>"http://soso12.ichunt.com", //本地235数据库
////////////////本地配置//////////////////////本地235数据库
"search_url"=> $domain_config['domain']['search_url'],
////////////////本地配置////////////////////
'export_source_items_id' => "30", //导出来源明细id
'company_info_origin'=>[ #原始公司信息
......@@ -52,12 +47,15 @@ return [
'frq_url' => $domain_config['domain']['frq_url'],
'search_url' => $domain_config['domain']['search_url'],
// 权限系统
'perm_url' => $domain_config['domain']['perm_url'],
// 获取用户权限接口
'perm_api' => $domain_config['domain']['perm_api'],
// 获取用户许可权限接口
'check_access_api' => $domain_config['domain']['check_access_api'],
'cloud_domain' => $domain_config['domain']['cloud_domain'],
//上传图片接口地址
'UploadUrl' => $domain_config['upload']['upload_url'],
......
......@@ -60,7 +60,7 @@ class ClassLoader
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
......
......@@ -9,11 +9,12 @@ return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'1d1b89d124cc9cb8219922c9d5569199' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'e7223560d890eab89cda23685e711e2c' => $vendorDir . '/psy/psysh/src/Psy/functions.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'd7f4f7522f962c095f835c50e6136087' => $vendorDir . '/hprose/hprose/src/init.php',
......
......@@ -6,7 +6,6 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'VladimirYuldashev\\LaravelQueueRabbitMQ' => array($vendorDir . '/vladimir-yuldashev/laravel-queue-rabbitmq/src'),
'Sabberworm\\CSS' => array($vendorDir . '/sabberworm/php-css-parser/lib'),
'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src'),
'PHPExcel' => array($vendorDir . '/phpoffice/phpexcel/Classes'),
......
......@@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'phpseclib3\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/reflection-docblock/src', $vendorDir . '/phpdocumentor/type-resolver/src'),
'XdgBaseDir\\' => array($vendorDir . '/dnoegel/php-xdg-base-dir/src'),
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
......@@ -34,6 +35,7 @@ return array(
'Predis\\' => array($vendorDir . '/predis/predis/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PhpAmqpLib\\' => array($vendorDir . '/php-amqplib/php-amqplib/PhpAmqpLib'),
'ParagonIE\\ConstantTime\\' => array($vendorDir . '/paragonie/constant_time_encoding/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'),
......
.idea/
vendor/
\ No newline at end of file
language: php
sudo: false
matrix:
fast_finish: true
include:
- php: "7.1"
- php: "7.2"
- php: "7.3"
- php: "7.4"
- php: "8.0"
- php: "nightly"
allow_failures:
- php: "nightly"
- php: "7.4"
- php: "8.0"
install:
- composer self-update
- composer update
script:
- vendor/bin/phpunit
- vendor/bin/psalm
The MIT License (MIT)
Copyright (c) 2016 - 2020 Paragon Initiative Enterprises
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.
------------------------------------------------------------------------------
This library was based on the work of Steve "Sc00bz" Thomas.
------------------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2014 Steve Thomas
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.
# Constant-Time Encoding
[![Build Status](https://travis-ci.org/paragonie/constant_time_encoding.svg?branch=master)](https://travis-ci.org/paragonie/constant_time_encoding)
[![Latest Stable Version](https://poser.pugx.org/paragonie/constant_time_encoding/v/stable)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![Latest Unstable Version](https://poser.pugx.org/paragonie/constant_time_encoding/v/unstable)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![License](https://poser.pugx.org/paragonie/constant_time_encoding/license)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![Downloads](https://img.shields.io/packagist/dt/paragonie/constant_time_encoding.svg)](https://packagist.org/packages/paragonie/constant_time_encoding)
Based on the [constant-time base64 implementation made by Steve "Sc00bz" Thomas](https://github.com/Sc00bz/ConstTimeEncoding),
this library aims to offer character encoding functions that do not leak
information about what you are encoding/decoding via processor cache
misses. Further reading on [cache-timing attacks](http://blog.ircmaxell.com/2014/11/its-all-about-time.html).
Our fork offers the following enchancements:
* `mbstring.func_overload` resistance
* Unit tests
* Composer- and Packagist-ready
* Base16 encoding
* Base32 encoding
* Uses `pack()` and `unpack()` instead of `chr()` and `ord()`
## PHP Version Requirements
Version 2 of this library should work on **PHP 7** or newer. For PHP 5
support, see [the v1.x branch](https://github.com/paragonie/constant_time_encoding/tree/v1.x).
If you are adding this as a dependency to a project intended to work on both PHP 5 and PHP 7, please set the required version to `^1|^2` instead of just `^1` or `^2`.
## How to Install
```sh
composer require paragonie/constant_time_encoding
```
## How to Use
```php
use \ParagonIE\ConstantTime\Encoding;
// possibly (if applicable):
// require 'vendor/autoload.php';
$data = random_bytes(32);
echo Encoding::base64Encode($data), "\n";
echo Encoding::base32EncodeUpper($data), "\n";
echo Encoding::base32Encode($data), "\n";
echo Encoding::hexEncode($data), "\n";
echo Encoding::hexEncodeUpper($data), "\n";
```
Example output:
```
1VilPkeVqirlPifk5scbzcTTbMT2clp+Zkyv9VFFasE=
2VMKKPSHSWVCVZJ6E7SONRY3ZXCNG3GE6ZZFU7TGJSX7KUKFNLAQ====
2vmkkpshswvcvzj6e7sonry3zxcng3ge6zzfu7tgjsx7kukfnlaq====
d558a53e4795aa2ae53e27e4e6c71bcdc4d36cc4f6725a7e664caff551456ac1
D558A53E4795AA2AE53E27E4E6C71BDCC4D36CC4F6725A7E664CAFF551456AC1
```
If you only need a particular variant, you can just reference the
required class like so:
```php
use \ParagonIE\ConstantTime\Base64;
use \ParagonIE\ConstantTime\Base32;
$data = random_bytes(32);
echo Base64::encode($data), "\n";
echo Base32::encode($data), "\n";
```
Example output:
```
1VilPkeVqirlPifk5scbzcTTbMT2clp+Zkyv9VFFasE=
2vmkkpshswvcvzj6e7sonry3zxcng3ge6zzfu7tgjsx7kukfnlaq====
```
## Support Contracts
If your company uses this library in their products or services, you may be
interested in [purchasing a support contract from Paragon Initiative Enterprises](https://paragonie.com/enterprise).
{
"name": "paragonie/constant_time_encoding",
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base64",
"encoding",
"rfc4648",
"base32",
"base16",
"hex",
"bin2hex",
"hex2bin",
"base64_encode",
"base64_decode",
"base32_encode",
"base32_decode"
],
"license": "MIT",
"type": "library",
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"support": {
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"email": "info@paragonie.com",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"require": {
"php": "^7|^8"
},
"require-dev": {
"phpunit/phpunit": "^6|^7|^8|^9",
"vimeo/psalm": "^1|^2|^3|^4"
},
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="true" backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnError="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Constant Time Encoding Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
</phpunit>
<?xml version="1.0"?>
<psalm
useDocblockTypes="true"
totallyTyped="true"
>
<projectFiles>
<directory name="src" />
</projectFiles>
</psalm>
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class Base32Hex
* [0-9][A-V]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base32Hex extends Base32
{
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* @param int $src
* @return int
*/
protected static function decode5Bits(int $src): int
{
$ret = -1;
// if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47);
// if ($src > 0x60 && $src < 0x77) ret += $src - 0x61 + 10 + 1; // -86
$ret += (((0x60 - $src) & ($src - 0x77)) >> 8) & ($src - 86);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* @param int $src
* @return int
*/
protected static function decode5BitsUpper(int $src): int
{
$ret = -1;
// if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47);
// if ($src > 0x40 && $src < 0x57) ret += $src - 0x41 + 10 + 1; // -54
$ret += (((0x40 - $src) & ($src - 0x57)) >> 8) & ($src - 54);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode5Bits(int $src): string
{
$src += 0x30;
// if ($src > 0x39) $src += 0x61 - 0x3a; // 39
$src += ((0x39 - $src) >> 8) & 39;
return \pack('C', $src);
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* Uppercase variant.
*
* @param int $src
* @return string
*/
protected static function encode5BitsUpper(int $src): string
{
$src += 0x30;
// if ($src > 0x39) $src += 0x41 - 0x3a; // 7
$src += ((0x39 - $src) >> 8) & 7;
return \pack('C', $src);
}
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class Base64
* [A-Z][a-z][0-9]+/
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64 implements EncoderInterface
{
/**
* Encode into Base64
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws \TypeError
*/
public static function encode(string $src): string
{
return static::doEncode($src, true);
}
/**
* Encode into Base64, no = padding
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
* @throws \TypeError
*/
public static function encodeUnpadded(string $src): string
{
return static::doEncode($src, false);
}
/**
* @param string $src
* @param bool $pad Include = padding?
* @return string
* @throws \TypeError
*/
protected static function doEncode(string $src, bool $pad = true): string
{
$dest = '';
$srcLen = Binary::safeStrlen($src);
// Main loop (no padding):
for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, 3));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
static::encode6Bits( $b0 >> 2 ) .
static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
static::encode6Bits( $b2 & 63);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
static::encode6Bits($b0 >> 2) .
static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
static::encode6Bits(($b1 << 2) & 63);
if ($pad) {
$dest .= '=';
}
} else {
$dest .=
static::encode6Bits( $b0 >> 2) .
static::encode6Bits(($b0 << 4) & 63);
if ($pad) {
$dest .= '==';
}
}
}
return $dest;
}
/**
* decode from base64 into binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $encodedString
* @param bool $strictPadding
* @return string
* @throws \RangeException
* @throws \TypeError
* @psalm-suppress RedundantCondition
*/
public static function decode(string $encodedString, bool $strictPadding = false): string
{
// Remove padding
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 3) === 0) {
if ($encodedString[$srcLen - 1] === '=') {
$srcLen--;
if ($encodedString[$srcLen - 1] === '=') {
$srcLen--;
}
}
}
if (($srcLen & 3) === 1) {
throw new \RangeException(
'Incorrect padding'
);
}
if ($encodedString[$srcLen - 1] === '=') {
throw new \RangeException(
'Incorrect padding'
);
}
} else {
$encodedString = \rtrim($encodedString, '=');
$srcLen = Binary::safeStrlen($encodedString);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, 4));
$c0 = static::decode6Bits($chunk[1]);
$c1 = static::decode6Bits($chunk[2]);
$c2 = static::decode6Bits($chunk[3]);
$c3 = static::decode6Bits($chunk[4]);
$dest .= \pack(
'CCC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff),
((($c2 << 6) | $c3 ) & 0xff)
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, $srcLen - $i));
$c0 = static::decode6Bits($chunk[1]);
if ($i + 2 < $srcLen) {
$c1 = static::decode6Bits($chunk[2]);
$c2 = static::decode6Bits($chunk[3]);
$dest .= \pack(
'CC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff)
);
$err |= ($c0 | $c1 | $c2) >> 8;
} elseif ($i + 1 < $srcLen) {
$c1 = static::decode6Bits($chunk[2]);
$dest .= \pack(
'C',
((($c0 << 2) | ($c1 >> 4)) & 0xff)
);
$err |= ($c0 | $c1) >> 8;
} elseif ($i < $srcLen && $strictPadding) {
$err |= 1;
}
}
/** @var bool $check */
$check = ($err === 0);
if (!$check) {
throw new \RangeException(
'Base64::decode() only expects characters in the correct base64 alphabet'
);
}
return $dest;
}
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] + /
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
// if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
// if ($src == 0x2b) $ret += 62 + 1;
$ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63;
// if ($src == 0x2f) ret += 63 + 1;
$ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64;
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$diff = 0x41;
// if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
$diff += ((25 - $src) >> 8) & 6;
// if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
$diff -= ((51 - $src) >> 8) & 75;
// if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15
$diff -= ((61 - $src) >> 8) & 15;
// if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3
$diff += ((62 - $src) >> 8) & 3;
return \pack('C', $src + $diff);
}
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class Base64DotSlash
* ./[A-Z][a-z][0-9]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64DotSlash extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* ./ [A-Z] [a-z] [0-9]
* 0x2e-0x2f, 0x41-0x5a, 0x61-0x7a, 0x30-0x39
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x2d && $src < 0x30) ret += $src - 0x2e + 1; // -45
$ret += (((0x2d - $src) & ($src - 0x30)) >> 8) & ($src - 45);
// if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 2 + 1; // -62
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 62);
// if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 28 + 1; // -68
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 68);
// if ($src > 0x2f && $src < 0x3a) ret += $src - 0x30 + 54 + 1; // 7
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 7);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$src += 0x2e;
// if ($src > 0x2f) $src += 0x41 - 0x30; // 17
$src += ((0x2f - $src) >> 8) & 17;
// if ($src > 0x5a) $src += 0x61 - 0x5b; // 6
$src += ((0x5a - $src) >> 8) & 6;
// if ($src > 0x7a) $src += 0x30 - 0x7b; // -75
$src -= ((0x7a - $src) >> 8) & 75;
return \pack('C', $src);
}
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class Base64DotSlashOrdered
* ./[0-9][A-Z][a-z]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64DotSlashOrdered extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [.-9] [A-Z] [a-z]
* 0x2e-0x39, 0x41-0x5a, 0x61-0x7a
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x2d && $src < 0x3a) ret += $src - 0x2e + 1; // -45
$ret += (((0x2d - $src) & ($src - 0x3a)) >> 8) & ($src - 45);
// if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 12 + 1; // -52
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 52);
// if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 38 + 1; // -58
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 58);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$src += 0x2e;
// if ($src > 0x39) $src += 0x41 - 0x3a; // 7
$src += ((0x39 - $src) >> 8) & 7;
// if ($src > 0x5a) $src += 0x61 - 0x5b; // 6
$src += ((0x5a - $src) >> 8) & 6;
return \pack('C', $src);
}
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class Base64UrlSafe
* [A-Z][a-z][0-9]\-_
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64UrlSafe extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] - _
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2d, 0x5f
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
// if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
// if ($src == 0x2c) $ret += 62 + 1;
$ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63;
// if ($src == 0x5f) ret += 63 + 1;
$ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64;
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$diff = 0x41;
// if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
$diff += ((25 - $src) >> 8) & 6;
// if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
$diff -= ((51 - $src) >> 8) & 75;
// if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13
$diff -= ((61 - $src) >> 8) & 13;
// if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3
$diff += ((62 - $src) >> 8) & 49;
return \pack('C', $src + $diff);
}
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class Binary
*
* Binary string operators that don't choke on
* mbstring.func_overload
*
* @package ParagonIE\ConstantTime
*/
abstract class Binary
{
/**
* Safe string length
*
* @ref mbstring.func_overload
*
* @param string $str
* @return int
*/
public static function safeStrlen(string $str): int
{
if (\function_exists('mb_strlen')) {
return (int) \mb_strlen($str, '8bit');
} else {
return \strlen($str);
}
}
/**
* Safe substring
*
* @ref mbstring.func_overload
*
* @staticvar boolean $exists
* @param string $str
* @param int $start
* @param int $length
* @return string
* @throws \TypeError
*/
public static function safeSubstr(
string $str,
int $start = 0,
$length = null
): string {
if ($length === 0) {
return '';
}
if (\function_exists('mb_substr')) {
return \mb_substr($str, $start, $length, '8bit');
}
// Unlike mb_substr(), substr() doesn't accept NULL for length
if ($length !== null) {
return \substr($str, $start, $length);
} else {
return \substr($str, $start);
}
}
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Interface EncoderInterface
* @package ParagonIE\ConstantTime
*/
interface EncoderInterface
{
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $binString (raw binary)
* @return string
*/
public static function encode(string $binString): string;
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $encodedString
* @param bool $strictPadding Error on invalid padding
* @return string (raw binary)
*/
public static function decode(string $encodedString, bool $strictPadding = false): string;
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class Encoding
* @package ParagonIE\ConstantTime
*/
abstract class Encoding
{
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32Encode(string $str): string
{
return Base32::encode($str);
}
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32EncodeUpper(string $str): string
{
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32 decoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32Decode(string $str): string
{
return Base32::decode($str);
}
/**
* RFC 4648 Base32 decoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32DecodeUpper(string $str): string
{
return Base32::decodeUpper($str);
}
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32HexEncode(string $str): string
{
return Base32Hex::encode($str);
}
/**
* RFC 4648 Base32Hex encoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32HexEncodeUpper(string $str): string
{
return Base32Hex::encodeUpper($str);
}
/**
* RFC 4648 Base32Hex decoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32HexDecode(string $str): string
{
return Base32Hex::decode($str);
}
/**
* RFC 4648 Base32Hex decoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32HexDecodeUpper(string $str): string
{
return Base32Hex::decodeUpper($str);
}
/**
* RFC 4648 Base64 encoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base64Encode(string $str): string
{
return Base64::encode($str);
}
/**
* RFC 4648 Base64 decoding
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base64Decode(string $str): string
{
return Base64::decode($str);
}
/**
* Encode into Base64
*
* Base64 character set "./[A-Z][a-z][0-9]"
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base64EncodeDotSlash(string $str): string
{
return Base64DotSlash::encode($str);
}
/**
* Decode from base64 to raw binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $str
* @return string
* @throws \RangeException
* @throws \TypeError
*/
public static function base64DecodeDotSlash(string $str): string
{
return Base64DotSlash::decode($str);
}
/**
* Encode into Base64
*
* Base64 character set "[.-9][A-Z][a-z]" or "./[0-9][A-Z][a-z]"
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base64EncodeDotSlashOrdered(string $str): string
{
return Base64DotSlashOrdered::encode($str);
}
/**
* Decode from base64 to raw binary
*
* Base64 character set "[.-9][A-Z][a-z]" or "./[0-9][A-Z][a-z]"
*
* @param string $str
* @return string
* @throws \RangeException
* @throws \TypeError
*/
public static function base64DecodeDotSlashOrdered(string $str): string
{
return Base64DotSlashOrdered::decode($str);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
* @throws \TypeError
*/
public static function hexEncode(string $bin_string): string
{
return Hex::encode($bin_string);
}
/**
* Convert a hexadecimal string into a binary string without cache-timing
* leaks
*
* @param string $hex_string
* @return string (raw binary)
* @throws \RangeException
*/
public static function hexDecode(string $hex_string): string
{
return Hex::decode($hex_string);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
* @throws \TypeError
*/
public static function hexEncodeUpper(string $bin_string): string
{
return Hex::encodeUpper($bin_string);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
*/
public static function hexDecodeUpper(string $bin_string): string
{
return Hex::decode($bin_string);
}
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class Hex
* @package ParagonIE\ConstantTime
*/
abstract class Hex implements EncoderInterface
{
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $binString (raw binary)
* @return string
* @throws \TypeError
*/
public static function encode(string $binString): string
{
/** @var string $hex */
$hex = '';
$len = Binary::safeStrlen($binString);
for ($i = 0; $i < $len; ++$i) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C', Binary::safeSubstr($binString, $i, 1));
/** @var int $c */
$c = $chunk[1] & 0xf;
/** @var int $b */
$b = $chunk[1] >> 4;
$hex .= pack(
'CC',
(87 + $b + ((($b - 10) >> 8) & ~38)),
(87 + $c + ((($c - 10) >> 8) & ~38))
);
}
return $hex;
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks, returning uppercase letters (as per RFC 4648)
*
* @param string $binString (raw binary)
* @return string
* @throws \TypeError
*/
public static function encodeUpper(string $binString): string
{
/** @var string $hex */
$hex = '';
/** @var int $len */
$len = Binary::safeStrlen($binString);
for ($i = 0; $i < $len; ++$i) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C', Binary::safeSubstr($binString, $i, 2));
/** @var int $c */
$c = $chunk[1] & 0xf;
/** @var int $b */
$b = $chunk[1] >> 4;
$hex .= pack(
'CC',
(55 + $b + ((($b - 10) >> 8) & ~6)),
(55 + $c + ((($c - 10) >> 8) & ~6))
);
}
return $hex;
}
/**
* Convert a hexadecimal string into a binary string without cache-timing
* leaks
*
* @param string $encodedString
* @param bool $strictPadding
* @return string (raw binary)
* @throws \RangeException
*/
public static function decode(string $encodedString, bool $strictPadding = false): string
{
/** @var int $hex_pos */
$hex_pos = 0;
/** @var string $bin */
$bin = '';
/** @var int $c_acc */
$c_acc = 0;
/** @var int $hex_len */
$hex_len = Binary::safeStrlen($encodedString);
/** @var int $state */
$state = 0;
if (($hex_len & 1) !== 0) {
if ($strictPadding) {
throw new \RangeException(
'Expected an even number of hexadecimal characters'
);
} else {
$encodedString = '0' . $encodedString;
++$hex_len;
}
}
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', $encodedString);
while ($hex_pos < $hex_len) {
++$hex_pos;
/** @var int $c */
$c = $chunk[$hex_pos];
/** @var int $c_num */
$c_num = $c ^ 48;
/** @var int $c_num0 */
$c_num0 = ($c_num - 10) >> 8;
/** @var int $c_alpha */
$c_alpha = ($c & ~32) - 55;
/** @var int $c_alpha0 */
$c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
if (($c_num0 | $c_alpha0) === 0) {
throw new \RangeException(
'Expected hexadecimal character'
);
}
/** @var int $c_val */
$c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
if ($state === 0) {
$c_acc = $c_val * 16;
} else {
$bin .= \pack('C', $c_acc | $c_val);
}
$state ^= 1;
}
return $bin;
}
}
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2018 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot 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.
*/
/**
* Class RFC4648
*
* This class conforms strictly to the RFC
*
* @package ParagonIE\ConstantTime
*/
abstract class RFC4648
{
/**
* RFC 4648 Base64 encoding
*
* "foo" -> "Zm9v"
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base64Encode(string $str): string
{
return Base64::encode($str);
}
/**
* RFC 4648 Base64 decoding
*
* "Zm9v" -> "foo"
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base64Decode(string $str): string
{
return Base64::decode($str, true);
}
/**
* RFC 4648 Base64 (URL Safe) encoding
*
* "foo" -> "Zm9v"
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base64UrlSafeEncode(string $str): string
{
return Base64UrlSafe::encode($str);
}
/**
* RFC 4648 Base64 (URL Safe) decoding
*
* "Zm9v" -> "foo"
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base64UrlSafeDecode(string $str): string
{
return Base64UrlSafe::decode($str, true);
}
/**
* RFC 4648 Base32 encoding
*
* "foo" -> "MZXW6==="
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32Encode(string $str): string
{
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32 encoding
*
* "MZXW6===" -> "foo"
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32Decode(string $str): string
{
return Base32::decodeUpper($str, true);
}
/**
* RFC 4648 Base32-Hex encoding
*
* "foo" -> "CPNMU==="
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32HexEncode(string $str): string
{
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32-Hex decoding
*
* "CPNMU===" -> "foo"
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base32HexDecode(string $str): string
{
return Base32::decodeUpper($str, true);
}
/**
* RFC 4648 Base16 decoding
*
* "foo" -> "666F6F"
*
* @param string $str
* @return string
* @throws \TypeError
*/
public static function base16Encode(string $str): string
{
return Hex::encodeUpper($str);
}
/**
* RFC 4648 Base16 decoding
*
* "666F6F" -> "foo"
*
* @param string $str
* @return string
*/
public static function base16Decode(string $str): string
{
return Hex::decode($str, true);
}
}
\ No newline at end of file
<?php
use \ParagonIE\ConstantTime\Base32Hex;
class Base32HexTest extends PHPUnit\Framework\TestCase
{
/**
* @covers Base32Hex::encode()
* @covers Base32Hex::decode()
* @covers Base32Hex::encodeUpper()
* @covers Base32Hex::decodeUpper()
*/
public function testRandom()
{
for ($i = 1; $i < 32; ++$i) {
for ($j = 0; $j < 50; ++$j) {
$random = \random_bytes($i);
$enc = Base32Hex::encode($random);
$this->assertSame(
$random,
Base32Hex::decode($enc)
);
$unpadded = \rtrim($enc, '=');
$this->assertSame(
$unpadded,
Base32Hex::encodeUnpadded($random)
);
$this->assertSame(
$random,
Base32Hex::decode($unpadded)
);
$enc = Base32Hex::encodeUpper($random);
$this->assertSame(
$random,
Base32Hex::decodeUpper($enc)
); $unpadded = \rtrim($enc, '=');
$this->assertSame(
$unpadded,
Base32Hex::encodeUpperUnpadded($random)
);
$this->assertSame(
$random,
Base32Hex::decodeUpper($unpadded)
);
}
}
}
}
<?php
use \ParagonIE\ConstantTime\Base32;
class Base32Test extends PHPUnit\Framework\TestCase
{
/**
* @covers Base32::encode()
* @covers Base32::decode()
* @covers Base32::encodeUpper()
* @covers Base32::decodeUpper()
*/
public function testRandom()
{
for ($i = 1; $i < 32; ++$i) {
for ($j = 0; $j < 50; ++$j) {
$random = \random_bytes($i);
$enc = Base32::encode($random);
$this->assertSame(
$random,
Base32::decode($enc)
);
$unpadded = \rtrim($enc, '=');
$this->assertSame(
$unpadded,
Base32::encodeUnpadded($random)
);
$this->assertSame(
$random,
Base32::decode($unpadded)
);
$enc = Base32::encodeUpper($random);
$this->assertSame(
$random,
Base32::decodeUpper($enc)
);
$unpadded = \rtrim($enc, '=');
$this->assertSame(
$unpadded,
Base32::encodeUpperUnpadded($random)
);
$this->assertSame(
$random,
Base32::decodeUpper($unpadded)
);
}
}
}
}
<?php
use \ParagonIE\ConstantTime\Base64DotSlashOrdered;
class Base64DotSlashOrderedTest extends PHPUnit\Framework\TestCase
{
/**
* @covers Base64DotSlashOrdered::encode()
* @covers Base64DotSlashOrdered::decode()
*/
public function testRandom()
{
for ($i = 1; $i < 32; ++$i) {
for ($j = 0; $j < 50; ++$j) {
$random = \random_bytes($i);
$enc = Base64DotSlashOrdered::encode($random);
$this->assertSame(
$random,
Base64DotSlashOrdered::decode($enc)
);
$unpadded = \rtrim($enc, '=');
$this->assertSame(
$random,
Base64DotSlashOrdered::decode($unpadded)
);
$this->assertSame(
$random,
Base64DotSlashOrdered::decode($unpadded)
);
}
}
}
}
<?php
use \ParagonIE\ConstantTime\Base64DotSlash;
class Base64DotSlashTest extends PHPUnit\Framework\TestCase
{
/**
* @covers Base64DotSlash::encode()
* @covers Base64DotSlash::decode()
*/
public function testRandom()
{
for ($i = 1; $i < 32; ++$i) {
for ($j = 0; $j < 50; ++$j) {
$random = \random_bytes($i);
$enc = Base64DotSlash::encode($random);
$this->assertSame(
$random,
Base64DotSlash::decode($enc)
);
$unpadded = \rtrim($enc, '=');
$this->assertSame(
$random,
Base64DotSlash::decode($unpadded)
);
$this->assertSame(
$random,
Base64DotSlash::decode($unpadded)
);
}
}
}
}
<?php
use \ParagonIE\ConstantTime\Base64;
class Base64Test extends PHPUnit\Framework\TestCase
{
/**
* @covers Base64::encode()
* @covers Base64::decode()
*/
public function testRandom()
{
for ($i = 1; $i < 32; ++$i) {
for ($j = 0; $j < 50; ++$j) {
$random = \random_bytes($i);
$enc = Base64::encode($random);
$this->assertSame(
$random,
Base64::decode($enc)
);
$this->assertSame(
\base64_encode($random),
$enc
);
$unpadded = \rtrim($enc, '=');
$this->assertSame(
$random,
Base64::decode($unpadded)
);
$this->assertSame(
$random,
Base64::decode($unpadded)
);
}
}
$str = 'MIIFzzCCBLegAwIBAgIDAfdlMA0GCSqGSIb3DQEBBQUAMHMxCzAJBgNVBAYTAlBM' .
'MSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMSQwIgYDVQQ' .
'DDBtDT1BFIFNaQUZJUiAtIEt3YWxpZmlrb3dhbnkxFDASBgNVBAUTC05yIHdwaXN1Oi' .
'A2MB4XDTExMTEwOTA2MDAwMFoXDTEzMTEwOTA2MDAwMFowgdkxCzAJBgNVBAYTAlBMM' .
'RwwGgYDVQQKDBNVcnrEhWQgTWlhc3RhIEdkeW5pMRswGQYDVQQFExJQRVNFTDogNjEw' .
'NjA2MDMxMTgxGTAXBgNVBAMMEEplcnp5IFByemV3b3Jza2kxTzBNBgNVBBAwRgwiQWw' .
'uIE1hcnN6YcWCa2EgUGnFgnN1ZHNraWVnbyA1Mi81NAwNODEtMzgyIEdkeW5pYQwGUG' .
'9sc2thDAlwb21vcnNraWUxDjAMBgNVBCoMBUplcnp5MRMwEQYDVQQEDApQcnpld29yc' .
'2tpMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMm5vjGqHPthJCMqKpqssSISRo' .
's0PYDTcEQzyyurfX67EJWKtZj6HNwuDMEGJ02iBNZfjUl7r8dIi28bSKhNlsfycXZKY' .
'RcIjp0+r5RqtR2auo9GQ6veKb61DEAGIqaR+uLLcJVTHCu0w9oXLGbRlGth5eNoj03C' .
'xXVAH2IfhbNwIDAQABo4IChzCCAoMwDAYDVR0TAQH/BAIwADCCAUgGA1UdIAEB/wSCA' .
'TwwggE4MIIBNAYJKoRoAYb3IwEBMIIBJTCB3QYIKwYBBQUHAgIwgdAMgc1EZWtsYXJh' .
'Y2phIHRhIGplc3Qgb8Wbd2lhZGN6ZW5pZW0gd3lkYXdjeSwgxbxlIHRlbiBjZXJ0eWZ' .
'pa2F0IHpvc3RhxYIgd3lkYW55IGpha28gY2VydHlmaWthdCBrd2FsaWZpa293YW55IH' .
'pnb2RuaWUgeiB3eW1hZ2FuaWFtaSB1c3Rhd3kgbyBwb2RwaXNpZSBlbGVrdHJvbmlje' .
'm55bSBvcmF6IHRvd2FyenlzesSFY3ltaSBqZWogcm96cG9yesSFZHplbmlhbWkuMEMG' .
'CCsGAQUFBwIBFjdodHRwOi8vd3d3Lmtpci5jb20ucGwvY2VydHlmaWthY2phX2tsdWN' .
'6eS9wb2xpdHlrYS5odG1sMAkGA1UdCQQCMAAwIQYDVR0RBBowGIEWai5wcnpld29yc2' .
'tpQGdkeW5pYS5wbDAOBgNVHQ8BAf8EBAMCBkAwgZ4GA1UdIwSBljCBk4AU3TGldJXip' .
'N4oGS3ZYmnBDMFs8gKhd6R1MHMxCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dh' .
'IEl6YmEgUm96bGljemVuaW93YSBTLkEuMSQwIgYDVQQDDBtDT1BFIFNaQUZJUiAtIEt' .
'3YWxpZmlrb3dhbnkxFDASBgNVBAUTC05yIHdwaXN1OiA2ggJb9jBIBgNVHR8EQTA/MD' .
'2gO6A5hjdodHRwOi8vd3d3Lmtpci5jb20ucGwvY2VydHlmaWthY2phX2tsdWN6eS9DU' .
'kxfT1pLMzIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQBYPIqnAreyeql7/opJjcar/qWZ' .
'y9ruhB2q0lZFsJOhwgMnbQXzp/4vv93YJqcHGAXdHP6EO8FQX47mjo2ZKQmi+cIHJHL' .
'ONdX/3Im+M17V0iNAh7Z1lOSfTRT+iiwe/F8phcEaD5q2RmvYusR7zXZq/cLL0If0hX' .
'oPZ/EHQxjN8pxzxiUx6bJAgturnIMEfRNesxwghdr1dkUjOhGLf3kHVzgM6j3VAM7oF' .
'mMUb5y5s96Bzl10DodWitjOEH0vvnIcsppSxH1C1dCAi0o9f/1y2XuLNhBNHMAyTqpY' .
'PX8Yvav1c+Z50OMaSXHAnTa20zv8UtiHbaAhwlifCelUMj93S';
try {
Base64::decode($str, true);
$this->fail('Strict padding not enforced');
} catch (\Exception $ex) {
$this->assertSame(
Base64::decode($str),
\base64_decode($str)
);
}
}
}
<?php
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\ConstantTime\Binary;
/**
* Class Base64UrlSafeTest
*/
class Base64UrlSafeTest extends PHPUnit\Framework\TestCase
{
/**
* @covers Base64UrlSafe::encode()
* @covers Base64UrlSafe::decode()
*
* @throws Exception
* @throws TypeError
*/
public function testRandom()
{
for ($i = 1; $i < 32; ++$i) {
for ($j = 0; $j < 50; ++$j) {
$random = \random_bytes($i);
$enc = Base64UrlSafe::encode($random);
$this->assertSame(
$random,
Base64UrlSafe::decode($enc)
);
$this->assertSame(
\strtr(\base64_encode($random), '+/', '-_'),
$enc
);
$unpadded = \rtrim($enc, '=');
$this->assertSame(
$unpadded,
Base64UrlSafe::encodeUnpadded($random)
);
$this->assertSame(
$random,
Base64UrlSafe::decode($unpadded)
);
}
}
$random = \random_bytes(1 << 20);
$enc = Base64UrlSafe::encode($random);
$this->assertTrue(Binary::safeStrlen($enc) > 65536);
$this->assertSame(
$random,
Base64UrlSafe::decode($enc)
);
$this->assertSame(
\strtr(\base64_encode($random), '+/', '-_'),
$enc
);
}
}
<?php
use \ParagonIE\ConstantTime\Base32;
use \ParagonIE\ConstantTime\Base32Hex;
use \ParagonIE\ConstantTime\Base64;
use \ParagonIE\ConstantTime\Base64DotSlash;
use \ParagonIE\ConstantTime\Base64DotSlashOrdered;
use \ParagonIE\ConstantTime\Base64UrlSafe;
use \ParagonIE\ConstantTime\Encoding;
use \ParagonIE\ConstantTime\Hex;
class EncodingTest extends PHPUnit\Framework\TestCase
{
public function testBase32Encode()
{
$this->assertSame(
Encoding::base32Encode("\x00"),
'aa======'
);
$this->assertSame(
Encoding::base32Encode("\x00\x00"),
'aaaa===='
);
$this->assertSame(
Encoding::base32Encode("\x00\x00\x00"),
'aaaaa==='
);
$this->assertSame(
Encoding::base32Encode("\x00\x00\x00\x00"),
'aaaaaaa='
);
$this->assertSame(
Encoding::base32Encode("\x00\x00\x00\x00\x00"),
'aaaaaaaa'
);
$this->assertSame(
Encoding::base32Encode("\x00\x00\x0F\xFF\xFF"),
'aaaa7777'
);
$this->assertSame(
Encoding::base32Encode("\xFF\xFF\xF0\x00\x00"),
'7777aaaa'
);
$this->assertSame(
Encoding::base32Encode("\xce\x73\x9c\xe7\x39"),
'zzzzzzzz'
);
$this->assertSame(
Encoding::base32Encode("\xd6\xb5\xad\x6b\x5a"),
'22222222'
);
$this->assertSame(
Base32::encodeUpper("\x00"),
'AA======'
);
$this->assertSame(
Base32::encodeUpper("\x00\x00"),
'AAAA===='
);
$this->assertSame(
Base32::encodeUpper("\x00\x00\x00"),
'AAAAA==='
);
$this->assertSame(
Base32::encodeUpper("\x00\x00\x00\x00"),
'AAAAAAA='
);
$this->assertSame(
Base32::encodeUpper("\x00\x00\x00\x00\x00"),
'AAAAAAAA'
);
$this->assertSame(
Base32::encodeUpper("\x00\x00\x0F\xFF\xFF"),
'AAAA7777'
);
$this->assertSame(
Base32::encodeUpper("\xFF\xFF\xF0\x00\x00"),
'7777AAAA'
);
$this->assertSame(
Base32::encodeUpper("\xce\x73\x9c\xe7\x39"),
'ZZZZZZZZ'
);
$this->assertSame(
Base32::encodeUpper("\xd6\xb5\xad\x6b\x5a"),
'22222222'
);
}
public function testBase32Hex()
{
$this->assertSame(
Base32Hex::encode("\x00"),
'00======'
);
$this->assertSame(
Base32Hex::encode("\x00\x00"),
'0000===='
);
$this->assertSame(
Base32Hex::encode("\x00\x00\x00"),
'00000==='
);
$this->assertSame(
Base32Hex::encode("\x00\x00\x00\x00"),
'0000000='
);
$this->assertSame(
Base32Hex::encode("\x00\x00\x00\x00\x00"),
'00000000'
);
$this->assertSame(
Base32Hex::encode("\x00\x00\x0F\xFF\xFF"),
'0000vvvv'
);
$this->assertSame(
Base32Hex::encode("\xFF\xFF\xF0\x00\x00"),
'vvvv0000'
);
}
/**
* Based on test vectors from RFC 4648
*/
public function testBase32Decode()
{
$this->assertSame(
"\x00\x00\x00\x00\x00\x00",
Encoding::base32Decode('aaaaaaaaaa======')
);
$this->assertSame(
"\x00\x00\x00\x00\x00\x00\x00",
Encoding::base32Decode('aaaaaaaaaaaa====')
);
$this->assertSame(
"\x00\x00\x00\x00\x00\x00\x00\x00",
Encoding::base32Decode('aaaaaaaaaaaaa===')
);
$this->assertSame(
"\x00\x00\x00\x00\x00\x00\x00\x00\x00",
Encoding::base32Decode('aaaaaaaaaaaaaaa=')
);
$this->assertSame(
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
Encoding::base32Decode('aaaaaaaaaaaaaaaa')
);
$this->assertSame(
"\x00",
Encoding::base32Decode('aa======')
);
$this->assertSame(
"\x00\x00",
Encoding::base32Decode('aaaa====')
);
$this->assertSame(
"\x00\x00\x00",
Encoding::base32Decode('aaaaa===')
);
$this->assertSame(
"\x00\x00\x00\x00",
Encoding::base32Decode('aaaaaaa=')
);
$this->assertSame(
"\x00\x00\x00\x00\x00",
Encoding::base32Decode('aaaaaaaa')
);
$this->assertSame(
"\x00\x00\x0F\xFF\xFF",
Encoding::base32Decode('aaaa7777')
);
$this->assertSame(
"\xFF\xFF\xF0\x00\x00",
Encoding::base32Decode('7777aaaa')
);
$this->assertSame(
"\xce\x73\x9c\xe7\x39",
Encoding::base32Decode('zzzzzzzz')
);
$this->assertSame(
"\xd6\xb5\xad\x6b\x5a",
Encoding::base32Decode('22222222')
);
$this->assertSame(
'foobar',
Encoding::base32Decode('mzxw6ytboi======')
);
$rand = random_bytes(9);
$enc = Encoding::base32Encode($rand);
$this->assertSame(
Encoding::base32Encode($rand),
Encoding::base32Encode(Encoding::base32Decode($enc))
);
$this->assertSame(
$rand,
Encoding::base32Decode($enc)
);
}
/**
* @covers Encoding::hexDecode()
* @covers Encoding::hexEncode()
* @covers Encoding::base32Decode()
* @covers Encoding::base32Encode()
* @covers Encoding::base64Decode()
* @covers Encoding::base64Encode()
* @covers Encoding::base64DotSlashDecode()
* @covers Encoding::base64DotSlashEncode()
* @covers Encoding::base64DotSlashOrderedDecode()
* @covers Encoding::base64DotSlashOrderedEncode()
*/
public function testBasicEncoding()
{
// Re-run the test at least 3 times for each length
for ($j = 0; $j < 3; ++$j) {
for ($i = 1; $i < 84; ++$i) {
$rand = random_bytes($i);
$enc = Encoding::hexEncode($rand);
$this->assertSame(
\bin2hex($rand),
$enc,
"Hex Encoding - Length: " . $i
);
$this->assertSame(
$rand,
Encoding::hexDecode($enc),
"Hex Encoding - Length: " . $i
);
// Uppercase variant:
$enc = Hex::encodeUpper($rand);
$this->assertSame(
\strtoupper(\bin2hex($rand)),
$enc,
"Hex Encoding - Length: " . $i
);
$this->assertSame(
$rand,
Hex::decode($enc),
"HexUpper Encoding - Length: " . $i
);
$enc = Encoding::base32Encode($rand);
$this->assertSame(
$rand,
Encoding::base32Decode($enc),
"Base32 Encoding - Length: " . $i
);
$enc = Encoding::base32EncodeUpper($rand);
$this->assertSame(
$rand,
Encoding::base32DecodeUpper($enc),
"Base32Upper Encoding - Length: " . $i
);
$enc = Encoding::base32HexEncode($rand);
$this->assertSame(
bin2hex($rand),
bin2hex(Encoding::base32HexDecode($enc)),
"Base32Hex Encoding - Length: " . $i
);
$enc = Encoding::base32HexEncodeUpper($rand);
$this->assertSame(
bin2hex($rand),
bin2hex(Encoding::base32HexDecodeUpper($enc)),
"Base32HexUpper Encoding - Length: " . $i
);
$enc = Encoding::base64Encode($rand);
$this->assertSame(
$rand,
Encoding::base64Decode($enc),
"Base64 Encoding - Length: " . $i
);
$enc = Encoding::base64EncodeDotSlash($rand);
$this->assertSame(
$rand,
Encoding::base64DecodeDotSlash($enc),
"Base64 DotSlash Encoding - Length: " . $i
);
$enc = Encoding::base64EncodeDotSlashOrdered($rand);
$this->assertSame(
$rand,
Encoding::base64DecodeDotSlashOrdered($enc),
"Base64 Ordered DotSlash Encoding - Length: " . $i
);
$enc = Base64UrlSafe::encode($rand);
$this->assertSame(
\strtr(\base64_encode($rand), '+/', '-_'),
$enc
);
$this->assertSame(
$rand,
Base64UrlSafe::decode($enc)
);
}
}
}
}
\ No newline at end of file
<?php
use \ParagonIE\ConstantTime\Hex;
class HexTest extends PHPUnit\Framework\TestCase
{
/**
* @covers Hex::encode()
* @covers Hex::decode()
* @covers Hex::encodeUpper()
*/
public function testRandom()
{
for ($i = 1; $i < 32; ++$i) {
for ($j = 0; $j < 50; ++$j) {
$random = \random_bytes($i);
$enc = Hex::encode($random);
$this->assertSame(
$random,
Hex::decode($enc)
);
$this->assertSame(
\bin2hex($random),
$enc
);
$enc = Hex::encodeUpper($random);
$this->assertSame(
$random,
Hex::decode($enc)
);
$this->assertSame(
\strtoupper(\bin2hex($random)),
$enc
);
}
}
}
}
<?php
use \ParagonIE\ConstantTime\Base32;
use \ParagonIE\ConstantTime\Base32Hex;
use \ParagonIE\ConstantTime\Base64;
use \ParagonIE\ConstantTime\Base64DotSlash;
use \ParagonIE\ConstantTime\Base64DotSlashOrdered;
use \ParagonIE\ConstantTime\Encoding;
use \ParagonIE\ConstantTime\Hex;
/**
* Class RFC4648Test
*
* @ref https://tools.ietf.org/html/rfc4648#section-10
*/
class RFC4648Test extends PHPUnit\Framework\TestCase
{
public function testVectorBase64()
{
$this->assertSame(Base64::encode(''), '');
$this->assertSame(Base64::encode('f'), 'Zg==');
$this->assertSame(Base64::encode('fo'), 'Zm8=');
$this->assertSame(Base64::encode('foo'), 'Zm9v');
$this->assertSame(Base64::encode('foob'), 'Zm9vYg==');
$this->assertSame(Base64::encode('fooba'), 'Zm9vYmE=');
$this->assertSame(Base64::encode('foobar'), 'Zm9vYmFy');
}
public function testVectorBase32()
{
$this->assertSame(Base32::encode(''), '');
$this->assertSame(Base32::encode('f'), 'my======');
$this->assertSame(Base32::encode('fo'), 'mzxq====');
$this->assertSame(Base32::encode('foo'), 'mzxw6===');
$this->assertSame(Base32::encode('foob'), 'mzxw6yq=');
$this->assertSame(Base32::encode('fooba'), 'mzxw6ytb');
$this->assertSame(Base32::encode('foobar'), 'mzxw6ytboi======');
$this->assertSame(Base32::encodeUpper(''), '');
$this->assertSame(Base32::encodeUpper('f'), 'MY======');
$this->assertSame(Base32::encodeUpper('fo'), 'MZXQ====');
$this->assertSame(Base32::encodeUpper('foo'), 'MZXW6===');
$this->assertSame(Base32::encodeUpper('foob'), 'MZXW6YQ=');
$this->assertSame(Base32::encodeUpper('fooba'), 'MZXW6YTB');
$this->assertSame(Base32::encodeUpper('foobar'), 'MZXW6YTBOI======');
}
public function testVectorBase32Hex()
{
$this->assertSame(Base32Hex::encode(''), '');
$this->assertSame(Base32Hex::encode('f'), 'co======');
$this->assertSame(Base32Hex::encode('fo'), 'cpng====');
$this->assertSame(Base32Hex::encode('foo'), 'cpnmu===');
$this->assertSame(Base32Hex::encode('foob'), 'cpnmuog=');
$this->assertSame(Base32Hex::encode('fooba'), 'cpnmuoj1');
$this->assertSame(Base32Hex::encode('foobar'), 'cpnmuoj1e8======');
$this->assertSame(Base32Hex::encodeUpper(''), '');
$this->assertSame(Base32Hex::encodeUpper('f'), 'CO======');
$this->assertSame(Base32Hex::encodeUpper('fo'), 'CPNG====');
$this->assertSame(Base32Hex::encodeUpper('foo'), 'CPNMU===');
$this->assertSame(Base32Hex::encodeUpper('foob'), 'CPNMUOG=');
$this->assertSame(Base32Hex::encodeUpper('fooba'), 'CPNMUOJ1');
$this->assertSame(Base32Hex::encodeUpper('foobar'), 'CPNMUOJ1E8======');
}
public function testVectorBase16()
{
$this->assertSame(Hex::encode(''), '');
$this->assertSame(Hex::encode('f'), '66');
$this->assertSame(Hex::encode('fo'), '666f');
$this->assertSame(Hex::encode('foo'), '666f6f');
$this->assertSame(Hex::encode('foob'), '666f6f62');
$this->assertSame(Hex::encode('fooba'), '666f6f6261');
$this->assertSame(Hex::encode('foobar'), '666f6f626172');
$this->assertSame(Hex::encodeUpper(''), '');
$this->assertSame(Hex::encodeUpper('f'), '66');
$this->assertSame(Hex::encodeUpper('fo'), '666F');
$this->assertSame(Hex::encodeUpper('foo'), '666F6F');
$this->assertSame(Hex::encodeUpper('foob'), '666F6F62');
$this->assertSame(Hex::encodeUpper('fooba'), '666F6F6261');
$this->assertSame(Hex::encodeUpper('foobar'), '666F6F626172');
}
}
......@@ -2,7 +2,113 @@
All Notable changes to `php-amqplib` will be documented in this file
## [Unreleased]
## 2.12.2 - 2021-02-12
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/18?closed=1)
## 2.12.1 - 2020-08-24
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/17?closed=1)
## 2.12.0 - 2020-08-24
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/14?closed=1)
## 2.11.3 - 2020-05-13
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/16?closed=1)
## 2.11.2 - 2020-04-30
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/15?closed=1)
## 2.11.1 - 2020-02-24
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/13?closed=1)
## 2.11.0 - 2019-11-19
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/12?closed=1)
## 2.10.1 - 2019-10-10
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/11?closed=1)
## 2.10.0 - 2019-08-09
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/10?closed=1)
- Heartbeats are disabled by default. This reverts the following changes: [Issue](https://github.com/php-amqplib/php-amqplib/issues/563) / [PR](https://github.com/php-amqplib/php-amqplib/pull/648)
## 2.9.2 - 2019-04-24
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/9?closed=1)
## 2.9.1 - 2019-03-26
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/8?closed=1)
## 2.9.0 - 2019-03-23
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/7?closed=1)
- heartbeats are now enabled by default [Issue](https://github.com/php-amqplib/php-amqplib/issues/563) / [PR](https://github.com/php-amqplib/php-amqplib/pull/648)
## 2.8.1 - 2018-11-13
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/6?closed=1)
- `ext-sockets` is now required: [PR](https://github.com/php-amqplib/php-amqplib/pull/610)
- Fix `errno=11 Resource temporarily unavailable` error: [Issue](https://github.com/php-amqplib/php-amqplib/issues/613) / [PR](https://github.com/php-amqplib/php-amqplib/pull/615)
## 2.8.0 - 2018-10-23
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/3?closed=1)
- Drop testing and support for PHP 5.3
- Use specific exceptions instead of general `AMQPRuntimeException`: [PR](https://github.com/php-amqplib/php-amqplib/pull/600)
- Allow overriding of `LIBRARY_PROPERTIES` - [PR](https://github.com/php-amqplib/php-amqplib/pull/606)
## 2.7.2 - 2018-02-11
[GitHub Milestone](https://github.com/php-amqplib/php-amqplib/milestone/5?closed=1)
- PHP `5.3` compatibility [PR](https://github.com/php-amqplib/php-amqplib/issues/539)
## 2.7.1 - 2018-02-01
- Support PHPUnit 6 [PR](https://github.com/php-amqplib/php-amqplib/pull/530)
- Use `tcp_nodelay` for `StreamIO` [PR](https://github.com/php-amqplib/php-amqplib/pull/517)
- Pass connection timeout to `wait` method [PR](https://github.com/php-amqplib/php-amqplib/pull/512)
- Fix possible indefinite waiting for data in StreamIO [PR](https://github.com/php-amqplib/php-amqplib/pull/423), [PR](https://github.com/php-amqplib/php-amqplib/pull/534)
- Change protected method check_heartbeat to public [PR](https://github.com/php-amqplib/php-amqplib/pull/520)
- Ensure access levels are consistent for calling `check_heartbeat` [PR](https://github.com/php-amqplib/php-amqplib/pull/535)
## 2.7.0 - 2017-09-20
### Added
- Increased overall test coverage
- Bring heartbeat support to socket connection
- Add message delivery tag for publisher confirms
- Add support for serializing DateTimeImmutable objects
### Fixed
- Fixed infinite loop on reconnect - check_heartbeat
- Fixed signal handling exit example
- Fixed exchange_unbind arguments
- Fixed invalid annotation for channel_id
- Fixed socket null error on php 5.3 version
- Fixed timeout parameters on HHVM before calling stream_select
### Changed
- declare(ticks=1) no longer needed after PHP5.3 / amqplib 2.4.1
- Minor DebugHelper improvements
### Enhancements
- Add extensions requirements to README.md
- Add PHP 7.1 to Travis build
- Reduce memory usage in StreamIO::write()
- Re-enable heartbeats after reconnection
## 2.6.3 - 2016-04-11
......
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer. All complaints will be reviewed
and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. Maintainers are obligated to maintain
confidentiality with regard to the reporter of an incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at
[http://contributor-covenant.org/version/1/3/0/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/3/0/
# Contributing
Contributions are **welcome** and will be fully **credited**.
We accept contributions via Pull Requests on [Github](https://github.com/videlalvaro/php-amqplib).
## Pull Requests
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
- **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date.
- **Consider our release cycle** - We try to follow semver. Randomly breaking public APIs is not an option.
- **Create topic branches** - Don't ask us to pull from your master branch.
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting.
## Running Tests
``` bash
$ phpunit
```
**Happy coding**!
Following people contributed to this project:
Barry Pederson <bp@barryp.org> - author of original Python lib
Vadim Zaliva <lord@crocodile.org> - PHP paort
taavi013@gmail.com patches - patches
Sean Murphy http://code.google.com/u/sgmurphy/ - patches
spiderbill http://code.google.com/u/spiderbill/ - patches
<?php
namespace PhpAmqpLib\Connection;
class AMQPLazyConnection extends AMQPStreamConnection
......@@ -28,9 +29,9 @@ class AMQPLazyConnection extends AMQPStreamConnection
/**
* @return null|\PhpAmqpLib\Wire\IO\AbstractIO
*/
protected function getIO()
public function getIO()
{
if (!$this->io) {
if (empty($this->io)) {
$this->connect();
}
......
......@@ -32,9 +32,9 @@ class AMQPLazySocketConnection extends AMQPSocketConnection
/**
* @return null|\PhpAmqpLib\Wire\IO\AbstractIO
*/
protected function getIO()
public function getIO()
{
if (!$this->io) {
if (empty($this->io)) {
$this->connect();
}
......@@ -50,4 +50,4 @@ class AMQPLazySocketConnection extends AMQPSocketConnection
{
return false;
}
}
\ No newline at end of file
}
<?php
namespace PhpAmqpLib\Connection;
class AMQPSSLConnection extends AMQPStreamConnection
......@@ -11,6 +12,7 @@ class AMQPSSLConnection extends AMQPStreamConnection
* @param string $vhost
* @param array $ssl_options
* @param array $options
* @param string $ssl_protocol
*/
public function __construct(
$host,
......@@ -19,9 +21,10 @@ class AMQPSSLConnection extends AMQPStreamConnection
$password,
$vhost = '/',
$ssl_options = array(),
$options = array()
$options = array(),
$ssl_protocol = 'ssl'
) {
$ssl_context = empty($ssl_options) ? null : $this->create_ssl_context($ssl_options);
$ssl_context = empty($ssl_options) ? null : $this->createSslContext($ssl_options);
parent::__construct(
$host,
$port,
......@@ -33,18 +36,26 @@ class AMQPSSLConnection extends AMQPStreamConnection
isset($options['login_response']) ? $options['login_response'] : null,
isset($options['locale']) ? $options['locale'] : 'en_US',
isset($options['connection_timeout']) ? $options['connection_timeout'] : 3,
isset($options['read_write_timeout']) ? $options['read_write_timeout'] : 3,
isset($options['read_write_timeout']) ? $options['read_write_timeout'] : 130,
$ssl_context,
isset($options['keepalive']) ? $options['keepalive'] : false,
isset($options['heartbeat']) ? $options['heartbeat'] : 0
isset($options['heartbeat']) ? $options['heartbeat'] : 0,
isset($options['channel_rpc_timeout']) ? $options['channel_rpc_timeout'] : 0.0,
$ssl_protocol
);
}
public static function try_create_connection($host, $port, $user, $password, $vhost, $options)
{
$ssl_options = isset($options['ssl_options']) ? $options['ssl_options'] : [];
return new static($host, $port, $user, $password, $vhost, $ssl_options, $options);
}
/**
* @param array $options
* @return resource
*/
private function create_ssl_context($options)
private function createSslContext($options)
{
$ssl_context = stream_context_create();
foreach ($options as $k => $v) {
......
<?php
namespace PhpAmqpLib\Connection;
use PhpAmqpLib\Wire\IO\SocketIO;
......@@ -13,10 +14,14 @@ class AMQPSocketConnection extends AbstractConnection
* @param string $vhost
* @param bool $insist
* @param string $login_method
* @param null $login_response
* @param null $login_response @deprecated
* @param string $locale
* @param float $timeout
* @param int|float $read_timeout
* @param bool $keepalive
* @param int $write_timeout
* @param int $heartbeat
* @param float $channel_rpc_timeout
* @throws \Exception
*/
public function __construct(
$host,
......@@ -28,10 +33,17 @@ class AMQPSocketConnection extends AbstractConnection
$login_method = 'AMQPLAIN',
$login_response = null,
$locale = 'en_US',
$timeout = 3,
$keepalive = false
$read_timeout = 3,
$keepalive = false,
$write_timeout = 3,
$heartbeat = 0,
$channel_rpc_timeout = 0.0
) {
$io = new SocketIO($host, $port, $timeout, $keepalive);
if ($channel_rpc_timeout > $read_timeout) {
throw new \InvalidArgumentException('channel RPC timeout must not be greater than I/O read timeout');
}
$io = new SocketIO($host, $port, $read_timeout, $keepalive, $write_timeout, $heartbeat);
parent::__construct(
$user,
......@@ -41,7 +53,45 @@ class AMQPSocketConnection extends AbstractConnection
$login_method,
$login_response,
$locale,
$io
$io,
$heartbeat,
max($read_timeout, $write_timeout),
$channel_rpc_timeout
);
}
protected static function try_create_connection($host, $port, $user, $password, $vhost, $options)
{
$insist = isset($options['insist']) ?
$options['insist'] : false;
$login_method = isset($options['login_method']) ?
$options['login_method'] : 'AMQPLAIN';
$login_response = isset($options['login_response']) ?
$options['login_response'] : null;
$locale = isset($options['locale']) ?
$options['locale'] : 'en_US';
$read_timeout = isset($options['read_timeout']) ?
$options['read_timeout'] : 3;
$keepalive = isset($options['keepalive']) ?
$options['keepalive'] : false;
$write_timeout = isset($options['write_timeout']) ?
$options['write_timeout'] : 3;
$heartbeat = isset($options['heartbeat']) ?
$options['heartbeat'] : 0;
return new static(
$host,
$port,
$user,
$password,
$vhost,
$insist,
$login_method,
$login_response,
$locale,
$read_timeout,
$keepalive,
$write_timeout,
$heartbeat
);
}
}
<?php
namespace PhpAmqpLib\Connection;
use PhpAmqpLib\Wire\IO\StreamIO;
......@@ -13,13 +14,15 @@ class AMQPStreamConnection extends AbstractConnection
* @param string $vhost
* @param bool $insist
* @param string $login_method
* @param null $login_response
* @param null $login_response @deprecated
* @param string $locale
* @param float $connection_timeout
* @param float $read_write_timeout
* @param null $context
* @param bool $keepalive
* @param int $heartbeat
* @param float $channel_rpc_timeout
* @param string|null $ssl_protocol
*/
public function __construct(
$host,
......@@ -35,8 +38,14 @@ class AMQPStreamConnection extends AbstractConnection
$read_write_timeout = 3.0,
$context = null,
$keepalive = false,
$heartbeat = 0
$heartbeat = 0,
$channel_rpc_timeout = 0.0,
$ssl_protocol = null
) {
if ($channel_rpc_timeout > $read_write_timeout) {
throw new \InvalidArgumentException('channel RPC timeout must not be greater than I/O read-write timeout');
}
$io = new StreamIO(
$host,
$port,
......@@ -44,7 +53,8 @@ class AMQPStreamConnection extends AbstractConnection
$read_write_timeout,
$context,
$keepalive,
$heartbeat
$heartbeat,
$ssl_protocol
);
parent::__construct(
......@@ -56,10 +66,50 @@ class AMQPStreamConnection extends AbstractConnection
$login_response,
$locale,
$io,
$heartbeat
$heartbeat,
$connection_timeout,
$channel_rpc_timeout
);
// save the params for the use of __clone, this will overwrite the parent
$this->construct_params = func_get_args();
}
protected static function try_create_connection($host, $port, $user, $password, $vhost, $options)
{
$insist = isset($options['insist']) ?
$options['insist'] : false;
$login_method = isset($options['login_method']) ?
$options['login_method'] : 'AMQPLAIN';
$login_response = isset($options['login_response']) ?
$options['login_response'] : null;
$locale = isset($options['locale']) ?
$options['locale'] : 'en_US';
$connection_timeout = isset($options['connection_timeout']) ?
$options['connection_timeout'] : 3.0;
$read_write_timeout = isset($options['read_write_timeout']) ?
$options['read_write_timeout'] : 130.0;
$context = isset($options['context']) ?
$options['context'] : null;
$keepalive = isset($options['keepalive']) ?
$options['keepalive'] : false;
$heartbeat = isset($options['heartbeat']) ?
$options['heartbeat'] : 60;
return new static(
$host,
$port,
$user,
$password,
$vhost,
$insist,
$login_method,
$login_response,
$locale,
$connection_timeout,
$read_write_timeout,
$context,
$keepalive,
$heartbeat
);
}
}
<?php
namespace PhpAmqpLib\Connection\Heartbeat;
use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Exception\AMQPRuntimeException;
/**
* Manages pcntl-based heartbeat sending for a {@link AbstractConnection}.
*/
final class PCNTLHeartbeatSender
{
/**
* @var AbstractConnection
*/
private $connection;
/**
* @param AbstractConnection $connection
* @throws AMQPRuntimeException
*/
public function __construct(AbstractConnection $connection)
{
if (!$this->isSupported()) {
throw new AMQPRuntimeException('Signal-based heartbeat sender is unsupported');
}
$this->connection = $connection;
}
public function __destruct()
{
$this->unregister();
}
/**
* @return bool
*/
private function isSupported()
{
return extension_loaded('pcntl')
&& function_exists('pcntl_async_signals')
&& (defined('AMQP_WITHOUT_SIGNALS') ? !AMQP_WITHOUT_SIGNALS : true);
}
public function register()
{
if (!$this->connection) {
throw new AMQPRuntimeException('Unable to re-register heartbeat sender');
}
if (!$this->connection->isConnected()) {
throw new AMQPRuntimeException('Unable to register heartbeat sender, connection is not active');
}
$timeout = $this->connection->getHeartbeat();
if ($timeout > 0) {
$interval = ceil($timeout / 2);
pcntl_async_signals(true);
$this->registerListener($interval);
pcntl_alarm($interval);
}
}
public function unregister()
{
$this->connection = null;
// restore default signal handler
pcntl_signal(SIGALRM, SIG_IGN);
}
/**
* @param int $interval
*/
private function registerListener($interval)
{
pcntl_signal(SIGALRM, function () use ($interval) {
if (!$this->connection) {
return;
}
if (!$this->connection->isConnected()) {
$this->unregister();
return;
}
if ($this->connection->isWriting()) {
pcntl_alarm($interval);
return;
}
if (time() > ($this->connection->getLastActivity() + $interval)) {
$this->connection->checkHeartBeat();
}
pcntl_alarm($interval);
});
}
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPBasicCancelException extends \Exception implements AMQPExceptionInterface
{
/** @var string */
/**
* @var string
* @internal Use getter getConsumerTag()
*/
public $consumerTag;
/**
......@@ -11,6 +15,15 @@ class AMQPBasicCancelException extends \Exception implements AMQPExceptionInterf
*/
public function __construct($consumerTag)
{
parent::__construct('Channel was canceled');
$this->consumerTag = $consumerTag;
}
/**
* @return string
*/
public function getConsumerTag()
{
return $this->consumerTag;
}
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPChannelClosedException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPConnectionBlockedException extends AMQPRuntimeException
{
public function __construct($message = '', $code = 0, $previous = null)
{
if (empty($message)) {
$message = 'Connection is blocked due to low resources';
}
parent::__construct($message, $code, $previous);
}
}
<?php
namespace PhpAmqpLib\Exception;
/**
* When connection was closed by server, proxy or some tunnel due to timeout or network issue.
*/
class AMQPConnectionClosedException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPDataReadException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPEmptyDeliveryTagException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;
//TODO refactor usage of static methods
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Helper\MiscHelper;
namespace PhpAmqpLib\Exception;
/**
* @deprecated use AMQPProtocolException instead
*/
class AMQPException extends \Exception
{
/** @var string */
/** @var int */
public $amqp_reply_code;
/** @var int */
/** @var string */
public $amqp_reply_text;
/** @var \Exception */
/** @var int[] */
public $amqp_method_sig;
/** @var array */
public $args;
/**
* @param string $reply_code
* @param int $reply_text
* @param array $method_sig
* @param int $reply_code
* @param string $reply_text
* @param int[] $method_sig
*/
public function __construct($reply_code, $reply_text, $method_sig)
{
......@@ -35,12 +32,6 @@ class AMQPException extends \Exception
$this->amqp_reply_text = $reply_text; // redundant, but kept for BC
$this->amqp_method_sig = $method_sig;
$ms = MiscHelper::methodSig($method_sig);
$protocolClass = AbstractChannel::$PROTOCOL_CONSTANTS_CLASS;
$mn = isset($protocolClass::$GLOBAL_METHOD_NAMES[$ms])
? $protocolClass::$GLOBAL_METHOD_NAMES[$ms]
: $mn = '';
$this->args = array($reply_code, $reply_text, $method_sig, $mn);
$this->args = array($reply_code, $reply_text, $method_sig, '');
}
}
<?php
namespace PhpAmqpLib\Exception;
interface AMQPExceptionInterface
......
<?php
namespace PhpAmqpLib\Exception;
class AMQPHeartbeatMissedException extends AMQPConnectionClosedException
{
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPIOException extends \Exception implements AMQPExceptionInterface
......
<?php
namespace PhpAmqpLib\Exception;
class AMQPIOWaitException extends AMQPRuntimeException
......
<?php
namespace PhpAmqpLib\Exception;
class AMQPInvalidArgumentException extends \RuntimeException implements AMQPExceptionInterface
......
<?php
namespace PhpAmqpLib\Exception;
class AMQPInvalidFrameException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPLogicException extends \LogicException implements AMQPExceptionInterface
......
<?php
namespace PhpAmqpLib\Exception;
/**
* Used mostly in non-blocking methods when no data is ready for processing.
*/
class AMQPNoDataException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPNotImplementedException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPOutOfBoundsException extends \OutOfBoundsException implements AMQPExceptionInterface
......
<?php
namespace PhpAmqpLib\Exception;
class AMQPOutOfRangeException extends \OutOfRangeException implements AMQPExceptionInterface
......
<?php
namespace PhpAmqpLib\Exception;
class AMQPProtocolChannelException extends AMQPProtocolException
......
<?php
namespace PhpAmqpLib\Exception;
/**
* @deprecated
*/
class AMQPProtocolConnectionException extends AMQPProtocolException
{
}
<?php
namespace PhpAmqpLib\Exception;
//TODO refactor usage of static methods
use PhpAmqpLib\Channel\AbstractChannel;
use PhpAmqpLib\Helper\MiscHelper;
namespace PhpAmqpLib\Exception;
class AMQPProtocolException extends \Exception implements AMQPExceptionInterface
{
/** @var string */
/** @var int */
public $amqp_reply_code;
/** @var int */
/** @var string */
public $amqp_reply_text;
/** @var \Exception */
/** @var int[] */
public $amqp_method_sig;
/** @var array */
public $args;
/**
* @param string $reply_code
* @param int $reply_text
* @param array $method_sig
* @param int $reply_code
* @param string $reply_text
* @param int[] $method_sig
*/
public function __construct($reply_code, $reply_text, $method_sig)
{
......@@ -32,13 +29,6 @@ class AMQPProtocolException extends \Exception implements AMQPExceptionInterface
$this->amqp_reply_text = $reply_text; // redundant, but kept for BC
$this->amqp_method_sig = $method_sig;
$ms = MiscHelper::methodSig($method_sig);
$protocolClass = AbstractChannel::$PROTOCOL_CONSTANTS_CLASS;
$mn = isset($protocolClass::$GLOBAL_METHOD_NAMES[$ms])
? $protocolClass::$GLOBAL_METHOD_NAMES[$ms]
: $mn = '';
$this->args = array($reply_code, $reply_text, $method_sig, $mn);
$this->args = array($reply_code, $reply_text, $method_sig);
}
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPRuntimeException extends \RuntimeException implements AMQPExceptionInterface
......
<?php
namespace PhpAmqpLib\Exception;
class AMQPSocketException extends AMQPRuntimeException
{
}
<?php
namespace PhpAmqpLib\Exception;
class AMQPTimeoutException extends \RuntimeException implements AMQPExceptionInterface
{
/**
* @var int|float|null
*/
private $timeout;
public function __construct($message = '', $timeout = 0, $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->timeout = $timeout;
}
/**
* @param int|float|null $timeout
* @param int $code
* @return self
*/
public static function writeTimeout($timeout, $code = 0)
{
return new self('Error sending data. Connection timed out.', $timeout, $code);
}
/**
* @return int|float|null
*/
public function getTimeout()
{
return $this->timeout;
}
}
<?php
namespace PhpAmqpLib\Exchange;
final class AMQPExchangeType
{
const DIRECT = 'direct';
const FANOUT = 'fanout';
const TOPIC = 'topic';
const HEADERS = 'headers';
}
<?php
namespace PhpAmqpLib\Helper;
use InvalidArgumentException;
class Assert
{
/**
* @param mixed $argument
* @throws \InvalidArgumentException
*/
public static function isCallable($argument)
{
if (!is_callable($argument)) {
throw new InvalidArgumentException(sprintf(
'Given argument "%s" should be callable. %s type was given.',
$argument,
gettype($argument)
));
}
}
}
<?php
// phpcs:ignoreFile
namespace PhpAmqpLib\Helper;
if (class_exists('phpseclib\Math\BigInteger')) {
class BigInteger extends \phpseclib\Math\BigInteger
{
}
} elseif (class_exists('phpseclib3\Math\BigInteger')) {
class BigInteger extends \phpseclib3\Math\BigInteger
{
}
} else {
throw new \RuntimeException('Cannot find supported phpseclib/phpseclib library');
}
<?php
namespace PhpAmqpLib\Helper;
use PhpAmqpLib\Wire\Constants;
class DebugHelper
{
/**
......@@ -9,22 +12,34 @@ class DebugHelper
protected $debug;
/**
* @var string
* @var resource
*/
protected $debug_output;
/**
* @var Constants
*/
protected $PROTOCOL_CONSTANTS_CLASS;
protected $constants;
/**
* @param string $PROTOCOL_CONSTANTS_CLASS
* @param Constants $constants
*/
public function __construct($PROTOCOL_CONSTANTS_CLASS) {
public function __construct(Constants $constants)
{
$this->debug = defined('AMQP_DEBUG') ? AMQP_DEBUG : false;
$this->PROTOCOL_CONSTANTS_CLASS = $PROTOCOL_CONSTANTS_CLASS;
if (defined('AMQP_DEBUG_OUTPUT')) {
$this->debug_output = AMQP_DEBUG_OUTPUT;
} else {
$this->debug_output = fopen('php://output', 'wb');
}
$this->constants = $constants;
}
/**
* @param string $msg
*/
public function debug_msg($msg) {
public function debug_msg($msg)
{
if ($this->debug) {
$this->print_msg($msg);
}
......@@ -33,19 +48,23 @@ class DebugHelper
/**
* @param array $allowed_methods
*/
public function debug_allowed_methods($allowed_methods) {
if ($allowed_methods) {
$msg = 'waiting for ' . implode(', ', $allowed_methods);
} else {
$msg = 'waiting for any method';
public function debug_allowed_methods($allowed_methods)
{
if ($this->debug) {
if ($allowed_methods) {
$msg = 'waiting for ' . implode(', ', $allowed_methods);
} else {
$msg = 'waiting for any method';
}
$this->debug_msg($msg);
}
$this->debug_msg($msg);
}
/**
* @param string $method_sig
*/
public function debug_method_signature1($method_sig) {
public function debug_method_signature1($method_sig)
{
$this->debug_method_signature('< %s:', $method_sig);
}
......@@ -53,27 +72,29 @@ class DebugHelper
* @param string $msg
* @param string $method_sig
*/
public function debug_method_signature($msg, $method_sig) {
public function debug_method_signature($msg, $method_sig)
{
if ($this->debug) {
$protocolClass = $this->PROTOCOL_CONSTANTS_CLASS;
$this->debug_msg(sprintf(
$msg . ': %s',
MiscHelper::methodSig($method_sig),
$protocolClass::$GLOBAL_METHOD_NAMES[MiscHelper::methodSig($method_sig)]
));
$constants = $this->constants;
$methods = $constants::$GLOBAL_METHOD_NAMES;
$key = MiscHelper::methodSig($method_sig);
$this->debug_msg(sprintf($msg . ': %s', $key, $methods[$key]));
}
}
/**
* @param string $data
*/
public function debug_hexdump($data) {
public function debug_hexdump($data)
{
if ($this->debug) {
$this->debug_msg(sprintf(
'< [hex]: %s%s',
PHP_EOL,
MiscHelper::hexdump($data, $htmloutput = false, $uppercase = true, $return = true)
));
$this->debug_msg(
sprintf(
'< [hex]: %s%s',
PHP_EOL,
MiscHelper::hexdump($data, $htmloutput = false, $uppercase = true, $return = true)
)
);
}
}
......@@ -84,23 +105,27 @@ class DebugHelper
* @param array $mechanisms
* @param array $locales
*/
public function debug_connection_start($version_major, $version_minor, $server_properties, $mechanisms, $locales) {
public function debug_connection_start($version_major, $version_minor, $server_properties, $mechanisms, $locales)
{
if ($this->debug) {
$this->debug_msg(sprintf(
'Start from server, version: %d.%d, properties: %s, mechanisms: %s, locales: %s',
$version_major,
$version_minor,
MiscHelper::dump_table($server_properties),
implode(', ', $mechanisms),
implode(', ', $locales)
));
$this->debug_msg(
sprintf(
'Start from server, version: %d.%d, properties: %s, mechanisms: %s, locales: %s',
$version_major,
$version_minor,
MiscHelper::dump_table($server_properties),
implode(', ', $mechanisms),
implode(', ', $locales)
)
);
}
}
/**
* @param string $s
*/
protected function print_msg($s) {
echo $s . PHP_EOL;
protected function print_msg($s)
{
fwrite($this->debug_output, $s . PHP_EOL);
}
}
<?php
namespace PhpAmqpLib\Helper;
class MiscHelper
......@@ -17,26 +18,16 @@ class MiscHelper
}
/**
* @param string $bytes
*/
public static function saveBytes($bytes)
{
$fh = fopen('/tmp/bytes', 'wb');
fwrite($fh, $bytes);
fclose($fh);
}
/**
* Gets a number (either int or float) and returns an array containing its integer part as first element and its
* decimal part mutliplied by 10^6. Useful for some PHP stream functions that need seconds and microseconds as
* different arguments
*
* @param int|float $number
* @return array
* @return int[]
*/
public static function splitSecondsMicroseconds($number)
{
return array(floor($number), ($number - floor($number)) * 1000000);
return array((int)floor($number), (int)(fmod($number, 1) * 1000000));
}
/**
......@@ -117,6 +108,8 @@ class MiscHelper
}
echo $dump;
return null;
}
/**
......
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
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 sign in to comment