118 changed files with 14906 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"require": { |
||||
|
"yansongda/pay": "^2.8", |
||||
|
"overtrue/wechat": "~4.0", |
||||
|
"qiniu/php-sdk": "^7.2", |
||||
|
"workerman/workerman": "^4.0", |
||||
|
"workerman/mysql": "^1.0" |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
|
After Width: | Height: | Size: 62 KiB |
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
global $_W; |
||||
|
pdo_query("CREATE TABLE IF NOT EXISTS " . tablename('wlmerchant_setting') . " ( |
||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT, |
||||
|
`uniacid` int(11) NOT NULL, |
||||
|
`plugin` varchar(200) NOT NULL, |
||||
|
`key` varchar(64) NOT NULL, |
||||
|
`value` text NOT NULL, |
||||
|
PRIMARY KEY (`id`) |
||||
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8;"); |
||||
|
?> |
||||
@ -0,0 +1,40 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<manifest xmlns="http://www.we7.cc" versionCode="1.0"> |
||||
|
<application setting="false"> |
||||
|
<name><![CDATA[智慧城市同城V4]]></name> |
||||
|
<identifie><![CDATA[weliam_smartcity]]></identifie> |
||||
|
<version><![CDATA[1.0.40]]></version> |
||||
|
<type><![CDATA[biz]]></type> |
||||
|
<ability><![CDATA[智慧城市同城V4]]></ability> |
||||
|
<description><![CDATA[智慧城市同城V4]]></description> |
||||
|
<author> <![CDATA[微连科技]]></author> |
||||
|
<url><![CDATA[http://bbs.we7.cc/]]></url> |
||||
|
</application> |
||||
|
<platform> |
||||
|
<subscribes> |
||||
|
<message type="subscribe"></message> |
||||
|
</subscribes> |
||||
|
<handles> |
||||
|
<message type="text" /> |
||||
|
<message type="image" /> |
||||
|
<message type="voice" /> |
||||
|
<message type="location" /> |
||||
|
<message type="subscribe" /> |
||||
|
<message type="qr" /> |
||||
|
<message type="trace" /> |
||||
|
<message type="click" /> |
||||
|
</handles> |
||||
|
<supports> |
||||
|
<item type="app" ></item> |
||||
|
</supports> |
||||
|
<rule embed="false" /> |
||||
|
<card embed="false" /> |
||||
|
</platform> |
||||
|
<bindings> |
||||
|
</bindings> |
||||
|
<permissions> |
||||
|
</permissions> |
||||
|
<install><![CDATA[install.php]]></install> |
||||
|
<uninstall><![CDATA[]]></uninstall> |
||||
|
<upgrade><![CDATA[]]></upgrade> |
||||
|
</manifest> |
||||
@ -0,0 +1,20 @@ |
|||||
|
<?php |
||||
|
defined('IN_IA') or exit('Access Denied'); |
||||
|
require_once __DIR__ . "/core/common/defines.php"; |
||||
|
require_once PATH_CORE . "common/autoload.php"; |
||||
|
Func_loader::core('global'); |
||||
|
|
||||
|
class Weliam_smartcityModule extends WeModule { |
||||
|
public function welcomeDisplay() { |
||||
|
global $_W, $_GPC; |
||||
|
header('location: ' . web_url('dashboard/dashboard/index')); |
||||
|
exit(); |
||||
|
} |
||||
|
} |
||||
|
class Weliam_smartcity1Module extends WeModule { |
||||
|
public function welcomeDisplay() { |
||||
|
global $_W, $_GPC; |
||||
|
header('location: ' . web_url('dashboard/dashboard/index')); |
||||
|
exit(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
<?php |
||||
|
defined('IN_IA') or exit('Access Denied'); |
||||
|
require_once __DIR__ . "/core/common/defines.php"; |
||||
|
require_once PATH_CORE . "common/autoload.php"; |
||||
|
require __DIR__ . "/vendor/autoload.php"; |
||||
|
Func_loader::core('global'); |
||||
|
|
||||
|
|
||||
|
class Weliam_smartcityModuleProcessor extends WeModuleProcessor { |
||||
|
|
||||
|
public function respond() { |
||||
|
global $_W; |
||||
|
$_W['wlsetting'] = Setting::wlsetting_load(); |
||||
|
$rule = pdo_fetch('select * from ' . tablename('rule') . ' where id=:id limit 1', array(':id' => $this->rule)); |
||||
|
if (empty($rule)) { |
||||
|
return false; |
||||
|
} |
||||
|
$message = $this->message; |
||||
|
$fansinfo = Member::wl_fans_info($message['from']); |
||||
|
$_W['wlmember'] = Member::wl_member_create($fansinfo, 'wechat'); |
||||
|
$_W['mid'] = $_W['wlmember']['id']; |
||||
|
$dotime = pdo_get('wlmerchant_member',array('id' => $_W['mid']),array('protime')); |
||||
|
$dotime = $dotime['protime']; |
||||
|
if(time() - 10 < $dotime){ |
||||
|
return false; |
||||
|
}else{ |
||||
|
pdo_update('wlmerchant_member',array('protime' => time()),array('id' => $_W['mid'])); |
||||
|
} |
||||
|
file_put_contents(PATH_DATA . "processor_qr.log", var_export($message, true) . PHP_EOL, FILE_APPEND); |
||||
|
|
||||
|
$names = explode(':', $rule['name']); |
||||
|
$plugin = (isset($names[1]) ? $names[1] : ''); |
||||
|
|
||||
|
if (!empty($plugin)) { |
||||
|
$plugin::Processor($message); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
class Weliam_smartcity1ModuleProcessor extends WeModuleProcessor { |
||||
|
|
||||
|
public function respond() { |
||||
|
global $_W; |
||||
|
$_W['wlsetting'] = Setting::wlsetting_load(); |
||||
|
$rule = pdo_fetch('select * from ' . tablename('rule') . ' where id=:id limit 1', array(':id' => $this->rule)); |
||||
|
if (empty($rule)) { |
||||
|
return false; |
||||
|
} |
||||
|
$message = $this->message; |
||||
|
$fansinfo = Member::wl_fans_info($message['from']); |
||||
|
$_W['wlmember'] = Member::wl_member_create($fansinfo, 'wechat'); |
||||
|
$_W['mid'] = $_W['wlmember']['id']; |
||||
|
file_put_contents(PATH_DATA . "processor_qr.log", var_export($message, true) . PHP_EOL, FILE_APPEND); |
||||
|
|
||||
|
$names = explode(':', $rule['name']); |
||||
|
$plugin = (isset($names[1]) ? $names[1] : ''); |
||||
|
|
||||
|
if (!empty($plugin)) { |
||||
|
$plugin::Processor($message); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,373 @@ |
|||||
|
<?php |
||||
|
defined('IN_IA') or exit('Access Denied'); |
||||
|
require_once __DIR__ . "/core/common/defines.php"; |
||||
|
require_once PATH_CORE . "common/autoload.php"; |
||||
|
Func_loader::core('global'); |
||||
|
|
||||
|
class Weliam_smartcityModuleReceiver extends WeModuleReceiver { |
||||
|
|
||||
|
public function receive() { |
||||
|
global $_W; |
||||
|
$_W['wlsetting'] = Setting::wlsetting_load(); |
||||
|
$message = $this->message; |
||||
|
$fansinfo = Member::wl_fans_info($message['from']); |
||||
|
$_W['wlmember'] = Member::wl_member_create($fansinfo, 'wechat'); |
||||
|
$_W['mid'] = $_W['wlmember']['id']; |
||||
|
$dotime = pdo_get('wlmerchant_member',array('id' => $_W['mid']),array('protime')); |
||||
|
$dotime = $dotime['protime']; |
||||
|
if(time() - 10 < $dotime){ |
||||
|
return false; |
||||
|
}else{ |
||||
|
pdo_update('wlmerchant_member',array('protime' => time()),array('id' => $_W['mid'])); |
||||
|
} |
||||
|
file_put_contents(PATH_DATA . "receiver_qr.log", var_export($message, true) . PHP_EOL, FILE_APPEND); |
||||
|
|
||||
|
if (!empty($message['scene'])) { |
||||
|
$name = pdo_getcolumn('qrcode', array('scene_str' => $message['scene'], 'uniacid' => $_W['uniacid']), 'name'); |
||||
|
$names = explode(':', $name); |
||||
|
$plugin = (isset($names[1]) ? $names[1] : ''); |
||||
|
if (!empty($plugin)) { |
||||
|
$plugin::Processor($message); |
||||
|
} |
||||
|
} else { |
||||
|
$scanrecord = pdo_fetch("SELECT cardid,scantime,type,url FROM " |
||||
|
. tablename('wlmerchant_halfcard_qrscan') |
||||
|
. " WHERE uniacid = {$_W['uniacid']} AND openid = '{$message['from']}' order by id desc"); |
||||
|
if (!empty($scanrecord) && ($scanrecord['scantime'] + 120) > time()) { |
||||
|
//一卡通实卡 |
||||
|
if (empty($scanrecord['type'])) { |
||||
|
$card = pdo_get('wlmerchant_halfcard_realcard', array('uniacid' => $_W['uniacid'], 'id' => $scanrecord['cardid'])); |
||||
|
if (!empty($card)) { |
||||
|
//一卡通未绑定 |
||||
|
if ($card['status'] == 1) { |
||||
|
$setting = Setting::wlsetting_read('halfcard'); |
||||
|
$imgurl = $setting['cardimg'] ? $setting['cardimg'] : URL_MODULE . 'plugin/halfcard/app/resource/images/cord-bg.jpg'; |
||||
|
$returnmess = array(array('title' => urlencode("点击立即激活此卡"), 'description' => urlencode('激活此卡'), 'picurl' => tomedia($imgurl), 'url' => app_url('halfcard/halfcard_app/realcard', array('cardsn' => $card['cardsn'], 'salt' => $card['salt'])))); |
||||
|
Weixinqrcode::send_news($returnmess, $message); |
||||
|
} |
||||
|
//一卡通已绑定 |
||||
|
if ($card['status'] == 2) { |
||||
|
Weixinqrcode::send_text('关注成功,请重新扫描二维码操作', $message); |
||||
|
} |
||||
|
//一卡通已禁止 |
||||
|
if ($card['status'] == 3) { |
||||
|
Weixinqrcode::send_text('抱歉,此卡已失效!', $message); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
switch ($scanrecord['type']) { |
||||
|
case 'rush': |
||||
|
$rushgoods = Rush::getSingleActive($scanrecord['cardid'] , 'name,thumb'); |
||||
|
$title = $rushgoods['name']; |
||||
|
$imgurl = $rushgoods['thumb']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 1 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
$desc = '手快有,手慢无...'; |
||||
|
break; |
||||
|
case 'wlcoupon': |
||||
|
$wlCoupon = wlCoupon::getSingleCoupons($scanrecord['cardid'] , 'title,logo'); |
||||
|
$title = $wlCoupon['title']; |
||||
|
$imgurl = $wlCoupon['logo']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 5 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
case 'wlfightgroup': |
||||
|
$group = Wlfightgroup::getSingleGood($scanrecord['cardid'] , 'name,logo'); |
||||
|
$title = $group['name']; |
||||
|
$imgurl = $group['logo']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 3 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
case 'wlgroupdetail': |
||||
|
$group = pdo_get('wlmerchant_fightgroup_group' , ['id' => $scanrecord['cardid']] , ['goodsid']); |
||||
|
$goods = Wlfightgroup::getSingleGood($group['goodsid'] , 'name,logo'); |
||||
|
$title = $goods['name']; |
||||
|
$imgurl = $goods['logo']; |
||||
|
$url = h5_url('pages/subPages/group/assemble/assemble' , ['group_id' => $scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'groupon': |
||||
|
$groupon = pdo_get('wlmerchant_groupon_activity' , ['id' => $scanrecord['cardid']] , [ |
||||
|
'name' , |
||||
|
'thumb' |
||||
|
]); |
||||
|
$title = $groupon['name']; |
||||
|
$imgurl = $groupon['thumb']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 2 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
case 'bargain': |
||||
|
$groupon = pdo_get('wlmerchant_bargain_activity' , ['id' => $scanrecord['cardid']] , [ |
||||
|
'name' , |
||||
|
'thumb' |
||||
|
]); |
||||
|
$title = $groupon['name']; |
||||
|
$imgurl = $groupon['thumb']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 7 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
case 'helpBargain': |
||||
|
$bargainuser = pdo_get('wlmerchant_bargain_userlist' , ['id' => $scanrecord['cardid']] , ['activityid']); |
||||
|
$barActivity = pdo_get('wlmerchant_bargain_activity' , ['id' => $bargainuser['activityid']] , [ |
||||
|
'name' , |
||||
|
'thumb' |
||||
|
]); |
||||
|
$title = $barActivity['name']; |
||||
|
$imgurl = $barActivity['thumb']; |
||||
|
$desc = '邀您一起砍!'; |
||||
|
$url = h5_url('pages/subPages/bargin/barginDetail/barginDetail' , ['bargin_id' => $scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'payOnline': |
||||
|
$title = '在线买单'; |
||||
|
$url = h5_url('pages/subPages2/newBuyOrder/buyOrder' , ['sid' => $scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'distribution': |
||||
|
$base = Setting::wlsetting_read('distribution'); |
||||
|
$title = $base['gztitle'] ? $base['gztitle'] : '申请分销商'; |
||||
|
$imgurl = tomedia($base['gzthumb']); |
||||
|
$desc = $base['gzdesc']; |
||||
|
$url = h5_url('pages/subPages/dealer/apply/apply'); |
||||
|
break; |
||||
|
case 'draw': |
||||
|
$activity = pdo_get('wlmerchant_draw',array('id' => $scanrecord['cardid']),array('title','share_image')); |
||||
|
$title = $activity['title']; |
||||
|
$imgurl = tomedia($activity['share_image']); |
||||
|
$desc = '快来参与吧'; |
||||
|
$url = h5_url('pages/subPages2/drawGame/drawGame',['id'=>$scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'activity': |
||||
|
$activity = pdo_get('wlmerchant_activitylist',array('id' => $scanrecord['cardid']),array('title','share_image','thumb')); |
||||
|
$title = $activity['title']; |
||||
|
$imgurl = !empty($activity['share_image'])? tomedia($activity['share_image']) : tomedia($activity['thumb']); |
||||
|
$desc = '快来报名吧'; |
||||
|
$url = h5_url('pages/subPages2/coursegoods/coursegoods',['id'=>$scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'housekeep': |
||||
|
$activity = pdo_get('wlmerchant_housekeep_service',array('id' => $scanrecord['cardid']),array('title','share_image','thumb')); |
||||
|
$title = $activity['title']; |
||||
|
$imgurl = !empty($activity['share_image'])? tomedia($activity['share_image']) : tomedia($activity['thumb']); |
||||
|
$desc = '快来看看吧'; |
||||
|
$url = h5_url('pages/subPages2/homemaking/homemakingDetails/homemakingDetails',['id'=>$scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'mobilerecharge': |
||||
|
$title = '点击充值'; |
||||
|
$desc = '欢迎使用'.$_W['wlsetting']['base']['name']; |
||||
|
$imgurl = $_W['wlsetting']['mobilerecharge']['share_image']; |
||||
|
$url = h5_url('pages/subPages2/voucherCenter/voucherCenter'); |
||||
|
break; |
||||
|
case 'integral': |
||||
|
$goods = pdo_get('wlmerchant_consumption_goods',array('id' => $scanrecord['cardid']),array('title','thumb')); |
||||
|
$title = $goods['title']; |
||||
|
$desc = '快来兑换吧'; |
||||
|
$imgurl = tomedia($goods['thumb']); |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'goodsType' => 'integral', |
||||
|
'goods_id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
default: |
||||
|
$title = $_W['wlsetting']['base']['name']; |
||||
|
$desc = '欢迎使用' . $_W['wlsetting']['base']['name']; |
||||
|
$imgurl = $_W['wlsetting']['base']['logo']; |
||||
|
$url = $scanrecord['url'] ? : h5_url('pages/mainPages/index/index'); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
//信息补充 |
||||
|
if(!$title) $title = $_W['wlsetting']['base']['name']; |
||||
|
if(!$desc) $desc = '欢迎使用'.$_W['wlsetting']['base']['name']; |
||||
|
if(!$imgurl) $imgurl = $_W['wlsetting']['base']['logo']; |
||||
|
if(!$url) $url = h5_url('pages/mainPages/index/index'); |
||||
|
|
||||
|
|
||||
|
$returnmess = array(array('title' => urlencode($title), 'description' => urlencode($desc), 'picurl' => tomedia($imgurl), 'url' => $url)); |
||||
|
Weixinqrcode::send_news($returnmess, $message); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
class Weliam_smartcity1ModuleReceiver extends WeModuleReceiver { |
||||
|
|
||||
|
public function receive() { |
||||
|
global $_W; |
||||
|
$_W['wlsetting'] = Setting::wlsetting_load(); |
||||
|
$message = $this->message; |
||||
|
$fansinfo = Member::wl_fans_info($message['from']); |
||||
|
$_W['wlmember'] = Member::wl_member_create($fansinfo, 'wechat'); |
||||
|
$_W['mid'] = $_W['wlmember']['id']; |
||||
|
file_put_contents(PATH_DATA . "receiver_qr.log", var_export($message, true) . PHP_EOL, FILE_APPEND); |
||||
|
|
||||
|
if (!empty($message['scene'])) { |
||||
|
$name = pdo_getcolumn('qrcode', array('scene_str' => $message['scene'], 'uniacid' => $_W['uniacid']), 'name'); |
||||
|
$names = explode(':', $name); |
||||
|
$plugin = (isset($names[1]) ? $names[1] : ''); |
||||
|
if (!empty($plugin)) { |
||||
|
$plugin::Processor($message); |
||||
|
} |
||||
|
} else { |
||||
|
$scanrecord = pdo_fetch("SELECT cardid,scantime,type,url FROM " |
||||
|
. tablename('wlmerchant_halfcard_qrscan') |
||||
|
. " WHERE uniacid = {$_W['uniacid']} AND openid = '{$message['from']}' order by id desc"); |
||||
|
if (!empty($scanrecord) && ($scanrecord['scantime'] + 120) > time()) { |
||||
|
//一卡通实卡 |
||||
|
if (empty($scanrecord['type'])) { |
||||
|
$card = pdo_get('wlmerchant_halfcard_realcard', array('uniacid' => $_W['uniacid'], 'id' => $scanrecord['cardid'])); |
||||
|
if (!empty($card)) { |
||||
|
//一卡通未绑定 |
||||
|
if ($card['status'] == 1) { |
||||
|
$setting = Setting::wlsetting_read('halfcard'); |
||||
|
$imgurl = $setting['cardimg'] ? $setting['cardimg'] : URL_MODULE . 'plugin/halfcard/app/resource/images/cord-bg.jpg'; |
||||
|
$returnmess = array(array('title' => urlencode("点击立即激活此卡"), 'description' => urlencode('激活此卡'), 'picurl' => tomedia($imgurl), 'url' => app_url('halfcard/halfcard_app/realcard', array('cardsn' => $card['cardsn'], 'salt' => $card['salt'])))); |
||||
|
Weixinqrcode::send_news($returnmess, $message); |
||||
|
} |
||||
|
//一卡通已绑定 |
||||
|
if ($card['status'] == 2) { |
||||
|
Weixinqrcode::send_text('关注成功,请重新扫描二维码操作', $message); |
||||
|
} |
||||
|
//一卡通已禁止 |
||||
|
if ($card['status'] == 3) { |
||||
|
Weixinqrcode::send_text('抱歉,此卡已失效!', $message); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
switch ($scanrecord['type']) { |
||||
|
case 'rush': |
||||
|
$rushgoods = Rush::getSingleActive($scanrecord['cardid'] , 'name,thumb'); |
||||
|
$title = $rushgoods['name']; |
||||
|
$imgurl = $rushgoods['thumb']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 1 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
$desc = '手快有,手慢无...'; |
||||
|
break; |
||||
|
case 'wlcoupon': |
||||
|
$wlCoupon = wlCoupon::getSingleCoupons($scanrecord['cardid'] , 'title,logo'); |
||||
|
$title = $wlCoupon['title']; |
||||
|
$imgurl = $wlCoupon['logo']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 5 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
case 'wlfightgroup': |
||||
|
$group = Wlfightgroup::getSingleGood($scanrecord['cardid'] , 'name,logo'); |
||||
|
$title = $group['name']; |
||||
|
$imgurl = $group['logo']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 3 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
case 'wlgroupdetail': |
||||
|
$group = pdo_get('wlmerchant_fightgroup_group' , ['id' => $scanrecord['cardid']] , ['goodsid']); |
||||
|
$goods = Wlfightgroup::getSingleGood($group['goodsid'] , 'name,logo'); |
||||
|
$title = $goods['name']; |
||||
|
$imgurl = $goods['logo']; |
||||
|
$url = h5_url('pages/subPages/group/assemble/assemble' , ['group_id' => $scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'groupon': |
||||
|
$groupon = pdo_get('wlmerchant_groupon_activity' , ['id' => $scanrecord['cardid']] , [ |
||||
|
'name' , |
||||
|
'thumb' |
||||
|
]); |
||||
|
$title = $groupon['name']; |
||||
|
$imgurl = $groupon['thumb']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 2 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
case 'bargain': |
||||
|
$groupon = pdo_get('wlmerchant_bargain_activity' , ['id' => $scanrecord['cardid']] , [ |
||||
|
'name' , |
||||
|
'thumb' |
||||
|
]); |
||||
|
$title = $groupon['name']; |
||||
|
$imgurl = $groupon['thumb']; |
||||
|
$url = h5_url('pages/subPages/goods/index' , [ |
||||
|
'type' => 7 , |
||||
|
'id' => $scanrecord['cardid'] |
||||
|
]); |
||||
|
break; |
||||
|
case 'helpBargain': |
||||
|
$bargainuser = pdo_get('wlmerchant_bargain_userlist' , ['id' => $scanrecord['cardid']] , ['activityid']); |
||||
|
$barActivity = pdo_get('wlmerchant_bargain_activity' , ['id' => $bargainuser['activityid']] , [ |
||||
|
'name' , |
||||
|
'thumb' |
||||
|
]); |
||||
|
$title = $barActivity['name']; |
||||
|
$imgurl = $barActivity['thumb']; |
||||
|
$desc = '邀您一起砍!'; |
||||
|
$url = h5_url('pages/subPages/bargin/barginDetail/barginDetail' , ['bargin_id' => $scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'payOnline': |
||||
|
$title = '在线买单'; |
||||
|
$url = h5_url('pages/subPages2/newBuyOrder/buyOrder' , ['sid' => $scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'distribution': |
||||
|
$base = Setting::wlsetting_read('distribution'); |
||||
|
$title = $base['gztitle'] ? $base['gztitle'] : '申请分销商'; |
||||
|
$imgurl = tomedia($base['gzthumb']); |
||||
|
$desc = $base['gzdesc']; |
||||
|
$url = h5_url('pages/subPages/dealer/apply/apply'); |
||||
|
break; |
||||
|
case 'draw': |
||||
|
$activity = pdo_get('wlmerchant_draw',array('id' => $scanrecord['cardid']),array('title','share_image')); |
||||
|
$title = $activity['title']; |
||||
|
$imgurl = tomedia($activity['share_image']); |
||||
|
$desc = '快来参与吧'; |
||||
|
$url = h5_url('pages/subPages2/drawGame/drawGame',['id'=>$scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'activity': |
||||
|
$activity = pdo_get('wlmerchant_activitylist',array('id' => $scanrecord['cardid']),array('title','share_image','thumb')); |
||||
|
$title = $activity['title']; |
||||
|
$imgurl = !empty($activity['share_image'])? tomedia($activity['share_image']) : tomedia($activity['thumb']); |
||||
|
$desc = '快来报名吧'; |
||||
|
$url = h5_url('pages/subPages2/coursegoods/coursegoods',['id'=>$scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'housekeep': |
||||
|
$activity = pdo_get('wlmerchant_housekeep_service',array('id' => $scanrecord['cardid']),array('title','share_image','thumb')); |
||||
|
$title = $activity['title']; |
||||
|
$imgurl = !empty($activity['share_image'])? tomedia($activity['share_image']) : tomedia($activity['thumb']); |
||||
|
$desc = '快来看看吧'; |
||||
|
$url = h5_url('pages/subPages2/homemaking/homemakingDetails/homemakingDetails',['id'=>$scanrecord['cardid']]); |
||||
|
break; |
||||
|
case 'mobilerecharge': |
||||
|
$title = '点击充值'; |
||||
|
$desc = '欢迎使用'.$_W['wlsetting']['base']['name']; |
||||
|
$imgurl = $_W['wlsetting']['mobilerecharge']['share_image']; |
||||
|
$url = h5_url('pages/subPages2/voucherCenter/voucherCenter'); |
||||
|
break; |
||||
|
default: |
||||
|
$title = $_W['wlsetting']['base']['name']; |
||||
|
$desc = '欢迎使用' . $_W['wlsetting']['base']['name']; |
||||
|
$imgurl = $_W['wlsetting']['base']['logo']; |
||||
|
$url = $scanrecord['url'] ? : h5_url('pages/mainPages/index/index'); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
//信息补充 |
||||
|
if(!$title) $title = $_W['wlsetting']['base']['name']; |
||||
|
if(!$desc) $desc = '欢迎使用'.$_W['wlsetting']['base']['name']; |
||||
|
if(!$imgurl) $imgurl = $_W['wlsetting']['base']['logo']; |
||||
|
if(!$url) $url = h5_url('pages/mainPages/index/index'); |
||||
|
|
||||
|
|
||||
|
$returnmess = array(array('title' => urlencode($title), 'description' => urlencode($desc), 'picurl' => tomedia($imgurl), 'url' => $url)); |
||||
|
Weixinqrcode::send_news($returnmess, $message); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,174 @@ |
|||||
|
<?php |
||||
|
defined('IN_IA') or exit('Access Denied'); |
||||
|
file_exists(__DIR__ . "/vendor/autoload.php") && require_once __DIR__ . "/vendor/autoload.php"; |
||||
|
require_once __DIR__ . "/core/common/defines.php"; |
||||
|
require_once PATH_CORE . "common/autoload.php"; |
||||
|
Func_loader::core('global'); |
||||
|
|
||||
|
class Weliam_smartcityModuleSite extends WeModuleSite { |
||||
|
|
||||
|
public function __call($name, $arguments) { |
||||
|
global $_W, $_GPC; |
||||
|
$isWeb = stripos($name, 'doWeb') === 0; |
||||
|
$isMobile = stripos($name, 'doMobile') === 0; |
||||
|
$_W['catalog'] = $catalog = !empty($isWeb) ? 'sys' : 'app'; |
||||
|
$_W['plugin'] = $plugin = !empty($_GPC['p']) ? $_GPC['p'] : 'dashboard'; |
||||
|
$_W['controller'] = $controller = !empty($_GPC['ac']) ? $_GPC['ac'] : 'dashboard'; |
||||
|
$_W['method'] = $method = !empty($_GPC['do']) ? $_GPC['do'] : 'index'; |
||||
|
$_W['wlsetting'] = Setting::wlsetting_load(); |
||||
|
$_W['wlsetting']['trade']['credittext'] = $_W['wlsetting']['trade']['credittext'] ? $_W['wlsetting']['trade']['credittext'] : '积分'; |
||||
|
$_W['wlsetting']['trade']['moneytext'] = $_W['wlsetting']['trade']['moneytext'] ? $_W['wlsetting']['trade']['moneytext'] : '余额'; |
||||
|
if (!in_array($_W['method'], array('qrcodeimg', 'Notify', 'captcha')) && !in_array($_W['controller'], array('wxapp')) && $_GPC['r'] != 'api') { |
||||
|
Func_loader::$catalog('cover'); |
||||
|
} |
||||
|
if ($isWeb || $isMobile) { |
||||
|
wl_new_method($plugin, $controller, $method, $_W['catalog']); |
||||
|
} |
||||
|
trigger_error("访问的模块 {$plugin} 不存在.", E_USER_WARNING); |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Comment: 微信自动登录 |
||||
|
*/ |
||||
|
public function doMobileWechatsign() { |
||||
|
global $_W, $_GPC; |
||||
|
$auth_userinfo = mc_oauth_userinfo(); |
||||
|
if (empty($auth_userinfo['openid'])) { |
||||
|
die('授权信息获取失败,请退出重试'); |
||||
|
} |
||||
|
$userinfo = array( |
||||
|
'uid' => intval(mc_openid2uid($auth_userinfo['openid'])), |
||||
|
'openid' => $auth_userinfo['openid'], |
||||
|
'nickname' => $auth_userinfo['nickname'], |
||||
|
'unionid' => $auth_userinfo['unionid'], |
||||
|
'avatar' => $auth_userinfo['headimgurl'] |
||||
|
); |
||||
|
$member = Member::wl_member_create($userinfo, 'wechat'); |
||||
|
$backurl = h5_url(urldecode($_GPC['vueurl'])); |
||||
|
$token = pdo_getcolumn(PDO_NAME.'login',array('token'=>$member['tokey'],'refresh_time >' => time()),'secret_key'); |
||||
|
if(empty($token)) { |
||||
|
$res = Login::generateToken($member['tokey'], 'login'); |
||||
|
$token = $res['message']; |
||||
|
} |
||||
|
wl_setcookie('user_token',$token,86400); |
||||
|
$url = $backurl ? $backurl: h5_url('pages/subPages/userCenter'); |
||||
|
|
||||
|
echo "<script>window.location=\"" . $url . "\";</script>"; |
||||
|
exit; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Comment: 通过分享链接进入平台时对链接的处理和跳转 |
||||
|
* Author: zzw |
||||
|
* Date: 2019/9/23 15:23 |
||||
|
*/ |
||||
|
public function doMobileReturnRequest() { |
||||
|
global $_W, $_GPC; |
||||
|
$link = $_GPC['link'] ?: h5_url('pages/mainPages/index/index'); |
||||
|
//链接转换操作 |
||||
|
$list = Links::getTransformationLink(); |
||||
|
foreach($list as $linkKey => $linkVal){ |
||||
|
$link = str_replace($linkKey,$linkVal,$link); |
||||
|
} |
||||
|
#1、判断link是否存在token信息,存在则删除 |
||||
|
$http = explode('?#', $link); |
||||
|
$linkArr = explode('?', $http[1]); |
||||
|
$paramsStr = $linkArr[1]; |
||||
|
$position = strpos($paramsStr, '&token=');//开始位置 |
||||
|
if ($position > 0) $linkArr[1] = substr($paramsStr, 0, $position) |
||||
|
. substr($paramsStr, 39 + strlen(substr($paramsStr, 0, $position)), strlen($paramsStr)); |
||||
|
$link = $http[0] . '?#' . $linkArr[0] . '?' . $linkArr[1]; |
||||
|
#2、规避head_id=undefined的参数 |
||||
|
$link = str_replace('&head_id=undefined', '', $link); |
||||
|
#3、跳转至分享链接 |
||||
|
|
||||
|
header('Location:' . $link); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
class Weliam_smartcity1ModuleSite extends WeModuleSite { |
||||
|
|
||||
|
public function __call($name, $arguments) { |
||||
|
global $_W, $_GPC; |
||||
|
$isWeb = stripos($name, 'doWeb') === 0; |
||||
|
$isMobile = stripos($name, 'doMobile') === 0; |
||||
|
$_W['catalog'] = $catalog = !empty($isWeb) ? 'sys' : 'app'; |
||||
|
$_W['plugin'] = $plugin = !empty($_GPC['p']) ? $_GPC['p'] : 'dashboard'; |
||||
|
$_W['controller'] = $controller = !empty($_GPC['ac']) ? $_GPC['ac'] : 'dashboard'; |
||||
|
$_W['method'] = $method = !empty($_GPC['do']) ? $_GPC['do'] : 'index'; |
||||
|
$_W['wlsetting'] = Setting::wlsetting_load(); |
||||
|
$_W['wlsetting']['trade']['credittext'] = $_W['wlsetting']['trade']['credittext'] ? $_W['wlsetting']['trade']['credittext'] : '积分'; |
||||
|
$_W['wlsetting']['trade']['moneytext'] = $_W['wlsetting']['trade']['moneytext'] ? $_W['wlsetting']['trade']['moneytext'] : '余额'; |
||||
|
if (!in_array($_W['method'], array('qrcodeimg', 'Notify', 'captcha')) && !in_array($_W['controller'], array('wxapp')) && $_GPC['r'] != 'api') { |
||||
|
Func_loader::$catalog('cover'); |
||||
|
} |
||||
|
if ($isWeb || $isMobile) { |
||||
|
wl_new_method($plugin, $controller, $method, $_W['catalog']); |
||||
|
} |
||||
|
trigger_error("访问的模块 {$plugin} 不存在.", E_USER_WARNING); |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Comment: 微信自动登录 |
||||
|
*/ |
||||
|
public function doMobileWechatsign() { |
||||
|
global $_W, $_GPC; |
||||
|
$auth_userinfo = mc_oauth_userinfo(); |
||||
|
if (empty($auth_userinfo['openid'])) { |
||||
|
die('授权信息获取失败,请退出重试'); |
||||
|
} |
||||
|
$userinfo = array( |
||||
|
'uid' => intval(mc_openid2uid($auth_userinfo['openid'])), |
||||
|
'openid' => $auth_userinfo['openid'], |
||||
|
'nickname' => $auth_userinfo['nickname'], |
||||
|
'unionid' => $auth_userinfo['unionid'], |
||||
|
'avatar' => $auth_userinfo['headimgurl'] |
||||
|
); |
||||
|
$member = Member::wl_member_create($userinfo, 'wechat'); |
||||
|
$backurl = h5_url(urldecode($_GPC['vueurl'])); |
||||
|
$token = pdo_getcolumn(PDO_NAME.'login',array('token'=>$member['tokey'],'refresh_time >' => time()),'secret_key'); |
||||
|
if(empty($token)) { |
||||
|
$res = Login::generateToken($member['tokey'], 'login'); |
||||
|
$token = $res['message']; |
||||
|
} |
||||
|
wl_setcookie('user_token',$token,86400); |
||||
|
$url = $backurl ? $backurl: h5_url('pages/subPages/userCenter'); |
||||
|
|
||||
|
echo "<script>window.location=\"" . $url . "\";</script>"; |
||||
|
exit; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Comment: 通过分享链接进入平台时对链接的处理和跳转 |
||||
|
* Author: zzw |
||||
|
* Date: 2019/9/23 15:23 |
||||
|
*/ |
||||
|
public function doMobileReturnRequest() { |
||||
|
global $_W, $_GPC; |
||||
|
$link = $_GPC['link'] ?: h5_url('pages/mainPages/index/index'); |
||||
|
//链接转换操作 |
||||
|
$list = Links::getTransformationLink(); |
||||
|
foreach($list as $linkKey => $linkVal){ |
||||
|
$link = str_replace($linkKey,$linkVal,$link); |
||||
|
} |
||||
|
#1、判断link是否存在token信息,存在则删除 |
||||
|
$http = explode('?#', $link); |
||||
|
$linkArr = explode('?', $http[1]); |
||||
|
$paramsStr = $linkArr[1]; |
||||
|
$position = strpos($paramsStr, '&token=');//开始位置 |
||||
|
if ($position > 0) $linkArr[1] = substr($paramsStr, 0, $position) |
||||
|
. substr($paramsStr, 39 + strlen(substr($paramsStr, 0, $position)), strlen($paramsStr)); |
||||
|
$link = $http[0] . '?#' . $linkArr[0] . '?' . $linkArr[1]; |
||||
|
#2、规避head_id=undefined的参数 |
||||
|
$link = str_replace('&head_id=undefined', '', $link); |
||||
|
#3、跳转至分享链接 |
||||
|
|
||||
|
header('Location:' . $link); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,96 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Contracts\EventDispatcher; |
||||
|
|
||||
|
use Psr\EventDispatcher\StoppableEventInterface; |
||||
|
|
||||
|
if (interface_exists(StoppableEventInterface::class)) { |
||||
|
/** |
||||
|
* Event is the base class for classes containing event data. |
||||
|
* |
||||
|
* This class contains no event data. It is used by events that do not pass |
||||
|
* state information to an event handler when an event is raised. |
||||
|
* |
||||
|
* You can call the method stopPropagation() to abort the execution of |
||||
|
* further listeners in your event listener. |
||||
|
* |
||||
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
|
* @author Jonathan Wage <jonwage@gmail.com> |
||||
|
* @author Roman Borschel <roman@code-factory.org> |
||||
|
* @author Bernhard Schussek <bschussek@gmail.com> |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
*/ |
||||
|
class Event implements StoppableEventInterface |
||||
|
{ |
||||
|
private $propagationStopped = false; |
||||
|
|
||||
|
/** |
||||
|
* Returns whether further event listeners should be triggered. |
||||
|
*/ |
||||
|
public function isPropagationStopped(): bool |
||||
|
{ |
||||
|
return $this->propagationStopped; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Stops the propagation of the event to further event listeners. |
||||
|
* |
||||
|
* If multiple event listeners are connected to the same event, no |
||||
|
* further event listener will be triggered once any trigger calls |
||||
|
* stopPropagation(). |
||||
|
*/ |
||||
|
public function stopPropagation(): void |
||||
|
{ |
||||
|
$this->propagationStopped = true; |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
/** |
||||
|
* Event is the base class for classes containing event data. |
||||
|
* |
||||
|
* This class contains no event data. It is used by events that do not pass |
||||
|
* state information to an event handler when an event is raised. |
||||
|
* |
||||
|
* You can call the method stopPropagation() to abort the execution of |
||||
|
* further listeners in your event listener. |
||||
|
* |
||||
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
||||
|
* @author Jonathan Wage <jonwage@gmail.com> |
||||
|
* @author Roman Borschel <roman@code-factory.org> |
||||
|
* @author Bernhard Schussek <bschussek@gmail.com> |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
*/ |
||||
|
class Event |
||||
|
{ |
||||
|
private $propagationStopped = false; |
||||
|
|
||||
|
/** |
||||
|
* Returns whether further event listeners should be triggered. |
||||
|
*/ |
||||
|
public function isPropagationStopped(): bool |
||||
|
{ |
||||
|
return $this->propagationStopped; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Stops the propagation of the event to further event listeners. |
||||
|
* |
||||
|
* If multiple event listeners are connected to the same event, no |
||||
|
* further event listener will be triggered once any trigger calls |
||||
|
* stopPropagation(). |
||||
|
*/ |
||||
|
public function stopPropagation(): void |
||||
|
{ |
||||
|
$this->propagationStopped = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Contracts\EventDispatcher; |
||||
|
|
||||
|
use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface; |
||||
|
|
||||
|
if (interface_exists(PsrEventDispatcherInterface::class)) { |
||||
|
/** |
||||
|
* Allows providing hooks on domain-specific lifecycles by dispatching events. |
||||
|
*/ |
||||
|
interface EventDispatcherInterface extends PsrEventDispatcherInterface |
||||
|
{ |
||||
|
/** |
||||
|
* Dispatches an event to all registered listeners. |
||||
|
* |
||||
|
* For BC with Symfony 4, the $eventName argument is not declared explicitly on the |
||||
|
* signature of the method. Implementations that are not bound by this BC constraint |
||||
|
* MUST declare it explicitly, as allowed by PHP. |
||||
|
* |
||||
|
* @param object $event The event to pass to the event handlers/listeners |
||||
|
* @param string|null $eventName The name of the event to dispatch. If not supplied, |
||||
|
* the class of $event should be used instead. |
||||
|
* |
||||
|
* @return object The passed $event MUST be returned |
||||
|
*/ |
||||
|
public function dispatch($event/*, string $eventName = null*/); |
||||
|
} |
||||
|
} else { |
||||
|
/** |
||||
|
* Allows providing hooks on domain-specific lifecycles by dispatching events. |
||||
|
*/ |
||||
|
interface EventDispatcherInterface |
||||
|
{ |
||||
|
/** |
||||
|
* Dispatches an event to all registered listeners. |
||||
|
* |
||||
|
* For BC with Symfony 4, the $eventName argument is not declared explicitly on the |
||||
|
* signature of the method. Implementations that are not bound by this BC constraint |
||||
|
* MUST declare it explicitly, as allowed by PHP. |
||||
|
* |
||||
|
* @param object $event The event to pass to the event handlers/listeners |
||||
|
* @param string|null $eventName The name of the event to dispatch. If not supplied, |
||||
|
* the class of $event should be used instead. |
||||
|
* |
||||
|
* @return object The passed $event MUST be returned |
||||
|
*/ |
||||
|
public function dispatch($event/*, string $eventName = null*/); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2018-2019 Fabien Potencier |
||||
|
|
||||
|
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. |
||||
@ -0,0 +1,9 @@ |
|||||
|
Symfony EventDispatcher Contracts |
||||
|
================================= |
||||
|
|
||||
|
A set of abstractions extracted out of the Symfony components. |
||||
|
|
||||
|
Can be used to build on semantics that the Symfony components proved useful - and |
||||
|
that already have battle tested implementations. |
||||
|
|
||||
|
See https://github.com/symfony/contracts/blob/master/README.md for more information. |
||||
@ -0,0 +1,34 @@ |
|||||
|
{ |
||||
|
"name": "symfony/event-dispatcher-contracts", |
||||
|
"type": "library", |
||||
|
"description": "Generic abstractions related to dispatching event", |
||||
|
"keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"], |
||||
|
"homepage": "https://symfony.com", |
||||
|
"license": "MIT", |
||||
|
"authors": [ |
||||
|
{ |
||||
|
"name": "Nicolas Grekas", |
||||
|
"email": "p@tchwork.com" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Symfony Community", |
||||
|
"homepage": "https://symfony.com/contributors" |
||||
|
} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": "^7.1.3" |
||||
|
}, |
||||
|
"suggest": { |
||||
|
"psr/event-dispatcher": "", |
||||
|
"symfony/event-dispatcher-implementation": "" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { "Symfony\\Contracts\\EventDispatcher\\": "" } |
||||
|
}, |
||||
|
"minimum-stability": "dev", |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.1-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,283 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* Copyright (c) 2014 TrueServer B.V. |
||||
|
* |
||||
|
* 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. |
||||
|
* |
||||
|
* Originally forked from |
||||
|
* https://github.com/true/php-punycode/blob/v2.1.1/src/Punycode.php |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Polyfill\Intl\Idn; |
||||
|
|
||||
|
/** |
||||
|
* Partial intl implementation in pure PHP. |
||||
|
* |
||||
|
* Implemented: |
||||
|
* - idn_to_ascii - Convert domain name to IDNA ASCII form |
||||
|
* - idn_to_utf8 - Convert domain name from IDNA ASCII to Unicode |
||||
|
* |
||||
|
* @author Renan Gonçalves <renan.saddam@gmail.com> |
||||
|
* @author Sebastian Kroczek <sk@xbug.de> |
||||
|
* @author Dmitry Lukashin <dmitry@lukashin.ru> |
||||
|
* @author Laurent Bassin <laurent@bassin.info> |
||||
|
* |
||||
|
* @internal |
||||
|
*/ |
||||
|
final class Idn |
||||
|
{ |
||||
|
const INTL_IDNA_VARIANT_2003 = 0; |
||||
|
const INTL_IDNA_VARIANT_UTS46 = 1; |
||||
|
|
||||
|
private static $encodeTable = array( |
||||
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', |
||||
|
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', |
||||
|
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
||||
|
); |
||||
|
|
||||
|
private static $decodeTable = array( |
||||
|
'a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4, 'f' => 5, |
||||
|
'g' => 6, 'h' => 7, 'i' => 8, 'j' => 9, 'k' => 10, 'l' => 11, |
||||
|
'm' => 12, 'n' => 13, 'o' => 14, 'p' => 15, 'q' => 16, 'r' => 17, |
||||
|
's' => 18, 't' => 19, 'u' => 20, 'v' => 21, 'w' => 22, 'x' => 23, |
||||
|
'y' => 24, 'z' => 25, '0' => 26, '1' => 27, '2' => 28, '3' => 29, |
||||
|
'4' => 30, '5' => 31, '6' => 32, '7' => 33, '8' => 34, '9' => 35, |
||||
|
); |
||||
|
|
||||
|
public static function idn_to_ascii($domain, $options, $variant, &$idna_info = array()) |
||||
|
{ |
||||
|
if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) { |
||||
|
@trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED); |
||||
|
} |
||||
|
|
||||
|
if (self::INTL_IDNA_VARIANT_UTS46 === $variant) { |
||||
|
$domain = mb_strtolower($domain, 'utf-8'); |
||||
|
} |
||||
|
|
||||
|
$parts = explode('.', $domain); |
||||
|
|
||||
|
foreach ($parts as $i => &$part) { |
||||
|
if ('' === $part && \count($parts) > 1 + $i) { |
||||
|
return false; |
||||
|
} |
||||
|
if (false === $part = self::encodePart($part)) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$output = implode('.', $parts); |
||||
|
|
||||
|
$idna_info = array( |
||||
|
'result' => \strlen($output) > 255 ? false : $output, |
||||
|
'isTransitionalDifferent' => false, |
||||
|
'errors' => 0, |
||||
|
); |
||||
|
|
||||
|
return $idna_info['result']; |
||||
|
} |
||||
|
|
||||
|
public static function idn_to_utf8($domain, $options, $variant, &$idna_info = array()) |
||||
|
{ |
||||
|
if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) { |
||||
|
@trigger_error('idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED); |
||||
|
} |
||||
|
|
||||
|
$parts = explode('.', $domain); |
||||
|
|
||||
|
foreach ($parts as &$part) { |
||||
|
$length = \strlen($part); |
||||
|
if ($length < 1 || 63 < $length) { |
||||
|
continue; |
||||
|
} |
||||
|
if (0 !== strpos($part, 'xn--')) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
$part = substr($part, 4); |
||||
|
$part = self::decodePart($part); |
||||
|
} |
||||
|
|
||||
|
$output = implode('.', $parts); |
||||
|
|
||||
|
$idna_info = array( |
||||
|
'result' => \strlen($output) > 255 ? false : $output, |
||||
|
'isTransitionalDifferent' => false, |
||||
|
'errors' => 0, |
||||
|
); |
||||
|
|
||||
|
return $idna_info['result']; |
||||
|
} |
||||
|
|
||||
|
private static function encodePart($input) |
||||
|
{ |
||||
|
$codePoints = self::listCodePoints($input); |
||||
|
|
||||
|
$n = 128; |
||||
|
$bias = 72; |
||||
|
$delta = 0; |
||||
|
$h = $b = \count($codePoints['basic']); |
||||
|
|
||||
|
$output = ''; |
||||
|
foreach ($codePoints['basic'] as $code) { |
||||
|
$output .= mb_chr($code, 'utf-8'); |
||||
|
} |
||||
|
if ($input === $output) { |
||||
|
return $output; |
||||
|
} |
||||
|
if ($b > 0) { |
||||
|
$output .= '-'; |
||||
|
} |
||||
|
|
||||
|
$codePoints['nonBasic'] = array_unique($codePoints['nonBasic']); |
||||
|
sort($codePoints['nonBasic']); |
||||
|
|
||||
|
$i = 0; |
||||
|
$length = mb_strlen($input, 'utf-8'); |
||||
|
while ($h < $length) { |
||||
|
$m = $codePoints['nonBasic'][$i++]; |
||||
|
$delta += ($m - $n) * ($h + 1); |
||||
|
$n = $m; |
||||
|
|
||||
|
foreach ($codePoints['all'] as $c) { |
||||
|
if ($c < $n || $c < 128) { |
||||
|
++$delta; |
||||
|
} |
||||
|
if ($c === $n) { |
||||
|
$q = $delta; |
||||
|
for ($k = 36;; $k += 36) { |
||||
|
$t = self::calculateThreshold($k, $bias); |
||||
|
if ($q < $t) { |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
$code = $t + (($q - $t) % (36 - $t)); |
||||
|
$output .= self::$encodeTable[$code]; |
||||
|
|
||||
|
$q = ($q - $t) / (36 - $t); |
||||
|
} |
||||
|
|
||||
|
$output .= self::$encodeTable[$q]; |
||||
|
$bias = self::adapt($delta, $h + 1, ($h === $b)); |
||||
|
$delta = 0; |
||||
|
++$h; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
++$delta; |
||||
|
++$n; |
||||
|
} |
||||
|
|
||||
|
$output = 'xn--'.$output; |
||||
|
|
||||
|
return \strlen($output) < 1 || 63 < \strlen($output) ? false : strtolower($output); |
||||
|
} |
||||
|
|
||||
|
private static function listCodePoints($input) |
||||
|
{ |
||||
|
$codePoints = array( |
||||
|
'all' => array(), |
||||
|
'basic' => array(), |
||||
|
'nonBasic' => array(), |
||||
|
); |
||||
|
|
||||
|
$length = mb_strlen($input, 'utf-8'); |
||||
|
for ($i = 0; $i < $length; ++$i) { |
||||
|
$char = mb_substr($input, $i, 1, 'utf-8'); |
||||
|
$code = mb_ord($char, 'utf-8'); |
||||
|
if ($code < 128) { |
||||
|
$codePoints['all'][] = $codePoints['basic'][] = $code; |
||||
|
} else { |
||||
|
$codePoints['all'][] = $codePoints['nonBasic'][] = $code; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $codePoints; |
||||
|
} |
||||
|
|
||||
|
private static function calculateThreshold($k, $bias) |
||||
|
{ |
||||
|
if ($k <= $bias + 1) { |
||||
|
return 1; |
||||
|
} |
||||
|
if ($k >= $bias + 26) { |
||||
|
return 26; |
||||
|
} |
||||
|
|
||||
|
return $k - $bias; |
||||
|
} |
||||
|
|
||||
|
private static function adapt($delta, $numPoints, $firstTime) |
||||
|
{ |
||||
|
$delta = (int) ($firstTime ? $delta / 700 : $delta / 2); |
||||
|
$delta += (int) ($delta / $numPoints); |
||||
|
|
||||
|
$k = 0; |
||||
|
while ($delta > 35 * 13) { |
||||
|
$delta = (int) ($delta / 35); |
||||
|
$k = $k + 36; |
||||
|
} |
||||
|
|
||||
|
return $k + (int) (36 * $delta / ($delta + 38)); |
||||
|
} |
||||
|
|
||||
|
private static function decodePart($input) |
||||
|
{ |
||||
|
$n = 128; |
||||
|
$i = 0; |
||||
|
$bias = 72; |
||||
|
$output = ''; |
||||
|
|
||||
|
$pos = strrpos($input, '-'); |
||||
|
if (false !== $pos) { |
||||
|
$output = substr($input, 0, $pos++); |
||||
|
} else { |
||||
|
$pos = 0; |
||||
|
} |
||||
|
|
||||
|
$outputLength = \strlen($output); |
||||
|
$inputLength = \strlen($input); |
||||
|
|
||||
|
while ($pos < $inputLength) { |
||||
|
$oldi = $i; |
||||
|
$w = 1; |
||||
|
|
||||
|
for ($k = 36;; $k += 36) { |
||||
|
$digit = self::$decodeTable[$input[$pos++]]; |
||||
|
$i += $digit * $w; |
||||
|
$t = self::calculateThreshold($k, $bias); |
||||
|
|
||||
|
if ($digit < $t) { |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
$w *= 36 - $t; |
||||
|
} |
||||
|
|
||||
|
$bias = self::adapt($i - $oldi, ++$outputLength, 0 === $oldi); |
||||
|
$n = $n + (int) ($i / $outputLength); |
||||
|
$i = $i % $outputLength; |
||||
|
$output = mb_substr($output, 0, $i, 'utf-8').mb_chr($n, 'utf-8').mb_substr($output, $i, $outputLength - 1, 'utf-8'); |
||||
|
|
||||
|
++$i; |
||||
|
} |
||||
|
|
||||
|
return $output; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2018-2019 Fabien Potencier |
||||
|
|
||||
|
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. |
||||
@ -0,0 +1,12 @@ |
|||||
|
Symfony Polyfill / Intl: Idn |
||||
|
============================ |
||||
|
|
||||
|
This component provides `idn_to_ascii` and `idn_to_utf8` functions to users who run php versions without the intl extension. |
||||
|
|
||||
|
More information can be found in the |
||||
|
[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). |
||||
|
|
||||
|
License |
||||
|
======= |
||||
|
|
||||
|
This library is released under the [MIT license](LICENSE). |
||||
@ -0,0 +1,59 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
use Symfony\Polyfill\Intl\Idn as p; |
||||
|
|
||||
|
if (!function_exists('idn_to_ascii')) { |
||||
|
define('U_IDNA_PROHIBITED_ERROR', 66560); |
||||
|
define('U_IDNA_ERROR_START', 66560); |
||||
|
define('U_IDNA_UNASSIGNED_ERROR', 66561); |
||||
|
define('U_IDNA_CHECK_BIDI_ERROR', 66562); |
||||
|
define('U_IDNA_STD3_ASCII_RULES_ERROR', 66563); |
||||
|
define('U_IDNA_ACE_PREFIX_ERROR', 66564); |
||||
|
define('U_IDNA_VERIFICATION_ERROR', 66565); |
||||
|
define('U_IDNA_LABEL_TOO_LONG_ERROR', 66566); |
||||
|
define('U_IDNA_ZERO_LENGTH_LABEL_ERROR', 66567); |
||||
|
define('U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR', 66568); |
||||
|
define('U_IDNA_ERROR_LIMIT', 66569); |
||||
|
define('U_STRINGPREP_PROHIBITED_ERROR', 66560); |
||||
|
define('U_STRINGPREP_UNASSIGNED_ERROR', 66561); |
||||
|
define('U_STRINGPREP_CHECK_BIDI_ERROR', 66562); |
||||
|
define('IDNA_DEFAULT', 0); |
||||
|
define('IDNA_ALLOW_UNASSIGNED', 1); |
||||
|
define('IDNA_USE_STD3_RULES', 2); |
||||
|
define('IDNA_CHECK_BIDI', 4); |
||||
|
define('IDNA_CHECK_CONTEXTJ', 8); |
||||
|
define('IDNA_NONTRANSITIONAL_TO_ASCII', 16); |
||||
|
define('IDNA_NONTRANSITIONAL_TO_UNICODE', 32); |
||||
|
define('INTL_IDNA_VARIANT_2003', 0); |
||||
|
define('INTL_IDNA_VARIANT_UTS46', 1); |
||||
|
define('IDNA_ERROR_EMPTY_LABEL', 1); |
||||
|
define('IDNA_ERROR_LABEL_TOO_LONG', 2); |
||||
|
define('IDNA_ERROR_DOMAIN_NAME_TOO_LONG', 4); |
||||
|
define('IDNA_ERROR_LEADING_HYPHEN', 8); |
||||
|
define('IDNA_ERROR_TRAILING_HYPHEN', 16); |
||||
|
define('IDNA_ERROR_HYPHEN_3_4', 32); |
||||
|
define('IDNA_ERROR_LEADING_COMBINING_MARK', 64); |
||||
|
define('IDNA_ERROR_DISALLOWED', 128); |
||||
|
define('IDNA_ERROR_PUNYCODE', 256); |
||||
|
define('IDNA_ERROR_LABEL_HAS_DOT', 512); |
||||
|
define('IDNA_ERROR_INVALID_ACE_LABEL', 1024); |
||||
|
define('IDNA_ERROR_BIDI', 2048); |
||||
|
define('IDNA_ERROR_CONTEXTJ', 4096); |
||||
|
|
||||
|
if (PHP_VERSION_ID < 70400) { |
||||
|
function idn_to_ascii($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_2003, &$idna_info = array()) { return p\Idn::idn_to_ascii($domain, $options, $variant, $idna_info); } |
||||
|
function idn_to_utf8($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_2003, &$idna_info = array()) { return p\Idn::idn_to_utf8($domain, $options, $variant, $idna_info); } |
||||
|
} else { |
||||
|
function idn_to_ascii($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { return p\Idn::idn_to_ascii($domain, $options, $variant, $idna_info); } |
||||
|
function idn_to_utf8($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { return p\Idn::idn_to_utf8($domain, $options, $variant, $idna_info); } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
{ |
||||
|
"name": "symfony/polyfill-intl-idn", |
||||
|
"type": "library", |
||||
|
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", |
||||
|
"keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "idn"], |
||||
|
"homepage": "https://symfony.com", |
||||
|
"license": "MIT", |
||||
|
"authors": [ |
||||
|
{ |
||||
|
"name": "Laurent Bassin", |
||||
|
"email": "laurent@bassin.info" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Symfony Community", |
||||
|
"homepage": "https://symfony.com/contributors" |
||||
|
} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": ">=5.3.3", |
||||
|
"symfony/polyfill-mbstring": "^1.3", |
||||
|
"symfony/polyfill-php72": "^1.9" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { "Symfony\\Polyfill\\Intl\\Idn\\": "" }, |
||||
|
"files": [ "bootstrap.php" ] |
||||
|
}, |
||||
|
"suggest": { |
||||
|
"ext-intl": "For best performance" |
||||
|
}, |
||||
|
"minimum-stability": "dev", |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.12-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2015-2019 Fabien Potencier |
||||
|
|
||||
|
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. |
||||
@ -0,0 +1,829 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Polyfill\Mbstring; |
||||
|
|
||||
|
/** |
||||
|
* Partial mbstring implementation in PHP, iconv based, UTF-8 centric. |
||||
|
* |
||||
|
* Implemented: |
||||
|
* - mb_chr - Returns a specific character from its Unicode code point |
||||
|
* - mb_convert_encoding - Convert character encoding |
||||
|
* - mb_convert_variables - Convert character code in variable(s) |
||||
|
* - mb_decode_mimeheader - Decode string in MIME header field |
||||
|
* - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED |
||||
|
* - mb_decode_numericentity - Decode HTML numeric string reference to character |
||||
|
* - mb_encode_numericentity - Encode character to HTML numeric string reference |
||||
|
* - mb_convert_case - Perform case folding on a string |
||||
|
* - mb_detect_encoding - Detect character encoding |
||||
|
* - mb_get_info - Get internal settings of mbstring |
||||
|
* - mb_http_input - Detect HTTP input character encoding |
||||
|
* - mb_http_output - Set/Get HTTP output character encoding |
||||
|
* - mb_internal_encoding - Set/Get internal character encoding |
||||
|
* - mb_list_encodings - Returns an array of all supported encodings |
||||
|
* - mb_ord - Returns the Unicode code point of a character |
||||
|
* - mb_output_handler - Callback function converts character encoding in output buffer |
||||
|
* - mb_scrub - Replaces ill-formed byte sequences with substitute characters |
||||
|
* - mb_strlen - Get string length |
||||
|
* - mb_strpos - Find position of first occurrence of string in a string |
||||
|
* - mb_strrpos - Find position of last occurrence of a string in a string |
||||
|
* - mb_str_split - Convert a string to an array |
||||
|
* - mb_strtolower - Make a string lowercase |
||||
|
* - mb_strtoupper - Make a string uppercase |
||||
|
* - mb_substitute_character - Set/Get substitution character |
||||
|
* - mb_substr - Get part of string |
||||
|
* - mb_stripos - Finds position of first occurrence of a string within another, case insensitive |
||||
|
* - mb_stristr - Finds first occurrence of a string within another, case insensitive |
||||
|
* - mb_strrchr - Finds the last occurrence of a character in a string within another |
||||
|
* - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive |
||||
|
* - mb_strripos - Finds position of last occurrence of a string within another, case insensitive |
||||
|
* - mb_strstr - Finds first occurrence of a string within another |
||||
|
* - mb_strwidth - Return width of string |
||||
|
* - mb_substr_count - Count the number of substring occurrences |
||||
|
* |
||||
|
* Not implemented: |
||||
|
* - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) |
||||
|
* - mb_ereg_* - Regular expression with multibyte support |
||||
|
* - mb_parse_str - Parse GET/POST/COOKIE data and set global variable |
||||
|
* - mb_preferred_mime_name - Get MIME charset string |
||||
|
* - mb_regex_encoding - Returns current encoding for multibyte regex as string |
||||
|
* - mb_regex_set_options - Set/Get the default options for mbregex functions |
||||
|
* - mb_send_mail - Send encoded mail |
||||
|
* - mb_split - Split multibyte string using regular expression |
||||
|
* - mb_strcut - Get part of string |
||||
|
* - mb_strimwidth - Get truncated string with specified width |
||||
|
* |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
* |
||||
|
* @internal |
||||
|
*/ |
||||
|
final class Mbstring |
||||
|
{ |
||||
|
const MB_CASE_FOLD = PHP_INT_MAX; |
||||
|
|
||||
|
private static $encodingList = array('ASCII', 'UTF-8'); |
||||
|
private static $language = 'neutral'; |
||||
|
private static $internalEncoding = 'UTF-8'; |
||||
|
private static $caseFold = array( |
||||
|
array('µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"), |
||||
|
array('μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'), |
||||
|
); |
||||
|
|
||||
|
public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) |
||||
|
{ |
||||
|
if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { |
||||
|
$fromEncoding = self::mb_detect_encoding($s, $fromEncoding); |
||||
|
} else { |
||||
|
$fromEncoding = self::getEncoding($fromEncoding); |
||||
|
} |
||||
|
|
||||
|
$toEncoding = self::getEncoding($toEncoding); |
||||
|
|
||||
|
if ('BASE64' === $fromEncoding) { |
||||
|
$s = base64_decode($s); |
||||
|
$fromEncoding = $toEncoding; |
||||
|
} |
||||
|
|
||||
|
if ('BASE64' === $toEncoding) { |
||||
|
return base64_encode($s); |
||||
|
} |
||||
|
|
||||
|
if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { |
||||
|
if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { |
||||
|
$fromEncoding = 'Windows-1252'; |
||||
|
} |
||||
|
if ('UTF-8' !== $fromEncoding) { |
||||
|
$s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); |
||||
|
} |
||||
|
|
||||
|
return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); |
||||
|
} |
||||
|
|
||||
|
if ('HTML-ENTITIES' === $fromEncoding) { |
||||
|
$s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); |
||||
|
$fromEncoding = 'UTF-8'; |
||||
|
} |
||||
|
|
||||
|
return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); |
||||
|
} |
||||
|
|
||||
|
public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) |
||||
|
{ |
||||
|
$vars = array(&$a, &$b, &$c, &$d, &$e, &$f); |
||||
|
|
||||
|
$ok = true; |
||||
|
array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { |
||||
|
if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { |
||||
|
$ok = false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return $ok ? $fromEncoding : false; |
||||
|
} |
||||
|
|
||||
|
public static function mb_decode_mimeheader($s) |
||||
|
{ |
||||
|
return iconv_mime_decode($s, 2, self::$internalEncoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) |
||||
|
{ |
||||
|
trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); |
||||
|
} |
||||
|
|
||||
|
public static function mb_decode_numericentity($s, $convmap, $encoding = null) |
||||
|
{ |
||||
|
if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { |
||||
|
trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (!\is_array($convmap) || !$convmap) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (null !== $encoding && !\is_scalar($encoding)) { |
||||
|
trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING); |
||||
|
|
||||
|
return ''; // Instead of null (cf. mb_encode_numericentity). |
||||
|
} |
||||
|
|
||||
|
$s = (string) $s; |
||||
|
if ('' === $s) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
|
||||
|
if ('UTF-8' === $encoding) { |
||||
|
$encoding = null; |
||||
|
if (!preg_match('//u', $s)) { |
||||
|
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); |
||||
|
} |
||||
|
} else { |
||||
|
$s = iconv($encoding, 'UTF-8//IGNORE', $s); |
||||
|
} |
||||
|
|
||||
|
$cnt = floor(\count($convmap) / 4) * 4; |
||||
|
|
||||
|
for ($i = 0; $i < $cnt; $i += 4) { |
||||
|
// collector_decode_htmlnumericentity ignores $convmap[$i + 3] |
||||
|
$convmap[$i] += $convmap[$i + 2]; |
||||
|
$convmap[$i + 1] += $convmap[$i + 2]; |
||||
|
} |
||||
|
|
||||
|
$s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { |
||||
|
$c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; |
||||
|
for ($i = 0; $i < $cnt; $i += 4) { |
||||
|
if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { |
||||
|
return Mbstring::mb_chr($c - $convmap[$i + 2]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $m[0]; |
||||
|
}, $s); |
||||
|
|
||||
|
if (null === $encoding) { |
||||
|
return $s; |
||||
|
} |
||||
|
|
||||
|
return iconv('UTF-8', $encoding.'//IGNORE', $s); |
||||
|
} |
||||
|
|
||||
|
public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) |
||||
|
{ |
||||
|
if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { |
||||
|
trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (!\is_array($convmap) || !$convmap) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (null !== $encoding && !\is_scalar($encoding)) { |
||||
|
trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING); |
||||
|
|
||||
|
return null; // Instead of '' (cf. mb_decode_numericentity). |
||||
|
} |
||||
|
|
||||
|
if (null !== $is_hex && !\is_scalar($is_hex)) { |
||||
|
trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', E_USER_WARNING); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
$s = (string) $s; |
||||
|
if ('' === $s) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
|
||||
|
if ('UTF-8' === $encoding) { |
||||
|
$encoding = null; |
||||
|
if (!preg_match('//u', $s)) { |
||||
|
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); |
||||
|
} |
||||
|
} else { |
||||
|
$s = iconv($encoding, 'UTF-8//IGNORE', $s); |
||||
|
} |
||||
|
|
||||
|
static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); |
||||
|
|
||||
|
$cnt = floor(\count($convmap) / 4) * 4; |
||||
|
$i = 0; |
||||
|
$len = \strlen($s); |
||||
|
$result = ''; |
||||
|
|
||||
|
while ($i < $len) { |
||||
|
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; |
||||
|
$uchr = substr($s, $i, $ulen); |
||||
|
$i += $ulen; |
||||
|
$c = self::mb_ord($uchr); |
||||
|
|
||||
|
for ($j = 0; $j < $cnt; $j += 4) { |
||||
|
if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { |
||||
|
$cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3]; |
||||
|
$result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';'; |
||||
|
continue 2; |
||||
|
} |
||||
|
} |
||||
|
$result .= $uchr; |
||||
|
} |
||||
|
|
||||
|
if (null === $encoding) { |
||||
|
return $result; |
||||
|
} |
||||
|
|
||||
|
return iconv('UTF-8', $encoding.'//IGNORE', $result); |
||||
|
} |
||||
|
|
||||
|
public static function mb_convert_case($s, $mode, $encoding = null) |
||||
|
{ |
||||
|
$s = (string) $s; |
||||
|
if ('' === $s) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
|
||||
|
if ('UTF-8' === $encoding) { |
||||
|
$encoding = null; |
||||
|
if (!preg_match('//u', $s)) { |
||||
|
$s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); |
||||
|
} |
||||
|
} else { |
||||
|
$s = iconv($encoding, 'UTF-8//IGNORE', $s); |
||||
|
} |
||||
|
|
||||
|
if (MB_CASE_TITLE == $mode) { |
||||
|
static $titleRegexp = null; |
||||
|
if (null === $titleRegexp) { |
||||
|
$titleRegexp = self::getData('titleCaseRegexp'); |
||||
|
} |
||||
|
$s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $s); |
||||
|
} else { |
||||
|
if (MB_CASE_UPPER == $mode) { |
||||
|
static $upper = null; |
||||
|
if (null === $upper) { |
||||
|
$upper = self::getData('upperCase'); |
||||
|
} |
||||
|
$map = $upper; |
||||
|
} else { |
||||
|
if (self::MB_CASE_FOLD === $mode) { |
||||
|
$s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); |
||||
|
} |
||||
|
|
||||
|
static $lower = null; |
||||
|
if (null === $lower) { |
||||
|
$lower = self::getData('lowerCase'); |
||||
|
} |
||||
|
$map = $lower; |
||||
|
} |
||||
|
|
||||
|
static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); |
||||
|
|
||||
|
$i = 0; |
||||
|
$len = \strlen($s); |
||||
|
|
||||
|
while ($i < $len) { |
||||
|
$ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; |
||||
|
$uchr = substr($s, $i, $ulen); |
||||
|
$i += $ulen; |
||||
|
|
||||
|
if (isset($map[$uchr])) { |
||||
|
$uchr = $map[$uchr]; |
||||
|
$nlen = \strlen($uchr); |
||||
|
|
||||
|
if ($nlen == $ulen) { |
||||
|
$nlen = $i; |
||||
|
do { |
||||
|
$s[--$nlen] = $uchr[--$ulen]; |
||||
|
} while ($ulen); |
||||
|
} else { |
||||
|
$s = substr_replace($s, $uchr, $i - $ulen, $ulen); |
||||
|
$len += $nlen - $ulen; |
||||
|
$i += $nlen - $ulen; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (null === $encoding) { |
||||
|
return $s; |
||||
|
} |
||||
|
|
||||
|
return iconv('UTF-8', $encoding.'//IGNORE', $s); |
||||
|
} |
||||
|
|
||||
|
public static function mb_internal_encoding($encoding = null) |
||||
|
{ |
||||
|
if (null === $encoding) { |
||||
|
return self::$internalEncoding; |
||||
|
} |
||||
|
|
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
|
||||
|
if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { |
||||
|
self::$internalEncoding = $encoding; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static function mb_language($lang = null) |
||||
|
{ |
||||
|
if (null === $lang) { |
||||
|
return self::$language; |
||||
|
} |
||||
|
|
||||
|
switch ($lang = strtolower($lang)) { |
||||
|
case 'uni': |
||||
|
case 'neutral': |
||||
|
self::$language = $lang; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static function mb_list_encodings() |
||||
|
{ |
||||
|
return array('UTF-8'); |
||||
|
} |
||||
|
|
||||
|
public static function mb_encoding_aliases($encoding) |
||||
|
{ |
||||
|
switch (strtoupper($encoding)) { |
||||
|
case 'UTF8': |
||||
|
case 'UTF-8': |
||||
|
return array('utf8'); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static function mb_check_encoding($var = null, $encoding = null) |
||||
|
{ |
||||
|
if (null === $encoding) { |
||||
|
if (null === $var) { |
||||
|
return false; |
||||
|
} |
||||
|
$encoding = self::$internalEncoding; |
||||
|
} |
||||
|
|
||||
|
return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); |
||||
|
} |
||||
|
|
||||
|
public static function mb_detect_encoding($str, $encodingList = null, $strict = false) |
||||
|
{ |
||||
|
if (null === $encodingList) { |
||||
|
$encodingList = self::$encodingList; |
||||
|
} else { |
||||
|
if (!\is_array($encodingList)) { |
||||
|
$encodingList = array_map('trim', explode(',', $encodingList)); |
||||
|
} |
||||
|
$encodingList = array_map('strtoupper', $encodingList); |
||||
|
} |
||||
|
|
||||
|
foreach ($encodingList as $enc) { |
||||
|
switch ($enc) { |
||||
|
case 'ASCII': |
||||
|
if (!preg_match('/[\x80-\xFF]/', $str)) { |
||||
|
return $enc; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case 'UTF8': |
||||
|
case 'UTF-8': |
||||
|
if (preg_match('//u', $str)) { |
||||
|
return 'UTF-8'; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
if (0 === strncmp($enc, 'ISO-8859-', 9)) { |
||||
|
return $enc; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static function mb_detect_order($encodingList = null) |
||||
|
{ |
||||
|
if (null === $encodingList) { |
||||
|
return self::$encodingList; |
||||
|
} |
||||
|
|
||||
|
if (!\is_array($encodingList)) { |
||||
|
$encodingList = array_map('trim', explode(',', $encodingList)); |
||||
|
} |
||||
|
$encodingList = array_map('strtoupper', $encodingList); |
||||
|
|
||||
|
foreach ($encodingList as $enc) { |
||||
|
switch ($enc) { |
||||
|
default: |
||||
|
if (strncmp($enc, 'ISO-8859-', 9)) { |
||||
|
return false; |
||||
|
} |
||||
|
// no break |
||||
|
case 'ASCII': |
||||
|
case 'UTF8': |
||||
|
case 'UTF-8': |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
self::$encodingList = $encodingList; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public static function mb_strlen($s, $encoding = null) |
||||
|
{ |
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
if ('CP850' === $encoding || 'ASCII' === $encoding) { |
||||
|
return \strlen($s); |
||||
|
} |
||||
|
|
||||
|
return @iconv_strlen($s, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) |
||||
|
{ |
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
if ('CP850' === $encoding || 'ASCII' === $encoding) { |
||||
|
return strpos($haystack, $needle, $offset); |
||||
|
} |
||||
|
|
||||
|
$needle = (string) $needle; |
||||
|
if ('' === $needle) { |
||||
|
trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return iconv_strpos($haystack, $needle, $offset, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) |
||||
|
{ |
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
if ('CP850' === $encoding || 'ASCII' === $encoding) { |
||||
|
return strrpos($haystack, $needle, $offset); |
||||
|
} |
||||
|
|
||||
|
if ($offset != (int) $offset) { |
||||
|
$offset = 0; |
||||
|
} elseif ($offset = (int) $offset) { |
||||
|
if ($offset < 0) { |
||||
|
$haystack = self::mb_substr($haystack, 0, $offset, $encoding); |
||||
|
$offset = 0; |
||||
|
} else { |
||||
|
$haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$pos = iconv_strrpos($haystack, $needle, $encoding); |
||||
|
|
||||
|
return false !== $pos ? $offset + $pos : false; |
||||
|
} |
||||
|
|
||||
|
public static function mb_str_split($string, $split_length = 1, $encoding = null) |
||||
|
{ |
||||
|
if (null !== $string && !\is_scalar($string) && !(\is_object($string) && \method_exists($string, '__toString'))) { |
||||
|
trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', E_USER_WARNING); |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if ($split_length < 1) { |
||||
|
trigger_error('The length of each segment must be greater than zero', E_USER_WARNING); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (null === $encoding) { |
||||
|
$encoding = mb_internal_encoding(); |
||||
|
} |
||||
|
|
||||
|
$result = array(); |
||||
|
$length = mb_strlen($string, $encoding); |
||||
|
|
||||
|
for ($i = 0; $i < $length; $i += $split_length) { |
||||
|
$result[] = mb_substr($string, $i, $split_length, $encoding); |
||||
|
} |
||||
|
|
||||
|
return $result; |
||||
|
} |
||||
|
|
||||
|
public static function mb_strtolower($s, $encoding = null) |
||||
|
{ |
||||
|
return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_strtoupper($s, $encoding = null) |
||||
|
{ |
||||
|
return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_substitute_character($c = null) |
||||
|
{ |
||||
|
if (0 === strcasecmp($c, 'none')) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return null !== $c ? false : 'none'; |
||||
|
} |
||||
|
|
||||
|
public static function mb_substr($s, $start, $length = null, $encoding = null) |
||||
|
{ |
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
if ('CP850' === $encoding || 'ASCII' === $encoding) { |
||||
|
return (string) substr($s, $start, null === $length ? 2147483647 : $length); |
||||
|
} |
||||
|
|
||||
|
if ($start < 0) { |
||||
|
$start = iconv_strlen($s, $encoding) + $start; |
||||
|
if ($start < 0) { |
||||
|
$start = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (null === $length) { |
||||
|
$length = 2147483647; |
||||
|
} elseif ($length < 0) { |
||||
|
$length = iconv_strlen($s, $encoding) + $length - $start; |
||||
|
if ($length < 0) { |
||||
|
return ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return (string) iconv_substr($s, $start, $length, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) |
||||
|
{ |
||||
|
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); |
||||
|
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); |
||||
|
|
||||
|
return self::mb_strpos($haystack, $needle, $offset, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) |
||||
|
{ |
||||
|
$pos = self::mb_stripos($haystack, $needle, 0, $encoding); |
||||
|
|
||||
|
return self::getSubpart($pos, $part, $haystack, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) |
||||
|
{ |
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
if ('CP850' === $encoding || 'ASCII' === $encoding) { |
||||
|
return strrchr($haystack, $needle, $part); |
||||
|
} |
||||
|
$needle = self::mb_substr($needle, 0, 1, $encoding); |
||||
|
$pos = iconv_strrpos($haystack, $needle, $encoding); |
||||
|
|
||||
|
return self::getSubpart($pos, $part, $haystack, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) |
||||
|
{ |
||||
|
$needle = self::mb_substr($needle, 0, 1, $encoding); |
||||
|
$pos = self::mb_strripos($haystack, $needle, $encoding); |
||||
|
|
||||
|
return self::getSubpart($pos, $part, $haystack, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) |
||||
|
{ |
||||
|
$haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); |
||||
|
$needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); |
||||
|
|
||||
|
return self::mb_strrpos($haystack, $needle, $offset, $encoding); |
||||
|
} |
||||
|
|
||||
|
public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) |
||||
|
{ |
||||
|
$pos = strpos($haystack, $needle); |
||||
|
if (false === $pos) { |
||||
|
return false; |
||||
|
} |
||||
|
if ($part) { |
||||
|
return substr($haystack, 0, $pos); |
||||
|
} |
||||
|
|
||||
|
return substr($haystack, $pos); |
||||
|
} |
||||
|
|
||||
|
public static function mb_get_info($type = 'all') |
||||
|
{ |
||||
|
$info = array( |
||||
|
'internal_encoding' => self::$internalEncoding, |
||||
|
'http_output' => 'pass', |
||||
|
'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', |
||||
|
'func_overload' => 0, |
||||
|
'func_overload_list' => 'no overload', |
||||
|
'mail_charset' => 'UTF-8', |
||||
|
'mail_header_encoding' => 'BASE64', |
||||
|
'mail_body_encoding' => 'BASE64', |
||||
|
'illegal_chars' => 0, |
||||
|
'encoding_translation' => 'Off', |
||||
|
'language' => self::$language, |
||||
|
'detect_order' => self::$encodingList, |
||||
|
'substitute_character' => 'none', |
||||
|
'strict_detection' => 'Off', |
||||
|
); |
||||
|
|
||||
|
if ('all' === $type) { |
||||
|
return $info; |
||||
|
} |
||||
|
if (isset($info[$type])) { |
||||
|
return $info[$type]; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static function mb_http_input($type = '') |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static function mb_http_output($encoding = null) |
||||
|
{ |
||||
|
return null !== $encoding ? 'pass' === $encoding : 'pass'; |
||||
|
} |
||||
|
|
||||
|
public static function mb_strwidth($s, $encoding = null) |
||||
|
{ |
||||
|
$encoding = self::getEncoding($encoding); |
||||
|
|
||||
|
if ('UTF-8' !== $encoding) { |
||||
|
$s = iconv($encoding, 'UTF-8//IGNORE', $s); |
||||
|
} |
||||
|
|
||||
|
$s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); |
||||
|
|
||||
|
return ($wide << 1) + iconv_strlen($s, 'UTF-8'); |
||||
|
} |
||||
|
|
||||
|
public static function mb_substr_count($haystack, $needle, $encoding = null) |
||||
|
{ |
||||
|
return substr_count($haystack, $needle); |
||||
|
} |
||||
|
|
||||
|
public static function mb_output_handler($contents, $status) |
||||
|
{ |
||||
|
return $contents; |
||||
|
} |
||||
|
|
||||
|
public static function mb_chr($code, $encoding = null) |
||||
|
{ |
||||
|
if (0x80 > $code %= 0x200000) { |
||||
|
$s = \chr($code); |
||||
|
} elseif (0x800 > $code) { |
||||
|
$s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); |
||||
|
} elseif (0x10000 > $code) { |
||||
|
$s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); |
||||
|
} else { |
||||
|
$s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); |
||||
|
} |
||||
|
|
||||
|
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { |
||||
|
$s = mb_convert_encoding($s, $encoding, 'UTF-8'); |
||||
|
} |
||||
|
|
||||
|
return $s; |
||||
|
} |
||||
|
|
||||
|
public static function mb_ord($s, $encoding = null) |
||||
|
{ |
||||
|
if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { |
||||
|
$s = mb_convert_encoding($s, 'UTF-8', $encoding); |
||||
|
} |
||||
|
|
||||
|
if (1 === \strlen($s)) { |
||||
|
return \ord($s); |
||||
|
} |
||||
|
|
||||
|
$code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; |
||||
|
if (0xF0 <= $code) { |
||||
|
return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; |
||||
|
} |
||||
|
if (0xE0 <= $code) { |
||||
|
return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; |
||||
|
} |
||||
|
if (0xC0 <= $code) { |
||||
|
return (($code - 0xC0) << 6) + $s[2] - 0x80; |
||||
|
} |
||||
|
|
||||
|
return $code; |
||||
|
} |
||||
|
|
||||
|
private static function getSubpart($pos, $part, $haystack, $encoding) |
||||
|
{ |
||||
|
if (false === $pos) { |
||||
|
return false; |
||||
|
} |
||||
|
if ($part) { |
||||
|
return self::mb_substr($haystack, 0, $pos, $encoding); |
||||
|
} |
||||
|
|
||||
|
return self::mb_substr($haystack, $pos, null, $encoding); |
||||
|
} |
||||
|
|
||||
|
private static function html_encoding_callback(array $m) |
||||
|
{ |
||||
|
$i = 1; |
||||
|
$entities = ''; |
||||
|
$m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); |
||||
|
|
||||
|
while (isset($m[$i])) { |
||||
|
if (0x80 > $m[$i]) { |
||||
|
$entities .= \chr($m[$i++]); |
||||
|
continue; |
||||
|
} |
||||
|
if (0xF0 <= $m[$i]) { |
||||
|
$c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; |
||||
|
} elseif (0xE0 <= $m[$i]) { |
||||
|
$c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; |
||||
|
} else { |
||||
|
$c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; |
||||
|
} |
||||
|
|
||||
|
$entities .= '&#'.$c.';'; |
||||
|
} |
||||
|
|
||||
|
return $entities; |
||||
|
} |
||||
|
|
||||
|
private static function title_case(array $s) |
||||
|
{ |
||||
|
return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, 'UTF-8'); |
||||
|
} |
||||
|
|
||||
|
private static function getData($file) |
||||
|
{ |
||||
|
if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { |
||||
|
return require $file; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private static function getEncoding($encoding) |
||||
|
{ |
||||
|
if (null === $encoding) { |
||||
|
return self::$internalEncoding; |
||||
|
} |
||||
|
|
||||
|
$encoding = strtoupper($encoding); |
||||
|
|
||||
|
if ('8BIT' === $encoding || 'BINARY' === $encoding) { |
||||
|
return 'CP850'; |
||||
|
} |
||||
|
if ('UTF8' === $encoding) { |
||||
|
return 'UTF-8'; |
||||
|
} |
||||
|
|
||||
|
return $encoding; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
Symfony Polyfill / Mbstring |
||||
|
=========================== |
||||
|
|
||||
|
This component provides a partial, native PHP implementation for the |
||||
|
[Mbstring](http://php.net/mbstring) extension. |
||||
|
|
||||
|
More information can be found in the |
||||
|
[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). |
||||
|
|
||||
|
License |
||||
|
======= |
||||
|
|
||||
|
This library is released under the [MIT license](LICENSE). |
||||
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
@ -0,0 +1,62 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
use Symfony\Polyfill\Mbstring as p; |
||||
|
|
||||
|
if (!function_exists('mb_strlen')) { |
||||
|
define('MB_CASE_UPPER', 0); |
||||
|
define('MB_CASE_LOWER', 1); |
||||
|
define('MB_CASE_TITLE', 2); |
||||
|
|
||||
|
function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } |
||||
|
function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } |
||||
|
function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } |
||||
|
function mb_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); } |
||||
|
function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); } |
||||
|
function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } |
||||
|
function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } |
||||
|
function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } |
||||
|
function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } |
||||
|
function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } |
||||
|
function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } |
||||
|
function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } |
||||
|
function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } |
||||
|
function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } |
||||
|
function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } |
||||
|
function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } |
||||
|
function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } |
||||
|
function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } |
||||
|
function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } |
||||
|
function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } |
||||
|
function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } |
||||
|
function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } |
||||
|
function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } |
||||
|
function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } |
||||
|
function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } |
||||
|
function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } |
||||
|
function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } |
||||
|
function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } |
||||
|
function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } |
||||
|
function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } |
||||
|
function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } |
||||
|
function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } |
||||
|
function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } |
||||
|
function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } |
||||
|
} |
||||
|
if (!function_exists('mb_chr')) { |
||||
|
function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } |
||||
|
function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } |
||||
|
function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } |
||||
|
} |
||||
|
|
||||
|
if (!function_exists('mb_str_split')) { |
||||
|
function mb_str_split($string, $split_length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $split_length, $encoding); } |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
{ |
||||
|
"name": "symfony/polyfill-mbstring", |
||||
|
"type": "library", |
||||
|
"description": "Symfony polyfill for the Mbstring extension", |
||||
|
"keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], |
||||
|
"homepage": "https://symfony.com", |
||||
|
"license": "MIT", |
||||
|
"authors": [ |
||||
|
{ |
||||
|
"name": "Nicolas Grekas", |
||||
|
"email": "p@tchwork.com" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Symfony Community", |
||||
|
"homepage": "https://symfony.com/contributors" |
||||
|
} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": ">=5.3.3" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, |
||||
|
"files": [ "bootstrap.php" ] |
||||
|
}, |
||||
|
"suggest": { |
||||
|
"ext-mbstring": "For best performance" |
||||
|
}, |
||||
|
"minimum-stability": "dev", |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.12-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2015-2019 Fabien Potencier |
||||
|
|
||||
|
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. |
||||
@ -0,0 +1,216 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Polyfill\Php72; |
||||
|
|
||||
|
/** |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com> |
||||
|
* |
||||
|
* @internal |
||||
|
*/ |
||||
|
final class Php72 |
||||
|
{ |
||||
|
private static $hashMask; |
||||
|
|
||||
|
public static function utf8_encode($s) |
||||
|
{ |
||||
|
$s .= $s; |
||||
|
$len = \strlen($s); |
||||
|
|
||||
|
for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) { |
||||
|
switch (true) { |
||||
|
case $s[$i] < "\x80": $s[$j] = $s[$i]; break; |
||||
|
case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break; |
||||
|
default: $s[$j] = "\xC3"; $s[++$j] = \chr(\ord($s[$i]) - 64); break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return substr($s, 0, $j); |
||||
|
} |
||||
|
|
||||
|
public static function utf8_decode($s) |
||||
|
{ |
||||
|
$s = (string) $s; |
||||
|
$len = \strlen($s); |
||||
|
|
||||
|
for ($i = 0, $j = 0; $i < $len; ++$i, ++$j) { |
||||
|
switch ($s[$i] & "\xF0") { |
||||
|
case "\xC0": |
||||
|
case "\xD0": |
||||
|
$c = (\ord($s[$i] & "\x1F") << 6) | \ord($s[++$i] & "\x3F"); |
||||
|
$s[$j] = $c < 256 ? \chr($c) : '?'; |
||||
|
break; |
||||
|
|
||||
|
case "\xF0": |
||||
|
++$i; |
||||
|
// no break |
||||
|
|
||||
|
case "\xE0": |
||||
|
$s[$j] = '?'; |
||||
|
$i += 2; |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
$s[$j] = $s[$i]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return substr($s, 0, $j); |
||||
|
} |
||||
|
|
||||
|
public static function php_os_family() |
||||
|
{ |
||||
|
if ('\\' === \DIRECTORY_SEPARATOR) { |
||||
|
return 'Windows'; |
||||
|
} |
||||
|
|
||||
|
$map = array( |
||||
|
'Darwin' => 'Darwin', |
||||
|
'DragonFly' => 'BSD', |
||||
|
'FreeBSD' => 'BSD', |
||||
|
'NetBSD' => 'BSD', |
||||
|
'OpenBSD' => 'BSD', |
||||
|
'Linux' => 'Linux', |
||||
|
'SunOS' => 'Solaris', |
||||
|
); |
||||
|
|
||||
|
return isset($map[PHP_OS]) ? $map[PHP_OS] : 'Unknown'; |
||||
|
} |
||||
|
|
||||
|
public static function spl_object_id($object) |
||||
|
{ |
||||
|
if (null === self::$hashMask) { |
||||
|
self::initHashMask(); |
||||
|
} |
||||
|
if (null === $hash = spl_object_hash($object)) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
return self::$hashMask ^ hexdec(substr($hash, 16 - \PHP_INT_SIZE, \PHP_INT_SIZE)); |
||||
|
} |
||||
|
|
||||
|
public static function sapi_windows_vt100_support($stream, $enable = null) |
||||
|
{ |
||||
|
if (!\is_resource($stream)) { |
||||
|
trigger_error('sapi_windows_vt100_support() expects parameter 1 to be resource, '.\gettype($stream).' given', E_USER_WARNING); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
$meta = stream_get_meta_data($stream); |
||||
|
|
||||
|
if ('STDIO' !== $meta['stream_type']) { |
||||
|
trigger_error('sapi_windows_vt100_support() was not able to analyze the specified stream', E_USER_WARNING); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// We cannot actually disable vt100 support if it is set |
||||
|
if (false === $enable || !self::stream_isatty($stream)) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// The native function does not apply to stdin |
||||
|
$meta = array_map('strtolower', $meta); |
||||
|
$stdin = 'php://stdin' === $meta['uri'] || 'php://fd/0' === $meta['uri']; |
||||
|
|
||||
|
return !$stdin |
||||
|
&& (false !== getenv('ANSICON') |
||||
|
|| 'ON' === getenv('ConEmuANSI') |
||||
|
|| 'xterm' === getenv('TERM') |
||||
|
|| 'Hyper' === getenv('TERM_PROGRAM')); |
||||
|
} |
||||
|
|
||||
|
public static function stream_isatty($stream) |
||||
|
{ |
||||
|
if (!\is_resource($stream)) { |
||||
|
trigger_error('stream_isatty() expects parameter 1 to be resource, '.\gettype($stream).' given', E_USER_WARNING); |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if ('\\' === \DIRECTORY_SEPARATOR) { |
||||
|
$stat = @fstat($stream); |
||||
|
// Check if formatted mode is S_IFCHR |
||||
|
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; |
||||
|
} |
||||
|
|
||||
|
return \function_exists('posix_isatty') && @posix_isatty($stream); |
||||
|
} |
||||
|
|
||||
|
private static function initHashMask() |
||||
|
{ |
||||
|
$obj = (object) array(); |
||||
|
self::$hashMask = -1; |
||||
|
|
||||
|
// check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below |
||||
|
$obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush'); |
||||
|
foreach (debug_backtrace(\PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) { |
||||
|
if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) { |
||||
|
$frame['line'] = 0; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (!empty($frame['line'])) { |
||||
|
ob_start(); |
||||
|
debug_zval_dump($obj); |
||||
|
self::$hashMask = (int) substr(ob_get_clean(), 17); |
||||
|
} |
||||
|
|
||||
|
self::$hashMask ^= hexdec(substr(spl_object_hash($obj), 16 - \PHP_INT_SIZE, \PHP_INT_SIZE)); |
||||
|
} |
||||
|
|
||||
|
public static function mb_chr($code, $encoding = null) |
||||
|
{ |
||||
|
if (0x80 > $code %= 0x200000) { |
||||
|
$s = \chr($code); |
||||
|
} elseif (0x800 > $code) { |
||||
|
$s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); |
||||
|
} elseif (0x10000 > $code) { |
||||
|
$s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); |
||||
|
} else { |
||||
|
$s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); |
||||
|
} |
||||
|
|
||||
|
if ('UTF-8' !== $encoding) { |
||||
|
$s = mb_convert_encoding($s, $encoding, 'UTF-8'); |
||||
|
} |
||||
|
|
||||
|
return $s; |
||||
|
} |
||||
|
|
||||
|
public static function mb_ord($s, $encoding = null) |
||||
|
{ |
||||
|
if (null == $encoding) { |
||||
|
$s = mb_convert_encoding($s, 'UTF-8'); |
||||
|
} elseif ('UTF-8' !== $encoding) { |
||||
|
$s = mb_convert_encoding($s, 'UTF-8', $encoding); |
||||
|
} |
||||
|
|
||||
|
if (1 === \strlen($s)) { |
||||
|
return \ord($s); |
||||
|
} |
||||
|
|
||||
|
$code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; |
||||
|
if (0xF0 <= $code) { |
||||
|
return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; |
||||
|
} |
||||
|
if (0xE0 <= $code) { |
||||
|
return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; |
||||
|
} |
||||
|
if (0xC0 <= $code) { |
||||
|
return (($code - 0xC0) << 6) + $s[2] - 0x80; |
||||
|
} |
||||
|
|
||||
|
return $code; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
Symfony Polyfill / Php72 |
||||
|
======================== |
||||
|
|
||||
|
This component provides functions added to PHP 7.2 core: |
||||
|
|
||||
|
- [`spl_object_id`](https://php.net/spl_object_id) |
||||
|
- [`stream_isatty`](https://php.net/stream_isatty) |
||||
|
|
||||
|
On Windows only: |
||||
|
|
||||
|
- [`sapi_windows_vt100_support`](https://php.net/sapi_windows_vt100_support) |
||||
|
|
||||
|
Moved to core since 7.2 (was in the optional XML extension earlier): |
||||
|
|
||||
|
- [`utf8_encode`](https://php.net/utf8_encode) |
||||
|
- [`utf8_decode`](https://php.net/utf8_decode) |
||||
|
|
||||
|
Also, it provides a constant added to PHP 7.2: |
||||
|
- [`PHP_OS_FAMILY`](http://php.net/manual/en/reserved.constants.php#constant.php-os-family) |
||||
|
|
||||
|
More information can be found in the |
||||
|
[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). |
||||
|
|
||||
|
License |
||||
|
======= |
||||
|
|
||||
|
This library is released under the [MIT license](LICENSE). |
||||
@ -0,0 +1,36 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
use Symfony\Polyfill\Php72 as p; |
||||
|
|
||||
|
if (PHP_VERSION_ID < 70200) { |
||||
|
if ('\\' === DIRECTORY_SEPARATOR && !function_exists('sapi_windows_vt100_support')) { |
||||
|
function sapi_windows_vt100_support($stream, $enable = null) { return p\Php72::sapi_windows_vt100_support($stream, $enable); } |
||||
|
} |
||||
|
if (!function_exists('stream_isatty')) { |
||||
|
function stream_isatty($stream) { return p\Php72::stream_isatty($stream); } |
||||
|
} |
||||
|
if (!function_exists('utf8_encode')) { |
||||
|
function utf8_encode($s) { return p\Php72::utf8_encode($s); } |
||||
|
function utf8_decode($s) { return p\Php72::utf8_decode($s); } |
||||
|
} |
||||
|
if (!function_exists('spl_object_id')) { |
||||
|
function spl_object_id($s) { return p\Php72::spl_object_id($s); } |
||||
|
} |
||||
|
if (!defined('PHP_OS_FAMILY')) { |
||||
|
define('PHP_OS_FAMILY', p\Php72::php_os_family()); |
||||
|
} |
||||
|
if (!function_exists('mb_chr')) { |
||||
|
function mb_ord($s, $enc = null) { return p\Php72::mb_ord($s, $enc); } |
||||
|
function mb_chr($code, $enc = null) { return p\Php72::mb_chr($code, $enc); } |
||||
|
function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
{ |
||||
|
"name": "symfony/polyfill-php72", |
||||
|
"type": "library", |
||||
|
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", |
||||
|
"keywords": ["polyfill", "shim", "compatibility", "portable"], |
||||
|
"homepage": "https://symfony.com", |
||||
|
"license": "MIT", |
||||
|
"authors": [ |
||||
|
{ |
||||
|
"name": "Nicolas Grekas", |
||||
|
"email": "p@tchwork.com" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Symfony Community", |
||||
|
"homepage": "https://symfony.com/contributors" |
||||
|
} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": ">=5.3.3" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { "Symfony\\Polyfill\\Php72\\": "" }, |
||||
|
"files": [ "bootstrap.php" ] |
||||
|
}, |
||||
|
"minimum-stability": "dev", |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.12-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
vendor/ |
||||
|
composer.lock |
||||
|
phpunit.xml |
||||
|
.php_cs.cache |
||||
@ -0,0 +1,24 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return PhpCsFixer\Config::create() |
||||
|
->setRules([ |
||||
|
'@Symfony' => true, |
||||
|
'@Symfony:risky' => true, |
||||
|
'@PHPUnit48Migration:risky' => true, |
||||
|
'php_unit_no_expectation_annotation' => false, // part of `PHPUnitXYMigration:risky` ruleset, to be enabled when PHPUnit 4.x support will be dropped, as we don't want to rewrite exceptions handling twice |
||||
|
'array_syntax' => ['syntax' => 'short'], |
||||
|
'fopen_flags' => false, |
||||
|
'ordered_imports' => true, |
||||
|
'protected_to_private' => false, |
||||
|
// Part of @Symfony:risky in PHP-CS-Fixer 2.13.0. To be removed from the config file once upgrading |
||||
|
'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced'], |
||||
|
// Part of future @Symfony ruleset in PHP-CS-Fixer To be removed from the config file once upgrading |
||||
|
'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], |
||||
|
]) |
||||
|
->setRiskyAllowed(true) |
||||
|
->setFinder( |
||||
|
PhpCsFixer\Finder::create() |
||||
|
->in(__DIR__) |
||||
|
->name('*.php') |
||||
|
) |
||||
|
; |
||||
@ -0,0 +1,47 @@ |
|||||
|
language: php |
||||
|
sudo: false |
||||
|
cache: |
||||
|
directories: |
||||
|
- $HOME/.composer/cache/files |
||||
|
- $HOME/symfony-bridge/.phpunit |
||||
|
|
||||
|
env: |
||||
|
global: |
||||
|
- PHPUNIT_FLAGS="-v" |
||||
|
- SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit" |
||||
|
|
||||
|
matrix: |
||||
|
fast_finish: true |
||||
|
include: |
||||
|
# Minimum supported dependencies with the latest and oldest PHP version |
||||
|
- php: 7.2 |
||||
|
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors" |
||||
|
|
||||
|
- php: 7.1 |
||||
|
- php: 7.2 |
||||
|
env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text" |
||||
|
|
||||
|
# Latest commit to master |
||||
|
- php: 7.2 |
||||
|
env: STABILITY="dev" |
||||
|
|
||||
|
allow_failures: |
||||
|
# Dev-master is allowed to fail. |
||||
|
- env: STABILITY="dev" |
||||
|
|
||||
|
before_install: |
||||
|
- if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi |
||||
|
- if ! [ -z "$STABILITY" ]; then composer config minimum-stability ${STABILITY}; fi; |
||||
|
- if ! [ -v "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi; |
||||
|
|
||||
|
install: |
||||
|
# To be removed when this issue will be resolved: https://github.com/composer/composer/issues/5355 |
||||
|
- if [[ "$COMPOSER_FLAGS" == *"--prefer-lowest"* ]]; then composer update --prefer-dist --no-interaction --prefer-stable --quiet; fi |
||||
|
- composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction |
||||
|
- ./vendor/bin/simple-phpunit install |
||||
|
|
||||
|
script: |
||||
|
- composer validate --strict --no-check-lock |
||||
|
# simple-phpunit is the PHPUnit wrapper provided by the PHPUnit Bridge component and |
||||
|
# it helps with testing legacy code and deprecations (composer require symfony/phpunit-bridge) |
||||
|
- ./vendor/bin/simple-phpunit $PHPUNIT_FLAGS |
||||
@ -0,0 +1,18 @@ |
|||||
|
CHANGELOG |
||||
|
========= |
||||
|
|
||||
|
* 1.1.0 (2018-08-30) |
||||
|
|
||||
|
* Added support for creating PSR-7 messages using PSR-17 factories |
||||
|
|
||||
|
* 1.0.2 (2017-12-19) |
||||
|
|
||||
|
* Fixed request target in PSR7 Request (mtibben) |
||||
|
|
||||
|
* 1.0.1 (2017-12-04) |
||||
|
|
||||
|
* Added support for Symfony 4 (dunglas) |
||||
|
|
||||
|
* 1.0.0 (2016-09-14) |
||||
|
|
||||
|
* Initial release |
||||
@ -0,0 +1,175 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Factory; |
||||
|
|
||||
|
@trigger_error(sprintf('The "%s" class is deprecated since symfony/psr-http-message-bridge 1.2, use PsrHttpFactory instead.', DiactorosFactory::class), E_USER_DEPRECATED); |
||||
|
|
||||
|
use Psr\Http\Message\UploadedFileInterface; |
||||
|
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; |
||||
|
use Symfony\Component\HttpFoundation\BinaryFileResponse; |
||||
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
||||
|
use Symfony\Component\HttpFoundation\Request; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
use Symfony\Component\HttpFoundation\StreamedResponse; |
||||
|
use Zend\Diactoros\Response as DiactorosResponse; |
||||
|
use Zend\Diactoros\ServerRequest; |
||||
|
use Zend\Diactoros\ServerRequestFactory as DiactorosRequestFactory; |
||||
|
use Zend\Diactoros\Stream as DiactorosStream; |
||||
|
use Zend\Diactoros\UploadedFile as DiactorosUploadedFile; |
||||
|
|
||||
|
/** |
||||
|
* Builds Psr\HttpMessage instances using the Zend Diactoros implementation. |
||||
|
* |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
* |
||||
|
* @deprecated since symfony/psr-http-message-bridge 1.2, use PsrHttpFactory instead |
||||
|
*/ |
||||
|
class DiactorosFactory implements HttpMessageFactoryInterface |
||||
|
{ |
||||
|
public function __construct() |
||||
|
{ |
||||
|
if (!class_exists('Zend\Diactoros\ServerRequestFactory')) { |
||||
|
throw new \RuntimeException('Zend Diactoros must be installed to use the DiactorosFactory.'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function createRequest(Request $symfonyRequest) |
||||
|
{ |
||||
|
$server = method_exists('Zend\Diactoros\ServerRequestFactory', 'normalizeServer') |
||||
|
? DiactorosRequestFactory::normalizeServer($symfonyRequest->server->all()) |
||||
|
: \Zend\Diactoros\normalizeServer($symfonyRequest->server->all()); |
||||
|
$headers = $symfonyRequest->headers->all(); |
||||
|
|
||||
|
$body = new DiactorosStream($symfonyRequest->getContent(true)); |
||||
|
|
||||
|
$files = method_exists('Zend\Diactoros\ServerRequestFactory', 'normalizeFiles') |
||||
|
? DiactorosRequestFactory::normalizeFiles($this->getFiles($symfonyRequest->files->all())) |
||||
|
: \Zend\Diactoros\normalizeUploadedFiles($this->getFiles($symfonyRequest->files->all())); |
||||
|
|
||||
|
$request = new ServerRequest( |
||||
|
$server, |
||||
|
$files, |
||||
|
$symfonyRequest->getSchemeAndHttpHost().$symfonyRequest->getRequestUri(), |
||||
|
$symfonyRequest->getMethod(), |
||||
|
$body, |
||||
|
$headers |
||||
|
); |
||||
|
|
||||
|
$request = $request |
||||
|
->withCookieParams($symfonyRequest->cookies->all()) |
||||
|
->withQueryParams($symfonyRequest->query->all()) |
||||
|
->withParsedBody($symfonyRequest->request->all()) |
||||
|
->withRequestTarget($symfonyRequest->getRequestUri()) |
||||
|
; |
||||
|
|
||||
|
foreach ($symfonyRequest->attributes->all() as $key => $value) { |
||||
|
$request = $request->withAttribute($key, $value); |
||||
|
} |
||||
|
|
||||
|
return $request; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Converts Symfony uploaded files array to the PSR one. |
||||
|
* |
||||
|
* @param array $uploadedFiles |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
private function getFiles(array $uploadedFiles) |
||||
|
{ |
||||
|
$files = []; |
||||
|
|
||||
|
foreach ($uploadedFiles as $key => $value) { |
||||
|
if (null === $value) { |
||||
|
$files[$key] = new DiactorosUploadedFile(null, 0, UPLOAD_ERR_NO_FILE, null, null); |
||||
|
continue; |
||||
|
} |
||||
|
if ($value instanceof UploadedFile) { |
||||
|
$files[$key] = $this->createUploadedFile($value); |
||||
|
} else { |
||||
|
$files[$key] = $this->getFiles($value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $files; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Creates a PSR-7 UploadedFile instance from a Symfony one. |
||||
|
* |
||||
|
* @param UploadedFile $symfonyUploadedFile |
||||
|
* |
||||
|
* @return UploadedFileInterface |
||||
|
*/ |
||||
|
private function createUploadedFile(UploadedFile $symfonyUploadedFile) |
||||
|
{ |
||||
|
return new DiactorosUploadedFile( |
||||
|
$symfonyUploadedFile->getRealPath(), |
||||
|
(int) $symfonyUploadedFile->getSize(), |
||||
|
$symfonyUploadedFile->getError(), |
||||
|
$symfonyUploadedFile->getClientOriginalName(), |
||||
|
$symfonyUploadedFile->getClientMimeType() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function createResponse(Response $symfonyResponse) |
||||
|
{ |
||||
|
if ($symfonyResponse instanceof BinaryFileResponse) { |
||||
|
$stream = new DiactorosStream($symfonyResponse->getFile()->getPathname(), 'r'); |
||||
|
} else { |
||||
|
$stream = new DiactorosStream('php://temp', 'wb+'); |
||||
|
if ($symfonyResponse instanceof StreamedResponse) { |
||||
|
ob_start(function ($buffer) use ($stream) { |
||||
|
$stream->write($buffer); |
||||
|
|
||||
|
return ''; |
||||
|
}); |
||||
|
|
||||
|
$symfonyResponse->sendContent(); |
||||
|
ob_end_clean(); |
||||
|
} else { |
||||
|
$stream->write($symfonyResponse->getContent()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$headers = $symfonyResponse->headers->all(); |
||||
|
if (!isset($headers['Set-Cookie']) && !isset($headers['set-cookie'])) { |
||||
|
$cookies = $symfonyResponse->headers->getCookies(); |
||||
|
if (!empty($cookies)) { |
||||
|
$headers['Set-Cookie'] = []; |
||||
|
foreach ($cookies as $cookie) { |
||||
|
$headers['Set-Cookie'][] = $cookie->__toString(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$response = new DiactorosResponse( |
||||
|
$stream, |
||||
|
$symfonyResponse->getStatusCode(), |
||||
|
$headers |
||||
|
); |
||||
|
|
||||
|
$protocolVersion = $symfonyResponse->getProtocolVersion(); |
||||
|
if ('1.1' !== $protocolVersion) { |
||||
|
$response = $response->withProtocolVersion($protocolVersion); |
||||
|
} |
||||
|
|
||||
|
return $response; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,280 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Factory; |
||||
|
|
||||
|
use Psr\Http\Message\ResponseInterface; |
||||
|
use Psr\Http\Message\ServerRequestInterface; |
||||
|
use Psr\Http\Message\StreamInterface; |
||||
|
use Psr\Http\Message\UploadedFileInterface; |
||||
|
use Psr\Http\Message\UriInterface; |
||||
|
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface; |
||||
|
use Symfony\Component\HttpFoundation\Cookie; |
||||
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
||||
|
use Symfony\Component\HttpFoundation\Request; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
use Symfony\Component\HttpFoundation\StreamedResponse; |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
* |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
class HttpFoundationFactory implements HttpFoundationFactoryInterface |
||||
|
{ |
||||
|
/** |
||||
|
* @var int The maximum output buffering size for each iteration when sending the response |
||||
|
*/ |
||||
|
private $responseBufferMaxLength; |
||||
|
|
||||
|
public function __construct(int $responseBufferMaxLength = 16372) |
||||
|
{ |
||||
|
$this->responseBufferMaxLength = $responseBufferMaxLength; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function createRequest(ServerRequestInterface $psrRequest) |
||||
|
{ |
||||
|
$server = []; |
||||
|
$uri = $psrRequest->getUri(); |
||||
|
|
||||
|
if ($uri instanceof UriInterface) { |
||||
|
$server['SERVER_NAME'] = $uri->getHost(); |
||||
|
$server['SERVER_PORT'] = $uri->getPort(); |
||||
|
$server['REQUEST_URI'] = $uri->getPath(); |
||||
|
$server['QUERY_STRING'] = $uri->getQuery(); |
||||
|
} |
||||
|
|
||||
|
$server['REQUEST_METHOD'] = $psrRequest->getMethod(); |
||||
|
|
||||
|
$server = array_replace($server, $psrRequest->getServerParams()); |
||||
|
|
||||
|
$parsedBody = $psrRequest->getParsedBody(); |
||||
|
$parsedBody = \is_array($parsedBody) ? $parsedBody : []; |
||||
|
|
||||
|
$request = new Request( |
||||
|
$psrRequest->getQueryParams(), |
||||
|
$parsedBody, |
||||
|
$psrRequest->getAttributes(), |
||||
|
$psrRequest->getCookieParams(), |
||||
|
$this->getFiles($psrRequest->getUploadedFiles()), |
||||
|
$server, |
||||
|
$psrRequest->getBody()->__toString() |
||||
|
); |
||||
|
$request->headers->replace($psrRequest->getHeaders()); |
||||
|
|
||||
|
return $request; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Converts to the input array to $_FILES structure. |
||||
|
* |
||||
|
* @param array $uploadedFiles |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
private function getFiles(array $uploadedFiles) |
||||
|
{ |
||||
|
$files = []; |
||||
|
|
||||
|
foreach ($uploadedFiles as $key => $value) { |
||||
|
if ($value instanceof UploadedFileInterface) { |
||||
|
$files[$key] = $this->createUploadedFile($value); |
||||
|
} else { |
||||
|
$files[$key] = $this->getFiles($value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $files; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Creates Symfony UploadedFile instance from PSR-7 ones. |
||||
|
* |
||||
|
* @param UploadedFileInterface $psrUploadedFile |
||||
|
* |
||||
|
* @return UploadedFile |
||||
|
*/ |
||||
|
private function createUploadedFile(UploadedFileInterface $psrUploadedFile) |
||||
|
{ |
||||
|
$temporaryPath = ''; |
||||
|
$clientFileName = ''; |
||||
|
if (UPLOAD_ERR_NO_FILE !== $psrUploadedFile->getError()) { |
||||
|
$temporaryPath = $this->getTemporaryPath(); |
||||
|
$psrUploadedFile->moveTo($temporaryPath); |
||||
|
|
||||
|
$clientFileName = $psrUploadedFile->getClientFilename(); |
||||
|
} |
||||
|
|
||||
|
if (class_exists('Symfony\Component\HttpFoundation\HeaderUtils')) { |
||||
|
// Symfony 4.1+ |
||||
|
return new UploadedFile( |
||||
|
$temporaryPath, |
||||
|
null === $clientFileName ? '' : $clientFileName, |
||||
|
$psrUploadedFile->getClientMediaType(), |
||||
|
$psrUploadedFile->getError(), |
||||
|
true |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return new UploadedFile( |
||||
|
$temporaryPath, |
||||
|
null === $clientFileName ? '' : $clientFileName, |
||||
|
$psrUploadedFile->getClientMediaType(), |
||||
|
$psrUploadedFile->getSize(), |
||||
|
$psrUploadedFile->getError(), |
||||
|
true |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Gets a temporary file path. |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
protected function getTemporaryPath() |
||||
|
{ |
||||
|
return tempnam(sys_get_temp_dir(), uniqid('symfony', true)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function createResponse(ResponseInterface $psrResponse, bool $streamed = false) |
||||
|
{ |
||||
|
$cookies = $psrResponse->getHeader('Set-Cookie'); |
||||
|
$psrResponse = $psrResponse->withoutHeader('Set-Cookie'); |
||||
|
|
||||
|
if ($streamed) { |
||||
|
$response = new StreamedResponse( |
||||
|
$this->createStreamedResponseCallback($psrResponse->getBody()), |
||||
|
$psrResponse->getStatusCode(), |
||||
|
$psrResponse->getHeaders() |
||||
|
); |
||||
|
} else { |
||||
|
$response = new Response( |
||||
|
$psrResponse->getBody()->__toString(), |
||||
|
$psrResponse->getStatusCode(), |
||||
|
$psrResponse->getHeaders() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
$response->setProtocolVersion($psrResponse->getProtocolVersion()); |
||||
|
|
||||
|
foreach ($cookies as $cookie) { |
||||
|
$response->headers->setCookie($this->createCookie($cookie)); |
||||
|
} |
||||
|
|
||||
|
return $response; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Creates a Cookie instance from a cookie string. |
||||
|
* |
||||
|
* Some snippets have been taken from the Guzzle project: https://github.com/guzzle/guzzle/blob/5.3/src/Cookie/SetCookie.php#L34 |
||||
|
* |
||||
|
* @param string $cookie |
||||
|
* |
||||
|
* @return Cookie |
||||
|
* |
||||
|
* @throws \InvalidArgumentException |
||||
|
*/ |
||||
|
private function createCookie($cookie) |
||||
|
{ |
||||
|
foreach (explode(';', $cookie) as $part) { |
||||
|
$part = trim($part); |
||||
|
|
||||
|
$data = explode('=', $part, 2); |
||||
|
$name = $data[0]; |
||||
|
$value = isset($data[1]) ? trim($data[1], " \n\r\t\0\x0B\"") : null; |
||||
|
|
||||
|
if (!isset($cookieName)) { |
||||
|
$cookieName = $name; |
||||
|
$cookieValue = $value; |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if ('expires' === strtolower($name) && null !== $value) { |
||||
|
$cookieExpire = new \DateTime($value); |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if ('path' === strtolower($name) && null !== $value) { |
||||
|
$cookiePath = $value; |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if ('domain' === strtolower($name) && null !== $value) { |
||||
|
$cookieDomain = $value; |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if ('secure' === strtolower($name)) { |
||||
|
$cookieSecure = true; |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if ('httponly' === strtolower($name)) { |
||||
|
$cookieHttpOnly = true; |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if ('samesite' === strtolower($name) && null !== $value) { |
||||
|
$samesite = $value; |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!isset($cookieName)) { |
||||
|
throw new \InvalidArgumentException('The value of the Set-Cookie header is malformed.'); |
||||
|
} |
||||
|
|
||||
|
return new Cookie( |
||||
|
$cookieName, |
||||
|
$cookieValue, |
||||
|
isset($cookieExpire) ? $cookieExpire : 0, |
||||
|
isset($cookiePath) ? $cookiePath : '/', |
||||
|
isset($cookieDomain) ? $cookieDomain : null, |
||||
|
isset($cookieSecure), |
||||
|
isset($cookieHttpOnly), |
||||
|
false, |
||||
|
isset($samesite) ? $samesite : null |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private function createStreamedResponseCallback(StreamInterface $body): callable |
||||
|
{ |
||||
|
return function () use ($body) { |
||||
|
if ($body->isSeekable()) { |
||||
|
$body->rewind(); |
||||
|
} |
||||
|
|
||||
|
if (!$body->isReadable()) { |
||||
|
echo $body; |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
while (!$body->eof()) { |
||||
|
echo $body->read($this->responseBufferMaxLength); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,172 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Factory; |
||||
|
|
||||
|
use Psr\Http\Message\ResponseFactoryInterface; |
||||
|
use Psr\Http\Message\ServerRequestFactoryInterface; |
||||
|
use Psr\Http\Message\StreamFactoryInterface; |
||||
|
use Psr\Http\Message\UploadedFileFactoryInterface; |
||||
|
use Psr\Http\Message\UploadedFileInterface; |
||||
|
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; |
||||
|
use Symfony\Component\HttpFoundation\BinaryFileResponse; |
||||
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
||||
|
use Symfony\Component\HttpFoundation\Request; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
use Symfony\Component\HttpFoundation\StreamedResponse; |
||||
|
|
||||
|
/** |
||||
|
* Builds Psr\HttpMessage instances using a PSR-17 implementation. |
||||
|
* |
||||
|
* @author Antonio J. García Lagar <aj@garcialagar.es> |
||||
|
*/ |
||||
|
class PsrHttpFactory implements HttpMessageFactoryInterface |
||||
|
{ |
||||
|
private $serverRequestFactory; |
||||
|
private $streamFactory; |
||||
|
private $uploadedFileFactory; |
||||
|
private $responseFactory; |
||||
|
|
||||
|
public function __construct(ServerRequestFactoryInterface $serverRequestFactory, StreamFactoryInterface $streamFactory, UploadedFileFactoryInterface $uploadedFileFactory, ResponseFactoryInterface $responseFactory) |
||||
|
{ |
||||
|
$this->serverRequestFactory = $serverRequestFactory; |
||||
|
$this->streamFactory = $streamFactory; |
||||
|
$this->uploadedFileFactory = $uploadedFileFactory; |
||||
|
$this->responseFactory = $responseFactory; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function createRequest(Request $symfonyRequest) |
||||
|
{ |
||||
|
$request = $this->serverRequestFactory->createServerRequest( |
||||
|
$symfonyRequest->getMethod(), |
||||
|
$symfonyRequest->getUri(), |
||||
|
$symfonyRequest->server->all() |
||||
|
); |
||||
|
|
||||
|
foreach ($symfonyRequest->headers->all() as $name => $value) { |
||||
|
$request = $request->withHeader($name, $value); |
||||
|
} |
||||
|
|
||||
|
$body = $this->streamFactory->createStreamFromResource($symfonyRequest->getContent(true)); |
||||
|
|
||||
|
$request = $request |
||||
|
->withBody($body) |
||||
|
->withUploadedFiles($this->getFiles($symfonyRequest->files->all())) |
||||
|
->withCookieParams($symfonyRequest->cookies->all()) |
||||
|
->withQueryParams($symfonyRequest->query->all()) |
||||
|
->withParsedBody($symfonyRequest->request->all()) |
||||
|
; |
||||
|
|
||||
|
foreach ($symfonyRequest->attributes->all() as $key => $value) { |
||||
|
$request = $request->withAttribute($key, $value); |
||||
|
} |
||||
|
|
||||
|
return $request; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Converts Symfony uploaded files array to the PSR one. |
||||
|
* |
||||
|
* @param array $uploadedFiles |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
private function getFiles(array $uploadedFiles) |
||||
|
{ |
||||
|
$files = []; |
||||
|
|
||||
|
foreach ($uploadedFiles as $key => $value) { |
||||
|
if (null === $value) { |
||||
|
$files[$key] = $this->uploadedFileFactory->createUploadedFile($this->streamFactory->createStream(), 0, UPLOAD_ERR_NO_FILE); |
||||
|
continue; |
||||
|
} |
||||
|
if ($value instanceof UploadedFile) { |
||||
|
$files[$key] = $this->createUploadedFile($value); |
||||
|
} else { |
||||
|
$files[$key] = $this->getFiles($value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $files; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Creates a PSR-7 UploadedFile instance from a Symfony one. |
||||
|
* |
||||
|
* @param UploadedFile $symfonyUploadedFile |
||||
|
* |
||||
|
* @return UploadedFileInterface |
||||
|
*/ |
||||
|
private function createUploadedFile(UploadedFile $symfonyUploadedFile) |
||||
|
{ |
||||
|
return $this->uploadedFileFactory->createUploadedFile( |
||||
|
$this->streamFactory->createStreamFromFile( |
||||
|
$symfonyUploadedFile->getRealPath() |
||||
|
), |
||||
|
(int) $symfonyUploadedFile->getSize(), |
||||
|
$symfonyUploadedFile->getError(), |
||||
|
$symfonyUploadedFile->getClientOriginalName(), |
||||
|
$symfonyUploadedFile->getClientMimeType() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function createResponse(Response $symfonyResponse) |
||||
|
{ |
||||
|
$response = $this->responseFactory->createResponse($symfonyResponse->getStatusCode(), Response::$statusTexts[$symfonyResponse->getStatusCode()] ?? ''); |
||||
|
|
||||
|
if ($symfonyResponse instanceof BinaryFileResponse) { |
||||
|
$stream = $this->streamFactory->createStreamFromFile( |
||||
|
$symfonyResponse->getFile()->getPathname() |
||||
|
); |
||||
|
} else { |
||||
|
$stream = $this->streamFactory->createStreamFromFile('php://temp', 'wb+'); |
||||
|
if ($symfonyResponse instanceof StreamedResponse) { |
||||
|
ob_start(function ($buffer) use ($stream) { |
||||
|
$stream->write($buffer); |
||||
|
|
||||
|
return ''; |
||||
|
}); |
||||
|
|
||||
|
$symfonyResponse->sendContent(); |
||||
|
ob_end_clean(); |
||||
|
} else { |
||||
|
$stream->write($symfonyResponse->getContent()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$response = $response->withBody($stream); |
||||
|
|
||||
|
$headers = $symfonyResponse->headers->all(); |
||||
|
$cookies = $symfonyResponse->headers->getCookies(); |
||||
|
if (!empty($cookies)) { |
||||
|
$headers['Set-Cookie'] = []; |
||||
|
|
||||
|
foreach ($cookies as $cookie) { |
||||
|
$headers['Set-Cookie'][] = $cookie->__toString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
foreach ($headers as $name => $value) { |
||||
|
$response = $response->withHeader($name, $value); |
||||
|
} |
||||
|
|
||||
|
$protocolVersion = $symfonyResponse->getProtocolVersion(); |
||||
|
$response = $response->withProtocolVersion($protocolVersion); |
||||
|
|
||||
|
return $response; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage; |
||||
|
|
||||
|
use Psr\Http\Message\ResponseInterface; |
||||
|
use Psr\Http\Message\ServerRequestInterface; |
||||
|
use Symfony\Component\HttpFoundation\Request; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
|
||||
|
/** |
||||
|
* Creates Symfony Request and Response instances from PSR-7 ones. |
||||
|
* |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
interface HttpFoundationFactoryInterface |
||||
|
{ |
||||
|
/** |
||||
|
* Creates a Symfony Request instance from a PSR-7 one. |
||||
|
* |
||||
|
* @param ServerRequestInterface $psrRequest |
||||
|
* |
||||
|
* @return Request |
||||
|
*/ |
||||
|
public function createRequest(ServerRequestInterface $psrRequest); |
||||
|
|
||||
|
/** |
||||
|
* Creates a Symfony Response instance from a PSR-7 one. |
||||
|
* |
||||
|
* @param ResponseInterface $psrResponse |
||||
|
* |
||||
|
* @return Response |
||||
|
*/ |
||||
|
public function createResponse(ResponseInterface $psrResponse); |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage; |
||||
|
|
||||
|
use Psr\Http\Message\ResponseInterface; |
||||
|
use Psr\Http\Message\ServerRequestInterface; |
||||
|
use Symfony\Component\HttpFoundation\Request; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
|
||||
|
/** |
||||
|
* Creates PSR HTTP Request and Response instances from Symfony ones. |
||||
|
* |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
interface HttpMessageFactoryInterface |
||||
|
{ |
||||
|
/** |
||||
|
* Creates a PSR-7 Request instance from a Symfony one. |
||||
|
* |
||||
|
* @param Request $symfonyRequest |
||||
|
* |
||||
|
* @return ServerRequestInterface |
||||
|
*/ |
||||
|
public function createRequest(Request $symfonyRequest); |
||||
|
|
||||
|
/** |
||||
|
* Creates a PSR-7 Response instance from a Symfony one. |
||||
|
* |
||||
|
* @param Response $symfonyResponse |
||||
|
* |
||||
|
* @return ResponseInterface |
||||
|
*/ |
||||
|
public function createResponse(Response $symfonyResponse); |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2004-2016 Fabien Potencier |
||||
|
|
||||
|
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. |
||||
@ -0,0 +1,20 @@ |
|||||
|
PSR-7 Bridge |
||||
|
============ |
||||
|
|
||||
|
Provides integration for PSR7. |
||||
|
|
||||
|
Resources |
||||
|
--------- |
||||
|
|
||||
|
* [Documentation](https://symfony.com/doc/current/components/psr7.html) |
||||
|
* [SensioFrameworkExtraBundle](https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html#psr-7-support) |
||||
|
|
||||
|
Running the tests |
||||
|
----------------- |
||||
|
|
||||
|
If you want to run the unit tests, install dev dependencies before |
||||
|
running PHPUnit: |
||||
|
|
||||
|
$ cd path/to/Symfony/Bridge/PsrHttpMessage/ |
||||
|
$ composer.phar install |
||||
|
$ phpunit |
||||
@ -0,0 +1,229 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Factory; |
||||
|
|
||||
|
use PHPUnit\Framework\TestCase; |
||||
|
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; |
||||
|
use Symfony\Component\HttpFoundation\BinaryFileResponse; |
||||
|
use Symfony\Component\HttpFoundation\Cookie; |
||||
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
||||
|
use Symfony\Component\HttpFoundation\Request; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
use Symfony\Component\HttpFoundation\StreamedResponse; |
||||
|
|
||||
|
/** |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
* @author Antonio J. García Lagar <aj@garcialagar.es> |
||||
|
*/ |
||||
|
abstract class AbstractHttpMessageFactoryTest extends TestCase |
||||
|
{ |
||||
|
private $factory; |
||||
|
private $tmpDir; |
||||
|
|
||||
|
/** |
||||
|
* @return HttpMessageFactoryInterface |
||||
|
*/ |
||||
|
abstract protected function buildHttpMessageFactory(); |
||||
|
|
||||
|
public function setup() |
||||
|
{ |
||||
|
$this->factory = $this->buildHttpMessageFactory(); |
||||
|
$this->tmpDir = sys_get_temp_dir(); |
||||
|
} |
||||
|
|
||||
|
public function testCreateRequest() |
||||
|
{ |
||||
|
$stdClass = new \stdClass(); |
||||
|
$request = new Request( |
||||
|
[ |
||||
|
'bar' => ['baz' => '42'], |
||||
|
'foo' => '1', |
||||
|
], |
||||
|
[ |
||||
|
'twitter' => [ |
||||
|
'@dunglas' => 'Kévin Dunglas', |
||||
|
'@coopTilleuls' => 'Les-Tilleuls.coop', |
||||
|
], |
||||
|
'baz' => '2', |
||||
|
], |
||||
|
[ |
||||
|
'a1' => $stdClass, |
||||
|
'a2' => ['foo' => 'bar'], |
||||
|
], |
||||
|
[ |
||||
|
'c1' => 'foo', |
||||
|
'c2' => ['c3' => 'bar'], |
||||
|
], |
||||
|
[ |
||||
|
'f1' => $this->createUploadedFile('F1', 'f1.txt', 'text/plain', UPLOAD_ERR_OK), |
||||
|
'foo' => ['f2' => $this->createUploadedFile('F2', 'f2.txt', 'text/plain', UPLOAD_ERR_OK)], |
||||
|
], |
||||
|
[ |
||||
|
'REQUEST_METHOD' => 'POST', |
||||
|
'HTTP_HOST' => 'dunglas.fr', |
||||
|
'HTTP_X_SYMFONY' => '2.8', |
||||
|
'REQUEST_URI' => '/testCreateRequest?bar[baz]=42&foo=1', |
||||
|
'QUERY_STRING' => 'bar[baz]=42&foo=1', |
||||
|
], |
||||
|
'Content' |
||||
|
); |
||||
|
|
||||
|
$psrRequest = $this->factory->createRequest($request); |
||||
|
|
||||
|
$this->assertEquals('Content', $psrRequest->getBody()->__toString()); |
||||
|
|
||||
|
$queryParams = $psrRequest->getQueryParams(); |
||||
|
$this->assertEquals('1', $queryParams['foo']); |
||||
|
$this->assertEquals('42', $queryParams['bar']['baz']); |
||||
|
|
||||
|
$requestTarget = $psrRequest->getRequestTarget(); |
||||
|
$this->assertEquals('/testCreateRequest?bar[baz]=42&foo=1', urldecode($requestTarget)); |
||||
|
|
||||
|
$parsedBody = $psrRequest->getParsedBody(); |
||||
|
$this->assertEquals('Kévin Dunglas', $parsedBody['twitter']['@dunglas']); |
||||
|
$this->assertEquals('Les-Tilleuls.coop', $parsedBody['twitter']['@coopTilleuls']); |
||||
|
$this->assertEquals('2', $parsedBody['baz']); |
||||
|
|
||||
|
$attributes = $psrRequest->getAttributes(); |
||||
|
$this->assertEquals($stdClass, $attributes['a1']); |
||||
|
$this->assertEquals('bar', $attributes['a2']['foo']); |
||||
|
|
||||
|
$cookies = $psrRequest->getCookieParams(); |
||||
|
$this->assertEquals('foo', $cookies['c1']); |
||||
|
$this->assertEquals('bar', $cookies['c2']['c3']); |
||||
|
|
||||
|
$uploadedFiles = $psrRequest->getUploadedFiles(); |
||||
|
$this->assertEquals('F1', $uploadedFiles['f1']->getStream()->__toString()); |
||||
|
$this->assertEquals('f1.txt', $uploadedFiles['f1']->getClientFilename()); |
||||
|
$this->assertEquals('text/plain', $uploadedFiles['f1']->getClientMediaType()); |
||||
|
$this->assertEquals(UPLOAD_ERR_OK, $uploadedFiles['f1']->getError()); |
||||
|
|
||||
|
$this->assertEquals('F2', $uploadedFiles['foo']['f2']->getStream()->__toString()); |
||||
|
$this->assertEquals('f2.txt', $uploadedFiles['foo']['f2']->getClientFilename()); |
||||
|
$this->assertEquals('text/plain', $uploadedFiles['foo']['f2']->getClientMediaType()); |
||||
|
$this->assertEquals(UPLOAD_ERR_OK, $uploadedFiles['foo']['f2']->getError()); |
||||
|
|
||||
|
$serverParams = $psrRequest->getServerParams(); |
||||
|
$this->assertEquals('POST', $serverParams['REQUEST_METHOD']); |
||||
|
$this->assertEquals('2.8', $serverParams['HTTP_X_SYMFONY']); |
||||
|
$this->assertEquals('POST', $psrRequest->getMethod()); |
||||
|
$this->assertEquals(['2.8'], $psrRequest->getHeader('X-Symfony')); |
||||
|
} |
||||
|
|
||||
|
public function testGetContentCanBeCalledAfterRequestCreation() |
||||
|
{ |
||||
|
$header = ['HTTP_HOST' => 'dunglas.fr']; |
||||
|
$request = new Request([], [], [], [], [], $header, 'Content'); |
||||
|
|
||||
|
$psrRequest = $this->factory->createRequest($request); |
||||
|
|
||||
|
$this->assertEquals('Content', $psrRequest->getBody()->__toString()); |
||||
|
$this->assertEquals('Content', $request->getContent()); |
||||
|
} |
||||
|
|
||||
|
private function createUploadedFile($content, $originalName, $mimeType, $error) |
||||
|
{ |
||||
|
$path = tempnam($this->tmpDir, uniqid()); |
||||
|
file_put_contents($path, $content); |
||||
|
|
||||
|
if (class_exists('Symfony\Component\HttpFoundation\HeaderUtils')) { |
||||
|
// Symfony 4.1+ |
||||
|
return new UploadedFile($path, $originalName, $mimeType, $error, true); |
||||
|
} |
||||
|
|
||||
|
return new UploadedFile($path, $originalName, $mimeType, filesize($path), $error, true); |
||||
|
} |
||||
|
|
||||
|
public function testCreateResponse() |
||||
|
{ |
||||
|
$response = new Response( |
||||
|
'Response content.', |
||||
|
202, |
||||
|
['X-Symfony' => ['3.4']] |
||||
|
); |
||||
|
$response->headers->setCookie(new Cookie('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT'), '/', null, false, true, false, 'lax')); |
||||
|
|
||||
|
$psrResponse = $this->factory->createResponse($response); |
||||
|
$this->assertEquals('Response content.', $psrResponse->getBody()->__toString()); |
||||
|
$this->assertEquals(202, $psrResponse->getStatusCode()); |
||||
|
$this->assertEquals(['3.4'], $psrResponse->getHeader('X-Symfony')); |
||||
|
|
||||
|
$cookieHeader = $psrResponse->getHeader('Set-Cookie'); |
||||
|
$this->assertInternalType('array', $cookieHeader); |
||||
|
$this->assertCount(1, $cookieHeader); |
||||
|
$this->assertRegExp('{city=Lille; expires=Wed, 13-Jan-2021 22:23:01 GMT;( max-age=\d+;)? path=/; httponly}i', $cookieHeader[0]); |
||||
|
} |
||||
|
|
||||
|
public function testCreateResponseFromStreamed() |
||||
|
{ |
||||
|
$response = new StreamedResponse(function () { |
||||
|
echo "Line 1\n"; |
||||
|
flush(); |
||||
|
|
||||
|
echo "Line 2\n"; |
||||
|
flush(); |
||||
|
}); |
||||
|
|
||||
|
$psrResponse = $this->factory->createResponse($response); |
||||
|
|
||||
|
$this->assertEquals("Line 1\nLine 2\n", $psrResponse->getBody()->__toString()); |
||||
|
} |
||||
|
|
||||
|
public function testCreateResponseFromBinaryFile() |
||||
|
{ |
||||
|
$path = tempnam($this->tmpDir, uniqid()); |
||||
|
file_put_contents($path, 'Binary'); |
||||
|
|
||||
|
$response = new BinaryFileResponse($path); |
||||
|
|
||||
|
$psrResponse = $this->factory->createResponse($response); |
||||
|
|
||||
|
$this->assertEquals('Binary', $psrResponse->getBody()->__toString()); |
||||
|
} |
||||
|
|
||||
|
public function testUploadErrNoFile() |
||||
|
{ |
||||
|
if (class_exists('Symfony\Component\HttpFoundation\HeaderUtils')) { |
||||
|
// Symfony 4.1+ |
||||
|
$file = new UploadedFile('', '', null, UPLOAD_ERR_NO_FILE, true); |
||||
|
} else { |
||||
|
$file = new UploadedFile('', '', null, 0, UPLOAD_ERR_NO_FILE, true); |
||||
|
} |
||||
|
$this->assertEquals(0, $file->getSize()); |
||||
|
$this->assertEquals(UPLOAD_ERR_NO_FILE, $file->getError()); |
||||
|
$this->assertFalse($file->getSize(), 'SplFile::getSize() returns false on error'); |
||||
|
|
||||
|
$request = new Request( |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
[ |
||||
|
'f1' => $file, |
||||
|
'f2' => ['name' => null, 'type' => null, 'tmp_name' => null, 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0], |
||||
|
], |
||||
|
[ |
||||
|
'REQUEST_METHOD' => 'POST', |
||||
|
'HTTP_HOST' => 'dunglas.fr', |
||||
|
'HTTP_X_SYMFONY' => '2.8', |
||||
|
], |
||||
|
'Content' |
||||
|
); |
||||
|
|
||||
|
$psrRequest = $this->factory->createRequest($request); |
||||
|
|
||||
|
$uploadedFiles = $psrRequest->getUploadedFiles(); |
||||
|
|
||||
|
$this->assertEquals(UPLOAD_ERR_NO_FILE, $uploadedFiles['f1']->getError()); |
||||
|
$this->assertEquals(UPLOAD_ERR_NO_FILE, $uploadedFiles['f2']->getError()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Factory; |
||||
|
|
||||
|
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory; |
||||
|
|
||||
|
/** |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
* @author Antonio J. García Lagar <aj@garcialagar.es> |
||||
|
* |
||||
|
* @group legacy |
||||
|
*/ |
||||
|
class DiactorosFactoryTest extends AbstractHttpMessageFactoryTest |
||||
|
{ |
||||
|
protected function buildHttpMessageFactory() |
||||
|
{ |
||||
|
if (!class_exists('Zend\Diactoros\ServerRequestFactory')) { |
||||
|
$this->markTestSkipped('Zend Diactoros is not installed.'); |
||||
|
} |
||||
|
|
||||
|
return new DiactorosFactory(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,250 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Factory; |
||||
|
|
||||
|
use PHPUnit\Framework\TestCase; |
||||
|
use Psr\Http\Message\UploadedFileInterface; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Response; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\ServerRequest; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Stream; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\UploadedFile; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Tests\Fixtures\Uri; |
||||
|
use Symfony\Component\HttpFoundation\Cookie; |
||||
|
|
||||
|
/** |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
class HttpFoundationFactoryTest extends TestCase |
||||
|
{ |
||||
|
/** @var HttpFoundationFactory */ |
||||
|
private $factory; |
||||
|
|
||||
|
/** @var string */ |
||||
|
private $tmpDir; |
||||
|
|
||||
|
public function setup() |
||||
|
{ |
||||
|
$this->factory = new HttpFoundationFactory(); |
||||
|
$this->tmpDir = sys_get_temp_dir(); |
||||
|
} |
||||
|
|
||||
|
public function testCreateRequest() |
||||
|
{ |
||||
|
$stdClass = new \stdClass(); |
||||
|
$serverRequest = new ServerRequest( |
||||
|
'1.1', |
||||
|
[ |
||||
|
'X-Dunglas-API-Platform' => '1.0', |
||||
|
'X-data' => ['a', 'b'], |
||||
|
], |
||||
|
new Stream('The body'), |
||||
|
'/about/kevin', |
||||
|
'GET', |
||||
|
'http://les-tilleuls.coop/about/kevin', |
||||
|
['country' => 'France'], |
||||
|
['city' => 'Lille'], |
||||
|
['url' => 'http://les-tilleuls.coop'], |
||||
|
[ |
||||
|
'doc1' => $this->createUploadedFile('Doc 1', UPLOAD_ERR_OK, 'doc1.txt', 'text/plain'), |
||||
|
'nested' => [ |
||||
|
'docs' => [ |
||||
|
$this->createUploadedFile('Doc 2', UPLOAD_ERR_OK, 'doc2.txt', 'text/plain'), |
||||
|
$this->createUploadedFile('Doc 3', UPLOAD_ERR_OK, 'doc3.txt', 'text/plain'), |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
['url' => 'http://dunglas.fr'], |
||||
|
['custom' => $stdClass] |
||||
|
); |
||||
|
|
||||
|
$symfonyRequest = $this->factory->createRequest($serverRequest); |
||||
|
$files = $symfonyRequest->files->all(); |
||||
|
|
||||
|
$this->assertEquals('http://les-tilleuls.coop', $symfonyRequest->query->get('url')); |
||||
|
$this->assertEquals('doc1.txt', $files['doc1']->getClientOriginalName()); |
||||
|
$this->assertEquals('doc2.txt', $files['nested']['docs'][0]->getClientOriginalName()); |
||||
|
$this->assertEquals('doc3.txt', $files['nested']['docs'][1]->getClientOriginalName()); |
||||
|
$this->assertEquals('http://dunglas.fr', $symfonyRequest->request->get('url')); |
||||
|
$this->assertEquals($stdClass, $symfonyRequest->attributes->get('custom')); |
||||
|
$this->assertEquals('Lille', $symfonyRequest->cookies->get('city')); |
||||
|
$this->assertEquals('France', $symfonyRequest->server->get('country')); |
||||
|
$this->assertEquals('The body', $symfonyRequest->getContent()); |
||||
|
$this->assertEquals('1.0', $symfonyRequest->headers->get('X-Dunglas-API-Platform')); |
||||
|
$this->assertEquals(['a', 'b'], $symfonyRequest->headers->get('X-data', null, false)); |
||||
|
} |
||||
|
|
||||
|
public function testCreateRequestWithNullParsedBody() |
||||
|
{ |
||||
|
$serverRequest = new ServerRequest( |
||||
|
'1.1', |
||||
|
[], |
||||
|
new Stream(), |
||||
|
'/', |
||||
|
'GET', |
||||
|
null, |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
null, |
||||
|
[] |
||||
|
); |
||||
|
|
||||
|
$this->assertCount(0, $this->factory->createRequest($serverRequest)->request); |
||||
|
} |
||||
|
|
||||
|
public function testCreateRequestWithObjectParsedBody() |
||||
|
{ |
||||
|
$serverRequest = new ServerRequest( |
||||
|
'1.1', |
||||
|
[], |
||||
|
new Stream(), |
||||
|
'/', |
||||
|
'GET', |
||||
|
null, |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
new \stdClass(), |
||||
|
[] |
||||
|
); |
||||
|
|
||||
|
$this->assertCount(0, $this->factory->createRequest($serverRequest)->request); |
||||
|
} |
||||
|
|
||||
|
public function testCreateRequestWithUri() |
||||
|
{ |
||||
|
$serverRequest = new ServerRequest( |
||||
|
'1.1', |
||||
|
[], |
||||
|
new Stream(), |
||||
|
'/', |
||||
|
'GET', |
||||
|
new Uri('http://les-tilleuls.coop/about/kevin'), |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
[], |
||||
|
null, |
||||
|
[] |
||||
|
); |
||||
|
|
||||
|
$this->assertEquals('/about/kevin', $this->factory->createRequest($serverRequest)->getPathInfo()); |
||||
|
} |
||||
|
|
||||
|
public function testCreateUploadedFile() |
||||
|
{ |
||||
|
$uploadedFile = $this->createUploadedFile('An uploaded file.', UPLOAD_ERR_OK, 'myfile.txt', 'text/plain'); |
||||
|
$symfonyUploadedFile = $this->callCreateUploadedFile($uploadedFile); |
||||
|
$size = $symfonyUploadedFile->getSize(); |
||||
|
|
||||
|
$uniqid = uniqid(); |
||||
|
$symfonyUploadedFile->move($this->tmpDir, $uniqid); |
||||
|
|
||||
|
$this->assertEquals($uploadedFile->getSize(), $size); |
||||
|
$this->assertEquals(UPLOAD_ERR_OK, $symfonyUploadedFile->getError()); |
||||
|
$this->assertEquals('myfile.txt', $symfonyUploadedFile->getClientOriginalName()); |
||||
|
$this->assertEquals('txt', $symfonyUploadedFile->getClientOriginalExtension()); |
||||
|
$this->assertEquals('text/plain', $symfonyUploadedFile->getClientMimeType()); |
||||
|
$this->assertEquals('An uploaded file.', file_get_contents($this->tmpDir.'/'.$uniqid)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileException |
||||
|
* @expectedExceptionMessage The file "e" could not be written on disk. |
||||
|
*/ |
||||
|
public function testCreateUploadedFileWithError() |
||||
|
{ |
||||
|
$uploadedFile = $this->createUploadedFile('Error.', UPLOAD_ERR_CANT_WRITE, 'e', 'text/plain'); |
||||
|
$symfonyUploadedFile = $this->callCreateUploadedFile($uploadedFile); |
||||
|
|
||||
|
$this->assertEquals(UPLOAD_ERR_CANT_WRITE, $symfonyUploadedFile->getError()); |
||||
|
|
||||
|
$symfonyUploadedFile->move($this->tmpDir, 'shouldFail.txt'); |
||||
|
} |
||||
|
|
||||
|
private function createUploadedFile($content, $error, $clientFileName, $clientMediaType) |
||||
|
{ |
||||
|
$filePath = tempnam($this->tmpDir, uniqid()); |
||||
|
file_put_contents($filePath, $content); |
||||
|
|
||||
|
return new UploadedFile($filePath, filesize($filePath), $error, $clientFileName, $clientMediaType); |
||||
|
} |
||||
|
|
||||
|
private function callCreateUploadedFile(UploadedFileInterface $uploadedFile) |
||||
|
{ |
||||
|
$reflection = new \ReflectionClass($this->factory); |
||||
|
$createUploadedFile = $reflection->getMethod('createUploadedFile'); |
||||
|
$createUploadedFile->setAccessible(true); |
||||
|
|
||||
|
return $createUploadedFile->invokeArgs($this->factory, [$uploadedFile]); |
||||
|
} |
||||
|
|
||||
|
public function testCreateResponse() |
||||
|
{ |
||||
|
$response = new Response( |
||||
|
'1.0', |
||||
|
[ |
||||
|
'X-Symfony' => ['2.8'], |
||||
|
'Set-Cookie' => [ |
||||
|
'theme=light', |
||||
|
'test', |
||||
|
'ABC=AeD; Domain=dunglas.fr; Path=/kevin; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; SameSite=Strict', |
||||
|
], |
||||
|
], |
||||
|
new Stream('The response body'), |
||||
|
200 |
||||
|
); |
||||
|
|
||||
|
$symfonyResponse = $this->factory->createResponse($response); |
||||
|
|
||||
|
$this->assertEquals('1.0', $symfonyResponse->getProtocolVersion()); |
||||
|
$this->assertEquals('2.8', $symfonyResponse->headers->get('X-Symfony')); |
||||
|
|
||||
|
$cookies = $symfonyResponse->headers->getCookies(); |
||||
|
$this->assertEquals('theme', $cookies[0]->getName()); |
||||
|
$this->assertEquals('light', $cookies[0]->getValue()); |
||||
|
$this->assertEquals(0, $cookies[0]->getExpiresTime()); |
||||
|
$this->assertNull($cookies[0]->getDomain()); |
||||
|
$this->assertEquals('/', $cookies[0]->getPath()); |
||||
|
$this->assertFalse($cookies[0]->isSecure()); |
||||
|
$this->assertFalse($cookies[0]->isHttpOnly()); |
||||
|
|
||||
|
$this->assertEquals('test', $cookies[1]->getName()); |
||||
|
$this->assertNull($cookies[1]->getValue()); |
||||
|
|
||||
|
$this->assertEquals('ABC', $cookies[2]->getName()); |
||||
|
$this->assertEquals('AeD', $cookies[2]->getValue()); |
||||
|
$this->assertEquals(strtotime('Wed, 13 Jan 2021 22:23:01 GMT'), $cookies[2]->getExpiresTime()); |
||||
|
$this->assertEquals('dunglas.fr', $cookies[2]->getDomain()); |
||||
|
$this->assertEquals('/kevin', $cookies[2]->getPath()); |
||||
|
$this->assertTrue($cookies[2]->isSecure()); |
||||
|
$this->assertTrue($cookies[2]->isHttpOnly()); |
||||
|
if (\defined('Symfony\Component\HttpFoundation\Cookie::SAMESITE_STRICT')) { |
||||
|
$this->assertEquals(Cookie::SAMESITE_STRICT, $cookies[2]->getSameSite()); |
||||
|
} |
||||
|
|
||||
|
$this->assertEquals('The response body', $symfonyResponse->getContent()); |
||||
|
$this->assertEquals(200, $symfonyResponse->getStatusCode()); |
||||
|
|
||||
|
$symfonyResponse = $this->factory->createResponse($response, true); |
||||
|
|
||||
|
ob_start(); |
||||
|
$symfonyResponse->sendContent(); |
||||
|
$sentContent = ob_get_clean(); |
||||
|
|
||||
|
$this->assertEquals('The response body', $sentContent); |
||||
|
$this->assertEquals(200, $symfonyResponse->getStatusCode()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Factory; |
||||
|
|
||||
|
use Nyholm\Psr7\Factory\Psr17Factory; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; |
||||
|
|
||||
|
/** |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
* @author Antonio J. García Lagar <aj@garcialagar.es> |
||||
|
*/ |
||||
|
class PsrHttpFactoryTest extends AbstractHttpMessageFactoryTest |
||||
|
{ |
||||
|
protected function buildHttpMessageFactory() |
||||
|
{ |
||||
|
$factory = new Psr17Factory(); |
||||
|
|
||||
|
return new PsrHttpFactory($factory, $factory, $factory, $factory); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures; |
||||
|
|
||||
|
use Psr\Http\Message\MessageInterface; |
||||
|
use Psr\Http\Message\StreamInterface; |
||||
|
|
||||
|
/** |
||||
|
* Message. |
||||
|
* |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
class Message implements MessageInterface |
||||
|
{ |
||||
|
private $version = '1.1'; |
||||
|
private $headers = []; |
||||
|
private $body; |
||||
|
|
||||
|
public function __construct($version = '1.1', array $headers = [], StreamInterface $body = null) |
||||
|
{ |
||||
|
$this->version = $version; |
||||
|
$this->headers = $headers; |
||||
|
$this->body = null === $body ? new Stream() : $body; |
||||
|
} |
||||
|
|
||||
|
public function getProtocolVersion() |
||||
|
{ |
||||
|
return $this->version; |
||||
|
} |
||||
|
|
||||
|
public function withProtocolVersion($version) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function getHeaders() |
||||
|
{ |
||||
|
return $this->headers; |
||||
|
} |
||||
|
|
||||
|
public function hasHeader($name) |
||||
|
{ |
||||
|
return isset($this->headers[$name]); |
||||
|
} |
||||
|
|
||||
|
public function getHeader($name) |
||||
|
{ |
||||
|
return $this->hasHeader($name) ? $this->headers[$name] : []; |
||||
|
} |
||||
|
|
||||
|
public function getHeaderLine($name) |
||||
|
{ |
||||
|
return $this->hasHeader($name) ? implode(',', $this->headers[$name]) : ''; |
||||
|
} |
||||
|
|
||||
|
public function withHeader($name, $value) |
||||
|
{ |
||||
|
$this->headers[$name] = (array) $value; |
||||
|
|
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
public function withAddedHeader($name, $value) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function withoutHeader($name) |
||||
|
{ |
||||
|
unset($this->headers[$name]); |
||||
|
|
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
public function getBody() |
||||
|
{ |
||||
|
return $this->body; |
||||
|
} |
||||
|
|
||||
|
public function withBody(StreamInterface $body) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures; |
||||
|
|
||||
|
use Psr\Http\Message\ResponseInterface; |
||||
|
use Psr\Http\Message\StreamInterface; |
||||
|
|
||||
|
/** |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
class Response extends Message implements ResponseInterface |
||||
|
{ |
||||
|
private $statusCode; |
||||
|
|
||||
|
public function __construct($version = '1.1', array $headers = [], StreamInterface $body = null, $statusCode = 200) |
||||
|
{ |
||||
|
parent::__construct($version, $headers, $body); |
||||
|
|
||||
|
$this->statusCode = $statusCode; |
||||
|
} |
||||
|
|
||||
|
public function getStatusCode() |
||||
|
{ |
||||
|
return $this->statusCode; |
||||
|
} |
||||
|
|
||||
|
public function withStatus($code, $reasonPhrase = '') |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function getReasonPhrase() |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,141 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures; |
||||
|
|
||||
|
use Psr\Http\Message\ServerRequestInterface; |
||||
|
use Psr\Http\Message\StreamInterface; |
||||
|
use Psr\Http\Message\UriInterface; |
||||
|
|
||||
|
/** |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
class ServerRequest extends Message implements ServerRequestInterface |
||||
|
{ |
||||
|
private $requestTarget; |
||||
|
private $method; |
||||
|
private $uri; |
||||
|
private $server; |
||||
|
private $cookies; |
||||
|
private $query; |
||||
|
private $uploadedFiles; |
||||
|
private $data; |
||||
|
private $attributes; |
||||
|
|
||||
|
public function __construct($version = '1.1', array $headers = [], StreamInterface $body = null, $requestTarget = '/', $method = 'GET', $uri = null, array $server = [], array $cookies = [], array $query = [], array $uploadedFiles = [], $data = null, array $attributes = []) |
||||
|
{ |
||||
|
parent::__construct($version, $headers, $body); |
||||
|
|
||||
|
$this->requestTarget = $requestTarget; |
||||
|
$this->method = $method; |
||||
|
$this->uri = $uri; |
||||
|
$this->server = $server; |
||||
|
$this->cookies = $cookies; |
||||
|
$this->query = $query; |
||||
|
$this->uploadedFiles = $uploadedFiles; |
||||
|
$this->data = $data; |
||||
|
$this->attributes = $attributes; |
||||
|
} |
||||
|
|
||||
|
public function getRequestTarget() |
||||
|
{ |
||||
|
return $this->requestTarget; |
||||
|
} |
||||
|
|
||||
|
public function withRequestTarget($requestTarget) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function getMethod() |
||||
|
{ |
||||
|
return $this->method; |
||||
|
} |
||||
|
|
||||
|
public function withMethod($method) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public function getUri() |
||||
|
{ |
||||
|
return $this->uri; |
||||
|
} |
||||
|
|
||||
|
public function withUri(UriInterface $uri, $preserveHost = false) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function getServerParams() |
||||
|
{ |
||||
|
return $this->server; |
||||
|
} |
||||
|
|
||||
|
public function getCookieParams() |
||||
|
{ |
||||
|
return $this->cookies; |
||||
|
} |
||||
|
|
||||
|
public function withCookieParams(array $cookies) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function getQueryParams() |
||||
|
{ |
||||
|
return $this->query; |
||||
|
} |
||||
|
|
||||
|
public function withQueryParams(array $query) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function getUploadedFiles() |
||||
|
{ |
||||
|
return $this->uploadedFiles; |
||||
|
} |
||||
|
|
||||
|
public function withUploadedFiles(array $uploadedFiles) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function getParsedBody() |
||||
|
{ |
||||
|
return $this->data; |
||||
|
} |
||||
|
|
||||
|
public function withParsedBody($data) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function getAttributes() |
||||
|
{ |
||||
|
return $this->attributes; |
||||
|
} |
||||
|
|
||||
|
public function getAttribute($name, $default = null) |
||||
|
{ |
||||
|
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; |
||||
|
} |
||||
|
|
||||
|
public function withAttribute($name, $value) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function withoutAttribute($name) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures; |
||||
|
|
||||
|
use Psr\Http\Message\StreamInterface; |
||||
|
|
||||
|
/** |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
class Stream implements StreamInterface |
||||
|
{ |
||||
|
private $stringContent; |
||||
|
private $eof = true; |
||||
|
|
||||
|
public function __construct($stringContent = '') |
||||
|
{ |
||||
|
$this->stringContent = $stringContent; |
||||
|
} |
||||
|
|
||||
|
public function __toString() |
||||
|
{ |
||||
|
return $this->stringContent; |
||||
|
} |
||||
|
|
||||
|
public function close() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public function detach() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public function getSize() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public function tell() |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public function eof() |
||||
|
{ |
||||
|
return $this->eof; |
||||
|
} |
||||
|
|
||||
|
public function isSeekable() |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public function seek($offset, $whence = SEEK_SET) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public function rewind() |
||||
|
{ |
||||
|
$this->eof = false; |
||||
|
} |
||||
|
|
||||
|
public function isWritable() |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function write($string) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public function isReadable() |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public function read($length) |
||||
|
{ |
||||
|
$this->eof = true; |
||||
|
|
||||
|
return $this->stringContent; |
||||
|
} |
||||
|
|
||||
|
public function getContents() |
||||
|
{ |
||||
|
return $this->stringContent; |
||||
|
} |
||||
|
|
||||
|
public function getMetadata($key = null) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures; |
||||
|
|
||||
|
use Psr\Http\Message\UploadedFileInterface; |
||||
|
|
||||
|
/** |
||||
|
* @author Kévin Dunglas <dunglas@gmail.com> |
||||
|
*/ |
||||
|
class UploadedFile implements UploadedFileInterface |
||||
|
{ |
||||
|
private $filePath; |
||||
|
private $size; |
||||
|
private $error; |
||||
|
private $clientFileName; |
||||
|
private $clientMediaType; |
||||
|
|
||||
|
public function __construct($filePath, $size = null, $error = UPLOAD_ERR_OK, $clientFileName = null, $clientMediaType = null) |
||||
|
{ |
||||
|
$this->filePath = $filePath; |
||||
|
$this->size = $size; |
||||
|
$this->error = $error; |
||||
|
$this->clientFileName = $clientFileName; |
||||
|
$this->clientMediaType = $clientMediaType; |
||||
|
} |
||||
|
|
||||
|
public function getStream() |
||||
|
{ |
||||
|
throw new \RuntimeException('No stream is available.'); |
||||
|
} |
||||
|
|
||||
|
public function moveTo($targetPath) |
||||
|
{ |
||||
|
rename($this->filePath, $targetPath); |
||||
|
} |
||||
|
|
||||
|
public function getSize() |
||||
|
{ |
||||
|
return $this->size; |
||||
|
} |
||||
|
|
||||
|
public function getError() |
||||
|
{ |
||||
|
return $this->error; |
||||
|
} |
||||
|
|
||||
|
public function getClientFilename() |
||||
|
{ |
||||
|
return $this->clientFileName; |
||||
|
} |
||||
|
|
||||
|
public function getClientMediaType() |
||||
|
{ |
||||
|
return $this->clientMediaType; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,135 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Fixtures; |
||||
|
|
||||
|
use Psr\Http\Message\UriInterface; |
||||
|
|
||||
|
/** |
||||
|
* @author Rougin Royce Gutib <rougingutib@gmail.com> |
||||
|
*/ |
||||
|
class Uri implements UriInterface |
||||
|
{ |
||||
|
private $scheme = ''; |
||||
|
private $userInfo = ''; |
||||
|
private $host = ''; |
||||
|
private $port; |
||||
|
private $path = ''; |
||||
|
private $query = ''; |
||||
|
private $fragment = ''; |
||||
|
private $uriString; |
||||
|
|
||||
|
public function __construct($uri = '') |
||||
|
{ |
||||
|
$parts = parse_url($uri); |
||||
|
|
||||
|
$this->scheme = isset($parts['scheme']) ? $parts['scheme'] : ''; |
||||
|
$this->userInfo = isset($parts['user']) ? $parts['user'] : ''; |
||||
|
$this->host = isset($parts['host']) ? $parts['host'] : ''; |
||||
|
$this->port = isset($parts['port']) ? $parts['port'] : null; |
||||
|
$this->path = isset($parts['path']) ? $parts['path'] : ''; |
||||
|
$this->query = isset($parts['query']) ? $parts['query'] : ''; |
||||
|
$this->fragment = isset($parts['fragment']) ? $parts['fragment'] : ''; |
||||
|
$this->uriString = $uri; |
||||
|
} |
||||
|
|
||||
|
public function getScheme() |
||||
|
{ |
||||
|
return $this->scheme; |
||||
|
} |
||||
|
|
||||
|
public function getAuthority() |
||||
|
{ |
||||
|
if (empty($this->host)) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
$authority = $this->host; |
||||
|
|
||||
|
if (!empty($this->userInfo)) { |
||||
|
$authority = $this->userInfo.'@'.$authority; |
||||
|
} |
||||
|
|
||||
|
$authority .= ':'.$this->port; |
||||
|
|
||||
|
return $authority; |
||||
|
} |
||||
|
|
||||
|
public function getUserInfo() |
||||
|
{ |
||||
|
return $this->userInfo; |
||||
|
} |
||||
|
|
||||
|
public function getHost() |
||||
|
{ |
||||
|
return $this->host; |
||||
|
} |
||||
|
|
||||
|
public function getPort() |
||||
|
{ |
||||
|
return $this->port; |
||||
|
} |
||||
|
|
||||
|
public function getPath() |
||||
|
{ |
||||
|
return $this->path; |
||||
|
} |
||||
|
|
||||
|
public function getQuery() |
||||
|
{ |
||||
|
return $this->query; |
||||
|
} |
||||
|
|
||||
|
public function getFragment() |
||||
|
{ |
||||
|
return $this->fragment; |
||||
|
} |
||||
|
|
||||
|
public function withScheme($scheme) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function withUserInfo($user, $password = null) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function withHost($host) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function withPort($port) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function withPath($path) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function withQuery($query) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function withFragment($fragment) |
||||
|
{ |
||||
|
throw new \BadMethodCallException('Not implemented.'); |
||||
|
} |
||||
|
|
||||
|
public function __toString() |
||||
|
{ |
||||
|
return $this->uriString; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,239 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Bridge\PsrHttpMessage\Tests\Functional; |
||||
|
|
||||
|
use Nyholm\Psr7\Factory\Psr17Factory; |
||||
|
use Nyholm\Psr7\Response as Psr7Response; |
||||
|
use Nyholm\Psr7\ServerRequest as Psr7Request; |
||||
|
use Nyholm\Psr7\Stream as Psr7Stream; |
||||
|
use PHPUnit\Framework\TestCase; |
||||
|
use Psr\Http\Message\ResponseInterface; |
||||
|
use Psr\Http\Message\ServerRequestInterface; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; |
||||
|
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; |
||||
|
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface; |
||||
|
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; |
||||
|
use Symfony\Component\HttpFoundation\Cookie; |
||||
|
use Symfony\Component\HttpFoundation\File\UploadedFile; |
||||
|
use Symfony\Component\HttpFoundation\Request; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
|
||||
|
/** |
||||
|
* Test to convert a request/response back and forth to make sure we do not loose data. |
||||
|
* |
||||
|
* @author Tobias Nyholm <tobias.nyholm@gmail.com> |
||||
|
*/ |
||||
|
class CovertTest extends TestCase |
||||
|
{ |
||||
|
private $tmpDir; |
||||
|
|
||||
|
public function setup() |
||||
|
{ |
||||
|
if (!class_exists('Nyholm\Psr7\ServerRequest')) { |
||||
|
$this->markTestSkipped('nyholm/psr7 is not installed.'); |
||||
|
} |
||||
|
|
||||
|
$this->tmpDir = sys_get_temp_dir(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @dataProvider requestProvider |
||||
|
* |
||||
|
* @param Request|ServerRequestInterface $request |
||||
|
* @param HttpFoundationFactoryInterface|HttpMessageFactoryInterface $firstFactory |
||||
|
* @param HttpFoundationFactoryInterface|HttpMessageFactoryInterface $secondFactory |
||||
|
*/ |
||||
|
public function testConvertRequestMultipleTimes($request, $firstFactory, $secondFactory) |
||||
|
{ |
||||
|
$temporaryRequest = $firstFactory->createRequest($request); |
||||
|
$finalRequest = $secondFactory->createRequest($temporaryRequest); |
||||
|
|
||||
|
if ($finalRequest instanceof Request) { |
||||
|
$this->assertEquals($request->getBasePath(), $finalRequest->getBasePath()); |
||||
|
$this->assertEquals($request->getBaseUrl(), $finalRequest->getBaseUrl()); |
||||
|
$this->assertEquals($request->getContent(), $finalRequest->getContent()); |
||||
|
$this->assertEquals($request->getEncodings(), $finalRequest->getEncodings()); |
||||
|
$this->assertEquals($request->getETags(), $finalRequest->getETags()); |
||||
|
$this->assertEquals($request->getHost(), $finalRequest->getHost()); |
||||
|
$this->assertEquals($request->getHttpHost(), $finalRequest->getHttpHost()); |
||||
|
$this->assertEquals($request->getMethod(), $finalRequest->getMethod()); |
||||
|
$this->assertEquals($request->getPassword(), $finalRequest->getPassword()); |
||||
|
$this->assertEquals($request->getPathInfo(), $finalRequest->getPathInfo()); |
||||
|
$this->assertEquals($request->getPort(), $finalRequest->getPort()); |
||||
|
$this->assertEquals($request->getProtocolVersion(), $finalRequest->getProtocolVersion()); |
||||
|
$this->assertEquals($request->getQueryString(), $finalRequest->getQueryString()); |
||||
|
$this->assertEquals($request->getRequestUri(), $finalRequest->getRequestUri()); |
||||
|
$this->assertEquals($request->getScheme(), $finalRequest->getScheme()); |
||||
|
$this->assertEquals($request->getSchemeAndHttpHost(), $finalRequest->getSchemeAndHttpHost()); |
||||
|
$this->assertEquals($request->getScriptName(), $finalRequest->getScriptName()); |
||||
|
$this->assertEquals($request->getUri(), $finalRequest->getUri()); |
||||
|
$this->assertEquals($request->getUser(), $finalRequest->getUser()); |
||||
|
$this->assertEquals($request->getUserInfo(), $finalRequest->getUserInfo()); |
||||
|
} elseif ($finalRequest instanceof ServerRequestInterface) { |
||||
|
$strToLower = function ($arr) { |
||||
|
foreach ($arr as $key => $value) { |
||||
|
yield strtolower($key) => $value; |
||||
|
} |
||||
|
}; |
||||
|
$this->assertEquals($request->getAttributes(), $finalRequest->getAttributes()); |
||||
|
$this->assertEquals($request->getCookieParams(), $finalRequest->getCookieParams()); |
||||
|
$this->assertEquals((array) $request->getParsedBody(), (array) $finalRequest->getParsedBody()); |
||||
|
$this->assertEquals($request->getQueryParams(), $finalRequest->getQueryParams()); |
||||
|
// PSR7 does not define a "withServerParams" so this is impossible to implement without knowing the PSR7 implementation. |
||||
|
//$this->assertEquals($request->getServerParams(), $finalRequest->getServerParams()); |
||||
|
$this->assertEquals($request->getUploadedFiles(), $finalRequest->getUploadedFiles()); |
||||
|
$this->assertEquals($request->getMethod(), $finalRequest->getMethod()); |
||||
|
$this->assertEquals($request->getRequestTarget(), $finalRequest->getRequestTarget()); |
||||
|
$this->assertEquals((string) $request->getUri(), (string) $finalRequest->getUri()); |
||||
|
$this->assertEquals((string) $request->getBody(), (string) $finalRequest->getBody()); |
||||
|
$this->assertEquals($strToLower($request->getHeaders()), $strToLower($finalRequest->getHeaders())); |
||||
|
$this->assertEquals($request->getProtocolVersion(), $finalRequest->getProtocolVersion()); |
||||
|
} else { |
||||
|
$this->fail('$finalRequest must be an instance of PSR7 or a HTTPFoundation request'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function requestProvider() |
||||
|
{ |
||||
|
$sfRequest = new Request( |
||||
|
[ |
||||
|
'foo' => '1', |
||||
|
'bar' => ['baz' => '42'], |
||||
|
], |
||||
|
[ |
||||
|
'twitter' => [ |
||||
|
'@dunglas' => 'Kévin Dunglas', |
||||
|
'@coopTilleuls' => 'Les-Tilleuls.coop', |
||||
|
], |
||||
|
'baz' => '2', |
||||
|
], |
||||
|
[ |
||||
|
'a2' => ['foo' => 'bar'], |
||||
|
], |
||||
|
[ |
||||
|
'c1' => 'foo', |
||||
|
'c2' => ['c3' => 'bar'], |
||||
|
], |
||||
|
[ |
||||
|
'f1' => $this->createUploadedFile('F1', 'f1.txt', 'text/plain', UPLOAD_ERR_OK), |
||||
|
'foo' => ['f2' => $this->createUploadedFile('F2', 'f2.txt', 'text/plain', UPLOAD_ERR_OK)], |
||||
|
], |
||||
|
[ |
||||
|
'REQUEST_METHOD' => 'POST', |
||||
|
'HTTP_HOST' => 'dunglas.fr', |
||||
|
'SERVER_NAME' => 'dunglas.fr', |
||||
|
'SERVER_PORT' => null, |
||||
|
'HTTP_X_SYMFONY' => '2.8', |
||||
|
'REQUEST_URI' => '/testCreateRequest?bar[baz]=42&foo=1', |
||||
|
'QUERY_STRING' => 'foo=1&bar[baz]=42', |
||||
|
], |
||||
|
'Content' |
||||
|
); |
||||
|
|
||||
|
$psr7Request = (new Psr7Request('POST', 'http://tnyholm.se/foo/?bar=biz')) |
||||
|
->withQueryParams(['bar' => 'biz']); |
||||
|
|
||||
|
$nyholmFactory = new Psr17Factory(); |
||||
|
$psr17Factory = new PsrHttpFactory($nyholmFactory, $nyholmFactory, $nyholmFactory, $nyholmFactory); |
||||
|
$symfonyFactory = new HttpFoundationFactory(); |
||||
|
|
||||
|
return [ |
||||
|
[$sfRequest, $psr17Factory, $symfonyFactory], |
||||
|
[$psr7Request, $symfonyFactory, $psr17Factory], |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @dataProvider responseProvider |
||||
|
* |
||||
|
* @param Response|ResponseInterface $response |
||||
|
* @param HttpFoundationFactoryInterface|HttpMessageFactoryInterface $firstFactory |
||||
|
* @param HttpFoundationFactoryInterface|HttpMessageFactoryInterface $secondFactory |
||||
|
*/ |
||||
|
public function testConvertResponseMultipleTimes($response, $firstFactory, $secondFactory) |
||||
|
{ |
||||
|
$temporaryResponse = $firstFactory->createResponse($response); |
||||
|
$finalResponse = $secondFactory->createResponse($temporaryResponse); |
||||
|
|
||||
|
if ($finalResponse instanceof Response) { |
||||
|
$this->assertEquals($response->getAge(), $finalResponse->getAge()); |
||||
|
$this->assertEquals($response->getCharset(), $finalResponse->getCharset()); |
||||
|
$this->assertEquals($response->getContent(), $finalResponse->getContent()); |
||||
|
$this->assertEquals($response->getDate(), $finalResponse->getDate()); |
||||
|
$this->assertEquals($response->getEtag(), $finalResponse->getEtag()); |
||||
|
$this->assertEquals($response->getExpires(), $finalResponse->getExpires()); |
||||
|
$this->assertEquals($response->getLastModified(), $finalResponse->getLastModified()); |
||||
|
$this->assertEquals($response->getMaxAge(), $finalResponse->getMaxAge()); |
||||
|
$this->assertEquals($response->getProtocolVersion(), $finalResponse->getProtocolVersion()); |
||||
|
$this->assertEquals($response->getStatusCode(), $finalResponse->getStatusCode()); |
||||
|
$this->assertEquals($response->getTtl(), $finalResponse->getTtl()); |
||||
|
} elseif ($finalResponse instanceof ResponseInterface) { |
||||
|
$strToLower = function ($arr) { |
||||
|
foreach ($arr as $key => $value) { |
||||
|
yield strtolower($key) => $value; |
||||
|
} |
||||
|
}; |
||||
|
$this->assertEquals($response->getStatusCode(), $finalResponse->getStatusCode()); |
||||
|
$this->assertEquals($response->getReasonPhrase(), $finalResponse->getReasonPhrase()); |
||||
|
$this->assertEquals((string) $response->getBody(), (string) $finalResponse->getBody()); |
||||
|
$this->assertEquals($strToLower($response->getHeaders()), $strToLower($finalResponse->getHeaders())); |
||||
|
$this->assertEquals($response->getProtocolVersion(), $finalResponse->getProtocolVersion()); |
||||
|
} else { |
||||
|
$this->fail('$finalResponse must be an instance of PSR7 or a HTTPFoundation response'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function responseProvider() |
||||
|
{ |
||||
|
$sfResponse = new Response( |
||||
|
'Response content.', |
||||
|
202, |
||||
|
['x-symfony' => ['3.4']] |
||||
|
); |
||||
|
|
||||
|
if (method_exists(Cookie::class, 'create')) { |
||||
|
$cookie = Cookie::create('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT')); |
||||
|
} else { |
||||
|
$cookie = new Cookie('city', 'Lille', new \DateTime('Wed, 13 Jan 2021 22:23:01 GMT')); |
||||
|
} |
||||
|
|
||||
|
$sfResponse->headers->setCookie($cookie); |
||||
|
$body = Psr7Stream::create(); |
||||
|
$status = 302; |
||||
|
$headers = [ |
||||
|
'location' => ['http://example.com/'], |
||||
|
]; |
||||
|
$zendResponse = new Psr7Response($status, $headers, $body); |
||||
|
|
||||
|
$nyholmFactory = new Psr17Factory(); |
||||
|
$psr17Factory = new PsrHttpFactory($nyholmFactory, $nyholmFactory, $nyholmFactory, $nyholmFactory); |
||||
|
$symfonyFactory = new HttpFoundationFactory(); |
||||
|
|
||||
|
return [ |
||||
|
[$sfResponse, $psr17Factory, $symfonyFactory], |
||||
|
[$zendResponse, $symfonyFactory, $psr17Factory], |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
private function createUploadedFile($content, $originalName, $mimeType, $error) |
||||
|
{ |
||||
|
$path = tempnam($this->tmpDir, uniqid()); |
||||
|
file_put_contents($path, $content); |
||||
|
|
||||
|
if (class_exists('Symfony\Component\HttpFoundation\HeaderUtils')) { |
||||
|
// Symfony 4.1+ |
||||
|
return new UploadedFile($path, $originalName, $mimeType, $error, true); |
||||
|
} |
||||
|
|
||||
|
return new UploadedFile($path, $originalName, $mimeType, filesize($path), $error, true); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
{ |
||||
|
"name": "symfony/psr-http-message-bridge", |
||||
|
"type": "symfony-bridge", |
||||
|
"description": "PSR HTTP message bridge", |
||||
|
"keywords": ["http", "psr-7", "psr-17", "http-message"], |
||||
|
"homepage": "http://symfony.com", |
||||
|
"license": "MIT", |
||||
|
"authors": [ |
||||
|
{ |
||||
|
"name": "Fabien Potencier", |
||||
|
"email": "fabien@symfony.com" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Symfony Community", |
||||
|
"homepage": "http://symfony.com/contributors" |
||||
|
} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": "^7.1", |
||||
|
"psr/http-message": "^1.0", |
||||
|
"symfony/http-foundation": "^3.4 || ^4.0" |
||||
|
}, |
||||
|
"require-dev": { |
||||
|
"symfony/phpunit-bridge": "^3.4.20 || ^4.0", |
||||
|
"nyholm/psr7": "^1.1", |
||||
|
"zendframework/zend-diactoros": "^1.4.1 || ^2.0" |
||||
|
}, |
||||
|
"suggest": { |
||||
|
"nyholm/psr7": "For a super lightweight PSR-7/17 implementation" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { "Symfony\\Bridge\\PsrHttpMessage\\": "" }, |
||||
|
"exclude-from-classmap": [ |
||||
|
"/Tests/" |
||||
|
] |
||||
|
}, |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.2-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<phpunit backupGlobals="false" |
||||
|
backupStaticAttributes="false" |
||||
|
colors="true" |
||||
|
convertErrorsToExceptions="true" |
||||
|
convertNoticesToExceptions="true" |
||||
|
convertWarningsToExceptions="true" |
||||
|
processIsolation="false" |
||||
|
stopOnFailure="false" |
||||
|
syntaxCheck="false" |
||||
|
bootstrap="vendor/autoload.php" |
||||
|
> |
||||
|
<testsuites> |
||||
|
<testsuite name="Symfony PSR-7 HTTP message Bridge Test Suite"> |
||||
|
<directory>./Tests/</directory> |
||||
|
</testsuite> |
||||
|
</testsuites> |
||||
|
|
||||
|
<filter> |
||||
|
<whitelist> |
||||
|
<directory>./</directory> |
||||
|
<exclude> |
||||
|
<directory>./Resources</directory> |
||||
|
<directory>./Tests</directory> |
||||
|
<directory>./vendor</directory> |
||||
|
</exclude> |
||||
|
</whitelist> |
||||
|
</filter> |
||||
|
</phpunit> |
||||
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2018-2019 Fabien Potencier |
||||
|
|
||||
|
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. |
||||
@ -0,0 +1,9 @@ |
|||||
|
Symfony Service Contracts |
||||
|
========================= |
||||
|
|
||||
|
A set of abstractions extracted out of the Symfony components. |
||||
|
|
||||
|
Can be used to build on semantics that the Symfony components proved useful - and |
||||
|
that already have battle tested implementations. |
||||
|
|
||||
|
See https://github.com/symfony/contracts/blob/master/README.md for more information. |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Contracts\Service; |
||||
|
|
||||
|
/** |
||||
|
* Provides a way to reset an object to its initial state. |
||||
|
* |
||||
|
* When calling the "reset()" method on an object, it should be put back to its |
||||
|
* initial state. This usually means clearing any internal buffers and forwarding |
||||
|
* the call to internal dependencies. All properties of the object should be put |
||||
|
* back to the same state it had when it was first ready to use. |
||||
|
* |
||||
|
* This method could be called, for example, to recycle objects that are used as |
||||
|
* services, so that they can be used to handle several requests in the same |
||||
|
* process loop (note that we advise making your services stateless instead of |
||||
|
* implementing this interface when possible.) |
||||
|
*/ |
||||
|
interface ResetInterface |
||||
|
{ |
||||
|
public function reset(); |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Contracts\Service; |
||||
|
|
||||
|
use Psr\Container\ContainerExceptionInterface; |
||||
|
use Psr\Container\NotFoundExceptionInterface; |
||||
|
|
||||
|
/** |
||||
|
* A trait to help implement ServiceProviderInterface. |
||||
|
* |
||||
|
* @author Robin Chalas <robin.chalas@gmail.com> |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
*/ |
||||
|
trait ServiceLocatorTrait |
||||
|
{ |
||||
|
private $factories; |
||||
|
private $loading = []; |
||||
|
private $providedTypes; |
||||
|
|
||||
|
/** |
||||
|
* @param callable[] $factories |
||||
|
*/ |
||||
|
public function __construct(array $factories) |
||||
|
{ |
||||
|
$this->factories = $factories; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
* |
||||
|
* @return bool |
||||
|
*/ |
||||
|
public function has($id) |
||||
|
{ |
||||
|
return isset($this->factories[$id]); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function get($id) |
||||
|
{ |
||||
|
if (!isset($this->factories[$id])) { |
||||
|
throw $this->createNotFoundException($id); |
||||
|
} |
||||
|
|
||||
|
if (isset($this->loading[$id])) { |
||||
|
$ids = array_values($this->loading); |
||||
|
$ids = \array_slice($this->loading, array_search($id, $ids)); |
||||
|
$ids[] = $id; |
||||
|
|
||||
|
throw $this->createCircularReferenceException($id, $ids); |
||||
|
} |
||||
|
|
||||
|
$this->loading[$id] = $id; |
||||
|
try { |
||||
|
return $this->factories[$id]($this); |
||||
|
} finally { |
||||
|
unset($this->loading[$id]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* {@inheritdoc} |
||||
|
*/ |
||||
|
public function getProvidedServices(): array |
||||
|
{ |
||||
|
if (null === $this->providedTypes) { |
||||
|
$this->providedTypes = []; |
||||
|
|
||||
|
foreach ($this->factories as $name => $factory) { |
||||
|
if (!\is_callable($factory)) { |
||||
|
$this->providedTypes[$name] = '?'; |
||||
|
} else { |
||||
|
$type = (new \ReflectionFunction($factory))->getReturnType(); |
||||
|
|
||||
|
$this->providedTypes[$name] = $type ? ($type->allowsNull() ? '?' : '').$type->getName() : '?'; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $this->providedTypes; |
||||
|
} |
||||
|
|
||||
|
private function createNotFoundException(string $id): NotFoundExceptionInterface |
||||
|
{ |
||||
|
if (!$alternatives = array_keys($this->factories)) { |
||||
|
$message = 'is empty...'; |
||||
|
} else { |
||||
|
$last = array_pop($alternatives); |
||||
|
if ($alternatives) { |
||||
|
$message = sprintf('only knows about the "%s" and "%s" services.', implode('", "', $alternatives), $last); |
||||
|
} else { |
||||
|
$message = sprintf('only knows about the "%s" service.', $last); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ($this->loading) { |
||||
|
$message = sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $message); |
||||
|
} else { |
||||
|
$message = sprintf('Service "%s" not found: the current service locator %s', $id, $message); |
||||
|
} |
||||
|
|
||||
|
return new class($message) extends \InvalidArgumentException implements NotFoundExceptionInterface { |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private function createCircularReferenceException(string $id, array $path): ContainerExceptionInterface |
||||
|
{ |
||||
|
return new class(sprintf('Circular reference detected for service "%s", path: "%s".', $id, implode(' -> ', $path))) extends \RuntimeException implements ContainerExceptionInterface { |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Contracts\Service; |
||||
|
|
||||
|
use Psr\Container\ContainerInterface; |
||||
|
|
||||
|
/** |
||||
|
* A ServiceProviderInterface exposes the identifiers and the types of services provided by a container. |
||||
|
* |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
* @author Mateusz Sip <mateusz.sip@gmail.com> |
||||
|
*/ |
||||
|
interface ServiceProviderInterface extends ContainerInterface |
||||
|
{ |
||||
|
/** |
||||
|
* Returns an associative array of service types keyed by the identifiers provided by the current container. |
||||
|
* |
||||
|
* Examples: |
||||
|
* |
||||
|
* * ['logger' => 'Psr\Log\LoggerInterface'] means the object provides a service named "logger" that implements Psr\Log\LoggerInterface |
||||
|
* * ['foo' => '?'] means the container provides service name "foo" of unspecified type |
||||
|
* * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null |
||||
|
* |
||||
|
* @return string[] The provided service types, keyed by service names |
||||
|
*/ |
||||
|
public function getProvidedServices(): array; |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Contracts\Service; |
||||
|
|
||||
|
/** |
||||
|
* A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method. |
||||
|
* |
||||
|
* The getSubscribedServices method returns an array of service types required by such instances, |
||||
|
* optionally keyed by the service names used internally. Service types that start with an interrogation |
||||
|
* mark "?" are optional, while the other ones are mandatory service dependencies. |
||||
|
* |
||||
|
* The injected service locators SHOULD NOT allow access to any other services not specified by the method. |
||||
|
* |
||||
|
* It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally. |
||||
|
* This interface does not dictate any injection method for these service locators, although constructor |
||||
|
* injection is recommended. |
||||
|
* |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
*/ |
||||
|
interface ServiceSubscriberInterface |
||||
|
{ |
||||
|
/** |
||||
|
* Returns an array of service types required by such instances, optionally keyed by the service names used internally. |
||||
|
* |
||||
|
* For mandatory dependencies: |
||||
|
* |
||||
|
* * ['logger' => 'Psr\Log\LoggerInterface'] means the objects use the "logger" name |
||||
|
* internally to fetch a service which must implement Psr\Log\LoggerInterface. |
||||
|
* * ['loggers' => 'Psr\Log\LoggerInterface[]'] means the objects use the "loggers" name |
||||
|
* internally to fetch an iterable of Psr\Log\LoggerInterface instances. |
||||
|
* * ['Psr\Log\LoggerInterface'] is a shortcut for |
||||
|
* * ['Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface'] |
||||
|
* |
||||
|
* otherwise: |
||||
|
* |
||||
|
* * ['logger' => '?Psr\Log\LoggerInterface'] denotes an optional dependency |
||||
|
* * ['loggers' => '?Psr\Log\LoggerInterface[]'] denotes an optional iterable dependency |
||||
|
* * ['?Psr\Log\LoggerInterface'] is a shortcut for |
||||
|
* * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface'] |
||||
|
* |
||||
|
* @return array The required service types, optionally keyed by service names |
||||
|
*/ |
||||
|
public static function getSubscribedServices(); |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Contracts\Service; |
||||
|
|
||||
|
use Psr\Container\ContainerInterface; |
||||
|
|
||||
|
/** |
||||
|
* Implementation of ServiceSubscriberInterface that determines subscribed services from |
||||
|
* private method return types. Service ids are available as "ClassName::methodName". |
||||
|
* |
||||
|
* @author Kevin Bond <kevinbond@gmail.com> |
||||
|
*/ |
||||
|
trait ServiceSubscriberTrait |
||||
|
{ |
||||
|
/** @var ContainerInterface */ |
||||
|
private $container; |
||||
|
|
||||
|
public static function getSubscribedServices(): array |
||||
|
{ |
||||
|
static $services; |
||||
|
|
||||
|
if (null !== $services) { |
||||
|
return $services; |
||||
|
} |
||||
|
|
||||
|
$services = \is_callable(['parent', __FUNCTION__]) ? parent::getSubscribedServices() : []; |
||||
|
|
||||
|
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) { |
||||
|
if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) { |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (self::class === $method->getDeclaringClass()->name && ($returnType = $method->getReturnType()) && !$returnType->isBuiltin()) { |
||||
|
$services[self::class.'::'.$method->name] = '?'.$returnType->getName(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $services; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @required |
||||
|
*/ |
||||
|
public function setContainer(ContainerInterface $container) |
||||
|
{ |
||||
|
$this->container = $container; |
||||
|
|
||||
|
if (\is_callable(['parent', __FUNCTION__])) { |
||||
|
return parent::setContainer($container); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Contracts\Service\Test; |
||||
|
|
||||
|
use PHPUnit\Framework\TestCase; |
||||
|
use Psr\Container\ContainerInterface; |
||||
|
use Symfony\Contracts\Service\ServiceLocatorTrait; |
||||
|
|
||||
|
class ServiceLocatorTest extends TestCase |
||||
|
{ |
||||
|
public function getServiceLocator(array $factories) |
||||
|
{ |
||||
|
return new class($factories) implements ContainerInterface { |
||||
|
use ServiceLocatorTrait; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public function testHas() |
||||
|
{ |
||||
|
$locator = $this->getServiceLocator([ |
||||
|
'foo' => function () { return 'bar'; }, |
||||
|
'bar' => function () { return 'baz'; }, |
||||
|
function () { return 'dummy'; }, |
||||
|
]); |
||||
|
|
||||
|
$this->assertTrue($locator->has('foo')); |
||||
|
$this->assertTrue($locator->has('bar')); |
||||
|
$this->assertFalse($locator->has('dummy')); |
||||
|
} |
||||
|
|
||||
|
public function testGet() |
||||
|
{ |
||||
|
$locator = $this->getServiceLocator([ |
||||
|
'foo' => function () { return 'bar'; }, |
||||
|
'bar' => function () { return 'baz'; }, |
||||
|
]); |
||||
|
|
||||
|
$this->assertSame('bar', $locator->get('foo')); |
||||
|
$this->assertSame('baz', $locator->get('bar')); |
||||
|
} |
||||
|
|
||||
|
public function testGetDoesNotMemoize() |
||||
|
{ |
||||
|
$i = 0; |
||||
|
$locator = $this->getServiceLocator([ |
||||
|
'foo' => function () use (&$i) { |
||||
|
++$i; |
||||
|
|
||||
|
return 'bar'; |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
$this->assertSame('bar', $locator->get('foo')); |
||||
|
$this->assertSame('bar', $locator->get('foo')); |
||||
|
$this->assertSame(2, $i); |
||||
|
} |
||||
|
|
||||
|
public function testThrowsOnUndefinedInternalService() |
||||
|
{ |
||||
|
if (!$this->getExpectedException()) { |
||||
|
$this->expectException('Psr\Container\NotFoundExceptionInterface'); |
||||
|
$this->expectExceptionMessage('The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service.'); |
||||
|
} |
||||
|
$locator = $this->getServiceLocator([ |
||||
|
'foo' => function () use (&$locator) { return $locator->get('bar'); }, |
||||
|
]); |
||||
|
|
||||
|
$locator->get('foo'); |
||||
|
} |
||||
|
|
||||
|
public function testThrowsOnCircularReference() |
||||
|
{ |
||||
|
$this->expectException('Psr\Container\ContainerExceptionInterface'); |
||||
|
$this->expectExceptionMessage('Circular reference detected for service "bar", path: "bar -> baz -> bar".'); |
||||
|
$locator = $this->getServiceLocator([ |
||||
|
'foo' => function () use (&$locator) { return $locator->get('bar'); }, |
||||
|
'bar' => function () use (&$locator) { return $locator->get('baz'); }, |
||||
|
'baz' => function () use (&$locator) { return $locator->get('bar'); }, |
||||
|
]); |
||||
|
|
||||
|
$locator->get('foo'); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
{ |
||||
|
"name": "symfony/service-contracts", |
||||
|
"type": "library", |
||||
|
"description": "Generic abstractions related to writing services", |
||||
|
"keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"], |
||||
|
"homepage": "https://symfony.com", |
||||
|
"license": "MIT", |
||||
|
"authors": [ |
||||
|
{ |
||||
|
"name": "Nicolas Grekas", |
||||
|
"email": "p@tchwork.com" |
||||
|
}, |
||||
|
{ |
||||
|
"name": "Symfony Community", |
||||
|
"homepage": "https://symfony.com/contributors" |
||||
|
} |
||||
|
], |
||||
|
"require": { |
||||
|
"php": "^7.1.3", |
||||
|
"psr/container": "^1.0" |
||||
|
}, |
||||
|
"suggest": { |
||||
|
"symfony/service-implementation": "" |
||||
|
}, |
||||
|
"autoload": { |
||||
|
"psr-4": { "Symfony\\Contracts\\Service\\": "" } |
||||
|
}, |
||||
|
"minimum-stability": "dev", |
||||
|
"extra": { |
||||
|
"branch-alias": { |
||||
|
"dev-master": "1.1-dev" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
composer.lock |
||||
|
phpunit.xml |
||||
|
vendor/ |
||||
@ -0,0 +1,7 @@ |
|||||
|
CHANGELOG |
||||
|
========= |
||||
|
|
||||
|
4.2.0 |
||||
|
----- |
||||
|
|
||||
|
* added the component |
||||
@ -0,0 +1,20 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter\Exception; |
||||
|
|
||||
|
class ClassNotFoundException extends \Exception implements ExceptionInterface |
||||
|
{ |
||||
|
public function __construct(string $class, \Throwable $previous = null) |
||||
|
{ |
||||
|
parent::__construct(sprintf('Class "%s" not found.', $class), 0, $previous); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter\Exception; |
||||
|
|
||||
|
interface ExceptionInterface extends \Throwable |
||||
|
{ |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter\Exception; |
||||
|
|
||||
|
class NotInstantiableTypeException extends \Exception implements ExceptionInterface |
||||
|
{ |
||||
|
public function __construct(string $type) |
||||
|
{ |
||||
|
parent::__construct(sprintf('Type "%s" is not instantiable.', $type)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,94 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter; |
||||
|
|
||||
|
use Symfony\Component\VarExporter\Exception\ExceptionInterface; |
||||
|
use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; |
||||
|
use Symfony\Component\VarExporter\Internal\Hydrator; |
||||
|
use Symfony\Component\VarExporter\Internal\Registry; |
||||
|
|
||||
|
/** |
||||
|
* A utility class to create objects without calling their constructor. |
||||
|
* |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
*/ |
||||
|
final class Instantiator |
||||
|
{ |
||||
|
/** |
||||
|
* Creates an object and sets its properties without calling its constructor nor any other methods. |
||||
|
* |
||||
|
* For example: |
||||
|
* |
||||
|
* // creates an empty instance of Foo |
||||
|
* Instantiator::instantiate(Foo::class); |
||||
|
* |
||||
|
* // creates a Foo instance and sets one of its properties |
||||
|
* Instantiator::instantiate(Foo::class, ['propertyName' => $propertyValue]); |
||||
|
* |
||||
|
* // creates a Foo instance and sets a private property defined on its parent Bar class |
||||
|
* Instantiator::instantiate(Foo::class, [], [ |
||||
|
* Bar::class => ['privateBarProperty' => $propertyValue], |
||||
|
* ]); |
||||
|
* |
||||
|
* Instances of ArrayObject, ArrayIterator and SplObjectHash can be created |
||||
|
* by using the special "\0" property name to define their internal value: |
||||
|
* |
||||
|
* // creates an SplObjectHash where $info1 is attached to $obj1, etc. |
||||
|
* Instantiator::instantiate(SplObjectStorage::class, ["\0" => [$obj1, $info1, $obj2, $info2...]]); |
||||
|
* |
||||
|
* // creates an ArrayObject populated with $inputArray |
||||
|
* Instantiator::instantiate(ArrayObject::class, ["\0" => [$inputArray]]); |
||||
|
* |
||||
|
* @param string $class The class of the instance to create |
||||
|
* @param array $properties The properties to set on the instance |
||||
|
* @param array $privateProperties The private properties to set on the instance, |
||||
|
* keyed by their declaring class |
||||
|
* |
||||
|
* @return object The created instance |
||||
|
* |
||||
|
* @throws ExceptionInterface When the instance cannot be created |
||||
|
*/ |
||||
|
public static function instantiate(string $class, array $properties = [], array $privateProperties = []) |
||||
|
{ |
||||
|
$reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); |
||||
|
|
||||
|
if (Registry::$cloneable[$class]) { |
||||
|
$wrappedInstance = [clone Registry::$prototypes[$class]]; |
||||
|
} elseif (Registry::$instantiableWithoutConstructor[$class]) { |
||||
|
$wrappedInstance = [$reflector->newInstanceWithoutConstructor()]; |
||||
|
} elseif (null === Registry::$prototypes[$class]) { |
||||
|
throw new NotInstantiableTypeException($class); |
||||
|
} elseif ($reflector->implementsInterface('Serializable') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__unserialize'))) { |
||||
|
$wrappedInstance = [unserialize('C:'.\strlen($class).':"'.$class.'":0:{}')]; |
||||
|
} else { |
||||
|
$wrappedInstance = [unserialize('O:'.\strlen($class).':"'.$class.'":0:{}')]; |
||||
|
} |
||||
|
|
||||
|
if ($properties) { |
||||
|
$privateProperties[$class] = isset($privateProperties[$class]) ? $properties + $privateProperties[$class] : $properties; |
||||
|
} |
||||
|
|
||||
|
foreach ($privateProperties as $class => $properties) { |
||||
|
if (!$properties) { |
||||
|
continue; |
||||
|
} |
||||
|
foreach ($properties as $name => $value) { |
||||
|
// because they're also used for "unserialization", hydrators |
||||
|
// deal with array of instances, so we need to wrap values |
||||
|
$properties[$name] = [$value]; |
||||
|
} |
||||
|
(Hydrator::$hydrators[$class] ?? Hydrator::getHydrator($class))($properties, $wrappedInstance); |
||||
|
} |
||||
|
|
||||
|
return $wrappedInstance[0]; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,406 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter\Internal; |
||||
|
|
||||
|
use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; |
||||
|
|
||||
|
/** |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
* |
||||
|
* @internal |
||||
|
*/ |
||||
|
class Exporter |
||||
|
{ |
||||
|
/** |
||||
|
* Prepares an array of values for VarExporter. |
||||
|
* |
||||
|
* For performance this method is public and has no type-hints. |
||||
|
* |
||||
|
* @param array &$values |
||||
|
* @param \SplObjectStorage $objectsPool |
||||
|
* @param array &$refsPool |
||||
|
* @param int &$objectsCount |
||||
|
* @param bool &$valuesAreStatic |
||||
|
* |
||||
|
* @return int |
||||
|
* |
||||
|
* @throws NotInstantiableTypeException When a value cannot be serialized |
||||
|
*/ |
||||
|
public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic) |
||||
|
{ |
||||
|
$refs = $values; |
||||
|
foreach ($values as $k => $value) { |
||||
|
if (\is_resource($value)) { |
||||
|
throw new NotInstantiableTypeException(get_resource_type($value).' resource'); |
||||
|
} |
||||
|
$refs[$k] = $objectsPool; |
||||
|
|
||||
|
if ($isRef = !$valueIsStatic = $values[$k] !== $objectsPool) { |
||||
|
$values[$k] = &$value; // Break hard references to make $values completely |
||||
|
unset($value); // independent from the original structure |
||||
|
$refs[$k] = $value = $values[$k]; |
||||
|
if ($value instanceof Reference && 0 > $value->id) { |
||||
|
$valuesAreStatic = false; |
||||
|
++$value->count; |
||||
|
continue; |
||||
|
} |
||||
|
$refsPool[] = [&$refs[$k], $value, &$value]; |
||||
|
$refs[$k] = $values[$k] = new Reference(-\count($refsPool), $value); |
||||
|
} |
||||
|
|
||||
|
if (\is_array($value)) { |
||||
|
if ($value) { |
||||
|
$value = self::prepare($value, $objectsPool, $refsPool, $objectsCount, $valueIsStatic); |
||||
|
} |
||||
|
goto handle_value; |
||||
|
} elseif (!\is_object($value) && !$value instanceof \__PHP_Incomplete_Class) { |
||||
|
goto handle_value; |
||||
|
} |
||||
|
|
||||
|
$valueIsStatic = false; |
||||
|
if (isset($objectsPool[$value])) { |
||||
|
++$objectsCount; |
||||
|
$value = new Reference($objectsPool[$value][0]); |
||||
|
goto handle_value; |
||||
|
} |
||||
|
|
||||
|
$class = \get_class($value); |
||||
|
$reflector = Registry::$reflectors[$class] ?? Registry::getClassReflector($class); |
||||
|
|
||||
|
if ($reflector->hasMethod('__serialize')) { |
||||
|
if (!$reflector->getMethod('__serialize')->isPublic()) { |
||||
|
throw new \Error(sprintf('Call to %s method %s::__serialize()', $reflector->getMethod('__serialize')->isProtected() ? 'protected' : 'private', $class)); |
||||
|
} |
||||
|
|
||||
|
if (!\is_array($properties = $value->__serialize())) { |
||||
|
throw new \Typerror($class.'::__serialize() must return an array'); |
||||
|
} |
||||
|
|
||||
|
goto prepare_value; |
||||
|
} |
||||
|
|
||||
|
$properties = []; |
||||
|
$sleep = null; |
||||
|
$arrayValue = (array) $value; |
||||
|
$proto = Registry::$prototypes[$class]; |
||||
|
|
||||
|
if (($value instanceof \ArrayIterator || $value instanceof \ArrayObject) && null !== $proto) { |
||||
|
// ArrayIterator and ArrayObject need special care because their "flags" |
||||
|
// option changes the behavior of the (array) casting operator. |
||||
|
$properties = self::getArrayObjectProperties($value, $arrayValue, $proto); |
||||
|
|
||||
|
// populates Registry::$prototypes[$class] with a new instance |
||||
|
Registry::getClassReflector($class, Registry::$instantiableWithoutConstructor[$class], Registry::$cloneable[$class]); |
||||
|
} elseif ($value instanceof \SplObjectStorage && Registry::$cloneable[$class] && null !== $proto) { |
||||
|
// By implementing Serializable, SplObjectStorage breaks |
||||
|
// internal references; let's deal with it on our own. |
||||
|
foreach (clone $value as $v) { |
||||
|
$properties[] = $v; |
||||
|
$properties[] = $value[$v]; |
||||
|
} |
||||
|
$properties = ['SplObjectStorage' => ["\0" => $properties]]; |
||||
|
} elseif ($value instanceof \Serializable || $value instanceof \__PHP_Incomplete_Class) { |
||||
|
++$objectsCount; |
||||
|
$objectsPool[$value] = [$id = \count($objectsPool), serialize($value), [], 0]; |
||||
|
$value = new Reference($id); |
||||
|
goto handle_value; |
||||
|
} |
||||
|
|
||||
|
if (method_exists($class, '__sleep')) { |
||||
|
if (!\is_array($sleep = $value->__sleep())) { |
||||
|
trigger_error('serialize(): __sleep should return an array only containing the names of instance-variables to serialize', E_USER_NOTICE); |
||||
|
$value = null; |
||||
|
goto handle_value; |
||||
|
} |
||||
|
foreach ($sleep as $name) { |
||||
|
if (property_exists($value, $name) && !$reflector->hasProperty($name)) { |
||||
|
$arrayValue[$name] = $value->$name; |
||||
|
} |
||||
|
} |
||||
|
$sleep = array_flip($sleep); |
||||
|
} |
||||
|
|
||||
|
$proto = (array) $proto; |
||||
|
|
||||
|
foreach ($arrayValue as $name => $v) { |
||||
|
$i = 0; |
||||
|
$n = (string) $name; |
||||
|
if ('' === $n || "\0" !== $n[0]) { |
||||
|
$c = 'stdClass'; |
||||
|
} elseif ('*' === $n[1]) { |
||||
|
$n = substr($n, 3); |
||||
|
$c = $reflector->getProperty($n)->class; |
||||
|
if ('Error' === $c) { |
||||
|
$c = 'TypeError'; |
||||
|
} elseif ('Exception' === $c) { |
||||
|
$c = 'ErrorException'; |
||||
|
} |
||||
|
} else { |
||||
|
$i = strpos($n, "\0", 2); |
||||
|
$c = substr($n, 1, $i - 1); |
||||
|
$n = substr($n, 1 + $i); |
||||
|
} |
||||
|
if (null !== $sleep) { |
||||
|
if (!isset($sleep[$n]) || ($i && $c !== $class)) { |
||||
|
continue; |
||||
|
} |
||||
|
$sleep[$n] = false; |
||||
|
} |
||||
|
if (!\array_key_exists($name, $proto) || $proto[$name] !== $v || "\x00Error\x00trace" === $name || "\x00Exception\x00trace" === $name) { |
||||
|
$properties[$c][$n] = $v; |
||||
|
} |
||||
|
} |
||||
|
if ($sleep) { |
||||
|
foreach ($sleep as $n => $v) { |
||||
|
if (false !== $v) { |
||||
|
trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $n), E_USER_NOTICE); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
prepare_value: |
||||
|
$objectsPool[$value] = [$id = \count($objectsPool)]; |
||||
|
$properties = self::prepare($properties, $objectsPool, $refsPool, $objectsCount, $valueIsStatic); |
||||
|
++$objectsCount; |
||||
|
$objectsPool[$value] = [$id, $class, $properties, method_exists($class, '__unserialize') ? -$objectsCount : (method_exists($class, '__wakeup') ? $objectsCount : 0)]; |
||||
|
|
||||
|
$value = new Reference($id); |
||||
|
|
||||
|
handle_value: |
||||
|
if ($isRef) { |
||||
|
unset($value); // Break the hard reference created above |
||||
|
} elseif (!$valueIsStatic) { |
||||
|
$values[$k] = $value; |
||||
|
} |
||||
|
$valuesAreStatic = $valueIsStatic && $valuesAreStatic; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
public static function export($value, $indent = '') |
||||
|
{ |
||||
|
switch (true) { |
||||
|
case \is_int($value) || \is_float($value): return var_export($value, true); |
||||
|
case [] === $value: return '[]'; |
||||
|
case false === $value: return 'false'; |
||||
|
case true === $value: return 'true'; |
||||
|
case null === $value: return 'null'; |
||||
|
case '' === $value: return "''"; |
||||
|
} |
||||
|
|
||||
|
if ($value instanceof Reference) { |
||||
|
if (0 <= $value->id) { |
||||
|
return '$o['.$value->id.']'; |
||||
|
} |
||||
|
if (!$value->count) { |
||||
|
return self::export($value->value, $indent); |
||||
|
} |
||||
|
$value = -$value->id; |
||||
|
|
||||
|
return '&$r['.$value.']'; |
||||
|
} |
||||
|
$subIndent = $indent.' '; |
||||
|
|
||||
|
if (\is_string($value)) { |
||||
|
$code = var_export($value, true); |
||||
|
|
||||
|
if (false !== strpos($value, "\n") || false !== strpos($value, "\r")) { |
||||
|
$code = strtr($code, [ |
||||
|
"\r\n" => "'.\"\\r\\n\"\n".$subIndent.".'", |
||||
|
"\r" => "'.\"\\r\"\n".$subIndent.".'", |
||||
|
"\n" => "'.\"\\n\"\n".$subIndent.".'", |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
if (false !== strpos($value, "\0")) { |
||||
|
$code = str_replace('\' . "\0" . \'', '\'."\0".\'', $code); |
||||
|
$code = str_replace('".\'\'."', '', $code); |
||||
|
} |
||||
|
|
||||
|
if (false !== strpos($code, "''.")) { |
||||
|
$code = str_replace("''.", '', $code); |
||||
|
} |
||||
|
|
||||
|
if (".''" === substr($code, -3)) { |
||||
|
$code = rtrim(substr($code, 0, -3)); |
||||
|
} |
||||
|
|
||||
|
return $code; |
||||
|
} |
||||
|
|
||||
|
if (\is_array($value)) { |
||||
|
$j = -1; |
||||
|
$code = ''; |
||||
|
foreach ($value as $k => $v) { |
||||
|
$code .= $subIndent; |
||||
|
if (!\is_int($k) || 1 !== $k - $j) { |
||||
|
$code .= self::export($k, $subIndent).' => '; |
||||
|
} |
||||
|
if (\is_int($k) && $k > $j) { |
||||
|
$j = $k; |
||||
|
} |
||||
|
$code .= self::export($v, $subIndent).",\n"; |
||||
|
} |
||||
|
|
||||
|
return "[\n".$code.$indent.']'; |
||||
|
} |
||||
|
|
||||
|
if ($value instanceof Values) { |
||||
|
$code = $subIndent."\$r = [],\n"; |
||||
|
foreach ($value->values as $k => $v) { |
||||
|
$code .= $subIndent.'$r['.$k.'] = '.self::export($v, $subIndent).",\n"; |
||||
|
} |
||||
|
|
||||
|
return "[\n".$code.$indent.']'; |
||||
|
} |
||||
|
|
||||
|
if ($value instanceof Registry) { |
||||
|
return self::exportRegistry($value, $indent, $subIndent); |
||||
|
} |
||||
|
|
||||
|
if ($value instanceof Hydrator) { |
||||
|
return self::exportHydrator($value, $indent, $subIndent); |
||||
|
} |
||||
|
|
||||
|
throw new \UnexpectedValueException(sprintf('Cannot export value of type "%s".', \is_object($value) ? \get_class($value) : \gettype($value))); |
||||
|
} |
||||
|
|
||||
|
private static function exportRegistry(Registry $value, string $indent, string $subIndent): string |
||||
|
{ |
||||
|
$code = ''; |
||||
|
$serializables = []; |
||||
|
$seen = []; |
||||
|
$prototypesAccess = 0; |
||||
|
$factoriesAccess = 0; |
||||
|
$r = '\\'.Registry::class; |
||||
|
$j = -1; |
||||
|
|
||||
|
foreach ($value as $k => $class) { |
||||
|
if (':' === ($class[1] ?? null)) { |
||||
|
$serializables[$k] = $class; |
||||
|
continue; |
||||
|
} |
||||
|
if (!Registry::$instantiableWithoutConstructor[$class]) { |
||||
|
if (is_subclass_of($class, 'Serializable') && !method_exists($class, '__unserialize')) { |
||||
|
$serializables[$k] = 'C:'.\strlen($class).':"'.$class.'":0:{}'; |
||||
|
} else { |
||||
|
$serializables[$k] = 'O:'.\strlen($class).':"'.$class.'":0:{}'; |
||||
|
} |
||||
|
if (is_subclass_of($class, 'Throwable')) { |
||||
|
$eol = is_subclass_of($class, 'Error') ? "\0Error\0" : "\0Exception\0"; |
||||
|
$serializables[$k] = substr_replace($serializables[$k], '1:{s:'.(5 + \strlen($eol)).':"'.$eol.'trace";a:0:{}}', -4); |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
$code .= $subIndent.(1 !== $k - $j ? $k.' => ' : ''); |
||||
|
$j = $k; |
||||
|
$eol = ",\n"; |
||||
|
$c = '['.self::export($class).']'; |
||||
|
|
||||
|
if ($seen[$class] ?? false) { |
||||
|
if (Registry::$cloneable[$class]) { |
||||
|
++$prototypesAccess; |
||||
|
$code .= 'clone $p'.$c; |
||||
|
} else { |
||||
|
++$factoriesAccess; |
||||
|
$code .= '$f'.$c.'()'; |
||||
|
} |
||||
|
} else { |
||||
|
$seen[$class] = true; |
||||
|
if (Registry::$cloneable[$class]) { |
||||
|
$code .= 'clone ('.($prototypesAccess++ ? '$p' : '($p = &'.$r.'::$prototypes)').$c.' ?? '.$r.'::p'; |
||||
|
} else { |
||||
|
$code .= '('.($factoriesAccess++ ? '$f' : '($f = &'.$r.'::$factories)').$c.' ?? '.$r.'::f'; |
||||
|
$eol = '()'.$eol; |
||||
|
} |
||||
|
$code .= '('.substr($c, 1, -1).'))'; |
||||
|
} |
||||
|
$code .= $eol; |
||||
|
} |
||||
|
|
||||
|
if (1 === $prototypesAccess) { |
||||
|
$code = str_replace('($p = &'.$r.'::$prototypes)', $r.'::$prototypes', $code); |
||||
|
} |
||||
|
if (1 === $factoriesAccess) { |
||||
|
$code = str_replace('($f = &'.$r.'::$factories)', $r.'::$factories', $code); |
||||
|
} |
||||
|
if ('' !== $code) { |
||||
|
$code = "\n".$code.$indent; |
||||
|
} |
||||
|
|
||||
|
if ($serializables) { |
||||
|
$code = $r.'::unserialize(['.$code.'], '.self::export($serializables, $indent).')'; |
||||
|
} else { |
||||
|
$code = '['.$code.']'; |
||||
|
} |
||||
|
|
||||
|
return '$o = '.$code; |
||||
|
} |
||||
|
|
||||
|
private static function exportHydrator(Hydrator $value, string $indent, string $subIndent): string |
||||
|
{ |
||||
|
$code = ''; |
||||
|
foreach ($value->properties as $class => $properties) { |
||||
|
$code .= $subIndent.' '.self::export($class).' => '.self::export($properties, $subIndent.' ').",\n"; |
||||
|
} |
||||
|
|
||||
|
$code = [ |
||||
|
self::export($value->registry, $subIndent), |
||||
|
self::export($value->values, $subIndent), |
||||
|
'' !== $code ? "[\n".$code.$subIndent.']' : '[]', |
||||
|
self::export($value->value, $subIndent), |
||||
|
self::export($value->wakeups, $subIndent), |
||||
|
]; |
||||
|
|
||||
|
return '\\'.\get_class($value)."::hydrate(\n".$subIndent.implode(",\n".$subIndent, $code)."\n".$indent.')'; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param \ArrayIterator|\ArrayObject $value |
||||
|
* @param \ArrayIterator|\ArrayObject $proto |
||||
|
*/ |
||||
|
private static function getArrayObjectProperties($value, array &$arrayValue, $proto): array |
||||
|
{ |
||||
|
$reflector = $value instanceof \ArrayIterator ? 'ArrayIterator' : 'ArrayObject'; |
||||
|
$reflector = Registry::$reflectors[$reflector] ?? Registry::getClassReflector($reflector); |
||||
|
|
||||
|
$properties = [ |
||||
|
$arrayValue, |
||||
|
$reflector->getMethod('getFlags')->invoke($value), |
||||
|
$value instanceof \ArrayObject ? $reflector->getMethod('getIteratorClass')->invoke($value) : 'ArrayIterator', |
||||
|
]; |
||||
|
|
||||
|
$reflector = $reflector->getMethod('setFlags'); |
||||
|
$reflector->invoke($proto, \ArrayObject::STD_PROP_LIST); |
||||
|
|
||||
|
if ($properties[1] & \ArrayObject::STD_PROP_LIST) { |
||||
|
$reflector->invoke($value, 0); |
||||
|
$properties[0] = (array) $value; |
||||
|
} else { |
||||
|
$reflector->invoke($value, \ArrayObject::STD_PROP_LIST); |
||||
|
$arrayValue = (array) $value; |
||||
|
} |
||||
|
$reflector->invoke($value, $properties[1]); |
||||
|
|
||||
|
if ([[], 0, 'ArrayIterator'] === $properties) { |
||||
|
$properties = []; |
||||
|
} else { |
||||
|
if ('ArrayIterator' === $properties[2]) { |
||||
|
unset($properties[2]); |
||||
|
} |
||||
|
$properties = [$reflector->class => ["\0" => $properties]]; |
||||
|
} |
||||
|
|
||||
|
return $properties; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,151 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter\Internal; |
||||
|
|
||||
|
use Symfony\Component\VarExporter\Exception\ClassNotFoundException; |
||||
|
|
||||
|
/** |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
* |
||||
|
* @internal |
||||
|
*/ |
||||
|
class Hydrator |
||||
|
{ |
||||
|
public static $hydrators = []; |
||||
|
|
||||
|
public $registry; |
||||
|
public $values; |
||||
|
public $properties; |
||||
|
public $value; |
||||
|
public $wakeups; |
||||
|
|
||||
|
public function __construct(?Registry $registry, ?Values $values, array $properties, $value, array $wakeups) |
||||
|
{ |
||||
|
$this->registry = $registry; |
||||
|
$this->values = $values; |
||||
|
$this->properties = $properties; |
||||
|
$this->value = $value; |
||||
|
$this->wakeups = $wakeups; |
||||
|
} |
||||
|
|
||||
|
public static function hydrate($objects, $values, $properties, $value, $wakeups) |
||||
|
{ |
||||
|
foreach ($properties as $class => $vars) { |
||||
|
(self::$hydrators[$class] ?? self::getHydrator($class))($vars, $objects); |
||||
|
} |
||||
|
foreach ($wakeups as $k => $v) { |
||||
|
if (\is_array($v)) { |
||||
|
$objects[-$k]->__unserialize($v); |
||||
|
} else { |
||||
|
$objects[$v]->__wakeup(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $value; |
||||
|
} |
||||
|
|
||||
|
public static function getHydrator($class) |
||||
|
{ |
||||
|
if ('stdClass' === $class) { |
||||
|
return self::$hydrators[$class] = static function ($properties, $objects) { |
||||
|
foreach ($properties as $name => $values) { |
||||
|
foreach ($values as $i => $v) { |
||||
|
$objects[$i]->$name = $v; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
if (!class_exists($class) && !interface_exists($class, false) && !trait_exists($class, false)) { |
||||
|
throw new ClassNotFoundException($class); |
||||
|
} |
||||
|
$classReflector = new \ReflectionClass($class); |
||||
|
|
||||
|
if (!$classReflector->isInternal()) { |
||||
|
return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, $class); |
||||
|
} |
||||
|
|
||||
|
if ($classReflector->name !== $class) { |
||||
|
return self::$hydrators[$classReflector->name] ?? self::getHydrator($classReflector->name); |
||||
|
} |
||||
|
|
||||
|
switch ($class) { |
||||
|
case 'ArrayIterator': |
||||
|
case 'ArrayObject': |
||||
|
$constructor = \Closure::fromCallable([$classReflector->getConstructor(), 'invokeArgs']); |
||||
|
|
||||
|
return self::$hydrators[$class] = static function ($properties, $objects) use ($constructor) { |
||||
|
foreach ($properties as $name => $values) { |
||||
|
if ("\0" !== $name) { |
||||
|
foreach ($values as $i => $v) { |
||||
|
$objects[$i]->$name = $v; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
foreach ($properties["\0"] ?? [] as $i => $v) { |
||||
|
$constructor($objects[$i], $v); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
case 'ErrorException': |
||||
|
return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, new class() extends \ErrorException { |
||||
|
}); |
||||
|
|
||||
|
case 'TypeError': |
||||
|
return self::$hydrators[$class] = (self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'))->bindTo(null, new class() extends \Error { |
||||
|
}); |
||||
|
|
||||
|
case 'SplObjectStorage': |
||||
|
return self::$hydrators[$class] = static function ($properties, $objects) { |
||||
|
foreach ($properties as $name => $values) { |
||||
|
if ("\0" === $name) { |
||||
|
foreach ($values as $i => $v) { |
||||
|
for ($j = 0; $j < \count($v); ++$j) { |
||||
|
$objects[$i]->attach($v[$j], $v[++$j]); |
||||
|
} |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
foreach ($values as $i => $v) { |
||||
|
$objects[$i]->$name = $v; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
$propertySetters = []; |
||||
|
foreach ($classReflector->getProperties() as $propertyReflector) { |
||||
|
if (!$propertyReflector->isStatic()) { |
||||
|
$propertyReflector->setAccessible(true); |
||||
|
$propertySetters[$propertyReflector->name] = \Closure::fromCallable([$propertyReflector, 'setValue']); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!$propertySetters) { |
||||
|
return self::$hydrators[$class] = self::$hydrators['stdClass'] ?? self::getHydrator('stdClass'); |
||||
|
} |
||||
|
|
||||
|
return self::$hydrators[$class] = static function ($properties, $objects) use ($propertySetters) { |
||||
|
foreach ($properties as $name => $values) { |
||||
|
if ($setValue = $propertySetters[$name] ?? null) { |
||||
|
foreach ($values as $i => $v) { |
||||
|
$setValue($objects[$i], $v); |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
foreach ($values as $i => $v) { |
||||
|
$objects[$i]->$name = $v; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter\Internal; |
||||
|
|
||||
|
/** |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
* |
||||
|
* @internal |
||||
|
*/ |
||||
|
class Reference |
||||
|
{ |
||||
|
public $id; |
||||
|
public $value; |
||||
|
public $count = 0; |
||||
|
|
||||
|
public function __construct(int $id, $value = null) |
||||
|
{ |
||||
|
$this->id = $id; |
||||
|
$this->value = $value; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter\Internal; |
||||
|
|
||||
|
use Symfony\Component\VarExporter\Exception\ClassNotFoundException; |
||||
|
use Symfony\Component\VarExporter\Exception\NotInstantiableTypeException; |
||||
|
|
||||
|
/** |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
* |
||||
|
* @internal |
||||
|
*/ |
||||
|
class Registry |
||||
|
{ |
||||
|
public static $reflectors = []; |
||||
|
public static $prototypes = []; |
||||
|
public static $factories = []; |
||||
|
public static $cloneable = []; |
||||
|
public static $instantiableWithoutConstructor = []; |
||||
|
|
||||
|
public function __construct(array $classes) |
||||
|
{ |
||||
|
foreach ($classes as $i => $class) { |
||||
|
$this->$i = $class; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static function unserialize($objects, $serializables) |
||||
|
{ |
||||
|
$unserializeCallback = ini_set('unserialize_callback_func', __CLASS__.'::getClassReflector'); |
||||
|
|
||||
|
try { |
||||
|
foreach ($serializables as $k => $v) { |
||||
|
$objects[$k] = unserialize($v); |
||||
|
} |
||||
|
} finally { |
||||
|
ini_set('unserialize_callback_func', $unserializeCallback); |
||||
|
} |
||||
|
|
||||
|
return $objects; |
||||
|
} |
||||
|
|
||||
|
public static function p($class) |
||||
|
{ |
||||
|
self::getClassReflector($class, true, true); |
||||
|
|
||||
|
return self::$prototypes[$class]; |
||||
|
} |
||||
|
|
||||
|
public static function f($class) |
||||
|
{ |
||||
|
$reflector = self::$reflectors[$class] ?? self::getClassReflector($class, true, false); |
||||
|
|
||||
|
return self::$factories[$class] = \Closure::fromCallable([$reflector, 'newInstanceWithoutConstructor']); |
||||
|
} |
||||
|
|
||||
|
public static function getClassReflector($class, $instantiableWithoutConstructor = false, $cloneable = null) |
||||
|
{ |
||||
|
if (!($isClass = class_exists($class)) && !interface_exists($class, false) && !trait_exists($class, false)) { |
||||
|
throw new ClassNotFoundException($class); |
||||
|
} |
||||
|
$reflector = new \ReflectionClass($class); |
||||
|
|
||||
|
if ($instantiableWithoutConstructor) { |
||||
|
$proto = $reflector->newInstanceWithoutConstructor(); |
||||
|
} elseif (!$isClass || $reflector->isAbstract()) { |
||||
|
throw new NotInstantiableTypeException($class); |
||||
|
} elseif ($reflector->name !== $class) { |
||||
|
$reflector = self::$reflectors[$name = $reflector->name] ?? self::getClassReflector($name, false, $cloneable); |
||||
|
self::$cloneable[$class] = self::$cloneable[$name]; |
||||
|
self::$instantiableWithoutConstructor[$class] = self::$instantiableWithoutConstructor[$name]; |
||||
|
self::$prototypes[$class] = self::$prototypes[$name]; |
||||
|
|
||||
|
return self::$reflectors[$class] = $reflector; |
||||
|
} else { |
||||
|
try { |
||||
|
$proto = $reflector->newInstanceWithoutConstructor(); |
||||
|
$instantiableWithoutConstructor = true; |
||||
|
} catch (\ReflectionException $e) { |
||||
|
$proto = $reflector->implementsInterface('Serializable') && !method_exists($class, '__unserialize') ? 'C:' : 'O:'; |
||||
|
if ('C:' === $proto && !$reflector->getMethod('unserialize')->isInternal()) { |
||||
|
$proto = null; |
||||
|
} elseif (false === $proto = @unserialize($proto.\strlen($class).':"'.$class.'":0:{}')) { |
||||
|
throw new NotInstantiableTypeException($class); |
||||
|
} |
||||
|
} |
||||
|
if (null !== $proto && !$proto instanceof \Throwable && !$proto instanceof \Serializable && !method_exists($class, '__sleep') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__serialize'))) { |
||||
|
try { |
||||
|
serialize($proto); |
||||
|
} catch (\Exception $e) { |
||||
|
throw new NotInstantiableTypeException($class, $e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (null === $cloneable) { |
||||
|
if (($proto instanceof \Reflector || $proto instanceof \ReflectionGenerator || $proto instanceof \ReflectionType || $proto instanceof \IteratorIterator || $proto instanceof \RecursiveIteratorIterator) && (!$proto instanceof \Serializable && !method_exists($proto, '__wakeup') && (\PHP_VERSION_ID < 70400 || !method_exists($class, '__unserialize')))) { |
||||
|
throw new NotInstantiableTypeException($class); |
||||
|
} |
||||
|
|
||||
|
$cloneable = $reflector->isCloneable() && !$reflector->hasMethod('__clone'); |
||||
|
} |
||||
|
|
||||
|
self::$cloneable[$class] = $cloneable; |
||||
|
self::$instantiableWithoutConstructor[$class] = $instantiableWithoutConstructor; |
||||
|
self::$prototypes[$class] = $proto; |
||||
|
|
||||
|
if ($proto instanceof \Throwable) { |
||||
|
static $setTrace; |
||||
|
|
||||
|
if (null === $setTrace) { |
||||
|
$setTrace = [ |
||||
|
new \ReflectionProperty(\Error::class, 'trace'), |
||||
|
new \ReflectionProperty(\Exception::class, 'trace'), |
||||
|
]; |
||||
|
$setTrace[0]->setAccessible(true); |
||||
|
$setTrace[1]->setAccessible(true); |
||||
|
$setTrace[0] = \Closure::fromCallable([$setTrace[0], 'setValue']); |
||||
|
$setTrace[1] = \Closure::fromCallable([$setTrace[1], 'setValue']); |
||||
|
} |
||||
|
|
||||
|
$setTrace[$proto instanceof \Exception]($proto, []); |
||||
|
} |
||||
|
|
||||
|
return self::$reflectors[$class] = $reflector; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/* |
||||
|
* This file is part of the Symfony package. |
||||
|
* |
||||
|
* (c) Fabien Potencier <fabien@symfony.com> |
||||
|
* |
||||
|
* For the full copyright and license information, please view the LICENSE |
||||
|
* file that was distributed with this source code. |
||||
|
*/ |
||||
|
|
||||
|
namespace Symfony\Component\VarExporter\Internal; |
||||
|
|
||||
|
/** |
||||
|
* @author Nicolas Grekas <p@tchwork.com> |
||||
|
* |
||||
|
* @internal |
||||
|
*/ |
||||
|
class Values |
||||
|
{ |
||||
|
public $values; |
||||
|
|
||||
|
public function __construct(array $values) |
||||
|
{ |
||||
|
$this->values = $values; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
Copyright (c) 2018-2019 Fabien Potencier |
||||
|
|
||||
|
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. |
||||
@ -0,0 +1,38 @@ |
|||||
|
VarExporter Component |
||||
|
===================== |
||||
|
|
||||
|
The VarExporter component allows exporting any serializable PHP data structure to |
||||
|
plain PHP code. While doing so, it preserves all the semantics associated with |
||||
|
the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`, |
||||
|
`__serialize`, `__unserialize`). |
||||
|
|
||||
|
It also provides an instantiator that allows creating and populating objects |
||||
|
without calling their constructor nor any other methods. |
||||
|
|
||||
|
The reason to use this component *vs* `serialize()` or |
||||
|
[igbinary](https://github.com/igbinary/igbinary) is performance: thanks to |
||||
|
OPcache, the resulting code is significantly faster and more memory efficient |
||||
|
than using `unserialize()` or `igbinary_unserialize()`. |
||||
|
|
||||
|
Unlike `var_export()`, this works on any serializable PHP value. |
||||
|
|
||||
|
It also provides a few improvements over `var_export()`/`serialize()`: |
||||
|
|
||||
|
* the output is PSR-2 compatible; |
||||
|
* the output can be re-indented without messing up with `\r` or `\n` in the data |
||||
|
* missing classes throw a `ClassNotFoundException` instead of being unserialized to |
||||
|
`PHP_Incomplete_Class` objects; |
||||
|
* references involving `SplObjectStorage`, `ArrayObject` or `ArrayIterator` |
||||
|
instances are preserved; |
||||
|
* `Reflection*`, `IteratorIterator` and `RecursiveIteratorIterator` classes |
||||
|
throw an exception when being serialized (their unserialized version is broken |
||||
|
anyway, see https://bugs.php.net/76737). |
||||
|
|
||||
|
Resources |
||||
|
--------- |
||||
|
|
||||
|
* [Documentation](https://symfony.com/doc/current/components/var_exporter.html) |
||||
|
* [Contributing](https://symfony.com/doc/current/contributing/index.html) |
||||
|
* [Report issues](https://github.com/symfony/symfony/issues) and |
||||
|
[send Pull Requests](https://github.com/symfony/symfony/pulls) |
||||
|
in the [main Symfony repository](https://github.com/symfony/symfony) |
||||
@ -0,0 +1,20 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['Symfony\\Component\\VarExporter\\Tests\\ConcreteClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Symfony\\Component\\VarExporter\\Tests\\ConcreteClass')), |
||||
|
], |
||||
|
null, |
||||
|
[ |
||||
|
'Symfony\\Component\\VarExporter\\Tests\\AbstractClass' => [ |
||||
|
'foo' => [ |
||||
|
123, |
||||
|
], |
||||
|
'bar' => [ |
||||
|
234, |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
$o[0], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,22 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['ArrayIterator'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('ArrayIterator')), |
||||
|
], |
||||
|
null, |
||||
|
[ |
||||
|
'ArrayIterator' => [ |
||||
|
"\0" => [ |
||||
|
[ |
||||
|
[ |
||||
|
123, |
||||
|
], |
||||
|
1, |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
$o[0], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,19 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['ArrayIterator'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('ArrayIterator')), |
||||
|
], |
||||
|
null, |
||||
|
[], |
||||
|
$o[0], |
||||
|
[ |
||||
|
[ |
||||
|
1, |
||||
|
[ |
||||
|
123, |
||||
|
], |
||||
|
[], |
||||
|
], |
||||
|
] |
||||
|
); |
||||
@ -0,0 +1,22 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['Symfony\\Component\\VarExporter\\Tests\\MyArrayObject'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Symfony\\Component\\VarExporter\\Tests\\MyArrayObject')), |
||||
|
], |
||||
|
null, |
||||
|
[ |
||||
|
'ArrayObject' => [ |
||||
|
"\0" => [ |
||||
|
[ |
||||
|
[ |
||||
|
234, |
||||
|
], |
||||
|
1, |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
$o[0], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,21 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['Symfony\\Component\\VarExporter\\Tests\\MyArrayObject'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Symfony\\Component\\VarExporter\\Tests\\MyArrayObject')), |
||||
|
], |
||||
|
null, |
||||
|
[], |
||||
|
$o[0], |
||||
|
[ |
||||
|
[ |
||||
|
1, |
||||
|
[ |
||||
|
234, |
||||
|
], |
||||
|
[ |
||||
|
"\0".'Symfony\\Component\\VarExporter\\Tests\\MyArrayObject'."\0".'unused' => 123, |
||||
|
], |
||||
|
], |
||||
|
] |
||||
|
); |
||||
@ -0,0 +1,29 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['ArrayObject'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('ArrayObject')), |
||||
|
clone $p['ArrayObject'], |
||||
|
], |
||||
|
null, |
||||
|
[ |
||||
|
'ArrayObject' => [ |
||||
|
"\0" => [ |
||||
|
[ |
||||
|
[ |
||||
|
1, |
||||
|
$o[0], |
||||
|
], |
||||
|
0, |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
'stdClass' => [ |
||||
|
'foo' => [ |
||||
|
$o[1], |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
$o[0], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,28 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['ArrayObject'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('ArrayObject')), |
||||
|
clone $p['ArrayObject'], |
||||
|
], |
||||
|
null, |
||||
|
[], |
||||
|
$o[0], |
||||
|
[ |
||||
|
[ |
||||
|
0, |
||||
|
[ |
||||
|
1, |
||||
|
$o[0], |
||||
|
], |
||||
|
[ |
||||
|
'foo' => $o[1], |
||||
|
], |
||||
|
], |
||||
|
-1 => [ |
||||
|
0, |
||||
|
[], |
||||
|
[], |
||||
|
], |
||||
|
] |
||||
|
); |
||||
@ -0,0 +1,3 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return true; |
||||
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
(($f = &\Symfony\Component\VarExporter\Internal\Registry::$factories)['Symfony\\Component\\VarExporter\\Tests\\MyCloneable'] ?? \Symfony\Component\VarExporter\Internal\Registry::f('Symfony\\Component\\VarExporter\\Tests\\MyCloneable'))(), |
||||
|
($f['Symfony\\Component\\VarExporter\\Tests\\MyNotCloneable'] ?? \Symfony\Component\VarExporter\Internal\Registry::f('Symfony\\Component\\VarExporter\\Tests\\MyNotCloneable'))(), |
||||
|
], |
||||
|
null, |
||||
|
[], |
||||
|
[ |
||||
|
$o[0], |
||||
|
$o[1], |
||||
|
], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,25 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['DateTime'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('DateTime')), |
||||
|
], |
||||
|
null, |
||||
|
[ |
||||
|
'stdClass' => [ |
||||
|
'date' => [ |
||||
|
'1970-01-01 00:00:00.000000', |
||||
|
], |
||||
|
'timezone_type' => [ |
||||
|
1, |
||||
|
], |
||||
|
'timezone' => [ |
||||
|
'+00:00', |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
$o[0], |
||||
|
[ |
||||
|
1 => 0, |
||||
|
] |
||||
|
); |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
(\Symfony\Component\VarExporter\Internal\Registry::$factories['Error'] ?? \Symfony\Component\VarExporter\Internal\Registry::f('Error'))(), |
||||
|
], |
||||
|
null, |
||||
|
[ |
||||
|
'TypeError' => [ |
||||
|
'file' => [ |
||||
|
\dirname(__DIR__).\DIRECTORY_SEPARATOR.'VarExporterTest.php', |
||||
|
], |
||||
|
'line' => [ |
||||
|
234, |
||||
|
], |
||||
|
], |
||||
|
'Error' => [ |
||||
|
'trace' => [ |
||||
|
[ |
||||
|
'file' => \dirname(__DIR__).\DIRECTORY_SEPARATOR.'VarExporterTest.php', |
||||
|
'line' => 123, |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
$o[0], |
||||
|
[ |
||||
|
1 => 0, |
||||
|
] |
||||
|
); |
||||
@ -0,0 +1,7 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
[ |
||||
|
123, |
||||
|
], |
||||
|
]; |
||||
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = \Symfony\Component\VarExporter\Internal\Registry::unserialize([], [ |
||||
|
'C:54:"Symfony\\Component\\VarExporter\\Tests\\FinalArrayIterator":49:{a:2:{i:0;i:123;i:1;s:21:"x:i:0;a:0:{};m:a:0:{}";}}', |
||||
|
]), |
||||
|
null, |
||||
|
[], |
||||
|
$o[0], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,17 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['Symfony\\Component\\VarExporter\\Tests\\FinalArrayIterator'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Symfony\\Component\\VarExporter\\Tests\\FinalArrayIterator')), |
||||
|
], |
||||
|
null, |
||||
|
[], |
||||
|
$o[0], |
||||
|
[ |
||||
|
[ |
||||
|
0, |
||||
|
[], |
||||
|
[], |
||||
|
], |
||||
|
] |
||||
|
); |
||||
@ -0,0 +1,27 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = \Symfony\Component\VarExporter\Internal\Registry::unserialize([], [ |
||||
|
'O:46:"Symfony\\Component\\VarExporter\\Tests\\FinalError":1:{s:12:"'."\0".'Error'."\0".'trace";a:0:{}}', |
||||
|
]), |
||||
|
null, |
||||
|
[ |
||||
|
'TypeError' => [ |
||||
|
'file' => [ |
||||
|
\dirname(__DIR__).\DIRECTORY_SEPARATOR.'VarExporterTest.php', |
||||
|
], |
||||
|
'line' => [ |
||||
|
123, |
||||
|
], |
||||
|
], |
||||
|
'Error' => [ |
||||
|
'trace' => [ |
||||
|
[], |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
$o[0], |
||||
|
[ |
||||
|
1 => 0, |
||||
|
] |
||||
|
); |
||||
@ -0,0 +1,27 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
(\Symfony\Component\VarExporter\Internal\Registry::$factories['Symfony\\Component\\VarExporter\\Tests\\FinalError'] ?? \Symfony\Component\VarExporter\Internal\Registry::f('Symfony\\Component\\VarExporter\\Tests\\FinalError'))(), |
||||
|
], |
||||
|
null, |
||||
|
[ |
||||
|
'TypeError' => [ |
||||
|
'file' => [ |
||||
|
\dirname(__DIR__).\DIRECTORY_SEPARATOR.'VarExporterTest.php', |
||||
|
], |
||||
|
'line' => [ |
||||
|
123, |
||||
|
], |
||||
|
], |
||||
|
'Error' => [ |
||||
|
'trace' => [ |
||||
|
[], |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
$o[0], |
||||
|
[ |
||||
|
1 => 0, |
||||
|
] |
||||
|
); |
||||
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
(\Symfony\Component\VarExporter\Internal\Registry::$factories['Symfony\\Component\\VarExporter\\Tests\\FinalStdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::f('Symfony\\Component\\VarExporter\\Tests\\FinalStdClass'))(), |
||||
|
], |
||||
|
null, |
||||
|
[], |
||||
|
$o[0], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = \Symfony\Component\VarExporter\Internal\Registry::unserialize([], [ |
||||
|
'C:51:"Symfony\\Component\\VarExporter\\Tests\\FooSerializable":20:{a:1:{i:0;s:3:"bar";}}', |
||||
|
]), |
||||
|
null, |
||||
|
[], |
||||
|
$o[0], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,16 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [], |
||||
|
[ |
||||
|
$r = [], |
||||
|
$r[1] = [ |
||||
|
&$r[1], |
||||
|
], |
||||
|
], |
||||
|
[], |
||||
|
[ |
||||
|
&$r[1], |
||||
|
], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,18 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = [ |
||||
|
clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['stdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('stdClass')), |
||||
|
], |
||||
|
[ |
||||
|
$r = [], |
||||
|
$r[1] = $o[0], |
||||
|
], |
||||
|
[], |
||||
|
[ |
||||
|
&$r[1], |
||||
|
&$r[1], |
||||
|
$o[0], |
||||
|
], |
||||
|
[] |
||||
|
); |
||||
@ -0,0 +1,11 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( |
||||
|
$o = \Symfony\Component\VarExporter\Internal\Registry::unserialize([], [ |
||||
|
'O:20:"SomeNotExistingClass":0:{}', |
||||
|
]), |
||||
|
null, |
||||
|
[], |
||||
|
$o[0], |
||||
|
[] |
||||
|
); |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue